package scanner import ( "context" "fmt" "os" "os/exec" "path/filepath" "time" ) const ( gotestwafTimeout = 120 * time.Second ) // RunGoTestWAF executes GoTestWAF scan against the given target domain. // Tries HTTPS first; if the report file isn't created (target not reachable // on port 443), retries with plain HTTP on port 80. // The report is saved to reports/ directory with the given reportName. // Returns the raw output for AI analysis. func RunGoTestWAF(ctx context.Context, projectRoot string, reportName string, targetDomain string) (string, error) { // Try HTTPS first, then HTTP as fallback for _, protocol := range []string{"https", "http"} { output, err := runWithProtocol(ctx, projectRoot, reportName, targetDomain, protocol) // If the report file exists, the scan produced output — success reportPath := filepath.Join(projectRoot, "reports", reportName+".html") if _, statErr := os.Stat(reportPath); statErr == nil { return output, nil } // No report file — try next protocol or return error if err != nil { fmt.Printf("gotestwaf: %s failed for %s: %v\n", protocol, targetDomain, err) } } return "", fmt.Errorf("GoTestWAF: %s not reachable on HTTPS or HTTP", targetDomain) } // runWithProtocol executes GoTestWAF with the given protocol (https or http). func runWithProtocol(ctx context.Context, projectRoot string, reportName string, targetDomain string, protocol string) (string, error) { targetURL := fmt.Sprintf("%s://%s", protocol, targetDomain) binaryPath := filepath.Join(projectRoot, "gotestwaf") if _, err := os.Stat(binaryPath); os.IsNotExist(err) { return "", fmt.Errorf("GoTestWAF binary not found at %s", binaryPath) } reportsDir := filepath.Join(projectRoot, "reports") if err := os.MkdirAll(reportsDir, 0755); err != nil { return "", fmt.Errorf("failed to create reports directory: %w", err) } scanCtx, cancel := context.WithTimeout(ctx, gotestwafTimeout) defer cancel() cmd := exec.CommandContext(scanCtx, binaryPath, "--url", targetURL, "--configPath", "gotestwaf-config.yaml", "--testCasesPath", "testcases", "--maxIdleConns", "2", "--maxRedirects", "10", "--reportPath", reportsDir, "--reportName", reportName, "--reportFormat", "html", "--wafName", "generic", "--skipWAFBlockCheck", "--nonBlockedAsPassed", "--tlsVerify=false", "--noEmailReport", "--quiet", ) cmd.Dir = projectRoot output, err := cmd.CombinedOutput() outputStr := string(output) if err != nil { if scanCtx.Err() == context.DeadlineExceeded { return outputStr, fmt.Errorf("GoTestWAF scan timed out after %v", gotestwafTimeout) } return outputStr, fmt.Errorf("GoTestWAF scan failed: %w", err) } return outputStr, nil }