Compare commits

...
4 Commits
Author SHA1 Message Date
Tony West 4f256efca6 Incremented version string 2026-09-11 21:44:23 -04:00
Tony West 48c4d83f54 fix: Moved remaining diagnostic output to stderr so stdout stays pipeable 2026-09-11 21:44:15 -04:00
Tony West 365f68696f Merge pull request #59 from emilloc/fix/auth-stderr
fix: send authentication diagnostics to stderr
2026-09-11 21:36:58 -04:00
rootkid fbf484c3a1 fix: send authentication diagnostics to stderr 2026-09-10 18:56:14 -06:00
9 changed files with 82 additions and 35 deletions

No files matched your search

+10 -12
View File
@@ -22,16 +22,14 @@ func CheckSecuritySchemes(spec map[string]interface{}) {
if ok && components != nil {
securitySchemes, ok := components["securitySchemes"].(map[string]interface{})
if !ok || len(securitySchemes) == 0 {
fmt.Println("No security schemes defined.")
printInfo("No security schemes defined.\n")
} else {
if outputFormat != "json" {
fmt.Println("Found security schemes:")
}
printInfo("Found security schemes:\n")
var apiKey string
var apiKeyName string
for mechanism, value := range securitySchemes {
fmt.Printf(" - %s\n", mechanism)
printInfo(" - %s\n", mechanism)
scheme, ok := value.(map[string]interface{})
if !ok {
continue
@@ -47,13 +45,13 @@ func CheckSecuritySchemes(spec map[string]interface{}) {
autoApplyBasicAuth = "n"
printWarn("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)")
printInfo("Basic Authentication is accepted. Supply a username and password? (y/N)\n")
fmt.Scanln(&autoApplyBasicAuth)
autoApplyBasicAuth = strings.ToLower(autoApplyBasicAuth)
if autoApplyBasicAuth == "y" {
fmt.Printf("Enter a username.")
printInfo("Enter a username.")
fmt.Scanln(&basicAuthUser)
fmt.Printf("Enter a password.")
printInfo("Enter a password.")
fmt.Scanln(&basicAuthPass)
basicAuth = []byte(basicAuthUser + ":" + basicAuthPass)
basicAuthString = base64.StdEncoding.EncodeToString(basicAuth)
@@ -83,7 +81,7 @@ func CheckSecuritySchemes(spec map[string]interface{}) {
if nameVal, ok := scheme["name"].(string); ok {
apiKeyName = nameVal
}
fmt.Printf("What value would you like to use for the API key (%s)?", apiKeyName)
printInfo("What value would you like to use for the API key (%s)?", apiKeyName)
fmt.Scanln(&apiKey)
printInfo("Using %s=%s as the API key in all requests.\n", apiKeyName, apiKey)
}
@@ -97,7 +95,7 @@ func CheckSecuritySchemes(spec map[string]interface{}) {
autoApplyBearer = strings.ToLower(autoApplyBearer)
}
if autoApplyBearer == "y" {
fmt.Printf("What value would you like to use for the Bearer Token? ")
printInfo("What value would you like to use for the Bearer Token? ")
fmt.Scanln(&bearerToken)
Headers = append(Headers, "Authorization: Bearer "+bearerToken)
} else {
@@ -114,7 +112,7 @@ func CheckSecuritySchemes(spec map[string]interface{}) {
}
if autoApplyAPIKey == "y" {
apiKeyName = nameVal
fmt.Printf("What value would you like to use for the API key (%s)?", apiKeyName)
printInfo("What value would you like to use for the API key (%s)?", apiKeyName)
fmt.Scanln(&apiKey)
Headers = append(Headers, nameVal+": "+apiKey)
}
@@ -126,7 +124,7 @@ func CheckSecuritySchemes(spec map[string]interface{}) {
}
if bearerFormat, ok := scheme["bearerFormat"].(string); ok {
fmt.Println(" - bearerFormat:", bearerFormat)
printInfo(" - bearerFormat: %s\n", bearerFormat)
}
}
}
+59
View File
@@ -0,0 +1,59 @@
package cmd
import (
"os"
"strings"
"testing"
)
func TestCheckSecuritySchemesJSONDiagnostics(t *testing.T) {
stdout, err := os.CreateTemp(t.TempDir(), "stdout")
if err != nil {
t.Fatal(err)
}
stderr, err := os.CreateTemp(t.TempDir(), "stderr")
if err != nil {
t.Fatal(err)
}
previousStdout, previousStderr := os.Stdout, os.Stderr
previousFormat, previousQuiet := outputFormat, quiet
defer func() {
os.Stdout, os.Stderr = previousStdout, previousStderr
outputFormat, quiet = previousFormat, previousQuiet
stdout.Close()
stderr.Close()
}()
os.Stdout, os.Stderr = stdout, stderr
outputFormat, quiet = "json", true
CheckSecuritySchemes(map[string]interface{}{
"components": map[string]interface{}{
"securitySchemes": map[string]interface{}{
"ExampleBearer": map[string]interface{}{
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT",
},
},
},
})
stdoutBytes, err := os.ReadFile(stdout.Name())
if err != nil {
t.Fatal(err)
}
if len(stdoutBytes) != 0 {
t.Fatalf("authentication diagnostics written to stdout: %q", stdoutBytes)
}
stderrBytes, err := os.ReadFile(stderr.Name())
if err != nil {
t.Fatal(err)
}
for _, expected := range []string{"ExampleBearer", "bearerFormat: JWT"} {
if !strings.Contains(string(stderrBytes), expected) {
t.Errorf("stderr does not contain %q: %q", expected, stderrBytes)
}
}
}
+2 -4
View File
@@ -1,7 +1,6 @@
package cmd
import (
"fmt"
"io"
"os"
"path/filepath"
@@ -46,8 +45,7 @@ responds in an abnormal way, manual testing should be conducted (prepare manual
_, err := time.Parse("2006-01-02", customDate)
if err != nil {
fmt.Println("An invalid date was supplied. Please supply a date in '2006-01-02' format.")
os.Exit(1)
die("An invalid date was supplied. Please supply a date in '2006-01-02' format.")
}
var bodyBytes []byte
@@ -55,7 +53,7 @@ responds in an abnormal way, manual testing should be conducted (prepare manual
client, replayClient := CheckAndConfigureProxy()
if strings.ToLower(outputFormat) != "json" {
fmt.Printf("\n")
printInfo("\n")
printInfo("Gathering API details.\n")
}
+1 -1
View File
@@ -378,7 +378,7 @@ func UnmarshalSpec(bodyBytes []byte) (newDoc *openapi3.T) {
} 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)
printErr("Error converting v2 document to v3: %s", err)
}
return newDoc
} else if os.Args[1] == "brute" {
+2 -2
View File
@@ -32,7 +32,7 @@ var convertCmd = &cobra.Command{
client, _ := CheckAndConfigureProxy()
if strings.ToLower(outputFormat) != "json" {
fmt.Printf("\n")
printInfo("\n")
printInfo("Gathering API details.\n\n")
}
@@ -65,7 +65,7 @@ var convertCmd = &cobra.Command{
} 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)
printErr("Error converting v2 document to v3: %s", err)
}
switch format {
+1 -2
View File
@@ -1,7 +1,6 @@
package cmd
import (
"fmt"
"io"
"os"
"path/filepath"
@@ -25,7 +24,7 @@ This list contains the raw endpoints (parameter values will not be appended or m
var bodyBytes []byte
fmt.Printf("\n")
printInfo("\n")
printInfo("Gathering endpoints.\n\n")
if swaggerURL != "" {
+2 -4
View File
@@ -1,7 +1,6 @@
package cmd
import (
"fmt"
"io"
"os"
"path/filepath"
@@ -25,13 +24,12 @@ This enables you to test specific API functions for common vulnerabilities or mi
_, err := time.Parse("2006-01-02", customDate)
if err != nil {
fmt.Println("An invalid date was supplied. Please supply a date in '2006-01-02' format.")
os.Exit(1)
die("An invalid date was supplied. Please supply a date in '2006-01-02' format.")
}
client, _ := CheckAndConfigureProxy()
fmt.Printf("\n")
printInfo("\n")
printInfo("Gathering API details.\n\n")
if swaggerURL != "" {
bodyBytes, _, _ := MakeRequest(client, "GET", swaggerURL, timeout, nil)
+1 -1
View File
@@ -53,7 +53,7 @@ $ sj convert -u https://petstore.swagger.io/v2/swagger.json -o openapi.json`,
printErr("Command not specified. See the --help flag for usage.")
}
},
Version: "2.8.1",
Version: "2.8.2",
}
func Execute() {
+4 -9
View File
@@ -639,11 +639,7 @@ func GenerateRequests(bodyBytes []byte, client http.Client, replayClient *http.C
for i := range servers {
if srv, ok := servers[i].(map[string]interface{}); ok {
if serverURL, ok := srv["url"].(string); ok {
if strings.Contains(serverURL, "://") {
fmt.Println(serverURL)
} else {
fmt.Println(apiTarget + serverURL)
}
printInfo("%s\n", serverURL)
}
}
}
@@ -827,7 +823,7 @@ func PrintSpecInfo(spec map[string]interface{}) {
if outputFormat == "json" {
specTitle = title
} else {
fmt.Printf("Title: %s\n", title)
printInfo("Title: %s\n", title)
}
}
@@ -836,7 +832,7 @@ func PrintSpecInfo(spec map[string]interface{}) {
if outputFormat == "json" {
specDescription = description
} else {
fmt.Printf("Description: %s\n", description)
printInfo("Description: %s\n", description)
}
}
}
@@ -881,8 +877,7 @@ func SafelyUnmarshalSpec(data []byte) map[string]interface{} {
var doc map[string]interface{}
if err := yaml.Unmarshal(data, &doc); err != nil {
fmt.Printf("Failed to unmarshal API documentation: %v\n", err)
os.Exit(1)
die("Failed to unmarshal API documentation: %v", err)
}
return doc