Skip to content

Commit 1646ad8

Browse files
committed
feat: Enhance logging and output handling across modules
- Updated logging messages to use a consistent format with "[ + ]" for success indicators. - Added handling for empty results in various scans, ensuring informative messages are written to output files. - Improved .gitignore to include backup directories and configuration files. - Refactored AEM and other modules to always save discovered instances and results, even if empty. - Enhanced README documentation for clarity on features and usage.
1 parent 98ba117 commit 1646ad8

34 files changed

Lines changed: 939 additions & 370 deletions

File tree

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,7 @@ roots.txt
9191
.sql.gz
9292
autoar
9393
.db-shm
94-
.db-wal
94+
.db-wal
95+
96+
/backups/
97+
Rules.yml

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,7 @@ AutoAR includes **KeyHack**, a comprehensive API key validation system with **77
472472

473473
- **📋 778+ Templates**: Comprehensive collection of API key validation templates from KeysKit and custom additions
474474
- **🔍 Smart Search**: Search templates by provider name or description
475-
- **Quick Validation**: Generate ready-to-use validation commands (curl or shell)
475+
- **[ + ]Quick Validation**: Generate ready-to-use validation commands (curl or shell)
476476
- **➕ Extensible**: Add custom validation templates via Discord or CLI
477477
- **🌐 Multi-Format Support**: Supports HTTP-based (curl) and shell-based (AWS CLI, etc.) validation methods
478478

internal/modules/aem/aem.go

Lines changed: 108 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}

internal/modules/apkx/apkx.go

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,12 @@ type Options struct {
3333

3434
// Result describes where apkX wrote its output.
3535
type Result struct {
36-
ReportDir string
37-
LogFile string
38-
Duration time.Duration
36+
ReportDir string
37+
LogFile string
38+
Duration time.Duration
3939
MITMPatchedAPK string // Path to MITM patched APK if MITM was enabled
40-
FromCache bool // True if this result was loaded from cache
40+
OriginalAPKPath string // Path to original downloaded APK (for RunFromPackage)
41+
FromCache bool // True if this result was loaded from cache
4142
}
4243

4344
// PackageOptions controls apkX scans where AutoAR first downloads the
@@ -449,7 +450,7 @@ func RunFromPackage(opts PackageOptions) (*Result, error) {
449450

450451
cachePath, found := CheckCache(packageName, cacheVersion)
451452
if found {
452-
fmt.Printf("[CACHE] Using cached results for %s v%s (skipping scan)\n", packageName, version)
453+
fmt.Printf("[CACHE] [ + ]Using cached results for %s v%s (skipping scan)\n", packageName, version)
453454

454455
// Load cached result
455456
if strings.HasPrefix(cachePath, "r2:") {
@@ -464,7 +465,7 @@ func RunFromPackage(opts PackageOptions) (*Result, error) {
464465
// Now load from local cache
465466
cachedResult, err := LoadCachedResult(localCachePath)
466467
if err == nil {
467-
fmt.Printf("[CACHE] Loaded cache from R2 for %s v%s\n", packageName, version)
468+
fmt.Printf("[CACHE] [ + ]Loaded cache from R2 for %s v%s\n", packageName, version)
468469
return cachedResult, nil
469470
}
470471
fmt.Printf("[CACHE] ⚠️ Failed to load downloaded cache: %v, doing fresh scan\n", err)
@@ -494,6 +495,11 @@ func RunFromPackage(opts PackageOptions) (*Result, error) {
494495
OutputDir: opts.OutputDir,
495496
MITM: opts.MITM,
496497
})
498+
499+
// Store the original APK path in the result
500+
if result != nil {
501+
result.OriginalAPKPath = inputPath
502+
}
497503

498504
// Save to cache after successful scan
499505
if err == nil && result != nil && packageName != "" {
@@ -530,7 +536,7 @@ func RunFromPackage(opts PackageOptions) (*Result, error) {
530536
}
531537

532538
if version != "" {
533-
fmt.Printf("[CACHE] Extracted version from decompiled manifest: %s v%s\n", packageName, version)
539+
fmt.Printf("[CACHE] [ + ]Extracted version from decompiled manifest: %s v%s\n", packageName, version)
534540
}
535541
}
536542
}

internal/modules/apkx/cache.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func CheckCache(packageName, version string) (string, bool) {
6161
if version == "latest" {
6262
foundVersion, found := findAnyCachedVersionInR2(packageName)
6363
if found {
64-
log.Printf("[CACHE] Found R2 cached results for %s v%s (was looking for 'latest')", packageName, foundVersion)
64+
log.Printf("[CACHE] [ + ]Found R2 cached results for %s v%s (was looking for 'latest')", packageName, foundVersion)
6565
r2CachePrefix := getR2CachePath(packageName, foundVersion)
6666
return "r2:" + r2CachePrefix, true
6767
}
@@ -70,7 +70,7 @@ func CheckCache(packageName, version string) (string, bool) {
7070
r2ResultsKey := r2CachePrefix + "/results.json"
7171
exists, err := r2storage.FileExists(r2ResultsKey)
7272
if err == nil && exists {
73-
log.Printf("[CACHE] Found R2 cached results for %s vlatest", packageName)
73+
log.Printf("[CACHE] [ + ]Found R2 cached results for %s vlatest", packageName)
7474
return "r2:" + r2CachePrefix, true
7575
}
7676
} else {
@@ -79,7 +79,7 @@ func CheckCache(packageName, version string) (string, bool) {
7979
r2ResultsKey := r2CachePrefix + "/results.json"
8080
exists, err := r2storage.FileExists(r2ResultsKey)
8181
if err == nil && exists {
82-
log.Printf("[CACHE] Found R2 cached results for %s v%s", packageName, version)
82+
log.Printf("[CACHE] [ + ]Found R2 cached results for %s v%s", packageName, version)
8383
return "r2:" + r2CachePrefix, true // Return with r2: prefix to indicate R2 location
8484
}
8585
}
@@ -90,15 +90,15 @@ func CheckCache(packageName, version string) (string, bool) {
9090
if version == "latest" {
9191
foundVersion, found := findAnyCachedVersionLocal(packageName)
9292
if found {
93-
log.Printf("[CACHE] Found local cached results for %s v%s (was looking for 'latest')", packageName, foundVersion)
93+
log.Printf("[CACHE] [ + ]Found local cached results for %s v%s (was looking for 'latest')", packageName, foundVersion)
9494
localCachePath := getCachePath(packageName, foundVersion)
9595
return localCachePath, true
9696
}
9797
} else {
9898
localCachePath := getCachePath(packageName, version)
9999
resultsJson := filepath.Join(localCachePath, "results.json")
100100
if _, err := os.Stat(resultsJson); err == nil {
101-
log.Printf("[CACHE] Found local cached results for %s v%s", packageName, version)
101+
log.Printf("[CACHE] [ + ]Found local cached results for %s v%s", packageName, version)
102102
return localCachePath, true
103103
}
104104
}
@@ -152,7 +152,7 @@ func CheckCache(packageName, version string) (string, bool) {
152152

153153
// Save to cache
154154
if err := SaveToCache(packageName, extractedVersion, "", existingResult); err == nil {
155-
log.Printf("[CACHE] Migrated existing scan to cache: %s v%s", packageName, extractedVersion)
155+
log.Printf("[CACHE] [ + ]Migrated existing scan to cache: %s v%s", packageName, extractedVersion)
156156
// Return the new cache path
157157
newCachePath := getCachePath(packageName, extractedVersion)
158158
return newCachePath, true
@@ -344,7 +344,7 @@ func SaveToCache(packageName, version, versionCode string, result *Result) error
344344
if err != nil {
345345
log.Printf("[CACHE] ⚠️ Failed to upload cache to R2: %v", err)
346346
} else {
347-
log.Printf("[CACHE] Cache uploaded to R2")
347+
log.Printf("[CACHE] [ + ]Cache uploaded to R2")
348348
}
349349
}
350350

internal/modules/backup/backup.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,12 @@ func Run(opts Options) (*Result, error) {
186186
foundCount++
187187
}
188188
}
189+
190+
// Always write a message if no results found
191+
if foundCount == 0 {
192+
resultsFH.WriteString("No backup files found.\n")
193+
}
194+
189195
log.Printf("[INFO] Backup scan: Wrote %d backup URLs to results file: %s", foundCount, resultsFile)
190196

191197
res.Duration = time.Since(start)

internal/modules/db/backup.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ func BackupDatabase(uploadToR2 bool) (string, string, error) {
4141
log.Printf("[DB] ⚠️ Failed to upload backup to R2: %v", err)
4242
// Don't fail the backup if R2 upload fails
4343
} else {
44-
log.Printf("[DB] Database backup uploaded to R2: %s", r2URL)
44+
log.Printf("[DB] [ + ]Database backup uploaded to R2: %s", r2URL)
4545
}
4646
}
4747

@@ -94,7 +94,7 @@ func backupSQLite() (string, error) {
9494
return "", fmt.Errorf("failed to copy database file: %w", err)
9595
}
9696

97-
log.Printf("[DB] SQLite backup created: %s", backupPath)
97+
log.Printf("[DB] [ + ]SQLite backup created: %s", backupPath)
9898
return backupPath, nil
9999
}
100100

@@ -150,7 +150,7 @@ func backupPostgreSQL() (string, error) {
150150
return "", fmt.Errorf("backup file was not created")
151151
}
152152

153-
log.Printf("[DB] PostgreSQL backup created: %s", backupPath)
153+
log.Printf("[DB] [ + ]PostgreSQL backup created: %s", backupPath)
154154
return backupPath, nil
155155
}
156156

0 commit comments

Comments
 (0)