@@ -108,35 +108,20 @@ func DiscoverAEM(opts Options) ([]string, error) {
108108 log .Printf ("[AEM] Starting AEM discovery (native Go)..." )
109109 discovered := DiscoverAEMFromURLs (urls , client , opts .Threads )
110110
111- // Save discovered instances
112- if len ( discovered ) > 0 {
113- discoveredFH , err := os . Create ( discoveredFile )
114- if err == nil {
111+ // Always save discovered instances (even if empty)
112+ discoveredFH , err := os . Create ( discoveredFile )
113+ if err == nil {
114+ if len ( discovered ) > 0 {
115115 for _ , url := range discovered {
116116 fmt .Fprintln (discoveredFH , url )
117117 }
118- discoveredFH .Close ()
119- }
120-
121- // Send findings to Discord webhook if configured
122- webhookURL := os .Getenv ("DISCORD_WEBHOOK" )
123- if webhookURL != "" {
124- if info , err := os .Stat (discoveredFile ); err == nil && info .Size () > 0 {
125- domain := opts .Domain
126- if domain == "" && opts .LiveHostsFile != "" {
127- domain = "targets"
128- }
129- utils .SendWebhookFileAsync (discoveredFile , fmt .Sprintf ("AEM Discovery: AEM instances found (%d discovered)" , len (discovered )))
130- utils .SendWebhookLogAsync (fmt .Sprintf ("AEM discovery: %d AEM instance(s) found" , len (discovered )))
131- }
132- }
133- } else {
134- // No findings
135- webhookURL := os .Getenv ("DISCORD_WEBHOOK" )
136- if webhookURL != "" {
137- utils .SendWebhookLogAsync ("AEM discovery completed with 0 AEM instances found" )
118+ } else {
119+ fmt .Fprintln (discoveredFH , "No AEM instances discovered." )
138120 }
121+ discoveredFH .Close ()
139122 }
123+
124+ // Don't send individual discovery messages - will be sent in consolidated file from Run()
140125
141126 return discovered , nil
142127}
@@ -180,27 +165,22 @@ func ScanAEM(url string, opts Options) ([]Finding, error) {
180165 log .Printf ("[AEM] Scanning %s for vulnerabilities (native Go)..." , url )
181166 findings := ScanAEMInstance (url , ssrfHost , client , opts .Handlers )
182167
183- // Save findings as JSON
168+ // Always save findings as JSON (even if empty)
169+ var data []byte
184170 if len (findings ) > 0 {
185- if data , err := json .MarshalIndent (findings , "" , " " ); err == nil {
171+ if jsonData , err := json .MarshalIndent (findings , "" , " " ); err == nil {
172+ data = jsonData
186173 os .WriteFile (resultsFile , data , 0644 )
187174 }
188-
189- // Send findings to Discord webhook if configured
190- webhookURL := os .Getenv ("DISCORD_WEBHOOK" )
191- if webhookURL != "" {
192- if info , err := os .Stat (resultsFile ); err == nil && info .Size () > 0 {
193- utils .SendWebhookFileAsync (resultsFile , fmt .Sprintf ("AEM Finding: Vulnerabilities found for %s (%d findings)" , url , len (findings )))
194- }
195- utils .SendWebhookLogAsync (fmt .Sprintf ("AEM scan completed for %s - %d vulnerability/vulnerabilities found" , url , len (findings )))
196- }
197175 } else {
198- // No findings
199- webhookURL := os . Getenv ( "DISCORD_WEBHOOK" )
200- if webhookURL != "" {
201- utils . SendWebhookLogAsync ( fmt . Sprintf ( "AEM scan completed for %s with 0 findings" , url ) )
176+ // Save empty findings array
177+ if jsonData , err := json . MarshalIndent ([] Finding {}, "" , " " ); err == nil {
178+ data = jsonData
179+ os . WriteFile ( resultsFile , data , 0644 )
202180 }
203181 }
182+
183+ // Don't send individual scan messages - will be sent in consolidated file from Run()
204184
205185 return findings , nil
206186}
@@ -260,8 +240,49 @@ func Run(opts Options) (*Result, error) {
260240 }
261241 res .DiscoveredCount = len (discovered )
262242
243+ // Always create consolidated result file even if no instances discovered
263244 if len (discovered ) == 0 {
264245 log .Printf ("[AEM] No AEM instances discovered" )
246+
247+ // Create consolidated file with "no results" message
248+ consolidatedFile := filepath .Join (outputDir , "aem-scan.txt" )
249+ consolidatedF , err := os .Create (consolidatedFile )
250+ if err == nil {
251+ defer consolidatedF .Close ()
252+ domain := opts .Domain
253+ if domain == "" && opts .LiveHostsFile != "" {
254+ domain = "targets"
255+ }
256+ fmt .Fprintf (consolidatedF , "AEM Scan Results for %s\n " , domain )
257+ fmt .Fprintf (consolidatedF , "========================================\n \n " )
258+ fmt .Fprintf (consolidatedF , "No AEM instances discovered.\n " )
259+ }
260+
261+ // Save empty results to JSON
262+ allResults := map [string ]interface {}{
263+ "discovered_count" : 0 ,
264+ "vulnerabilities" : 0 ,
265+ "discovered" : []string {},
266+ "findings" : []Finding {},
267+ "scan_time" : time .Now ().Format (time .RFC3339 ),
268+ }
269+ if data , err := json .MarshalIndent (allResults , "" , " " ); err == nil {
270+ os .WriteFile (resultsFile , data , 0644 )
271+ }
272+
273+ // Send to Discord
274+ webhookURL := os .Getenv ("DISCORD_WEBHOOK" )
275+ if webhookURL != "" {
276+ domain := opts .Domain
277+ if domain == "" && opts .LiveHostsFile != "" {
278+ domain = "targets"
279+ }
280+ if info , err := os .Stat (consolidatedFile ); err == nil && info .Size () > 0 {
281+ utils .SendWebhookFileAsync (consolidatedFile , fmt .Sprintf ("AEM Scan Results: 0 AEM instances found for %s" , domain ))
282+ }
283+ utils .SendWebhookLogAsync (fmt .Sprintf ("AEM scan completed for %s: 0 AEM instances discovered" , domain ))
284+ }
285+
265286 res .Duration = time .Since (startTime )
266287 return res , nil
267288 }
@@ -283,7 +304,7 @@ func Run(opts Options) (*Result, error) {
283304
284305 res .Vulnerabilities = len (allFindings )
285306
286- // Save all results
307+ // Save all results to JSON files
287308 allResults := map [string ]interface {}{
288309 "discovered_count" : res .DiscoveredCount ,
289310 "vulnerabilities" : res .Vulnerabilities ,
@@ -297,23 +318,58 @@ func Run(opts Options) (*Result, error) {
297318 os .WriteFile (scannedFile , data , 0644 )
298319 }
299320
300- // Send findings to Discord webhook if configured
321+ // Always create consolidated aem-scan.txt file (even with 0 findings)
322+ consolidatedFile := filepath .Join (outputDir , "aem-scan.txt" )
323+ consolidatedF , err := os .Create (consolidatedFile )
324+ if err == nil {
325+ defer consolidatedF .Close ()
326+
327+ domain := opts .Domain
328+ if domain == "" && opts .LiveHostsFile != "" {
329+ domain = "targets"
330+ }
331+
332+ fmt .Fprintf (consolidatedF , "AEM Scan Results for %s\n " , domain )
333+ fmt .Fprintf (consolidatedF , "========================================\n \n " )
334+ fmt .Fprintf (consolidatedF , "Discovered AEM Instances: %d\n " , res .DiscoveredCount )
335+ fmt .Fprintf (consolidatedF , "Vulnerabilities Found: %d\n \n " , res .Vulnerabilities )
336+
337+ if len (discovered ) > 0 {
338+ fmt .Fprintf (consolidatedF , "Discovered Instances:\n " )
339+ for _ , url := range discovered {
340+ fmt .Fprintf (consolidatedF , " - %s\n " , url )
341+ }
342+ fmt .Fprintf (consolidatedF , "\n " )
343+ }
344+
345+ if len (allFindings ) > 0 {
346+ fmt .Fprintf (consolidatedF , "Vulnerabilities:\n " )
347+ for _ , finding := range allFindings {
348+ fmt .Fprintf (consolidatedF , " - [%s] %s\n " , finding .Name , finding .URL )
349+ if finding .Description != "" {
350+ fmt .Fprintf (consolidatedF , " Description: %s\n " , finding .Description )
351+ }
352+ }
353+ } else {
354+ fmt .Fprintf (consolidatedF , "No vulnerabilities found.\n " )
355+ }
356+ }
357+
358+ // Send findings to Discord webhook if configured (only the consolidated file)
301359 webhookURL := os .Getenv ("DISCORD_WEBHOOK" )
302360 if webhookURL != "" {
361+ domain := opts .Domain
362+ if domain == "" && opts .LiveHostsFile != "" {
363+ domain = "targets"
364+ }
365+
366+ if info , err := os .Stat (consolidatedFile ); err == nil && info .Size () > 0 {
367+ utils .SendWebhookFileAsync (consolidatedFile , fmt .Sprintf ("AEM Scan Results: %d AEM instances, %d vulnerabilities for %s" , res .DiscoveredCount , res .Vulnerabilities , domain ))
368+ }
369+
303370 if res .Vulnerabilities > 0 {
304- if info , err := os .Stat (resultsFile ); err == nil && info .Size () > 0 {
305- domain := opts .Domain
306- if domain == "" && opts .LiveHostsFile != "" {
307- domain = "targets"
308- }
309- utils .SendWebhookFileAsync (resultsFile , fmt .Sprintf ("AEM Scan Summary: %d AEM instances, %d vulnerabilities for %s" , res .DiscoveredCount , res .Vulnerabilities , domain ))
310- }
311371 utils .SendWebhookLogAsync (fmt .Sprintf ("AEM scan completed: %d AEM instance(s), %d vulnerability/vulnerabilities found" , res .DiscoveredCount , res .Vulnerabilities ))
312372 } else {
313- domain := opts .Domain
314- if domain == "" && opts .LiveHostsFile != "" {
315- domain = "targets"
316- }
317373 utils .SendWebhookLogAsync (fmt .Sprintf ("AEM scan completed for %s: %d AEM instance(s), 0 vulnerabilities" , domain , res .DiscoveredCount ))
318374 }
319375 }
0 commit comments