Files
sj/cmd/root.go
T
2026-10-29 21:26:40 -04:00

117 lines
6.1 KiB
Go

package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
var allContentTypes bool
var apiTarget string
var basePath string
var customDate string
var customEmail string
var customURL string
var force bool
var format string
var insecure bool
var localFile string
var Mode string
var outfile string
var proxy string
var replayProxy string
var quiet bool
var randomUserAgent bool
var rateLimit int
var rawValues bool
var safeWords []string
var swaggerURL string
var timeout int64
var rootCmd = &cobra.Command{
Use: "sj",
Short: "A tool for auditing documented (swagger/openapi) API endpoints.",
Long: `The process of reviewing and testing exposed API definition files is often tedious and requires a large investment of time for a thorough review.
sj (swaggerjacker) is a CLI tool that can be used to perform an initial check of API endpoints identified through exposed Swagger/OpenAPI definition files.
Once you determine what endpoints require authentication and which do not, you can use the "prepare" command to generate command templates for further (manual) testing.
Example usage:
Perform a quick check of endpoints which require authentication:
$ sj automate -u https://petstore.swagger.io/v2/swagger.json
Generate a list of commands to use for manual testing:
$ sj prepare -u https://petstore.swagger.io/v2/swagger.json
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
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`,
// PersistentPreRun captures the invoked subcommand name once, so the
// shared request-generation code can branch on the mode without reaching
// back into os.Args[1] (which panics or misbehaves when these functions
// are called programmatically rather than through the CLI).
PersistentPreRun: func(cmd *cobra.Command, args []string) {
Mode = cmd.Name()
},
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("command not specified. See the --help flag for usage")
}
return nil
},
Version: "2.8.4",
// Errors and usage are surfaced once by Execute() with our own styling, so
// cobra should not also print them on a RunE failure.
SilenceErrors: true,
SilenceUsage: true,
}
// Execute runs the root command. It is the single place in the package that
// terminates the process: command and library code return errors up to here
// instead of calling os.Exit, which keeps that logic testable and reusable.
func Execute() {
if err := rootCmd.Execute(); err != nil {
printErr("%v", err)
os.Exit(1)
}
}
func init() {
rootCmd.AddCommand(automateCmd)
rootCmd.AddCommand(endpointsCmd)
rootCmd.AddCommand(prepareCmd)
rootCmd.AddCommand(bruteCmd)
rootCmd.AddCommand(convertCmd)
rootCmd.PersistentFlags().BoolVar(&allContentTypes, "all-content-types", false, "Send a separate request for every request body content type an operation declares, instead of only the preferred one.")
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(&customURL, "custom-url", "c", "https://bishopfox.com", "Set a custom URL to test discovered URL parameters.")
rootCmd.PersistentFlags().StringVarP(&customDate, "custom-date", "d", "1990-01-01", "A custom date to test discovered date parameters.")
rootCmd.PersistentFlags().StringVar(&customEmail, "custom-email", "[email protected]", "A custom email address to test discovered email parameters.")
rootCmd.PersistentFlags().BoolVar(&force, "force", false, "Send requests without prompting, even if dangerous keywords are detected.")
rootCmd.PersistentFlags().StringVarP(&format, "format", "f", "json", "Declare the format of the definition file (json/yaml/yml/js).")
rootCmd.PersistentFlags().StringArrayVarP(&Headers, "headers", "H", nil, "Add custom headers, separated by a colon (\"Name: Value\"). Multiple flags are accepted.")
rootCmd.PersistentFlags().BoolVarP(&insecure, "insecure", "i", false, "Ignores server certificate validation.")
rootCmd.PersistentFlags().StringVarP(&localFile, "local-file", "l", "", "Loads the documentation from a local file.")
rootCmd.PersistentFlags().StringVarP(&outfile, "outfile", "o", "", "Output the results to a file. Only supported for the 'automate' and 'brute' commands at this time.")
rootCmd.PersistentFlags().StringVarP(&proxy, "proxy", "p", "NOPROXY", "Proxy host and port. Example: http://127.0.0.1:8080")
rootCmd.PersistentFlags().StringVar(&replayProxy, "replay-proxy", "", "Replay matched requests using this proxy.")
rootCmd.PersistentFlags().BoolVarP(&quiet, "quiet", "q", false, "Do not prompt for user input - uses default values for all requests.")
rootCmd.PersistentFlags().BoolVar(&randomUserAgent, "randomize-user-agent", false, "Randomizes the user agent string. Default is 'false'.")
rootCmd.PersistentFlags().BoolVar(&rawValues, "raw-values", false, "Do not URL-encode generated parameter names and values. Useful for parser-differential and WAF testing.")
// rootCmd.PersistentFlags().IntVarP(&rateLimit, "rate", "r", 15, "Limit the number of requests per second.") // NEED TO RE-IMPLEMENT RATE LIMIT
rootCmd.PersistentFlags().StringArrayVarP(&safeWords, "safe-word", "s", nil, "Avoids 'dangerous word' check for the specified word(s). Multiple flags are accepted.")
rootCmd.PersistentFlags().StringVarP(&apiTarget, "target", "T", "", "Manually set a target for the requests to be made if separate from the host the documentation resides on.")
rootCmd.PersistentFlags().Int64VarP(&timeout, "timeout", "t", 30, "Set the request timeout period.")
rootCmd.PersistentFlags().StringVarP(&swaggerURL, "url", "u", "", "Loads the documentation file from a URL")
rootCmd.CompletionOptions.DisableDefaultCmd = true
}