diff --git a/internal/mailer/smtp.go b/internal/mailer/smtp.go
index 888b634..a6d403f 100644
--- a/internal/mailer/smtp.go
+++ b/internal/mailer/smtp.go
@@ -3,9 +3,11 @@ package mailer
import (
"bytes"
"fmt"
+ "html"
"log"
"net/smtp"
"os"
+ "sort"
"nucleus/internal/models"
)
@@ -48,6 +50,27 @@ func SendReport(target models.Target, findings []models.Finding) {
if len(findings) == 0 {
body.WriteString("
No findings were detected.
")
} else {
+ // Calculate summary
+ counts := map[string]int{"critical": 0, "high": 0, "medium": 0, "low": 0, "info": 0}
+ for _, f := range findings {
+ counts[f.Severity]++
+ }
+
+ body.WriteString("Summary
")
+ if counts["critical"] > 0 { body.WriteString(fmt.Sprintf("- Critical: %d
", counts["critical"])) }
+ if counts["high"] > 0 { body.WriteString(fmt.Sprintf("- High: %d
", counts["high"])) }
+ if counts["medium"] > 0 { body.WriteString(fmt.Sprintf("- Medium: %d
", counts["medium"])) }
+ if counts["low"] > 0 { body.WriteString(fmt.Sprintf("- Low: %d
", counts["low"])) }
+ if counts["info"] > 0 { body.WriteString(fmt.Sprintf("- Info: %d
", counts["info"])) }
+ body.WriteString("
")
+
+ // Sort findings by severity
+ severityScore := map[string]int{"critical": 5, "high": 4, "medium": 3, "low": 2, "info": 1}
+ sort.Slice(findings, func(i, j int) bool {
+ return severityScore[findings[i].Severity] > severityScore[findings[j].Severity]
+ })
+
+ body.WriteString("Details
")
body.WriteString("")
body.WriteString("| Severity | Name | Host | Template |
")
for _, f := range findings {
@@ -59,7 +82,12 @@ func SendReport(target models.Target, findings []models.Finding) {
case "low": color = "#e6f2ff"
case "info": color = "#f2f2f2"
}
- body.WriteString(fmt.Sprintf("| %s | %s | %s | %s |
", color, f.Severity, f.Name, f.Host, f.TemplateID))
+ body.WriteString(fmt.Sprintf("| %s | %s | %s | %s |
",
+ color,
+ html.EscapeString(f.Severity),
+ html.EscapeString(f.Name),
+ html.EscapeString(f.Host),
+ html.EscapeString(f.TemplateID)))
}
body.WriteString("
")
}