Skip to content

Commit

Permalink
Added the 'convert' subcommand
Browse files Browse the repository at this point in the history
  • Loading branch information
twest-bf committed Aug 26, 2024
1 parent 69b316e commit 291edfe
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 5 deletions.
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@

sj is a command line tool designed to assist with auditing of exposed Swagger/OpenAPI definition files by checking the associated API endpoints for weak authentication. It also provides command templates for manual vulnerability testing.

It does this by parsing the definition file for paths, parameters, and accepted methods before using the results with one of four commands:
It does this by parsing the definition file for paths, parameters, and accepted methods before using the results with one of five sub-commands:
- `automate` - Crafts a series of requests and analyzes the status code of the response.
- `prepare` - Generates a list of commands to use for manual testing.
- `endpoints` - Generates a list of raw API routes. *Path values will not be replaced with test data*.
- `brute` - Sends a series of requests to a target to find operation definitions based on commonly used file paths.
- `convert` - Converts a definition file from v2 to v3.

## Build

Expand Down Expand Up @@ -132,6 +133,16 @@ INFO[0015] Definition file found: https://petstore.swagger.io/v2/swagger
{"...SNIP..."}
```
> Use the `convert` command to convert a definition file from version 2 to version 3.
```bash
$ sj convert -u https://petstore.swagger.io/v2/swagger.json -o openapi.json

INFO[0000] Gathering API details.

INFO[0000] Wrote file to /current/directory/openapi.json
```
## Help
A full list of commands can be found by using the `--help` flag:
Expand All @@ -157,13 +168,17 @@ $ sj endpoints -u https://petstore.swagger.io/v2/swagger.json
Perform a brute-force attack against the target to identify hidden definition files:
$ sj brute -u https://petstore.swagger.io

Convert a Swagger (v2) definition file to an OpenAPI (v3) definition file:
$ sj convert -u https://petstore.swagger.io/v2/swagger.json -o openapi.json

Usage:
sj [flags]
sj [command]

Available Commands:
automate Sends a series of automated requests to the discovered endpoints.
brute Sends a series of automated requests to discover hidden API operation definitions.
convert Converts a Swagger definition file to an OpenAPI v3 definition file.
endpoints Prints a list of endpoints from the target.
help Help about any command
prepare Prepares a set of commands for manual testing of each endpoint.
Expand Down
120 changes: 120 additions & 0 deletions cmd/convert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package cmd

import (
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"strings"

"github.com/getkin/kin-openapi/openapi2"
"github.com/getkin/kin-openapi/openapi2conv"
"github.com/getkin/kin-openapi/openapi3"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"gopkg.in/yaml.v3"
)

var convertCmd = &cobra.Command{
Use: "convert",
Short: "Converts a Swagger definition file to an OpenAPI v3 definition file.",
Long: `The convert command converts a provided definition file from the Swagger specification (v2) to the OpenAPI specification (v3) and stores it into an output file.`,
Run: func(cmd *cobra.Command, args []string) {

var bodyBytes []byte

client := CheckAndConfigureProxy()

if strings.ToLower(outputFormat) != "json" {
fmt.Printf("\n")
log.Infof("Gathering API details.\n\n")
}

if swaggerURL != "" {
bodyBytes, _, _ = MakeRequest(client, "GET", swaggerURL, timeout, nil)
} else {
specFile, err := os.Open(localFile)
if err != nil {
log.Fatal("Error opening definition file:", err)
}

bodyBytes, _ = io.ReadAll(specFile)
}

var doc openapi2.T
var doc3 openapi3.T

format = strings.ToLower(format)
if format == "yaml" || format == "yml" || strings.HasSuffix(swaggerURL, ".yaml") || strings.HasSuffix(swaggerURL, ".yml") {
_ = yaml.Unmarshal(bodyBytes, &doc)
_ = yaml.Unmarshal(bodyBytes, &doc3)
} else {
_ = json.Unmarshal(bodyBytes, &doc)
_ = json.Unmarshal(bodyBytes, &doc3)
}

if strings.HasPrefix(doc3.OpenAPI, "3") {
log.Fatal("Definition file is already version 3.")
} else if strings.HasPrefix(doc.Swagger, "2") {
newDoc, err := openapi2conv.ToV3(&doc)
if err != nil {
fmt.Printf("Error converting v2 document to v3: %s\n", err)
}

if format == "json" {
if strings.HasSuffix(outfile, "yaml") || strings.HasSuffix(outfile, "yml") {
log.Warn("It looks like you're trying to save the file in YAML format. Supply the '-f yaml' option to do so (default: json).")
}
converted, err := json.Marshal(newDoc)
if err != nil {
log.Fatal("Error converting definition file to v3:", err)
}
if !strings.HasSuffix(string(converted), "}") {
if outfile == "" {
fmt.Println(string(converted))
} else {
WriteConvertedDefinitionFile(converted)
}
} else {
endOfJSON := strings.LastIndex(string(converted), "}") + 1
if outfile == "" {
fmt.Println(string(converted)[:endOfJSON])
} else {
WriteConvertedDefinitionFile(converted)
}
}
} else if format == "yaml" || format == "yml" {
converted, err := yaml.Marshal(newDoc)
if err != nil {
log.Fatal("Error converting definition file to v3:", err)
}
if outfile == "" {
fmt.Println(string(converted))
} else {
WriteConvertedDefinitionFile(converted)
}
}

} else {
log.Fatal("Error parsing definition file.")
}
},
}

func WriteConvertedDefinitionFile(data []byte) {
file, err := os.OpenFile(outfile, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Errorf("Error opening file: %s\n", err)
}

defer file.Close()

_, err = file.Write(data)
if err != nil {
log.Errorf("Error writing file: %s\n", err)
} else {
f, _ := filepath.Abs(outfile)
log.Infof("Wrote file to %s\n", f)
}
}
9 changes: 6 additions & 3 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"github.com/spf13/cobra"
)

var accessibleEndpointFound bool
var apiTarget string
var basePath string
var format string
Expand Down Expand Up @@ -39,14 +38,17 @@ Generate a list of raw API routes for use with custom scripts:
$ sj endpoints -u https://petstore.swagger.io/v2/swagger.json
Perform a brute-force attack against the target to identify hidden definition files:
$ sj brute -u https://petstore.swagger.io`,
$ sj brute -u https://petstore.swagger.io
Convert a Swagger (v2) definition file to an OpenAPI (v3) definition file:
$ sj convert -u https://petstore.swagger.io/v2/swagger.json -o openapi.json`,

Run: func(cmd *cobra.Command, args []string) {
if len(args) < 1 {
log.Error("Command not specified. See the --help flag for usage.")
}
},
Version: "1.7.4",
Version: "1.8.0",
}

func Execute() {
Expand All @@ -58,6 +60,7 @@ func init() {
rootCmd.AddCommand(endpointsCmd)
rootCmd.AddCommand(prepareCmd)
rootCmd.AddCommand(bruteCmd)
rootCmd.AddCommand(convertCmd)
rootCmd.PersistentFlags().StringVarP(&UserAgent, "agent", "a", "Swagger Jacker (github.com/BishopFox/sj)", "Set the User-Agent string.")
rootCmd.PersistentFlags().StringVarP(&basePath, "base-path", "b", "", "Set the API base path if not defined in the definition file (i.e. /V2/).")
rootCmd.PersistentFlags().StringVarP(&format, "format", "f", "json", "Declare the format of the definition file (json/yaml/yml/js).")
Expand Down
1 change: 0 additions & 1 deletion cmd/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,6 @@ func (s SwaggerRequest) BuildDefinedRequests(client http.Client, method string,
if os.Args[1] == "automate" {
_, resp, sc := MakeRequest(client, method, s.URL.String(), timeout, bytes.NewReader(s.BodyData))
if sc == 200 {
accessibleEndpointFound = true
accessibleEndpoints = append(accessibleEndpoints, s.URL.String())
}
if strings.ToLower(outputFormat) != "console" {
Expand Down

0 comments on commit 291edfe

Please sign in to comment.