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:
+
+ - The target is not an API endpoint (e.g. a website or blog)
+ - The target is behind a strict WAF or CDN that blocks scan traffic
+ - The target is unreachable or returns unexpected responses
+
+
+
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)
}