mirror of
https://github.com/BishopFox/sj.git
synced 2026-11-09 01:49:38 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bdea5822e8 | ||
|
|
7bb228d81c | ||
|
|
3258ca341f | ||
|
|
58d4e91760 | ||
|
|
1f5a946011 | ||
|
|
143fdfda1b | ||
|
|
a33eef47f4 |
No files matched your search
+18
-13
@@ -45,20 +45,25 @@ func CheckSecuritySchemes(spec map[string]interface{}) {
|
||||
if schemeType := scheme["scheme"]; schemeType != nil {
|
||||
switch schemeType {
|
||||
case "basic":
|
||||
fmt.Println("Basic Authentication is accepted. Supply a username and password? (y/N)")
|
||||
fmt.Scanln(&autoApplyBasicAuth)
|
||||
autoApplyBasicAuth = strings.ToLower(autoApplyBasicAuth)
|
||||
if autoApplyBasicAuth == "y" {
|
||||
fmt.Printf("Enter a username.")
|
||||
fmt.Scanln(&basicAuthUser)
|
||||
fmt.Printf("Enter a password.")
|
||||
fmt.Scanln(&basicAuthPass)
|
||||
basicAuth = []byte(basicAuthUser + ":" + basicAuthPass)
|
||||
basicAuthString = base64.StdEncoding.EncodeToString(basicAuth)
|
||||
log.Infof("Using %s as the Basic Auth value.", basicAuthString)
|
||||
Headers = append(Headers, "Authorization: Basic "+basicAuthString)
|
||||
} else {
|
||||
if quiet {
|
||||
autoApplyBasicAuth = "n"
|
||||
log.Warn("A basic authentication header is accepted. Review the spec and craft a header manually using the -H flag.")
|
||||
} else {
|
||||
fmt.Println("Basic Authentication is accepted. Supply a username and password? (y/N)")
|
||||
fmt.Scanln(&autoApplyBasicAuth)
|
||||
autoApplyBasicAuth = strings.ToLower(autoApplyBasicAuth)
|
||||
if autoApplyBasicAuth == "y" {
|
||||
fmt.Printf("Enter a username.")
|
||||
fmt.Scanln(&basicAuthUser)
|
||||
fmt.Printf("Enter a password.")
|
||||
fmt.Scanln(&basicAuthPass)
|
||||
basicAuth = []byte(basicAuthUser + ":" + basicAuthPass)
|
||||
basicAuthString = base64.StdEncoding.EncodeToString(basicAuth)
|
||||
log.Infof("Using %s as the Basic Auth value.", basicAuthString)
|
||||
Headers = append(Headers, "Authorization: Basic "+basicAuthString)
|
||||
} else {
|
||||
log.Warn("A basic authentication header is accepted. Review the spec and craft a header manually using the -H flag.")
|
||||
}
|
||||
}
|
||||
case "bearer":
|
||||
log.Warn("A bearer token is accepted. Review the spec and craft a token manually using the -H flag.")
|
||||
|
||||
@@ -166,6 +166,9 @@ func findDefinitionFile(urls []string, client http.Client) (bool, *openapi3.T) {
|
||||
regexPattern := regexp.MustCompile(`(?s)let\s+(\w+)\s*=\s*({.*?});`)
|
||||
matches := regexPattern.FindAllStringSubmatch(bodyString, -1)
|
||||
for _, match := range matches {
|
||||
if len(match) < 3 {
|
||||
continue
|
||||
}
|
||||
jsonContent := match[2]
|
||||
checkSpec := UnmarshalSpec([]byte(jsonContent))
|
||||
if strings.HasPrefix(checkSpec.OpenAPI, "2") || strings.HasPrefix(checkSpec.OpenAPI, "3") {
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ This list contains the raw endpoints (parameter values will not be appended or m
|
||||
log.Infof("Gathering endpoints.\n\n")
|
||||
|
||||
if swaggerURL != "" {
|
||||
bodyBytes, _, _ := MakeRequest(client, "GET", swaggerURL, timeout, nil)
|
||||
bodyBytes, _, _ = MakeRequest(client, "GET", swaggerURL, timeout, nil)
|
||||
GenerateRequests(bodyBytes, client)
|
||||
} else {
|
||||
specFile, err := os.Open(localFile)
|
||||
|
||||
+44
-41
@@ -34,12 +34,14 @@ func writeLog(sc int, target, method, errorMsg, response string) {
|
||||
}
|
||||
|
||||
if outfile != "" {
|
||||
file, err := os.OpenFile(outfile, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0644)
|
||||
var err error
|
||||
file, err = os.OpenFile(outfile, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
fmt.Println("Output file does not exist or cannot be created")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
tempLogger.SetOutput(file)
|
||||
}
|
||||
|
||||
@@ -54,52 +56,53 @@ func writeLog(sc int, target, method, errorMsg, response string) {
|
||||
}
|
||||
|
||||
if verbose {
|
||||
if sc != 200 {
|
||||
if sc == 401 || sc == 403 {
|
||||
logVerboseUnauth(sc, target, method, errorMsg, response, tempLogger)
|
||||
} else if sc == 301 || sc == 302 {
|
||||
logVerboseRedirect(sc, target, method, response, tempLogger)
|
||||
} else if sc == 0 {
|
||||
logVerboseBad(sc, target, method, response, tempLogger)
|
||||
} else if sc == 404 {
|
||||
logNotFound(sc, target, method, errorMsg, tempLogger)
|
||||
} else if sc == 1 {
|
||||
logDangerous(target, method, tempLogger)
|
||||
} else if sc == 8899 {
|
||||
logVerboseJSON(specTitle, specDescription)
|
||||
} else {
|
||||
logVerboseManual(sc, target, method, errorMsg, response, tempLogger)
|
||||
}
|
||||
} else {
|
||||
switch sc {
|
||||
case 0:
|
||||
logVerboseBad(sc, target, method, response, tempLogger)
|
||||
case 1:
|
||||
logDangerous(target, method, tempLogger)
|
||||
case 200:
|
||||
logVerboseAccessible(sc, target, method, response, tempLogger)
|
||||
case 301:
|
||||
logVerboseRedirect(sc, target, method, response, tempLogger)
|
||||
case 302:
|
||||
logVerboseRedirect(sc, target, method, response, tempLogger)
|
||||
case 401:
|
||||
logVerboseUnauth(sc, target, method, errorMsg, response, tempLogger)
|
||||
case 403:
|
||||
logVerboseUnauth(sc, target, method, errorMsg, response, tempLogger)
|
||||
case 404:
|
||||
logNotFound(sc, target, method, errorMsg, tempLogger)
|
||||
case 8899:
|
||||
logVerboseJSON(specTitle, specDescription)
|
||||
default:
|
||||
logVerboseManual(sc, target, method, errorMsg, response, tempLogger)
|
||||
}
|
||||
} else {
|
||||
if sc != 200 {
|
||||
if sc == 401 || sc == 403 {
|
||||
logUnauth(sc, target, method, errorMsg, tempLogger)
|
||||
} else if sc == 301 || sc == 302 {
|
||||
logRedirect(sc, target, method, tempLogger)
|
||||
} else if sc == 0 {
|
||||
logBad(sc, target, method, tempLogger)
|
||||
} else if sc == 404 {
|
||||
logNotFound(sc, target, method, errorMsg, tempLogger)
|
||||
} else if sc == 1 {
|
||||
logDangerous(target, method, tempLogger)
|
||||
} else if sc == 8899 {
|
||||
logJSON(specTitle, specDescription)
|
||||
} else {
|
||||
logManual(sc, target, method, errorMsg, tempLogger)
|
||||
}
|
||||
} else {
|
||||
if sc == 8899 {
|
||||
logJSON(specTitle, specDescription)
|
||||
} else {
|
||||
logAccessible(sc, target, method, tempLogger)
|
||||
}
|
||||
switch sc {
|
||||
case 0:
|
||||
logBad(sc, target, method, tempLogger)
|
||||
case 1:
|
||||
logDangerous(target, method, tempLogger)
|
||||
case 200:
|
||||
logAccessible(sc, target, method, tempLogger)
|
||||
case 301:
|
||||
logRedirect(sc, target, method, tempLogger)
|
||||
case 302:
|
||||
logRedirect(sc, target, method, tempLogger)
|
||||
case 401:
|
||||
logUnauth(sc, target, method, errorMsg, tempLogger)
|
||||
case 403:
|
||||
logUnauth(sc, target, method, errorMsg, tempLogger)
|
||||
case 404:
|
||||
logNotFound(sc, target, method, errorMsg, tempLogger)
|
||||
case 8899:
|
||||
logJSON(specTitle, specDescription)
|
||||
default:
|
||||
logManual(sc, target, method, errorMsg, tempLogger)
|
||||
}
|
||||
}
|
||||
responsePreviewLength = tempResponsePreviewLength
|
||||
file.Close()
|
||||
}
|
||||
|
||||
func logAccessible(status int, target, method string, logger *log.Logger) {
|
||||
|
||||
+24
-7
@@ -20,6 +20,7 @@ var (
|
||||
avoidDangerousRequests string
|
||||
contentType string
|
||||
dangerousStrings []string = []string{"add", "block", "build", "buy", "change", "clear", "create", "delete", "deploy", "destroy", "drop", "edit", "emergency", "erase", "execute", "insert", "modify", "order", "overwrite", "pause", "purchase", "rebuild", "remove", "replace", "reset", "restart", "revoke", "run", "sell", "send", "set", "start", "stop", "update", "upload", "write"}
|
||||
depth int = 0
|
||||
Headers []string
|
||||
requestStatus int
|
||||
riskSurveyed bool = false
|
||||
@@ -59,7 +60,11 @@ func MakeRequest(client http.Client, method, target string, timeout int64, reqDa
|
||||
}
|
||||
|
||||
// Handling of dangerous keywords
|
||||
u, _ := url.Parse(target)
|
||||
u, err := url.Parse(target)
|
||||
if err != nil || u == nil {
|
||||
log.Printf("Error parsing URL '%s': %v - skipping request.", target, err)
|
||||
return nil, "", 0
|
||||
}
|
||||
endpoint := u.RawPath + "?" + u.RawQuery
|
||||
for _, v := range dangerousStrings {
|
||||
if os.Args[1] == "automate" && strings.Contains(endpoint, v) && !strings.Contains(strings.Join(safeWords, ","), v) {
|
||||
@@ -87,8 +92,11 @@ func MakeRequest(client http.Client, method, target string, timeout int64, reqDa
|
||||
defer cancel()
|
||||
|
||||
req, err := http.NewRequest(method, target, reqData)
|
||||
if err != nil && err != context.Canceled && err != io.EOF {
|
||||
log.Fatal("Error: could not create HTTP request - ", err)
|
||||
if err != nil {
|
||||
if err != context.Canceled && err != io.EOF {
|
||||
log.Fatal("Error: could not create HTTP request - ", err)
|
||||
}
|
||||
return nil, "", 0
|
||||
}
|
||||
|
||||
for i := range Headers {
|
||||
@@ -156,26 +164,35 @@ func MakeRequest(client http.Client, method, target string, timeout int64, reqDa
|
||||
bodyBytes, _ := io.ReadAll(resp.Body)
|
||||
bodyString := string(bodyBytes)
|
||||
|
||||
if (resp.StatusCode == 301 || resp.StatusCode == 302) && strings.Contains(bodyString, "<html>") {
|
||||
if (resp.StatusCode == 301 || resp.StatusCode == 302) && strings.Contains(bodyString, "<html>") && depth < 10 {
|
||||
depth += 1
|
||||
redirect, _ := resp.Location()
|
||||
bodyBytes, bodyString, requestStatus = MakeRequest(client, method, redirect.Scheme+"://"+redirect.Host+redirect.Path, timeout, reqData)
|
||||
return bodyBytes, bodyString, requestStatus
|
||||
}
|
||||
|
||||
requestStatus = resp.StatusCode
|
||||
depth = 0
|
||||
|
||||
return bodyBytes, bodyString, requestStatus
|
||||
}
|
||||
|
||||
func CheckContentType(client http.Client, target string) string {
|
||||
u, _ := url.Parse(target)
|
||||
u, err := url.Parse(target)
|
||||
if err != nil || u == nil {
|
||||
log.Printf("Error parsing URL '%s': %v - skipping request.", target, err)
|
||||
return ""
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
|
||||
defer cancel()
|
||||
|
||||
req, err := http.NewRequest("GET", target, nil)
|
||||
if err != nil && err != context.Canceled && err != io.EOF {
|
||||
log.Fatal("Error: could not create HTTP request - ", err)
|
||||
if err != nil {
|
||||
if err != context.Canceled && err != io.EOF {
|
||||
log.Fatal("Error: could not create HTTP request - ", err)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// User-Agent handling
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ $ sj convert -u https://petstore.swagger.io/v2/swagger.json -o openapi.json`,
|
||||
log.Error("Command not specified. See the --help flag for usage.")
|
||||
}
|
||||
},
|
||||
Version: "2.3.0",
|
||||
Version: "2.3.2",
|
||||
}
|
||||
|
||||
func Execute() {
|
||||
|
||||
+9
-2
@@ -198,7 +198,11 @@ func BuildRequestsFromPaths(spec map[string]interface{}, client http.Client) {
|
||||
}
|
||||
}
|
||||
|
||||
logURL, _ := url.Parse(targetURL)
|
||||
logURL, parseErr := url.Parse(targetURL)
|
||||
if parseErr != nil || logURL == nil {
|
||||
log.Printf("Error parsing URL '%s': %v - skipping endpoint.", targetURL, parseErr)
|
||||
continue
|
||||
}
|
||||
switch os.Args[1] {
|
||||
case "automate":
|
||||
var postBodyData string
|
||||
@@ -403,7 +407,10 @@ func GenerateRequests(bodyBytes []byte, client http.Client) {
|
||||
// Checks defined security schemes and prompts for authentication
|
||||
CheckSecuritySchemes(spec)
|
||||
|
||||
u, _ := url.Parse(swaggerURL)
|
||||
u, parseErr := url.Parse(swaggerURL)
|
||||
if parseErr != nil {
|
||||
u = &url.URL{}
|
||||
}
|
||||
|
||||
// Gets the target server and base path from the specification file
|
||||
if apiTarget == "" {
|
||||
|
||||
Reference in new issue
Block a user