Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -356,10 +356,10 @@ func initConfig() {
SkipDiscovery: args.SkipDiscovery,
HarFilePath: args.HarFilePath,
// Issue #695 and #764 flags
DetailedAnalysis: args.DetailedAnalysis,
FastScan: args.FastScan,
MagicCharTest: args.MagicCharTest,
ContextAware: args.ContextAware,
DetailedAnalysis: args.DetailedAnalysis,
FastScan: args.FastScan,
MagicCharTest: args.MagicCharTest,
ContextAware: args.ContextAware,
Comment on lines +359 to +362
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The alignment of these fields is inconsistent with the alignment of the fields on lines 356-357. For consistency, consider aligning these fields as well.

Suggested change
DetailedAnalysis: args.DetailedAnalysis,
FastScan: args.FastScan,
MagicCharTest: args.MagicCharTest,
ContextAware: args.ContextAware,
DetailedAnalysis: args.DetailedAnalysis,
FastScan: args.FastScan,
MagicCharTest: args.MagicCharTest,
ContextAware: args.ContextAware,

}

// If configuration file was loaded, apply values from it for options not specified via CLI
Expand Down
8 changes: 4 additions & 4 deletions cmd/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import (
)

// Command-line flags for server configuration
var port int // Port to bind the server to
var host, serverType, apiKey string // Host address, server type, and API Key
var allowedOrigins []string // Allowed origins for CORS
var jsonp bool // Enable JSONP responses
var port int // Port to bind the server to
var host, serverType, apiKey string // Host address, server type, and API Key
var allowedOrigins []string // Allowed origins for CORS
var jsonp bool // Enable JSONP responses

// serverCmd represents the server command for starting API servers
var serverCmd = &cobra.Command{
Expand Down
2 changes: 0 additions & 2 deletions cmd/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import (
// This file contains utility functions for managing command-line help output
// and other shared functionality used across the different commands



// SubCommandCustomHelpFunc provides a custom help formatter for subcommands
// This function is shared across all subcommands to ensure consistent help display
// It leverages templates to create a more organized and user-friendly help output
Expand Down
158 changes: 79 additions & 79 deletions internal/payload/remote_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ func Test_getAssetHahwul(t *testing.T) {
apiContent: validAssetJSON,
dataContent: emptyPayloadData,
expectedPayloads: []string{}, // splitLines on "" might give {""} or {}, depends on impl.
// current splitLines gives empty slice for empty string.
expectedLine: "2",
expectedSize: "10 bytes",
// current splitLines gives empty slice for empty string.
expectedLine: "2",
expectedSize: "10 bytes",
},
{
name: "malformed JSON (unmarshal error)",
Expand All @@ -92,7 +92,7 @@ func Test_getAssetHahwul(t *testing.T) {
// originalHTTPGet := httpGet // No longer needed
// defer func() { httpGet = originalHTTPGet }()

originalBaseURL := assetHahwulBaseURL // Store original base URL
originalBaseURL := assetHahwulBaseURL // Store original base URL
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The alignment of this comment is inconsistent with the alignment of the comment on the next line. Consider using a single space for alignment.

Suggested change
originalBaseURL := assetHahwulBaseURL // Store original base URL
originalBaseURL := assetHahwulBaseURL // Store original base URL

defer func() { assetHahwulBaseURL = originalBaseURL }() // Restore it after all tests in this function

for _, tt := range tests {
Expand Down Expand Up @@ -186,7 +186,7 @@ func TestGetPortswiggerPayload(t *testing.T) {
} else if strings.HasSuffix(r.URL.Path, "xss-portswigger.txt") {
fmt.Fprintln(w, payloadData)
} else {
http.NotFound(w,r)
http.NotFound(w, r)
}
}))
assetHahwulBaseURL = mockServer.URL
Expand Down Expand Up @@ -221,7 +221,7 @@ func TestGetPayloadBoxPayload(t *testing.T) {
} else if strings.HasSuffix(r.URL.Path, "xss-payloadbox.txt") {
fmt.Fprintln(w, payloadData)
} else {
http.NotFound(w,r)
http.NotFound(w, r)
}
}))
assetHahwulBaseURL = mockServer.URL
Expand Down Expand Up @@ -256,7 +256,7 @@ func TestGetBurpWordlist(t *testing.T) {
} else if strings.HasSuffix(r.URL.Path, "wl-params.txt") {
fmt.Fprintln(w, payloadData)
} else {
http.NotFound(w,r)
http.NotFound(w, r)
}
}))
assetHahwulBaseURL = mockServer.URL
Expand Down Expand Up @@ -290,7 +290,7 @@ func TestGetAssetnoteWordlist(t *testing.T) {
} else if strings.HasSuffix(r.URL.Path, "wl-assetnote-params.txt") {
fmt.Fprintln(w, payloadData)
} else {
http.NotFound(w,r)
http.NotFound(w, r)
}
}))
assetHahwulBaseURL = mockServer.URL
Expand Down Expand Up @@ -469,103 +469,103 @@ type testAsset struct {
}

func TestGetAssetHahwul_JsonUnmarshalError(t *testing.T) {
// Simulate a scenario where JSON unmarshalling fails
mockAPIContent := `{"line": 123, "size": "not a string field for Line"}` // Invalid: line is number
mockDataContent := "payload1\npayload2"

server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.HasSuffix(r.URL.Path, ".json") { // Simplified to any .json for this test's purpose
w.Header().Set("Content-Type", "application/json")
fmt.Fprintln(w, mockAPIContent)
} else {
fmt.Fprintln(w, mockDataContent)
}
}))
// Simulate a scenario where JSON unmarshalling fails
mockAPIContent := `{"line": 123, "size": "not a string field for Line"}` // Invalid: line is number
mockDataContent := "payload1\npayload2"

server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.HasSuffix(r.URL.Path, ".json") { // Simplified to any .json for this test's purpose
w.Header().Set("Content-Type", "application/json")
fmt.Fprintln(w, mockAPIContent)
} else {
fmt.Fprintln(w, mockDataContent)
}
}))
originalBaseURL := assetHahwulBaseURL
assetHahwulBaseURL = server.URL
defer func() {
defer func() {
assetHahwulBaseURL = originalBaseURL
server.Close()
}()

// The actual Asset struct in remote.go is { Line string, Size string }
// If json.Unmarshal receives a number for 'Line', it will error out.
// The function getAssetHahwul will then proceed with default/empty Asset values.
payloads, line, size := getAssetHahwul("api.json", "data.txt") // Endpoints are relative to assets.hahwul.com

// Payloads should still be fetched
expectedPayloads := []string{"payload1", "payload2"}
if !equalSlices(payloads, expectedPayloads) {
t.Errorf("getAssetHahwul with JSON unmarshal error, payloads = %v, want %v", payloads, expectedPayloads)
}
// Line should be empty due to unmarshal error for the 'Line' field (type mismatch)
if line != "" {
t.Errorf("getAssetHahwul with JSON unmarshal error, line = %q, want \"\"", line)
}
// Size should be "not a string field for Line" as it's a valid string in JSON and struct
if size != "not a string field for Line" {
t.Errorf("getAssetHahwul with JSON unmarshal error, size = %q, want %q", size, "not a string field for Line")
}
// The actual Asset struct in remote.go is { Line string, Size string }
// If json.Unmarshal receives a number for 'Line', it will error out.
// The function getAssetHahwul will then proceed with default/empty Asset values.
payloads, line, size := getAssetHahwul("api.json", "data.txt") // Endpoints are relative to assets.hahwul.com

// Payloads should still be fetched
expectedPayloads := []string{"payload1", "payload2"}
if !equalSlices(payloads, expectedPayloads) {
t.Errorf("getAssetHahwul with JSON unmarshal error, payloads = %v, want %v", payloads, expectedPayloads)
}
// Line should be empty due to unmarshal error for the 'Line' field (type mismatch)
if line != "" {
t.Errorf("getAssetHahwul with JSON unmarshal error, line = %q, want \"\"", line)
}
// Size should be "not a string field for Line" as it's a valid string in JSON and struct
if size != "not a string field for Line" {
t.Errorf("getAssetHahwul with JSON unmarshal error, size = %q, want %q", size, "not a string field for Line")
}
}

func TestGetAssetHahwul_HttpErrorOnApi(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.HasSuffix(r.URL.Path, "someapi.json") { // Target specific API endpoint for error
http.Error(w, "API down", http.StatusServiceUnavailable)
} else {
fmt.Fprintln(w, "somedata") // Or handle other requests if necessary
}
}))
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.HasSuffix(r.URL.Path, "someapi.json") { // Target specific API endpoint for error
http.Error(w, "API down", http.StatusServiceUnavailable)
} else {
fmt.Fprintln(w, "somedata") // Or handle other requests if necessary
}
}))
originalBaseURL := assetHahwulBaseURL
assetHahwulBaseURL = server.URL
defer func() {
defer func() {
assetHahwulBaseURL = originalBaseURL
server.Close()
}()

payloads, line, size := getAssetHahwul("someapi.json", "somedata.txt")

if len(payloads) != 0 {
t.Errorf("Expected empty payloads on API HTTP error, got %v", payloads)
}
if line != "" {
t.Errorf("Expected empty line on API HTTP error, got %s", line)
}
if size != "" {
t.Errorf("Expected empty size on API HTTP error, got %s", size)
}
payloads, line, size := getAssetHahwul("someapi.json", "somedata.txt")

if len(payloads) != 0 {
t.Errorf("Expected empty payloads on API HTTP error, got %v", payloads)
}
if line != "" {
t.Errorf("Expected empty line on API HTTP error, got %s", line)
}
if size != "" {
t.Errorf("Expected empty size on API HTTP error, got %s", size)
}
}

func TestGetAssetHahwul_HttpErrorOnData(t *testing.T) {
mockAPIContent := `{"line":"1","size":"1B"}`
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.HasSuffix(r.URL.Path, "somedata.txt") { // Target specific data endpoint for error
http.Error(w, "Data down", http.StatusServiceUnavailable)
} else if strings.HasSuffix(r.URL.Path, "someapi.json") {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.HasSuffix(r.URL.Path, "somedata.txt") { // Target specific data endpoint for error
http.Error(w, "Data down", http.StatusServiceUnavailable)
} else if strings.HasSuffix(r.URL.Path, "someapi.json") {
w.Header().Set("Content-Type", "application/json")
fmt.Fprintln(w, mockAPIContent)
fmt.Fprintln(w, mockAPIContent)
} else {
http.NotFound(w,r)
http.NotFound(w, r)
}
}))
}))
originalBaseURL := assetHahwulBaseURL
assetHahwulBaseURL = server.URL
defer func() {
defer func() {
assetHahwulBaseURL = originalBaseURL
server.Close()
}()

payloads, line, size := getAssetHahwul("someapi.json", "somedata.txt")

if len(payloads) != 0 {
t.Errorf("Expected empty payloads on Data HTTP error, got %v", payloads)
}
// Line and Size might be populated from the API call if it succeeded before data call failed.
// The current implementation of getAssetHahwul returns empty for all if dataResp fails.
if line != "" {
t.Errorf("Expected empty line on Data HTTP error, got %s", line)
}
if size != "" {
t.Errorf("Expected empty size on Data HTTP error, got %s", size)
}
payloads, line, size := getAssetHahwul("someapi.json", "somedata.txt")

if len(payloads) != 0 {
t.Errorf("Expected empty payloads on Data HTTP error, got %v", payloads)
}
// Line and Size might be populated from the API call if it succeeded before data call failed.
// The current implementation of getAssetHahwul returns empty for all if dataResp fails.
if line != "" {
t.Errorf("Expected empty line on Data HTTP error, got %s", line)
}
if size != "" {
t.Errorf("Expected empty size on Data HTTP error, got %s", size)
}
}
40 changes: 20 additions & 20 deletions internal/utils/magic.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,52 +15,52 @@ var MagicCharacters = []string{
// ContextSpecificMagic contains magic characters for specific contexts
var ContextSpecificMagic = map[string][]string{
"html": {"<", ">", "'", "\"", "&"},
"js": {"'", "\"", ";", "{", "}", "(", ")", "`"},
"css": {"{", "}", ";", ":", "/*", "*/", "'", "\""},
"url": {"&", "=", "?", "#", "%", "+", " "},
"js": {"'", "\"", ";", "{", "}", "(", ")", "`"},
"css": {"{", "}", ";", ":", "/*", "*/", "'", "\""},
"url": {"&", "=", "?", "#", "%", "+", " "},
"json": {"{", "}", "[", "]", ":", ",", "\""},
"xml": {"<", ">", "&", "'", "\""},
"sql": {"'", "\"", ";", "--", "/*", "*/", "(", ")"},
"xml": {"<", ">", "&", "'", "\""},
"sql": {"'", "\"", ";", "--", "/*", "*/", "(", ")"},
}

// GenerateMagicCharacter generates a magic character based on context
func GenerateMagicCharacter(context string) string {
if chars, exists := ContextSpecificMagic[strings.ToLower(context)]; exists {
return chars[rand.Intn(len(chars))]
}

return MagicCharacters[rand.Intn(len(MagicCharacters))]
}

// GenerateMagicString generates a string with multiple magic characters
func GenerateMagicString(context string, length int) string {
var result strings.Builder

for i := 0; i < length; i++ {
result.WriteString(GenerateMagicCharacter(context))
}

return result.String()
}

// GetBypassHints returns WAF bypass hints for specific characters
func GetBypassHints(char string) []string {
bypassMap := map[string][]string{
"<": {"&lt;", "\\u003c", "\\x3c", "%3c", "\\074"},
">": {"&gt;", "\\u003e", "\\x3e", "%3e", "\\076"},
"'": {"&apos;", "\\u0027", "\\x27", "%27", "\\047"},
"<": {"&lt;", "\\u003c", "\\x3c", "%3c", "\\074"},
">": {"&gt;", "\\u003e", "\\x3e", "%3e", "\\076"},
"'": {"&apos;", "\\u0027", "\\x27", "%27", "\\047"},
"\"": {"&quot;", "\\u0022", "\\x22", "%22", "\\042"},
"&": {"&amp;", "\\u0026", "\\x26", "%26", "\\046"},
"(": {"\\u0028", "\\x28", "%28", "\\050"},
")": {"\\u0029", "\\x29", "%29", "\\051"},
";": {"\\u003b", "\\x3b", "%3b", "\\073"},
" ": {"%20", "+", "\\u0020", "\\x20"},
"&": {"&amp;", "\\u0026", "\\x26", "%26", "\\046"},
"(": {"\\u0028", "\\x28", "%28", "\\050"},
")": {"\\u0029", "\\x29", "%29", "\\051"},
";": {"\\u003b", "\\x3b", "%3b", "\\073"},
" ": {"%20", "+", "\\u0020", "\\x20"},
}

if hints, exists := bypassMap[char]; exists {
return hints
}

return []string{}
}

Expand All @@ -82,7 +82,7 @@ func DetectContext(response string, param string, value string) string {
if strings.Contains(response, "<?xml") {
return "xml"
}

return "html" // Default context
}

Expand All @@ -102,4 +102,4 @@ func GenerateTestPayload(context string) string {
default:
return "<script>alert('XSS')</script>"
}
}
}
Loading
Loading