@@ -12,6 +12,7 @@ import (
1212 "time"
1313
1414 kxsstool "github.com/h0tak88r/AutoAR/internal/tools/kxss"
15+ dalfoxtool "github.com/h0tak88r/AutoAR/internal/tools/dalfox"
1516 "github.com/h0tak88r/AutoAR/internal/scanner/urls"
1617 "github.com/h0tak88r/AutoAR/internal/utils"
1718)
@@ -137,13 +138,6 @@ func ScanReflectionWithOptions(opts Options) (*Result, error) {
137138
138139 // Scan URLs with concurrency and timeout
139140 kxssResults , err := scanURLsWithConcurrency (ctx , validURLs , opts .Threads )
140- type xssFinding struct {
141- TemplateID string `json:"template-id"` // VULN TYPE column
142- MatchedAt string `json:"matched-at"` // TARGET column
143- Severity string `json:"severity"`
144- Param string `json:"param"`
145- Unfiltered []string `json:"unfiltered"`
146- }
147141 findings := make ([]xssFinding , 0 , len (kxssResults ))
148142 for _ , r := range kxssResults {
149143 if r .URL == "" || r .Param == "" || len (r .Chars ) == 0 {
@@ -194,7 +188,6 @@ func ScanReflectionWithOptions(opts Options) (*Result, error) {
194188 }
195189
196190 // Write structured JSON for the dashboard — one object per kxss finding.
197- // Template: TARGET=url, VULN TYPE='xss @ param | Unfiltered: [chars]', SEV=medium.
198191 if scanID := utils .GetCurrentScanID (); scanID != "" {
199192 if len (findings ) > 0 {
200193 if err := utils .WriteJSONToScanDir (scanID , "xss-reflection-vulnerabilities.json" , findings ); err != nil {
@@ -205,13 +198,138 @@ func ScanReflectionWithOptions(opts Options) (*Result, error) {
205198 }
206199 }
207200
201+ // ── Dalfox: confirm XSS on kxss findings that had {<} or {>} unfiltered ──
202+ // These characters indicate the site doesn't filter angle brackets —
203+ // the strongest signal for exploitable XSS. Feed only those URLs to dalfox.
204+ xssCandidateURLs := extractAngleBracketURLs (findings )
205+ if len (xssCandidateURLs ) > 0 {
206+ logger .GetLogger ().Infof ("[INFO] Running dalfox on %d kxss angle-bracket candidates" , len (xssCandidateURLs ))
207+ runDalfoxOnURLs (xssCandidateURLs , opts .Domain , opts .Threads )
208+ } else {
209+ logger .GetLogger ().Infof ("[INFO] No angle-bracket candidates from kxss — skipping dalfox" )
210+ if scanID := utils .GetCurrentScanID (); scanID != "" {
211+ _ = utils .WriteNoFindingsJSON (scanID , opts .Domain , "xss-detection" , "dalfox-xss-results.json" )
212+ }
213+ }
214+
208215 return & Result {
209216 Domain : opts .Domain ,
210217 Reflections : reflectionCount ,
211218 OutputFile : outFile ,
212219 }, nil
213220}
214221
222+ // xssFinding is one structured kxss result persisted to the dashboard.
223+ type xssFinding struct {
224+ TemplateID string `json:"template-id"`
225+ MatchedAt string `json:"matched-at"`
226+ Severity string `json:"severity"`
227+ Param string `json:"param"`
228+ Unfiltered []string `json:"unfiltered"`
229+ }
230+
231+ // extractAngleBracketURLs returns unique URLs from kxss findings where
232+ // either '{<}' or '{>}' (or both) was unfiltered — the strongest XSS signal.
233+ func extractAngleBracketURLs (findings []xssFinding ) []string {
234+ seen := make (map [string ]struct {})
235+ var out []string
236+ for _ , f := range findings {
237+ for _ , ch := range f .Unfiltered {
238+ if ch == "{<}" || ch == "{>}" {
239+ if _ , ok := seen [f .MatchedAt ]; ! ok {
240+ seen [f .MatchedAt ] = struct {}{}
241+ out = append (out , f .MatchedAt )
242+ }
243+ break
244+ }
245+ }
246+ }
247+ return out
248+ }
249+
250+ // runDalfoxOnURLs writes the candidate URLs to a temp file, runs dalfox (Go package)
251+ // and persists results as 'dalfox-xss-results.json' — a separate dashboard module.
252+ func runDalfoxOnURLs (candidateURLs []string , domain string , threads int ) {
253+ if threads <= 0 {
254+ threads = 50
255+ }
256+ // Write URLs to a temp file for dalfoxtool.ScanFile
257+ tmpFile , err := os .CreateTemp ("" , "dalfox-targets-*.txt" )
258+ if err != nil {
259+ logger .GetLogger ().Infof ("[WARN] dalfox: failed to create temp file: %v" , err )
260+ return
261+ }
262+ defer os .Remove (tmpFile .Name ())
263+ for _ , u := range candidateURLs {
264+ _ , _ = fmt .Fprintln (tmpFile , u )
265+ }
266+ tmpFile .Close ()
267+
268+ results , err := dalfoxtool .ScanFile (tmpFile .Name (), dalfoxtool.Options {Threads : threads })
269+ if err != nil {
270+ logger .GetLogger ().Infof ("[WARN] dalfox scan failed: %v" , err )
271+ return
272+ }
273+
274+ scanID := utils .GetCurrentScanID ()
275+ if scanID == "" {
276+ return
277+ }
278+
279+ if len (results ) == 0 {
280+ _ = utils .WriteNoFindingsJSON (scanID , domain , "xss-detection" , "dalfox-xss-results.json" )
281+ return
282+ }
283+
284+ type dalfoxFinding struct {
285+ TemplateID string `json:"template-id"`
286+ MatchedAt string `json:"matched-at"`
287+ Severity string `json:"severity"`
288+ Type string `json:"type,omitempty"`
289+ Parameter string `json:"parameter,omitempty"`
290+ Payload string `json:"payload,omitempty"`
291+ Module string `json:"module"`
292+ }
293+
294+ seen := make (map [string ]struct {})
295+ var dfindings []dalfoxFinding
296+ for _ , r := range results {
297+ fType := strings .TrimSpace (r .Type )
298+ if fType == "" {
299+ fType = "xss"
300+ }
301+ sev := strings .TrimSpace (strings .ToLower (r .Severity ))
302+ if sev == "" {
303+ sev = "high"
304+ }
305+ label := fmt .Sprintf ("XSS (%s)" , strings .ToUpper (fType ))
306+ key := label + "|" + r .Target + "|" + r .Parameter
307+ if _ , ok := seen [key ]; ok {
308+ continue
309+ }
310+ seen [key ] = struct {}{}
311+ dfindings = append (dfindings , dalfoxFinding {
312+ TemplateID : label ,
313+ MatchedAt : r .Target ,
314+ Severity : sev ,
315+ Type : fType ,
316+ Parameter : r .Parameter ,
317+ Payload : r .Payload ,
318+ Module : "xss-detection" ,
319+ })
320+ }
321+
322+ if len (dfindings ) > 0 {
323+ logger .GetLogger ().Infof ("[OK] Dalfox confirmed %d XSS finding(s)" , len (dfindings ))
324+ if err := utils .WriteJSONToScanDir (scanID , "dalfox-xss-results.json" , dfindings ); err != nil {
325+ logger .GetLogger ().Infof ("[WARN] Failed to write dalfox-xss JSON: %v" , err )
326+ }
327+ } else {
328+ logger .GetLogger ().Infof ("[INFO] Dalfox found no confirmed XSS from kxss candidates" )
329+ _ = utils .WriteNoFindingsJSON (scanID , domain , "xss-detection" , "dalfox-xss-results.json" )
330+ }
331+ }
332+
215333// scanURLsWithConcurrency scans URLs with concurrency and context timeout
216334func scanURLsWithConcurrency (ctx context.Context , urls []string , concurrency int ) ([]kxsstool.Result , error ) {
217335 if len (urls ) == 0 {
0 commit comments