Files
T
mukul975 c21af3347e Complete folder anatomy for all 649 cybersecurity skills + update LICENSE to Mahipal
- Add scripts/agent.py and references/api-reference.md to all remaining skills
- Update all 648 LICENSE files: copyright now reads 'Mahipal'
- Add implementing-security-monitoring-with-datadog (new skill with full anatomy)
- All 649 skills now have: SKILL.md, LICENSE, scripts/agent.py, references/api-reference.md
2026-03-11 00:22:12 +01:00

174 lines
6.3 KiB
Python

#!/usr/bin/env python3
"""Agent for detecting insecure data storage in mobile applications."""
import argparse
import json
import os
import re
import subprocess
import sys
import sqlite3
from datetime import datetime, timezone
ANDROID_SENSITIVE_PATHS = [
"/data/data/{package}/shared_prefs/",
"/data/data/{package}/databases/",
"/data/data/{package}/files/",
"/data/data/{package}/cache/",
"/sdcard/Android/data/{package}/",
]
SENSITIVE_PATTERNS = {
"api_key": re.compile(r'["\']?api[_-]?key["\']?\s*[:=]\s*["\']([^"\']+)', re.I),
"token": re.compile(r'["\']?(?:access|auth|bearer)[_-]?token["\']?\s*[:=]\s*["\']([^"\']+)', re.I),
"password": re.compile(r'["\']?password["\']?\s*[:=]\s*["\']([^"\']+)', re.I),
"private_key": re.compile(r'-----BEGIN (?:RSA )?PRIVATE KEY-----'),
"base64_cred": re.compile(r'["\']?(?:auth|credential)["\']?\s*[:=]\s*["\']([A-Za-z0-9+/=]{20,})', re.I),
}
def scan_shared_prefs(prefs_dir):
"""Scan Android SharedPreferences XML files for sensitive data."""
findings = []
if not os.path.isdir(prefs_dir):
return findings
for fname in os.listdir(prefs_dir):
if not fname.endswith(".xml"):
continue
fpath = os.path.join(prefs_dir, fname)
try:
with open(fpath, "r", errors="replace") as f:
content = f.read()
for pattern_name, pattern in SENSITIVE_PATTERNS.items():
matches = pattern.findall(content)
if matches:
findings.append({
"file": fpath,
"type": "shared_prefs",
"pattern": pattern_name,
"match_count": len(matches),
"severity": "HIGH",
})
except PermissionError:
pass
return findings
def scan_sqlite_databases(db_dir):
"""Scan SQLite databases for unencrypted sensitive data."""
findings = []
if not os.path.isdir(db_dir):
return findings
for fname in os.listdir(db_dir):
if not fname.endswith((".db", ".sqlite", ".sqlite3")):
continue
fpath = os.path.join(db_dir, fname)
try:
conn = sqlite3.connect(fpath)
cursor = conn.cursor()
cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
tables = cursor.fetchall()
for (table_name,) in tables:
cursor.execute(f"PRAGMA table_info({table_name})")
columns = cursor.fetchall()
sensitive_cols = []
for col in columns:
col_name = col[1].lower()
for sf in ["password", "token", "secret", "key", "ssn", "credit"]:
if sf in col_name:
sensitive_cols.append(col[1])
if sensitive_cols:
cursor.execute(f"SELECT COUNT(*) FROM {table_name}")
row_count = cursor.fetchone()[0]
findings.append({
"file": fpath,
"table": table_name,
"sensitive_columns": sensitive_cols,
"row_count": row_count,
"encrypted": False,
"severity": "CRITICAL",
})
conn.close()
except (sqlite3.Error, PermissionError):
pass
return findings
def scan_file_storage(files_dir):
"""Scan app file storage for sensitive data."""
findings = []
if not os.path.isdir(files_dir):
return findings
for root, _, files in os.walk(files_dir):
for fname in files:
fpath = os.path.join(root, fname)
try:
with open(fpath, "r", errors="replace") as f:
content = f.read(4096)
for pattern_name, pattern in SENSITIVE_PATTERNS.items():
if pattern.search(content):
findings.append({
"file": fpath,
"pattern": pattern_name,
"severity": "HIGH",
})
except (PermissionError, UnicodeDecodeError):
pass
return findings
def adb_pull_app_data(package_name, output_dir):
"""Pull application data via ADB for analysis."""
os.makedirs(output_dir, exist_ok=True)
paths = [p.format(package=package_name) for p in ANDROID_SENSITIVE_PATHS]
results = []
for path in paths:
try:
subprocess.check_output(
["adb", "pull", path, output_dir],
text=True, errors="replace", timeout=15
)
results.append({"path": path, "status": "pulled"})
except subprocess.SubprocessError:
results.append({"path": path, "status": "failed"})
return results
def main():
parser = argparse.ArgumentParser(
description="Detect insecure data storage in mobile apps (authorized testing only)"
)
parser.add_argument("--scan-dir", help="Directory containing app data to scan")
parser.add_argument("--package", help="Android package name for ADB pull")
parser.add_argument("--pull-dir", default="/tmp/mobile_audit")
parser.add_argument("--output", "-o", help="Output JSON report")
args = parser.parse_args()
print("[*] Insecure Mobile Data Storage Detection Agent")
report = {"timestamp": datetime.now(timezone.utc).isoformat(), "findings": []}
scan_dir = args.scan_dir or args.pull_dir
if args.package:
adb_pull_app_data(args.package, args.pull_dir)
if os.path.isdir(scan_dir):
report["findings"].extend(scan_shared_prefs(scan_dir))
report["findings"].extend(scan_sqlite_databases(scan_dir))
report["findings"].extend(scan_file_storage(scan_dir))
critical = sum(1 for f in report["findings"] if f.get("severity") == "CRITICAL")
report["risk_level"] = "CRITICAL" if critical else "HIGH" if report["findings"] else "LOW"
print(f"[*] Findings: {len(report['findings'])} (CRITICAL: {critical})")
if args.output:
with open(args.output, "w") as f:
json.dump(report, f, indent=2)
print(f"[*] Report saved to {args.output}")
else:
print(json.dumps(report, indent=2))
if __name__ == "__main__":
main()