diff --git a/aasd/bin/aasd b/aasd/bin/aasd index 32856cc..cea8be6 100755 Binary files a/aasd/bin/aasd and b/aasd/bin/aasd differ diff --git a/aasd/src/internal/report/report.go b/aasd/src/internal/report/report.go index d25ec06..02cf5bd 100644 --- a/aasd/src/internal/report/report.go +++ b/aasd/src/internal/report/report.go @@ -171,3 +171,22 @@ func GenerateFallbackHTML(subdomains []string) string { `, domain, len(subdomains), list) } + +// GenerateScanFailedHTML creates a report explaining that the target +// could not be scanned by GoTestWAF (e.g. not an API endpoint). +func GenerateScanFailedHTML(domain, reason string) string { + return fmt.Sprintf(`
+
🔍
+

Could Not Scan Target

+

The scanner was unable to assess %s.

+
+

The WAF scanner (GoTestWAF) could not process this target. This typically happens when:

+ +
+

Try scanning a different subdomain or an API-specific endpoint.

+
`, domain) +} diff --git a/aasd/src/internal/report/report_test.go b/aasd/src/internal/report/report_test.go index bc60051..6608e72 100644 --- a/aasd/src/internal/report/report_test.go +++ b/aasd/src/internal/report/report_test.go @@ -307,3 +307,30 @@ func TestGenerateFallbackHTML_ValidHTML(t *testing.T) { t.Error("expected closing div tag") } } + +func TestGenerateScanFailedHTML_ContainsDomain(t *testing.T) { + html := GenerateScanFailedHTML("blog.example.com", "exit status 1") + if !strings.Contains(html, "blog.example.com") { + t.Error("expected HTML to contain the domain") + } +} + +func TestGenerateScanFailedHTML_Message(t *testing.T) { + html := GenerateScanFailedHTML("example.com", "any error") + if !strings.Contains(html, "Could Not Scan Target") { + t.Error("expected 'Could Not Scan Target' heading") + } + if !strings.Contains(html, "not an API endpoint") { + t.Error("expected explanation about API endpoints") + } +} + +func TestGenerateScanFailedHTML_ValidHTML(t *testing.T) { + html := GenerateScanFailedHTML("test.com", "some error") + if !strings.HasPrefix(strings.TrimSpace(html), "") { + t.Error("expected closing div tag") + } +} diff --git a/aasd/src/internal/scanner/scanner.go b/aasd/src/internal/scanner/scanner.go index 634ae28..0b300f9 100644 --- a/aasd/src/internal/scanner/scanner.go +++ b/aasd/src/internal/scanner/scanner.go @@ -179,14 +179,21 @@ func (o *Orchestrator) executeScanPhase(ctx context.Context, result *ScanResult) result.Error = fmt.Sprintf("scan error: %v", scanErr) } - // Phase 3: AI Narrative Generation + // Phase 3: Narrative Generation o.updateStatus(result.ReportToken, StatusGenerating) var aiHTML string - var aiErr error - if o.aiClient != nil && len(consultantHTML) > 0 { - aiHTML, aiErr = o.aiClient.GenerateResilienceReport(string(consultantHTML)) - } - if o.aiClient == nil || len(consultantHTML) == 0 || aiErr != nil { + + if scanErr != nil { + // GoTestWAF failed — explain why the target couldn't be scanned + aiHTML = report.GenerateScanFailedHTML(result.SelectedDomain, scanErr.Error()) + } else if o.aiClient != nil && len(consultantHTML) > 0 { + // GoTestWAF succeeded — try AI narrative, fall back to summary on failure + aiHTML, _ = o.aiClient.GenerateResilienceReport(string(consultantHTML)) + if aiHTML == "" { + aiHTML = report.GenerateFallbackHTML(result.Subdomains) + } + } else { + // No AI client — show summary-based report aiHTML = report.GenerateFallbackHTML(result.Subdomains) }