Files
T
mukul975 c47eed6a64 Production hardening: security fixes, code quality, 724 skills complete
- Fix 25 shell=True subprocess calls with list-based commands
- Fix 49 verify=False in defensive skills (env-var override)
- Add timeout to 231 HTTP/subprocess/socket calls
- Fix 6 SQL injection patterns with whitelist validation
- Replace 8 __import__() with standard imports
- Remove 701 unused imports across 442 files
- Add authorized-testing disclaimers to all offensive skills
- Complete 11 incomplete skill directories
- Expand 10 stub SKILL.md files with full content
- Fix 2 YAML parse errors in frontmatter
- Fix 5 pre-existing syntax errors
- Convert 22 hardcoded paths/ports to environment variables
- Back up 21 redundant skill pairs to .bak
- Fix 2 global declaration errors
- 724/724 skills with full folder anatomy (SKILL.md + agent.py + api-reference.md + LICENSE)
- 0 compile errors across all 724 agent.py files
2026-03-19 13:26:49 +01:00

5.5 KiB

API Reference: MobSF Android Static Analysis

Libraries Used

Library Purpose
requests HTTP client for MobSF REST API v1
json Parse scan reports and finding data
os Read MOBSF_URL and MOBSF_API_KEY environment variables

Installation

pip install requests

# MobSF server (Docker)
docker pull opensecurity/mobile-security-framework-mobsf
docker run -it -p 8000:8000 opensecurity/mobile-security-framework-mobsf

Authentication

MobSF uses API key authentication. The default key is shown on the MobSF dashboard:

import requests
import os

MOBSF_URL = os.environ.get("MOBSF_URL", "http://localhost:8000")
MOBSF_KEY = os.environ["MOBSF_API_KEY"]
headers = {"Authorization": MOBSF_KEY}

REST API v1 Endpoints

Method Endpoint Description
POST /api/v1/upload Upload APK, IPA, ZIP, or APPX for analysis
POST /api/v1/scan Trigger static analysis on uploaded file
GET /api/v1/report_json Get full JSON analysis report
POST /api/v1/download_pdf Download PDF report
GET /api/v1/scans List recent scans
POST /api/v1/delete_scan Delete a scan and its data
POST /api/v1/search Search scans by hash or filename
POST /api/v1/compare Compare two app scans
GET /api/v1/scorecard Get app security scorecard
GET /api/v1/scan_logs View live scan logs

Core Operations

Upload an APK

def upload_apk(file_path):
    with open(file_path, "rb") as f:
        resp = requests.post(
            f"{MOBSF_URL}/api/v1/upload",
            files={"file": (os.path.basename(file_path), f, "application/octet-stream")},
            headers=headers,
            timeout=120,
        )
    resp.raise_for_status()
    result = resp.json()
    return result["hash"], result["scan_type"], result["file_name"]
    # hash: SHA-256 of the uploaded file
    # scan_type: "apk", "ipa", "zip", "appx"

Trigger Static Analysis

def start_scan(file_hash, scan_type, file_name):
    resp = requests.post(
        f"{MOBSF_URL}/api/v1/scan",
        data={
            "hash": file_hash,
            "scan_type": scan_type,
            "file_name": file_name,
        },
        headers=headers,
        timeout=600,  # Scans can take several minutes
    )
    resp.raise_for_status()
    return resp.json()

Retrieve JSON Report

def get_report(file_hash):
    resp = requests.post(
        f"{MOBSF_URL}/api/v1/report_json",
        data={"hash": file_hash},
        headers=headers,
        timeout=60,
    )
    resp.raise_for_status()
    return resp.json()

Extract Key Findings

def extract_findings(report):
    findings = {
        "security_score": report.get("security_score", "N/A"),
        "app_name": report.get("app_name"),
        "package_name": report.get("package_name"),
        "target_sdk": report.get("target_sdk"),
        "min_sdk": report.get("min_sdk"),
        "permissions": {
            "dangerous": [],
            "normal": [],
        },
        "manifest_issues": [],
        "code_issues": [],
        "binary_issues": [],
    }

    # Dangerous permissions
    for perm, details in report.get("permissions", {}).items():
        status = details.get("status", "normal")
        if status == "dangerous":
            findings["permissions"]["dangerous"].append(perm)
        else:
            findings["permissions"]["normal"].append(perm)

    # Manifest analysis
    for issue in report.get("manifest_analysis", []):
        if issue.get("severity") in ("high", "warning"):
            findings["manifest_issues"].append({
                "title": issue["title"],
                "severity": issue["severity"],
                "description": issue["description"],
            })

    # Code analysis
    for issue_key, issue_data in report.get("code_analysis", {}).items():
        findings["code_issues"].append({
            "rule": issue_key,
            "severity": issue_data.get("level"),
            "description": issue_data.get("description"),
            "files": issue_data.get("path", [])[:5],
        })

    return findings

Download PDF Report

def download_pdf(file_hash, output_path):
    resp = requests.post(
        f"{MOBSF_URL}/api/v1/download_pdf",
        data={"hash": file_hash},
        headers=headers,
        timeout=120,
    )
    resp.raise_for_status()
    with open(output_path, "wb") as f:
        f.write(resp.content)

Compare Two Applications

resp = requests.post(
    f"{MOBSF_URL}/api/v1/compare",
    data={"hash1": hash_v1, "hash2": hash_v2},
    headers=headers,
    timeout=120,
)
comparison = resp.json()
# Shows permission changes, new vulnerabilities, code changes

Output Format

{
  "file_name": "app-debug.apk",
  "app_name": "TestApp",
  "package_name": "com.example.testapp",
  "security_score": 42,
  "target_sdk": "33",
  "min_sdk": "24",
  "permissions": {
    "android.permission.INTERNET": {"status": "normal", "description": "..."},
    "android.permission.READ_CONTACTS": {"status": "dangerous", "description": "..."}
  },
  "manifest_analysis": [
    {"title": "Application is debuggable", "severity": "high", "description": "..."}
  ],
  "code_analysis": {
    "android_insecure_random": {
      "level": "high",
      "description": "Insecure Random Number Generator",
      "path": ["com/example/CryptoUtils.java"]
    }
  },
  "binary_analysis": [
    {"title": "NX bit not set", "severity": "high"}
  ]
}