From 426e2835462d20deb14c7d54bc3fc7b5d57c5133 Mon Sep 17 00:00:00 2001 From: tes Date: Mon, 16 Feb 2026 20:34:51 -0300 Subject: [PATCH 1/5] Add Intrusion Logging acquisition module --- README.md | 16 +++ adb/adb.go | 21 +++- modules/intrusion_logs.go | 256 ++++++++++++++++++++++++++++++++++++++ modules/modules.go | 1 + 4 files changed, 293 insertions(+), 1 deletion(-) create mode 100644 modules/intrusion_logs.go diff --git a/README.md b/README.md index 394dc91..930d434 100644 --- a/README.md +++ b/README.md @@ -72,10 +72,12 @@ The following data can be extracted: | The output of the dumpsys shell command, providing diagnostic information about the device. | | `dumpsys.txt` | | A list of all packages installed and related distribution files. | | `packages.json` | | Copy of all installed APKs or of only those not marked as system apps. | ✅ | `apks/*` | +| Intrusion Logging logs. Contains private data such as navigation history. | ✅ | `intrusion_logs/*` | | A list of files on the system. | | `files.json` | | A copy of the files available in temp folders. | | `tmp/*` | | A bug report containing system and app-specific logs, with no private data included. | | `bugreport.zip` | + ### About optional data collection #### Backup @@ -116,6 +118,20 @@ Would you like to download copies of all apps or only non-system ones? | Only non-system packages | Don't download any packages listed in `adb pm list packages -s` | | Do not download any | Don't download any packages | +### Intrusion Logs + +``` +Would you like to take the Intrusion Logs of the device? + +? Intrusion Logs: + ▸ Yes + No +``` + +| Option | Explanation | +|--------|-------------| +| Yes | Intrusion Logs will be retrieved from the phone. | +| No | Intrusion Logs acquisition is skipped. | ## Encryption & Potential Threats diff --git a/adb/adb.go b/adb/adb.go index f12076c..ddb9c8b 100644 --- a/adb/adb.go +++ b/adb/adb.go @@ -173,6 +173,21 @@ func (a *ADB) Bugreport() error { return err } +// IL prompts the user to download Intrusion Logs +func (a *ADB) IL() error { + // adb shell am start -n com.google.android.gms/.intrusiondetection.ui.retrieval.IntrusionDetectionRetrievalActivity + cmd, err := a.Shell( + "am", + "start", + "-n", + "com.google.android.gms/.intrusiondetection.ui.retrieval.IntrusionDetectionRetrievalActivity", + ) + if err != nil { + return fmt.Errorf("Failed to start IL activity: %v: %s", err, cmd) + } + return nil +} + // check if file exists func (a *ADB) FileExists(path string) (bool, error) { out, err := a.Shell("[", "-f", path, "] || echo 1") @@ -188,8 +203,12 @@ func (a *ADB) FileExists(path string) (bool, error) { // List files in a folder using ls, returns array of strings. func (a *ADB) ListFiles(remotePath string, recursive bool) ([]string, error) { var remoteFiles []string + + // Quote remotePath so files with spaces on their name work + qPath := fmt.Sprintf("'%s'", remotePath) + if recursive { - out, _ := a.Shell("find", remotePath, "2>", "/dev/null") + out, _ := a.Shell("find", qPath, "2>", "/dev/null") if out != "" { tmpFiles := strings.Split(out, "\n") for _, file := range tmpFiles { diff --git a/modules/intrusion_logs.go b/modules/intrusion_logs.go new file mode 100644 index 0000000..d7bbfd9 --- /dev/null +++ b/modules/intrusion_logs.go @@ -0,0 +1,256 @@ +// androidqf - Android Quick Forensics +// Copyright (c) 2021-2023 Claudio Guarnieri. +// Use of this software is governed by the MVT License 1.1 that can be found at +// https://license.mvt.re/1.1/ + +package modules + +import ( + "fmt" + "os" + "path/filepath" + "context" + "os/signal" + "strings" + "time" + + "github.com/manifoldco/promptui" + "github.com/mvt-project/androidqf/acquisition" + "github.com/mvt-project/androidqf/adb" + "github.com/mvt-project/androidqf/log" +) + +const ( + acquireIL = "Yes" + skipIL = "No" +) + +type IL struct { + StoragePath string + ILPath string + DirOnDevice string + PropName string +} + +func NewIL() *IL { + return &IL{ + DirOnDevice: "/sdcard/Download/Intrusion Logging/", + PropName: "security.perf_harden", // TODO: temporary placeholder, switch to real IL prop when known + } +} + +func (m *IL) Name() string { + return "intrusion_logs" +} + +func (m *IL) InitStorage(storagePath string) error { + m.StoragePath = storagePath + m.ILPath = filepath.Join(storagePath, "intrusion_logs") + + // Only create directory in traditional mode + if storagePath != "" { + err := os.Mkdir(m.ILPath, 0o755) + if err != nil && !os.IsExist(err) { + return fmt.Errorf("Failed to create Intrusion Logging folder: %v", err) + } + } + + return nil +} + +func (m *IL) Run(acq *acquisition.Acquisition, fast bool) error { + // Check if IL is enabled on device + enabled, err := m.isILEnabledOnDevice() + if err != nil { + // Don't break acquisition if the check fails; just log and skip. + log.Debugf("Failed to check prop %s: %v", m.PropName, err) + return nil + } + if !enabled { + log.Debug("Intrusion Logging is not enabled; skipping") + return nil + } + + // Ask user first + log.Info("Would you like to take the Intrusion Logs of the device?") + promptIL := promptui.Select{ + Label: "Intrusion Logs", + Items: []string{acquireIL, skipIL}, + } + + _, ILOption, err := promptIL.Run() + if err != nil { + return fmt.Errorf("Failed to make selection for IL option: %v", err) + } + + // User declined so we continue acquisition normally + if ILOption == skipIL { + log.Info("Skipping Intrusion Logging extraction...") + return nil + } + + // Snapshot of Intrusion Logs folder before triggering new log download + before, err := m.listDirSet(m.DirOnDevice) + + if err != nil { + log.Errorf("IL: failed to list %s: %v", m.DirOnDevice, err) + return nil + } + + // Start the Activity to prompt the user to download a new Intrusion Log + if err := adb.Client.IL(); err != nil { + log.Errorf("IL: failed to start activity: %v", err) + // Still allow pulling existing files if user wants; continue anyway. + } + + log.Info("On the device: open the screen that appears, scroll to the 'Download and Decrypt' button, and tap it.") + log.Info("Waiting for a new file to appear in " + m.DirOnDevice + " (Ctrl+C to skip waiting and continue acquisition)...") + + + // Watch directory (Ctrl+C cancels watch but continues acquisition) + ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) + defer stop() + + // Pulls every 2 seconds. Timeout after 10 minutes. + _, watchErr := m.waitForNewFile(ctx, m.DirOnDevice, before, 2*time.Second, 10*time.Minute) + if watchErr != nil { + // If user Ctrl+C, context is canceled and acquisition continues + log.Info("Stopped waiting; continuing with acquisition...") + } + + // Pull all files (old + new) + files, err := adb.Client.ListFiles(m.DirOnDevice, true) + if err != nil { + log.Errorf("IL: failed to list files for pull in %s: %v", m.DirOnDevice, err) + return nil + } + if len(files) == 0 { + log.Info("No files found in " + m.DirOnDevice) + return nil + } + + if err := m.pullAll(acq, files); err != nil { + log.Errorf("IL: failed pulling IL files: %v", err) + // continue acquisition + return nil + } + + log.Debug("Intrusion Logging acquisition is completed; continuing with acquisition ...") + return nil +} + + +func (m *IL) isILEnabledOnDevice() (bool, error) { + out, err := adb.Client.Shell("getprop", m.PropName) + if err != nil { + return false, err + } + + val := strings.TrimSpace(out) + + // We expect [prop.name]: [true] or [prop.name]: [1] + // so we need to parse it + if strings.HasPrefix(val, "[") && strings.Contains(val, "]:") { + parts := strings.SplitN(val, "]:", 2) + if len(parts) == 2 { + val = strings.TrimSpace(parts[1]) // now should be like: [true] + val = strings.Trim(val, "[] \t\r\n") + } + } + + return strings.EqualFold(val, "true") || val == "1", nil +} + +func (m *IL) listDirSet(dir string) (map[string]struct{}, error) { + files, err := adb.Client.ListFiles(dir, true) + if err != nil { + return nil, err + } + log.Debugf("IL: poll found %d files", len(files)) + log.Debugf("IL: poll files: %v", files) + set := make(map[string]struct{}, len(files)) + for _, f := range files { + set[f] = struct{}{} + } + return set, nil +} + +func (m *IL) waitForNewFile( + ctx context.Context, + dir string, + before map[string]struct{}, + pollEvery time.Duration, + maxWait time.Duration, +) (string, error) { + deadline := time.NewTimer(maxWait) + ticker := time.NewTicker(pollEvery) + defer deadline.Stop() + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + // Ctrl+C => continue acquisition (treat as non-fatal) + return "", ctx.Err() + case <-deadline.C: + return "", fmt.Errorf("timeout waiting for new IL file") + case <-ticker.C: + now, err := m.listDirSet(m.DirOnDevice) + if err != nil { + // non-fatal; keep polling + log.Debugf("IL: poll list failed: %v", err) + continue + } + for f := range now { + if _, existed := before[f]; !existed { + log.Info("Detected new file: " + f) + return f, nil + } + } + } + } +} + +func (m *IL) pullAll(acq *acquisition.Acquisition, deviceFiles []string) error { + for _, file := range deviceFiles { + if file == m.DirOnDevice { + continue + } + + rel := strings.TrimPrefix(file, m.DirOnDevice) + rel = strings.TrimPrefix(rel, "/") // optional safety if DirOnDevice lacks trailing / + + if acq.StreamingMode && acq.EncryptedWriter != nil { + zipPath := fmt.Sprintf("intrusion_logs/%s", rel) + + writer, err := acq.EncryptedWriter.CreateFile(zipPath) + if err != nil { + log.Errorf("Failed to create zip entry for IL file %s: %v\n", file, err) + continue + } + + err = acq.StreamingPuller.PullToWriter(file, writer) + if err != nil { + log.Errorf("Failed to stream IL file %s: %v\n", file, err) + continue + } + + log.Debugf("Streamed IL file %s directly to encrypted archive as %s", file, zipPath) + } else { + destPath := filepath.Join(m.ILPath, rel) + + if err := os.MkdirAll(filepath.Dir(destPath), 0o755); err != nil { + log.Errorf("Failed to create folders for IL file %s: %v\n", destPath, err) + continue + } + + out, err := adb.Client.Pull(file, destPath) + if err != nil { + log.Errorf("Failed to pull IL file %s: %s\n", file, strings.TrimSpace(out)) + continue + } + } + } + + return nil +} diff --git a/modules/modules.go b/modules/modules.go index 525ca90..4af36db 100644 --- a/modules/modules.go +++ b/modules/modules.go @@ -22,6 +22,7 @@ type Module interface { func List() []Module { return []Module{ NewBackup(), + NewIL(), NewPackages(), NewGetProp(), NewDumpsys(), From 33453f15956c5702ada4a2ddb02261f3805bdf41 Mon Sep 17 00:00:00 2001 From: tes Date: Wed, 18 Feb 2026 14:47:18 -0300 Subject: [PATCH 2/5] Add AAPM compatibility + enabled checks to IL module --- modules/intrusion_logs.go | 106 ++++++++++++++++++++++++-------------- 1 file changed, 68 insertions(+), 38 deletions(-) diff --git a/modules/intrusion_logs.go b/modules/intrusion_logs.go index d7bbfd9..8f7e227 100644 --- a/modules/intrusion_logs.go +++ b/modules/intrusion_logs.go @@ -29,13 +29,11 @@ type IL struct { StoragePath string ILPath string DirOnDevice string - PropName string } func NewIL() *IL { return &IL{ DirOnDevice: "/sdcard/Download/Intrusion Logging/", - PropName: "security.perf_harden", // TODO: temporary placeholder, switch to real IL prop when known } } @@ -59,15 +57,19 @@ func (m *IL) InitStorage(storagePath string) error { } func (m *IL) Run(acq *acquisition.Acquisition, fast bool) error { - // Check if IL is enabled on device - enabled, err := m.isILEnabledOnDevice() + // Check whether the device supports AAPM. + compatible, err := m.isAAPMCompatibleDevice() if err != nil { - // Don't break acquisition if the check fails; just log and skip. - log.Debugf("Failed to check prop %s: %v", m.PropName, err) + // Don't break acquisition if the check fails, just log and skip. + log.Debugf("Failed to check AAPM compatibility: %v", err) return nil } - if !enabled { - log.Debug("Intrusion Logging is not enabled; skipping") + + // TODO: Investigate whether IL data could exist on a non-compatible device + // (for example, restored or migrated from another device on the same Google account). + // If so, skipping here might miss existing data. + if !compatible { + log.Info("Device is not AAPM-compatible, skipping Intrusion Logging acquisition.") return nil } @@ -89,33 +91,48 @@ func (m *IL) Run(acq *acquisition.Acquisition, fast bool) error { return nil } - // Snapshot of Intrusion Logs folder before triggering new log download - before, err := m.listDirSet(m.DirOnDevice) + // Check whether AAPM is enabled right now. If disabled, don't start the activity + // or wait for a new file just pull whatever is already present. + // We still proceed with acquisition because older IL files may remain on disk + // and should be collected with user consent. + aapmEnabled, err := m.isAAPMEnabled() if err != nil { - log.Errorf("IL: failed to list %s: %v", m.DirOnDevice, err) - return nil + log.Debugf("Failed to check AAPM enabled state: %v", err) + aapmEnabled = false } - // Start the Activity to prompt the user to download a new Intrusion Log - if err := adb.Client.IL(); err != nil { - log.Errorf("IL: failed to start activity: %v", err) - // Still allow pulling existing files if user wants; continue anyway. - } + if aapmEnabled { + // Snapshot of Intrusion Logs folder before triggering new log download + before, err := m.listDirSet(m.DirOnDevice) - log.Info("On the device: open the screen that appears, scroll to the 'Download and Decrypt' button, and tap it.") - log.Info("Waiting for a new file to appear in " + m.DirOnDevice + " (Ctrl+C to skip waiting and continue acquisition)...") + if err != nil { + log.Errorf("IL: failed to list %s: %v", m.DirOnDevice, err) + return nil + } + // Start the Activity to prompt the user to download a new Intrusion Log + if err := adb.Client.IL(); err != nil { + log.Errorf("IL: failed to start activity: %v", err) + // Still allow pulling existing files if user wants; continue anyway. + } - // Watch directory (Ctrl+C cancels watch but continues acquisition) - ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) - defer stop() + log.Info("On the device: open the screen that appears, scroll to the 'Download and Decrypt' button, and tap it.") + log.Info("Waiting for a new file to appear in " + m.DirOnDevice + " (Ctrl+C to skip waiting and continue acquisition)...") - // Pulls every 2 seconds. Timeout after 10 minutes. - _, watchErr := m.waitForNewFile(ctx, m.DirOnDevice, before, 2*time.Second, 10*time.Minute) - if watchErr != nil { - // If user Ctrl+C, context is canceled and acquisition continues - log.Info("Stopped waiting; continuing with acquisition...") + + // Watch directory (Ctrl+C cancels watch but continues acquisition) + ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) + defer stop() + + // Pulls every 2 seconds. Timeout after 15 minutes. + _, watchErr := m.waitForNewFile(ctx, m.DirOnDevice, before, 2*time.Second, 15*time.Minute) + if watchErr != nil { + // If user Ctrl+C, context is canceled and acquisition continues + log.Info("Stopped waiting, continuing with acquisition...") + } + } else { + log.Debug("AAPM is disabled, skipping activity launch and new file watcher (pulling existing files only).") } // Pull all files (old + new) @@ -139,26 +156,39 @@ func (m *IL) Run(acq *acquisition.Acquisition, fast bool) error { return nil } +func (m *IL) isAAPMCompatibleDevice() (bool, error) { + // adb shell settings get secure advanced_protection_mode + out, err := adb.Client.Shell("settings", "get", "secure", "advanced_protection_mode") + if err != nil { + return false, err + } + + val := strings.TrimSpace(out) + // If the key does not exist, Android prints "null". We infer this is not compatible. + if strings.EqualFold(val, "null") || val == "" { + return false, nil + } + + // If it's compatible, it should be "0" or "1" (treat anything non-null as compatible) + return true, nil +} -func (m *IL) isILEnabledOnDevice() (bool, error) { - out, err := adb.Client.Shell("getprop", m.PropName) +func (m *IL) isAAPMEnabled() (bool, error) { + // adb shell settings get secure advanced_protection_mode + out, err := adb.Client.Shell("settings", "get", "secure", "advanced_protection_mode") if err != nil { return false, err } val := strings.TrimSpace(out) - // We expect [prop.name]: [true] or [prop.name]: [1] - // so we need to parse it - if strings.HasPrefix(val, "[") && strings.Contains(val, "]:") { - parts := strings.SplitN(val, "]:", 2) - if len(parts) == 2 { - val = strings.TrimSpace(parts[1]) // now should be like: [true] - val = strings.Trim(val, "[] \t\r\n") - } + // If the key is missing Android returns "null" + if strings.EqualFold(val, "null") || val == "" { + return false, nil } - return strings.EqualFold(val, "true") || val == "1", nil + // AAPM is enabled only when the value is exactly "1" + return val == "1", nil } func (m *IL) listDirSet(dir string) (map[string]struct{}, error) { From d6e6cade05b6ea2c984ad9f2dfa60c800c923572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donncha=20=C3=93=20Cearbhaill?= Date: Sun, 22 Feb 2026 23:38:45 +0100 Subject: [PATCH 3/5] Improve CLI logs in Intrusion Logging command --- modules/intrusion_logs.go | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/modules/intrusion_logs.go b/modules/intrusion_logs.go index 8f7e227..a75dd3f 100644 --- a/modules/intrusion_logs.go +++ b/modules/intrusion_logs.go @@ -1,16 +1,16 @@ // androidqf - Android Quick Forensics -// Copyright (c) 2021-2023 Claudio Guarnieri. +// Copyright (c) 2021-2026 Claudio Guarnieri. // Use of this software is governed by the MVT License 1.1 that can be found at // https://license.mvt.re/1.1/ package modules import ( + "context" "fmt" "os" - "path/filepath" - "context" "os/signal" + "path/filepath" "strings" "time" @@ -22,7 +22,7 @@ import ( const ( acquireIL = "Yes" - skipIL = "No" + skipIL = "No" ) type IL struct { @@ -74,7 +74,7 @@ func (m *IL) Run(acq *acquisition.Acquisition, fast bool) error { } // Ask user first - log.Info("Would you like to take the Intrusion Logs of the device?") + log.Info("Would you like to download Intrusion Logs from the device?") promptIL := promptui.Select{ Label: "Intrusion Logs", Items: []string{acquireIL, skipIL}, @@ -91,7 +91,6 @@ func (m *IL) Run(acq *acquisition.Acquisition, fast bool) error { return nil } - // Check whether AAPM is enabled right now. If disabled, don't start the activity // or wait for a new file just pull whatever is already present. // We still proceed with acquisition because older IL files may remain on disk @@ -113,14 +112,14 @@ func (m *IL) Run(acq *acquisition.Acquisition, fast bool) error { // Start the Activity to prompt the user to download a new Intrusion Log if err := adb.Client.IL(); err != nil { - log.Errorf("IL: failed to start activity: %v", err) + log.Errorf("Failed to launch intrusion detection activity: %v\n", err) // Still allow pulling existing files if user wants; continue anyway. } - log.Info("On the device: open the screen that appears, scroll to the 'Download and Decrypt' button, and tap it.") - log.Info("Waiting for a new file to appear in " + m.DirOnDevice + " (Ctrl+C to skip waiting and continue acquisition)...") - + log.Info("Launched the Intrusion Logging settings page.") + log.Info("On the device: scroll down, tap 'Access Logs', then press 'Download and Decrypt'.\n") + log.Info("Waiting for intrusion logs to be written to device. (Ctrl+C to skip waiting and continue acquisition)...") // Watch directory (Ctrl+C cancels watch but continues acquisition) ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) defer stop() @@ -151,9 +150,9 @@ func (m *IL) Run(acq *acquisition.Acquisition, fast bool) error { // continue acquisition return nil } - - log.Debug("Intrusion Logging acquisition is completed; continuing with acquisition ...") - return nil + log.Infof("Downloaded %d Instrusion Logging files from the phone.", len(files)) + log.Info("Intrusion Logging acquisition is completed; continuing with acquisition ...") + return nil } func (m *IL) isAAPMCompatibleDevice() (bool, error) { @@ -196,8 +195,7 @@ func (m *IL) listDirSet(dir string) (map[string]struct{}, error) { if err != nil { return nil, err } - log.Debugf("IL: poll found %d files", len(files)) - log.Debugf("IL: poll files: %v", files) + log.Debugf("IL: Polling found %d intrusion logging files on device at '%s'", len(files), dir) set := make(map[string]struct{}, len(files)) for _, f := range files { set[f] = struct{}{} From 3ec3c626f3609625c48db3347620a20fe866954d Mon Sep 17 00:00:00 2001 From: tes Date: Tue, 24 Feb 2026 17:28:15 -0300 Subject: [PATCH 4/5] Improve IL acquisition by waiting for intrusion logs from multiple devices until Ctrl+C or timeout --- modules/intrusion_logs.go | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/modules/intrusion_logs.go b/modules/intrusion_logs.go index a75dd3f..a7278ec 100644 --- a/modules/intrusion_logs.go +++ b/modules/intrusion_logs.go @@ -117,15 +117,15 @@ func (m *IL) Run(acq *acquisition.Acquisition, fast bool) error { } log.Info("Launched the Intrusion Logging settings page.") - log.Info("On the device: scroll down, tap 'Access Logs', then press 'Download and Decrypt'.\n") + log.Info("On the device: scroll down, tap 'Access Logs', then press 'Download and Decrypt' for each listed device.\n") log.Info("Waiting for intrusion logs to be written to device. (Ctrl+C to skip waiting and continue acquisition)...") // Watch directory (Ctrl+C cancels watch but continues acquisition) ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) defer stop() - // Pulls every 2 seconds. Timeout after 15 minutes. - _, watchErr := m.waitForNewFile(ctx, m.DirOnDevice, before, 2*time.Second, 15*time.Minute) + // Pulls every 2 seconds. Stops on Ctrl+C or after 15 minutes. + watchErr := m.waitForNewFiles(ctx, m.DirOnDevice, before, 2*time.Second, 15*time.Minute) if watchErr != nil { // If user Ctrl+C, context is canceled and acquisition continues log.Info("Stopped waiting, continuing with acquisition...") @@ -203,36 +203,45 @@ func (m *IL) listDirSet(dir string) (map[string]struct{}, error) { return set, nil } -func (m *IL) waitForNewFile( +// Watch for new files until Ctrl+C or timeout. +func (m *IL) waitForNewFiles( ctx context.Context, dir string, before map[string]struct{}, pollEvery time.Duration, maxWait time.Duration, -) (string, error) { - deadline := time.NewTimer(maxWait) +) error { ticker := time.NewTicker(pollEvery) - defer deadline.Stop() + timeout := time.NewTimer(maxWait) + defer ticker.Stop() + defer timeout.Stop() for { select { case <-ctx.Done(): - // Ctrl+C => continue acquisition (treat as non-fatal) - return "", ctx.Err() - case <-deadline.C: - return "", fmt.Errorf("timeout waiting for new IL file") + // Ctrl+C => continue acquisition (non-fatal) + log.Info("Ctrl+C detected. Continuing acquisition...") + return nil + + case <-timeout.C: + log.Info("Finished waiting for intrusion logs (15 minute timeout reached).") + return nil + case <-ticker.C: - now, err := m.listDirSet(m.DirOnDevice) + now, err := m.listDirSet(dir) if err != nil { - // non-fatal; keep polling log.Debugf("IL: poll list failed: %v", err) continue } + for f := range now { if _, existed := before[f]; !existed { - log.Info("Detected new file: " + f) - return f, nil + log.Infof( + "Detected new file: %s.\nIf you finished downloading logs, press Ctrl+C to continue acquisition.", + f, + ) + before[f] = struct{}{} } } } From efb313e1b2eb91f2e8ed8eb8b3135d3a39d88f2f Mon Sep 17 00:00:00 2001 From: tes Date: Mon, 11 May 2026 12:05:35 -0300 Subject: [PATCH 5/5] Fix lowercase error strings for staticcheck --- adb/adb.go | 2 +- modules/intrusion_logs.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/adb/adb.go b/adb/adb.go index ddb9c8b..7e92766 100644 --- a/adb/adb.go +++ b/adb/adb.go @@ -183,7 +183,7 @@ func (a *ADB) IL() error { "com.google.android.gms/.intrusiondetection.ui.retrieval.IntrusionDetectionRetrievalActivity", ) if err != nil { - return fmt.Errorf("Failed to start IL activity: %v: %s", err, cmd) + return fmt.Errorf("failed to start IL activity: %v: %s", err, cmd) } return nil } diff --git a/modules/intrusion_logs.go b/modules/intrusion_logs.go index a7278ec..d0c2219 100644 --- a/modules/intrusion_logs.go +++ b/modules/intrusion_logs.go @@ -49,7 +49,7 @@ func (m *IL) InitStorage(storagePath string) error { if storagePath != "" { err := os.Mkdir(m.ILPath, 0o755) if err != nil && !os.IsExist(err) { - return fmt.Errorf("Failed to create Intrusion Logging folder: %v", err) + return fmt.Errorf("failed to create Intrusion Logging folder: %v", err) } } @@ -82,7 +82,7 @@ func (m *IL) Run(acq *acquisition.Acquisition, fast bool) error { _, ILOption, err := promptIL.Run() if err != nil { - return fmt.Errorf("Failed to make selection for IL option: %v", err) + return fmt.Errorf("failed to make selection for IL option: %v", err) } // User declined so we continue acquisition normally