gitex2026/aasd/src/internal/scanner/gotestwaf.go
administrator b91406ada4 fix: email report URL, duplicate case; feat: Home button, email UI, scan timer; test: 44 unit tests; refactor: IsIP to pkg/netutil; docs: SMTP env vars
- Fix email report URL: /{token}.html → /visitor_{token}.html
- Remove duplicate case 'generating' in simulation.html polling
- Add defensive guard against empty Subdomains in SelectAndScan
- Add Home link and Start New Scan button to visitor report
- Replace QR code injection with email-send form in consultant report
- Add scan timer animation (barberpole + elapsed counter) to frontend
- Move IsIP() from scanner/probe.go to pkg/netutil for reuse
- Add 44 unit tests across scanner, report, and netutil packages
- Create Makefile with build/vet/test/deploy targets
- Document SMTP environment variables in README and AGENT.md
2026-04-29 07:30:42 +00:00

85 lines
2.7 KiB
Go

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
}