APSA mobile app security audit

io.github.ictechgy由 GitHub 账号 ictechgy 发布v1.7.0更新于 Nov 3, 2026

Local, evidence-graded Android/iOS security audits of source, APK, AAB and IPA for AI agents.

发布方自己的服务:audit.domains - Domain Valuation

概览

AI 生成的概览

本地 MCP 服务器,让助手对源码、APK、AAB 和 IPA 执行分级取证式 Android/iOS 安全审计,并查看已保存的报告。

可以做什么
  • 查询能力、输入类型与运行时要求,并对本地源码、APK、AAB、APKS 或 IPA 执行静态审计,支持后台持久任务的状态查询与取消。
  • 读取、分页、比较和追踪已保存报告,映射到 MASVS 检查清单,导入其他工具的 SARIF 结果,并导出经批准的基线。
  • 检索本地缓存的 CVE 与 OWASP 情报、同步公开公告,并将清单中的依赖版本与 OSV 比对。
  • 校验项目污点规范,交叉核对其他工具或 AI 审查者提出的发现,并评估团队策略阈值与豁免。
  • 生成运行时测试场景计划,并列出 adb 设备与 iOS 模拟器而不改变应用状态。
适用场景
当你希望助手审计自己拥有或获授权测试的应用、长期跟踪与比较发现,或核实代码审查者提出的安全结论时使用。适合希望把本地、优先离线的移动安全检测接入智能体或 CI 流程的团队。
运行要求
通过 uvx 从 apsa PyPI 包以本地 stdio 进程运行;需要 macOS 或 Linux 上的 uv,以及 CPython 3.11 或 3.12。必须提供显式 --root 以限制审计目标与报告访问范围。不需要模型提供方或 LLM API 密钥。仅在同步公告和查询 OSV 依赖时需要网络;可选设备工具由 doctor 检查。
安装前请注意
审计会读取本地源码与构建产物,并在本地写入报告;联网时会把符合条件的依赖名称和版本发送给 OSV,因此每次调用 dependency_check 都要传入排除列表,因为不会继承报告中记录的扫描排除项。设备执行需显式开启,且仅在启动参数下注册。发现是分级证据而非安全证明:未运行或无发现都不代表安全,签名块也不能证明真实性。报告元数据可能被发送到客户端所用的模型提供方。

安装

在 SourceWeft 中

  1. 打开 控制台中的 APSA mobile app security audit,将其添加到工作区。
  2. 为需要使用其工具的对话启用该服务。

Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。

其他 MCP 客户端

参照 仓库 中的启动说明。

README

APSA

APSA 1.7.0. See the release scope and upgrade notes. Release readiness separates local validation from the hosted publication and public installation gates.

CI semantics change in 1.7.0. Coverage now separates execution gaps (partial, exit 3) from precision limits of the bounded static model (limited, exit 0 unless --strict-coverage or policy strict_coverage is set), and a threshold failure (exit 4) now takes precedence over an incomplete audit (exit 3). Waivers can record the approved severity, dependency advisory findings keep matching across advisory alias changes, and detection is broader for Kotlin, Swift/SwiftUI, archives and platform configuration. Review the upgrade notes before reusing 1.6.x CI conditions or baselines.

Evidence-first security audits for Android and iOS.

English · 한국어

This English README is the source of truth. The Korean README follows it.

AI-assisted attack follow-up in 1.6.3. Packaged plaintext credentials now have a separate, masked candidate check; compiled source-flow coverage remains distinct. Model context retains the untrusted-evidence guidance on detailed and truncated pages. See scope and research. For easier use today, run apsa tui. A desktop app roadmap describes a future local wrapper around the same engine; no desktop installer is included in this release.

APSA 1.6.2 fixes Objective-C binding scope, incomplete OSV queries, precise claim verification, runtime cancellation and APK-set finding provenance. See patch scope and upgrade effects.

Upgrading to 1.6.3. Rescan before comparing reports or approving baselines: explicit synthetic evidence path kinds and deterministic CVE-alias selection can change finding IDs. Candidate-only SARIF descriptor IDs also migrate; APSA rule IDs and finding fingerprints remain unchanged by that SARIF change. Review baseline and waiver effects in the upgrade notes.

APSA 1.6 analysis extensions. This version adds bounded native cross-file flow and input-bound path guards, JS/TS/Dart candidates, recorded Gradle variant selection, OSV batching and transmission exclusions, authenticated runtime evidence, AAB scalar resources, detached CMS consistency and paired verification. See scope, options and remaining limits and the new labeled synthetic results. The remaining follow-up adds explicit .apks device selection, IPA sparse-page/resource seals and supplied-anchor chain checks, and a typed runtime recorder exercised with fresh synthetic app models. The synthetic bundletool selection checks pass; installed split merging, Apple signer identity and physical-device validation remain outside verified scope. Fresh comparator samples remain partial and do not establish accuracy rankings. iOS OS URL delivery is still under validation; callback dispatch alone is not proof of app acquisition or backend revocation. The five validation gates record new compiler, signature-mutation and installed iOS simulator evidence, along with the separate package, comparator, bundletool and backend measurement scopes. See 1.6 release validation for the original 1.6 validation gates and upgrade effects, including the changed MCP tool manifest.

APSA 1.6 large-input follow-up. Rule version: 2026.10.15.apsa.160. Input staging and AST analysis share code priorities and round-robin scheduling within inferred module groups. Lock inventories follow app code; ambiguous *Tests/ and plain Frameworks/ directories retain production priority. Reports record module omissions, comparisons expose input completeness, and the Action summary shows staging omissions. Partial fingerprints remain SBOM metadata instead of application hashes. See scope and validation.

APSA 1.6 review fixes. Bounded device-spec metadata, per-file partial reasons, text/compiled hybrid coverage, explicit signature binding scope, portable source comparisons and exact package-content checks are documented in strengthening scope and evidence limits. Synthetic acquisition adapters are source-test helpers and are excluded from the installed wheel.

APSA helps developers and security teams audit their own mobile apps. It inspects source code and APK/AAB/IPA builds, correlates public vulnerability information, and keeps evidence, coverage, and report history together. Use the CLI, terminal UI, or the same audit engine through MCP and reusable skills.

Pronounced “ap-sah”; Korean name 앱사. The name connects “app + audit” with App Security Audit. APSA combines the earlier Quaygate lint engine and Mobile Audit workflows in one package.

APSA 1.1 adds bounded model report pages, portable approved baselines, exported policy decisions, and explicit source module/configuration selection. See model and team workflows for the contract and release sequence for later analysis milestones.

Mixed unsupported source languages and truncated pattern results are incomplete. Declared Gradle versions remain candidates until resolved-build evidence is supplied. OS-CVE correlation reports a bounded recent window, so feed freshness alone cannot satisfy historical coverage.

APSA 1.2 adds AAB base-manifest/module DEX analysis, embedded IPA Mach-O metadata, bounded Objective-C .m candidates and parser OS isolation that is on by default where it can start. Every AAB audit stays partial, and embedded IPA metadata does not authenticate signatures. See analysis scope and limits and the independent holdout.

Upgrading from 1.1. With the default APSA_PARSER_SANDBOX=auto, the parser runs under macOS Seatbelt or Linux bubblewrap after an input-free activation probe succeeds. Without a backend, the report records parser_isolation.state: unavailable and parses with resource limits only. If the backend is present but cannot start (for example inside another sandbox or with blocked user namespaces), the report also records the attempted backend and probe reason and adds an inventory warning. required refuses such audits; off skips the backend. A parser failure after isolation has started aborts the audit without an unsandboxed retry. Dependencies declared only in a Gradle version catalog no longer receive CVE matches until build usage or resolved versions are supplied. New rules (AST-CRYPTO-ECB, AST-CRYPTO-WEAK-HASH, AST-SQL-CONCAT, OBJC-CRYPTO-WEAK-HASH, OBJC-WEBVIEW-UNTRUSTED-REQUEST) can add findings relative to existing 1.1 baselines.

APSA 1.3 links Gradle version-catalog aliases to the shipped dependency configurations that use them and reads application-module gradle.lockfile coordinates as resolved versions. It also decodes AAB feature-module manifests, recomputes Mach-O CodeDirectory page and entitlement hashes without authenticating the signature, adapts more Swift and Objective-C syntax, and can backfill an explicit range of Android security bulletins with SoC vendor and vendor patch-level context. See the dependency evidence results, the independent source pairs and analysis scope and limits.

Upgrading from 1.2.

  • Catalog aliases referenced from shipped configurations become declared candidates and can receive CVE matches again; unreferenced, test-only and build-tooling aliases stay unresolved. Application-module lockfile coordinates are exact, so their matches are version-affected, and the default policy fails on high and critical ones; other modules' lockfile coordinates stay declared candidates. A declared candidate that every shipping application resolved is superseded and leaves CVE correlation. Partial inputs keep every candidate, and with unparsed project references only an application's own declarations can be superseded.
  • Lockfile assumptions: APSA does not check that dependency locking is enabled or that the lockfile is current, and it merges the release runtime classpaths of every application module (flavors and wear, TV or automotive apps). --source-module scans only that module's directory, so a root version catalog and library-module declarations outside it are not read.
  • Baselines and waivers: a lockfile-resolved finding has a new location (gradle.lockfile) and status, so only_new treats it as new and waivers for the old finding ID no longer match. Re-approve baselines after upgrading.
  • --online sends dependency names and versions to OSV, including shipped catalog aliases, transitive lockfile coordinates and private group IDs unless excluded. The default auto mode uses legacy queries for at most 100 eligible unique package-version coordinates and batch queries for larger inventories. Default online budgets are 1000 packages, 100 packages per batch and 2000 OSV requests, including pagination and vulnerability-record lookups. scan --online --osv-query-mode legacy retains the 100-package limit; --osv-query-mode batch explicitly selects batching with the default budgets. Use scan's --osv-exclude-ecosystem, --osv-exclude-name or --osv-exclude-coordinate for local transmission exclusions. Excluded dependencies are never sent or marked checked, even with a prior cached result. Unchecked, unresolved, unsupported or excluded dependencies keep online coverage incomplete; a fresh result for one package does not complete the rest.
  • IPA reports gain BINARY-IOS-CODE-INTEGRITY. Consistent hashes do not prove authenticity, because a modified binary that was re-signed is consistent. A mismatch adds a warning, not a finding; App Store encrypted and unsigned executables stay not-run.
  • intel sync refreshes only the recent Android bulletin window; use intel backfill for older months. Records cached by 1.2 derive their vendor scope from the component name; entries outside chipset and kernel sections use the platform patch level only. AAB feature-module installation stays unknown.
  • Adapted Swift and Objective-C files stay partial, and an Objective-C function overlapping a normalized preprocessor conditional is skipped as uncertain. Unmodified 1.2 skills upgrade with apsa skill install.

APSA 1.4 is easier to put in front of developers and coding agents. A GitHub Action uploads SARIF to code scanning with stable fingerprints, security severity, MASWE tags and evidence status; every report carries an OWASP MASWE v1.0 coverage matrix that names the weaknesses APSA did not assess; and the MCP server is packaged for the MCP Registry and as a Claude Code plugin, with a documented MCP security model.

Upgrading from 1.3.

  • SARIF exports now list a descriptor for every rule with results (help, security-severity, precision, MASVS/MASWE tags), add partialFingerprints from the finding identity, and report not-run and partial coverage as tool execution notifications. Candidate findings are warning rather than error and their rules are tagged candidate. --sarif-root makes locations repository-relative; URIs are percent-encoded, and APK/AAB/IPA members and files outside the target appear as logical locations on the target. Code scanning may therefore show re-keyed alerts once.
  • New source checks (exported components, backup, targetSdk, privacy manifests, ATS exceptions, hard-coded secrets, insecure randomness, trust-all TLS, external storage, Java deserialization, more SQL sinks, mutable PendingIntent) add findings and coverage entries. A policy with required_rules or a severity gate can fail where 1.3 passed; review the first 1.4 report before tightening gates.
  • Rule metadata gains MASWE v1.0 identifiers; model context and capabilities gain a maswe section, and Markdown reports a MASWE coverage table. Reports derived by reports ingest add external_inputs and findings with origin: external; existing fields keep their meaning.
  • The MCP server adds verify_finding, specs_validate, reports_ingest_sarif, reports_checklist and reports_history. tool_manifest_sha256 now covers a normalized APSA-owned surface, so its value changes; update any pinned hash. Unmodified 1.3 skills upgrade with apsa skill install.

APSA 1.5 widens what a source scan looks for. New checks cover the OWASP MASWE weaknesses behind most MASTG demo misses: broken ciphers and implicit ECB, key material and IVs built from constants, short (512–1536-bit) RSA, DSA and DH key sizes and CryptoKit Insecure digests; biometric results not bound to a key, device-credential fallback and keys that survive new enrollment; TLS below 1.2, iOS connections outside App Transport Security, and user CAs or cleartext in the network security configuration; unverified App Links, intent redirection and implicit internal Intents; path traversal and keyed unarchiving without secure coding; and Safe Browsing turned off, UIWebView and WKWebView file access. On a blind holdout of 24 public vulnerable/fixed pairs labeled by MASWE weakness, APSA 1.5.0 found 7 of the 21 scored in-scope pairs at the labeled lines (Wilson 95% 17–55%). Two of the seven come from rules new in 1.5 and five from rules 1.4 already had; the fixed-side FP upper bound is 4 of 21, and a 22nd in-scope pair failed to scan. See the blind pair results and analysis scope and limits.

Upgrading from 1.4.

  • New rules add findings and coverage entries: AST-CRYPTO-WEAK-CIPHER, IOS-CRYPTO-WEAK-CIPHER, AST-CRYPTO-HARDCODED-KEY, AST-CRYPTO-STATIC-IV, SOURCE-WEAK-KEY-SIZE, SOURCE-BIOMETRIC-EVENT-BOUND, SOURCE-BIOMETRIC-FALLBACK, SOURCE-BIOMETRIC-ENROLLMENT, SOURCE-WEAK-TLS-VERSION, IOS-ATS-BYPASS-API, ANDROID-NSC-USER-CA, ANDROID-DEEPLINK-AUTOVERIFY, AST-INTENT-REDIRECTION, AST-IMPLICIT-INTENT, IOS-WEBVIEW-FILE-ACCESS, WEBVIEW-SAFE-BROWSING-OFF, IOS-UIWEBVIEW, AST-PATH-TRAVERSAL and IOS-INSECURE-UNARCHIVE. Candidates are not gated by default. The new configuration-confirmed findings (ANDROID-NSC-USER-CA, and ANDROID-CLEARTEXT from the network security configuration) count under the default allowed_statuses, but medium and low thresholds are off by default. Policies that gate candidates or medium/low findings, and only_new baselines, will see them.
  • The MASWE matrix changes: MASWE-0003, 0013, 0020, 0021 and 0022 leave not-assessed (they are not-run for APK and IPA, which these source checks do not read), and ANDROID-NSC-CONFIG adds coverage to MASWE-0026 and 0027.
  • Existing rules find more: AST-CRYPTO-ECB also reports a bare "AES" transformation (its title is now "AES in ECB mode is selected"; fingerprints are unchanged), AST-CRYPTO-WEAK-HASH reports CryptoKit Insecure.MD5 and Insecure.SHA1, AST-SQL-CONCAT reads SQLiteQueryBuilder.query, the groupBy/having/orderBy/limit arguments and nested exported providers, and SOURCE-INSECURE-RANDOM runs on Swift and Objective-C (its coverage on iOS-only trees changes from not-applicable to checked).
  • The AST engine now follows Kotlin let, also, use, apply, run and with lambdas, so every structural rule can report code inside them. A ?.let lambda, or one with return@, merges like a branch that may not run; a reassignment inside a lambda that always runs can now also remove a 1.4 finding. Other lambdas and Swift closures are still not analyzed.
  • ANDROID-CLEARTEXT also comes from the referenced network security configuration (configuration-confirmed, so code scanning rates it). The manifest's usesCleartextTraffic candidate is dropped when that configuration was parsed, minSdk is 24 or higher, and the configuration states cleartextTrafficPermitted or targetSdk is 28 or higher. Findings from a configuration that several source sets define are candidates.
  • New coverage states can make an audit incomplete: ANDROID-NSC-USER-CA and ANDROID-NSC-CONFIG are partial when a referenced configuration cannot be parsed, is missing from the scanned sources or is a build placeholder, and checks that stop at their match budget report partial. With the default fail_on_partial, such a scan fails the gate and cannot be exported as a baseline. Compiled AAB configurations are not-run.
  • Text rules no longer count binary XML from packages as checked, so WEBVIEW-SAFE-BROWSING-OFF is not-run on APKs. The rules resource may carry case_sensitive and once_per_file; inventory gains deep_links[].browsable and network_security_parsed. Unmodified 1.4 skills upgrade with apsa skill install; the MCP tool manifest hash is unchanged.

Upgrading from 1.5.0. 1.5.1 audits large source trees partially instead of refusing them and fixes a slowdown on Android translations.

  • A source tree over the text staging budgets (64 MiB, 12,000 files) no longer fails with an input-budget error (exit 1). Files are staged by kind: app code and configuration, other shipped text, localized or qualified Android res/values-* files, then tests. inventory.input_snapshot.omitted counts what was left out, and the audit is incomplete (exit 3). With the default fail_on_partial the gate still fails, and a partial report cannot become a baseline. The 100,000-entry and 64-level budgets also stop reading instead of refusing.
  • If app code, configuration or other shipped text is left out (app_scope_complete: false), not-applicable coverage becomes partial too and DEPENDENCY-CVE gains a partial entry (unless --sbom supplies the dependencies), so required rules cannot pass even with fail_on_partial = false.
  • SARIF from such an audit carries a warning notification and run.properties.inputSnapshot. A workflow with fail-on-incomplete: "false" now passes with a warning and uploads partial results where 1.5.0 failed; code scanning sees no results for omitted files. Scan a narrower path (one module per job, with its own category) for complete results. MCP report context gains input.input_snapshot.
  • In 1.5.0, strings.xml files with many escaped apostrophes (\'), common in French and Italian translations, made WEBVIEW-SAFE-BROWSING-OFF slow enough to hit the parser timeout (about 150 s for a 694 KiB file). XML files are now cleaned of <!-- --> comments only, so // in XML text no longer hides a match. Comment cleaning also stays linear on unterminated strings and comments; a /* with no closing */ after it is read as text.
  • Unmodified 1.5.0 skills upgrade with apsa skill install; the MCP tool manifest hash is unchanged.

What it checks

AreaAvailable checks
Source codeJava/Kotlin/Swift AST analysis; bounded Objective-C .m candidates; WebView and deep-link patterns; Manifest, Info.plist, storage, and dependency inspection, including Gradle catalog usage and application lockfiles; exported components, backup and targetSdk; Apple required-reason APIs against privacy manifests; known credential formats (masked); weak random for security values; mutable implicit PendingIntents; trust-all TrustManagers and hostname verifiers, unevaluated iOS server trust and weakened ATS exception domains; external storage writes; Java deserialization; (1.5) broken ciphers, constant keys and IVs, short RSA keys, biometric binding and fallback, TLS versions, iOS APIs outside ATS, network security configuration, App Links, intent redirection and implicit Intents, path traversal, keyed unarchiving, Safe Browsing, UIWebView and WKWebView file access
Android buildsDEX calls and constant flow; AAB base and feature-module manifests and module DEX (always partial); resources and network configuration; exported components/providers; signing-block and v1 certificate evidence; ELF hardening
iOS buildsMach-O headers, including embedded framework/extension metadata; CodeDirectory page and entitlement hash integrity (not signature authentication); limited entitlement/configuration checks (embedded XML entitlements, ATS exceptions, provisioning indicators); PIE, canary, and string evidence
Public intelligenceApple/Android advisories with bounded Android bulletin backfill, CVE, CISA KEV, OWASP guidance, and OSV dependency correlation
Reports and CISQLite history, comparison and reassessment, JSON/Markdown/SARIF export, coverage requirements, and expiring waivers
RuntimePrepared scenarios for owned Android test apps and iOS simulator apps; physical iOS devices are unsupported
Model integrationstdio MCP tools/resources and packaged skills, without a required model provider or LLM API key

Findings distinguish candidate, configuration-confirmed, version-affected, and runtime-confirmed evidence. coverage and warnings show what actually ran. A signing block does not prove signature authenticity, and an affected dependency version does not prove exploitability. A scan with no findings does not establish that the whole app is secure.

OWASP mappings describe relevant checks; APSA does not certify MASVS compliance or implement every MASTG test. Public advisories cannot reveal undisclosed zero-days. App files alone cannot establish a device's OS patch state. See OWASP coverage and the support matrix, both currently in Korean, for tested scope and limits.

Install and run

Install uv on macOS or Linux. APSA targets CPython 3.11 and 3.12; not every host/Python combination has been tested (see the support matrix). The examples select Python 3.12, which uv can download if needed. Initial installation can use the network; scans can then use local inputs and cached intelligence.

After the 1.7.0 publication gates pass, install it from PyPI. Check release readiness for publication status:

sh
uv tool install --python 3.12 apsa==1.7.0apsa --versionapsa doctor --jsonapsa demo --out ./apsa-demo

The package provides apsa and the compatibility aliases quaygate and mobile-audit. If the command is not found, run uv tool update-shell and open a new terminal. GitHub Releases provides the wheel, source distribution, checksums, and a verified bundle with locked requirements, attribution, and the build manifest.

For development or installation with the repository's locked dependencies, use a checkout:

sh
git clone https://github.com/ictechgy/apsa.gitcd apsauv sync --locked --python 3.12uv run --locked apsa doctor --jsonuv run --locked apsa demo --out ./apsa-demo

doctor checks parsers, optional device tools, and offline readiness. demo writes an intentionally vulnerable example and scans it; choose a new output directory.

To register the checkout's commands on your PATH:

sh
uv tool install --editable . --force --python 3.12 --constraints requirements-release.txtapsa --version

This registers apsa and the compatibility aliases quaygate and mobile-audit. --force replaces existing tools with those command names. An editable installation depends on this checkout; keep it in place. If the command is not found, run uv tool update-shell and open a new terminal. The examples below assume apsa is on PATH. Without a global installation, prefix them with uv run --locked from the checkout.

sh
apsa scan /path/to/owned/mobile-projectapsa scan /path/to/owned/app.apkapsa scan /path/to/owned/app.ipa --sbom /path/to/build.cdx.jsonapsa tui

Use a CycloneDX JSON SBOM from the actual build to improve dependency correlation. tui, or apsa without arguments, opens the terminal interface. Run apsa COMMAND --help for options.

Public intelligence and network use

A default scan reads local files and cached intelligence without uploading source code or builds. Public-feed collection and OSV queries use the network:

CommandNetwork behavior
apsa scan TARGETUses local inputs and cached intelligence
apsa intel syncFetches public vulnerability sources
apsa intel watchPolls public sources and reassesses saved inventories; does not reread app files or implicitly query OSV
apsa intel backfill --source android --since YYYY-MMFetches an explicit, bounded range of monthly Android security bulletins
apsa scan TARGET --onlineSends eligible dependency names and versions to OSV; default auto selects legacy for at most 100 eligible unique coordinates and batch above 100, with a 1000-package default budget. Scan accepts explicit mode and local exclusion flags below
apsa intel watch --onlineSends saved dependency names and versions using auto and the default budgets for each saved inventory on each poll; has no mode/exclusion flags and does not inherit scan exclusions
sh
apsa intel syncapsa intel statusapsa intel watch --interval 900apsa scan /path/to/owned/app --onlineapsa scan /path/to/owned/app --online --osv-query-mode legacyapsa scan /path/to/owned/app --online --osv-exclude-coordinate 'Maven:com.private:*'

Scan supports repeatable --osv-exclude-ecosystem, --osv-exclude-name and --osv-exclude-coordinate flags. Patterns are case-sensitive shell globs; coordinates use ecosystem:name. MCP dependency_check accepts query_mode and the corresponding exclude_ecosystems, exclude_names, exclude_coordinates lists. Exclusions are applied locally before transmission and do not count as checked from old cache. Each MCP call uses only its supplied lists; a report's recorded scan exclusions are not inherited. Pass the required exclusions on every dependency_check call. SwiftURL name and coordinate exclusions match both the stored repository name and its canonical OSV transmission name (for example github.com/owner/repo). An unsupported repository identity stays untransmitted when such a policy is active. Use scan or MCP when an exclusion policy is required: intel watch --online neither accepts these controls nor carries over a scan's exclusions. No source code or builds are sent to OSV. The request budget counts OSV operations, including pagination and record lookups, rather than individual HTTP redirect hops.

Watch polls every 900 seconds by default, with a 60-second minimum; it is not a push stream. It runs until interrupted unless --cycles sets a finite number of polls. Reassessment saves a new snapshot when findings, coverage, or intelligence state changes. Inspect feed freshness, failures, and pending CVE processing with intel status. Fetching a CVE document and completing its processing are separate states. The default pending-item policy allows no backlog; use intel_max_pending to set an explicit allowance.

sh
apsa reports reassess latestapsa reports compare audit_BEFORE audit_AFTERapsa reports export latest --format sarif --out audit.sarifapsa reports verify

Reassessment applies current intelligence to a saved inventory; run scan again for changed files or new static checks. If connected to an AI client, that client may send report metadata to its model provider. APSA's MCP context excludes source excerpts, full source files, and screenshot bytes; client-side data handling still depends on the client.

Finding follow-up in 1.6.3: inspect one finding after a change with apsa reports followup audit_BEFORE audit_AFTER --finding F-ID --out followup.json, or MCP reports_followup. It preserves report hashes, identifies possible continuations after line changes, and separates observations from missing coverage or changed scope. It never calls disappearance proof of remediation. See follow-up usage and limits and competitive research and decisions.

CI and background jobs

sh
apsa policy init --out apsa.tomlapsa scan /path/to/owned/app --policy apsa.toml --out audit.jsonapsa scan /path/to/owned/app --fail-on high --include-candidatesapsa scan /path/to/owned/app --strict-coverageapsa scan /path/to/owned/app --background --jsonapsa jobs status JOB_ID --json

Severity gates exclude candidate findings by default. Opt in with --include-candidates or the policy's allowed_statuses. Required rules accept checked, limited or not-applicable coverage (limited only while strict_coverage is false); partial execution and missing required checks do not pass. Waivers need a finding ID, a reason, and an expiry date. A background job being completed means it finished; check its audit_incomplete flag and report before treating the audit as complete.

A waiver can record the state that was approved with an optional severity, plus known_exploited = true when a known-exploited finding was approved. If the finding later rises above that severity or becomes known exploited, the waiver stops suppressing it: the finding goes through the thresholds again, the active waiver gets an escalated record, and the gate adds a waiver-severity-escalated notice. A waiver without severity keeps suppressing as before and adds a waiver-severity-unrecorded notice. Notices are in the gate's notices list, separate from reasons, and never change the gate state or exit code by themselves. Dependency advisory findings keep their finding IDs and list equivalent advisory IDs in advisory_ids (the display ID, grouped OSV IDs and aliases, at most 32). Waivers, only_new baselines and report comparisons written before a GHSA advisory gained a CVE alias, or before a lower CVE alias appeared, still match the renamed finding at the same dependency location, including reports from 1.6.4. Alias matching goes only through a display ID (the earlier display ID is among the finding's advisory_ids, or the reverse) and is one-to-one: a finding that matches under its own ID gains no alias match, and an alias shared with several earlier findings or waivers matches nothing, so the finding is reopened. When several active waivers match one finding, any escalated one reopens it.

Unreadable source directories and files leave warnings and incomplete coverage; a source tree with no readable supported files fails explicitly. A source tree over the text staging budgets (64 MiB, 12,000 files) is audited partially rather than refused. Files are staged by kind in this order: app code and configuration (configuration first, then the app's code, then headers and vendored code such as .build/ or vendor/), other shipped text (base resources, JSON, YAML), localized or qualified Android res/values-* files, then tests. inventory.input_snapshot.omitted counts the files and bytes left out by kind (code_and_config, other_text, localized_values, tests), including files over the 8 MiB file limit and files that could not be read or staged. Unreadable directories, and directories past the tree-wide budgets of 100,000 entries (read in name order) or 64 levels, are reported as warnings instead. When app code, configuration or other shipped text is left out, app_scope_complete is false and not-applicable coverage also becomes partial. For a complete audit, scan a narrower folder such as the app module. Storage capture failures remain not-run and cannot establish that a canary was deleted.

Coverage separates execution gaps from precision limits. partial means part of the stated scope was not examined: a budget was reached, a file was skipped or had parse errors or a lossy adaptation, input was truncated, an online query failed, or the latest runtime run was partial. A partial entry makes the audit incomplete (exit 3). limited means every input in scope was examined, but the bounded static model abstained on some paths, for example unresolved native-flow call targets, Objective-C seen only through its reduced model (OBJC-* rules; AST-* rules never examine Objective-C, so beside other languages they are partial), rules without a Swift model beside Kotlin or Java sources, or token-level JS/TS/Dart flow. A limited audit stays complete for the stated scope; reports show the limitation (summary.limited, SARIF notifications, the MASWE matrix and checklists). Across entries, partial outranks limited, which outranks checked. scan --strict-coverage, policy evaluate --strict-coverage or policy strict_coverage = true also treat limited as incomplete; without strict coverage, limited also satisfies policy required_rules. Neither checked nor limited means safe.

Exit codeMeaning for the unified CLI
0Command completed or policy passed; coverage may be limited
1Execution error
2Invalid arguments
3Incomplete audit or policy (with strict coverage, also limited coverage), failed report verification, or failed or partial intelligence synchronization
4Findings exceeded the configured CI threshold or policy; takes precedence over 3
130Interrupted

--json emits an envelope containing ok, data or error, and exit_code; watch emits one JSON envelope per cycle (NDJSON). ok is true for codes 0 and 4; code 4 means evaluation succeeded but the CI threshold was exceeded. An audit that is both incomplete and over a threshold exits 4 with ok true; the report's summary.incomplete and the policy's partial-audit reason still record the incompleteness. CI must check exit_code and the policy result. Reports may still be produced for codes 3 and 4.

GitHub code scanning

yaml
permissions:  contents: read  security-events: write  actions: read            # private repositories: lets the upload read the workflow runsteps:  - uses: actions/checkout@v7    with:      persist-credentials: false  - uses: ictechgy/apsa@<commit-sha> # v1.7.0; pin the full release commit SHA    with:      path: android            # source folder, or a built APK/AAB/IPA in the workspace      fail-on-incomplete: "true"

The action installs apsa from PyPI at the matching version with the release's hash-locked dependencies, runs scan with --format sarif --sarif-root, writes a job summary, uploads the SARIF file to code scanning and then applies the result. A policy failure or a fail-on threshold fails the job even when the audit is also incomplete (APSA exits 4 then, and the action also reads the report rather than the exit code alone). With a policy, every unmet policy condition (required rules, required intelligence, expired waivers, baseline checks) fails the job; only a partial audit is left to fail-on-incomplete, which fails on exit 3 unless set to "false". Limited coverage does not fail the job unless strict-coverage: "true" (which passes --strict-coverage) or the policy sets strict_coverage = true; either counts limited coverage as incomplete. The incomplete output reports the same decision, including a partial-audit reason behind exit 4. Use policy (with baseline-file and baseline-sha256) for team gates, or fail-on for a severity threshold that excludes candidates.

By default the action is offline: it does no CVE or dependency-advisory correlation, and dependency findings need intel-sync (fetch public advisories first; a partial sync is a warning) and online (send dependency names and versions to OSV, see network use). Private repositories need GitHub Code Security to upload SARIF; set upload-sarif: "false" to keep only the file. Pull requests from forks and every Dependabot-triggered run get a read-only token, so the action skips the upload there and still writes the file and summary.

Each alert's fingerprint is APSA's finding identity: the rule plus the evidence location (file, line, function or archive member). Editing the flagged line or moving the file can therefore close one alert and open another. Candidate findings are uploaded at warning level and tagged candidate, and a rule whose results are all candidates carries no security-severity, so it does not trip code scanning's default check for High or Critical alerts. A rule with confirmed results takes its security-severity from those, and its candidate alerts share that rating. Code scanning tracks alerts per category (default apsa); if you uploaded APSA SARIF before under another category, keep that value, because a new category starts new alerts and leaves the old category's alerts open until you delete its analyses. GitHub may offer Copilot Autofix suggestions on third-party alerts where your plan enables it; this has not been tested with APSA alerts, and APSA's evidence status stays in each alert's properties.

Without the action:

sh
apsa scan android --format sarif --out apsa.sarif --sarif-root androidapsa reports export latest --format maswe --out maswe.jsonapsa reports export latest --format cyclonedx --out apsa.cdx.json

reports ingest REPORT --sarif FILE --tool NAME (MCP reports_ingest_sarif) imports another tool's SARIF 2.1.0 results, such as Android Lint, CodeQL or Semgrep, into a new report. Imported results stay candidates with origin: external, add no APSA coverage, and are cross-linked with APSA findings of a related weakness at the same location. Their text is sanitized and treated as untrusted.

--format checklist --checklist masvs-v2 (or a TOML file mapping your own inspection items to MASWE identifiers and APSA rules) lists, per item, the related checks' states and findings; statuses never claim an item passed. reports history TARGET lists when each finding was first and last observed and when a later report stopped observing it, as inspection and remediation records. See using APSA with Korean inspection standards (Korean).

--format cyclonedx writes a CycloneDX 1.6 SBOM of the dependencies APSA discovered, with package URLs and the evidence behind each version, and embeds APSA's dependency matches as VEX entries. Each entry carries the evidence status, the CISA KEV flag and when APSA first observed it for that target, which helps run vulnerability-handling clocks such as the EU Cyber Resilience Act's. Every analysis state is in_triage: APSA never marks a vulnerability not_affected; record that decision yourself with a justification. The SBOM is not a complete build inventory and does not make an app compliant with any regulation.

See the CI example and operations guide (Korean) for policies, backup, limits, and troubleshooting.

MCP and skills

sh
apsa integrations --root /absolute/path/to/owned-appsapsa mcp --root /absolute/path/to/owned-appsapsa skill installapsa context --report latest --section findings --limit 20 --json

Use integrations to generate a configuration with the installed executable path, or a python -m apsa fallback when no apsa executable is found. The shape below is illustrative; replace both absolute paths:

json
{  "mcpServers": {    "apsa": {      "command": "/absolute/path/to/apsa",      "args": ["mcp", "--root", "/absolute/path/to/owned-apps"]    }  }}

Install for agents

ClientInstall
Claude Code/plugin marketplace add ictechgy/apsa, then /plugin install apsa@apsa. The plugin runs uvx --python 3.12 apsa@VERSION mcp --root <current project> and adds the skill.
Any MCP clientUse the configuration from apsa integrations, or the MCP Registry entry io.github.ictechgy/apsa (PyPI package, uvx, required --root).
Codex and other skill runtimesapsa skill install, plus the integrations configuration.

During 1.4 development, Claude Code 2.1.295 (the plugin loaded from the checkout with --plugin-dir, root ${CLAUDE_PROJECT_DIR}) and Codex CLI 0.162.0 were each verified calling capabilities and audit_scan against that historical local build on a freshly generated synthetic project. These are not 1.6 client checks. Marketplace and MCP Registry installs are checked after the package is published. Other clients are not yet verified.

MCP uses stdio and requires an explicit --root; repeat it for multiple roots. integrations defaults to the current directory when no root is supplied. Roots restrict audited targets and access to their reports and jobs. --allow-any-root explicitly removes that restriction. APSA does not change model-client configuration automatically; client authentication belongs to the client.

PurposeMCP tools
Auditscapabilities, audit_scan, audit_start, audit_reassess, verify_finding, specs_validate
Jobsjobs_list, jobs_status, jobs_cancel
Reportsreports_list, reports_get, reports_compare, reports_export_baseline, reports_ingest_sarif, reports_checklist, reports_history
Intelligenceintelligence_sync, intelligence_search, intelligence_get, dependency_check
Policypolicy_evaluate
Runtime planningruntime_plan, runtime_devices

verify_finding (CLI apsa verify) cross-checks a finding claimed by another tool or AI reviewer against APSA's evidence and never refutes it. Project taint specifications name project-specific sources and sinks, such as a deep-link parser or an in-app browser wrapper, for APSA to follow deterministically; see project specifications.

Exact rule claims match that rule; MASWE claims may use related checks. Precise CWE corroboration currently supports CWE-22, CWE-89, CWE-295 and CWE-502 through explicit check mappings. Other CWE claims return not-assessed; broad MASWE overlap alone does not corroborate them. When both a rule and weakness are supplied, both must match. Mixed checked and unexecuted coverage remains partial; so does limited coverage, because a bounded-model abstention cannot support not-observed.

capabilities reports tool_manifest_sha256, a hash of the APSA-owned tool surface (names, descriptions, annotation hints and parameter shapes); the tool set never changes within a process. Compare a hash computed from tools/list with the published values rather than trusting the server's own report. Read-only tools are annotated read-only and idempotent, network tools open-world. App content and advisory text are treated as untrusted data and model context omits source excerpts; see the MCP security model.

Resources include apsa://rules and apsa://reports/{report_id}. The previous quaygate:// and mobile-audit:// resource schemes remain compatible.

Report context defaults to 20 records and 64 KiB. Resolve latest once, then page with the returned immutable report ID and page.next_cursor, keeping section/filters fixed. partial_response describes the page; audit_incomplete describes execution. Oversized records are explicitly omitted and remain available in local exports. Portable baselines require explicit approval provenance and a SHA-256 pinned in reviewed CI configuration. See workflow examples.

The default server exposes runtime planning. runtime_execute and runtime_start are registered only with --allow-runtime at server startup. Both preview a scenario by default; execute=true runs it. runtime_start starts a cancellable background device job. Runtime tests need an authorized, prepared test app; the default audit does not boot devices or install apps. See the operations guide (Korean) before running a scenario.

skill install copies the packaged APSA skill to ~/.codex/skills/apsa. To install directly into another model runtime's skill directory:

sh
apsa skill install --name apsa --dest /path/to/runtime/skills/apsa

Use --name quaygate or --name mobile-audit to update a skill installed under an older default name; custom edits are preserved unless --force is supplied. integrations reports skill status. For clients without MCP, context provides report context that excludes source excerpts, full source files, and screenshot bytes.

Compatibility and stored data

quaygate and mobile-audit invoke the same unified CLI. Python entry points python -m apsa, python -m quaygate, and python -m mobile_audit remain available. Existing reports and QG-* rule IDs retain their identity.

Data is stored by default in ~/.local/share/mobile-audit. Path precedence is --home → APSA_HOME → QUAYGATE_HOME → MOBILE_AUDIT_HOME → that default. Renaming does not copy the database or move history. Existing MCP configurations must include an authorized --root.

The legacy apk, ipa, and device subcommands retain quick lint output and exit codes 0/1/2; they do not save unified audit history or correlate vulnerability intelligence. Use scan for the full workflow. Older cached OSV records without severity need a fresh scan --online or intel watch --online; reassessment alone cannot recover missing scores.

Development, validation, and licensing

sh
uv sync --locked --extra dev --python 3.12make test benchmarkmake export-releasemake release RELEASE_OUT=dist/apsa-local-release

Choose a new or empty release directory. Release verification requires uv 0.12.1 and builds wheel/sdist twice, compares their hashes, and checks a clean installation outside the checkout with offline source/APK scans, MCP, and skill installation. It writes hashes, an SBOM, dependency notices, and a release manifest without publishing. Initial dependency preparation can use the network. The same-host repeat check does not claim byte-identical builds across platforms.

Tagged releases use GitHub Actions to publish the verified distributions to PyPI after the supported CI matrix passes. See release publishing for the workflow and download contents.

Recorded product validation is in RELEASE_READINESS.md. The curated benchmark is a regression corpus, not a measure of production detection rates. Integration boundaries and the threat model are currently in Korean. Historical reviews remain tied to their original snapshots.

A reproducible APSA/MobSF comparison and its recorded results use freshly generated synthetic source projects and APKs. They measure development candidates recorded before APSA 1.2.0, not the published package; these results are not production accuracy estimates. A rerun on the APSA 1.3.0 release runtime with newly generated inputs reproduced the candidate figures (see RELEASE_READINESS.md).

A separate public-source and CVE evaluation records initial results on six pinned app source snapshots, selected dependency/CVE units and OS advisory fixtures. It preserves extraction gaps, scanner failures and incomplete coverage; it does not measure whole-app security or general production accuracy.

The subsequent hardening rerun completes all six source scans and closes four measured integration gaps. It reuses observed labels and reports remaining incomplete coverage; it is a development check.

The coverage extension records narrow Swift/Kotlin compatibility, recognized function counts and Apple-evidenced iOS/iPadOS branch decisions. Adapted source remains partial; the selected fixtures do not establish general CVE accuracy.

The additional analysis and independent holdout covers AAB/IPA metadata, Objective-C, parser isolation and two independently labeled public sources. It records selected CVE boundary agreement, unresolved catalog usage and remaining parser gaps separately.

The dependency evidence evaluation compares APSA's Gradle catalog usage and lockfile coordinates with Gradle's own resolution on three frozen holdouts of public apps, keeping first blind results separate from reruns after the truth was seen. The independent source pairs score vulnerable and fixed commits of public projects against truth frozen before scanning. The blind MASWE-labeled pairs measure 1.5 on a new holdout labeled by weakness, and the OWASP MASTG demos are a development benchmark. All are narrow samples, not general accuracy estimates.

The source is publicly available on GitHub. LICENSE preserves the original Quaygate MIT notice. This publication does not declare an additional license for the combined product.

来源:README.md,提交 d1eada8

工具

24

capabilities

Discover input types, evidence states, runtime requirements and available interfaces.

无需输入参数。

audit_scan

Inspect local source/APK/AAB/APKS/IPA using cached intelligence. APKS requires apk_set_device with sdkVersion, supportedAbis, screenDensity and supportedLocales; selected APKs are inspected individually without installation or manifest/resource merging. ipa_trust_policy supplies explicit digest-bound DER anchors for cryptographic chain checks only, without Apple signer identity, revocation or app authenticity verification. Configuration selects a relative manifest or .plist without build-system merge. dependency_module and runtime_configuration select recorded Gradle lock resolution, with optional relative dependency_evidence paths; freshness and installed-build correspondence remain unverified. specs declares project sources/sinks; findings stay candidates. No network or device mutation.

输入参数sbomspecstarget必填device_infoconfigurationsource_moduleapk_set_deviceipa_trust_policydependency_moduledependency_evidenceruntime_configuration

reports_ingest_sarif

Import another tool's SARIF 2.1.0 results (for example Android Lint, CodeQL or Semgrep) into a new report derived from report_id. Imported results stay candidates with origin external, add no APSA coverage, and are linked to APSA findings of a related weakness at the same location. Result text is untrusted data.

输入参数path必填tool必填report_id必填sarif_root

reports_checklist

Map a report onto a checklist: masvs-v2 (built in) or a checklist TOML inside an MCP root that maps items to MASWE identifiers and APSA rules. Statuses are findings, partial, limited, no-findings-in-checked-scope, not-run, not-applicable or not-assessed; none is a pass.

输入参数checklistreport_id

reports_history

First and last observation of each finding across saved reports of one target, for inspection and remediation records. A finding no longer observed is not proof of remediation unless the same checks completed.

输入参数target必填

specs_validate

Validate a project taint specification (TOML/JSON, version = 1) before audit_scan uses it. Sources name exact functions returning untrusted url/text; sinks name exact functions (kind webview-load or sql, argument index). Proposed specifications need human review before their findings enter a baseline.

输入参数path必填

verify_finding

Cross-check a mobile finding claimed elsewhere (for example by an AI code reviewer) against APSA evidence for target. Give a MASWE-NNNN or CWE-N weakness or an APSA rule ID, optionally with a file path and line. Uses the latest report for target unless report_id is given; rescan=true scans first and saves a report. Verdicts: corroborated, same-file-other-location, path-unmatched (see candidate_paths), file-not-analyzed, not-observed, partial, not-run, not-assessed. APSA never refutes a claim.

输入参数linepathrulerescantarget必填weaknessreport_id

intelligence_sync

Fetch official public mobile advisories and update local cache. Returns per-source failures and scope.

输入参数limitsources

audit_start

Start a persistent offline audit. APKS requires apk_set_device with sdkVersion, supportedAbis, screenDensity and supportedLocales; selected APKs are inspected individually without installation or manifest/resource merging. ipa_trust_policy supplies explicit digest-bound DER anchors for cryptographic chain checks only, without Apple signer identity, revocation or app authenticity verification. Configuration selects a relative manifest or .plist without build-system merge. dependency_module and runtime_configuration select recorded Gradle lock resolution; optional dependency_evidence paths narrow it, without verifying freshness or installation. specs declares project sources/sinks. Use jobs_status for progress and jobs_cancel to stop it.

输入参数sbomspecstarget必填device_infoconfigurationsource_moduleapk_set_deviceipa_trust_policydependency_moduledependency_evidenceruntime_configuration

jobs_status

Read durable job state and progress. An interrupted worker never becomes a successful audit.

输入参数job_id必填

jobs_cancel

Request cancellation of an authorized job; poll jobs_status until terminal.

输入参数job_id必填

jobs_list

List background jobs visible inside this server's configured roots.

输入参数limit

intelligence_get

Read an exact CVE; refresh=true fetches its official CNA record into cache.

输入参数cve_id必填refresh

reports_list

List saved audit IDs for exact follow-up reads.

输入参数limit

reports_get

Read bounded context pages. Continue using the returned immutable report_id; finding_id pages its evidence. Oversized records remain in local exports.

输入参数limitcursorstatussectionseveritymax_bytesreport_idfinding_id

reports_compare

Compare exact report IDs, retaining coverage differences and unproven remediation states. verification=true adds source traces and scope/build/assertion pairing; portable_inputs=true requires verification and only normalizes absolute targets for identical, complete canonical SHA256 source inputs with unchanged analysis scope. Binary and runtime bindings remain strict; disappearance never proves remediation. No source excerpts; bounded by max_bytes (4096-262144) with omitted counts.

输入参数after必填before必填max_bytesverificationportable_inputs

reports_followup

Follow one original finding across immutable reports. Distinguish exact observations from possible continuations after location changes, retain comparison blockers and next evidence steps, and bind results to report hashes. No source excerpts, rescans, network or device actions. Disappearance never proves remediation. portable_inputs only permits the existing identical-complete-source relocation rule; binary and runtime bindings stay strict.

输入参数after必填before必填finding_id必填portable_inputs

audit_reassess

Re-evaluate a saved inventory against current cached intelligence and save a new report.

输入参数report_id必填

dependency_check

Query OSV for inventoried package versions and save a reassessed report. query_mode is auto, legacy or batch; auto batches above 100 packages. Exclusion lists are case-sensitive globs for ecosystems, package names or ecosystem:name coordinates. Excluded packages are never transmitted or marked checked from cache. Batched queries preserve per-package pagination and partial failures.

输入参数report_idquery_modeexclude_namesexclude_ecosystemsexclude_coordinates

runtime_plan

Generate an editable test scenario. Does not run it or log out the app automatically.

输入参数packageplatformreport_id

policy_evaluate

Evaluate team thresholds, explicit coverage requirements and expiring waivers without changing reports.

输入参数report_idbaseline_idpolicy_path必填baseline_pathbaseline_sha256

reports_export_baseline

Export an explicitly approved baseline inside configured roots. Do not invent approval; pin the returned artifact hash in reviewed CI configuration.

输入参数report_id必填approved_by必填output_path必填approval_reference必填

runtime_devices

List adb devices and available iOS simulators without changing app state.

无需输入参数。

版本历史

8
  1. v1.7.0最新Nov 1, 2026
  2. v1.6.4Oct 30, 2026
  3. v1.6.3Oct 29, 2026
  4. v1.6.2Oct 22, 2026
  5. v1.6.0Oct 18, 2026
  6. v1.5.1Oct 11, 2026
  7. v1.5.0Oct 10, 2026
  8. v1.4.0Oct 9, 2026

评分

已满足适用检查项的 90%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通25 分
  • 已声明认证方式10 分

    请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。

  • 已列出工具必需15 分
  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分

其他 Audit 集成