Files
Simone AvogadroandClaude Sonnet 5.5 b3fe549259 refactor(detect-protection): drop the vendor signature list, detect packed code generically
The per-vendor packer/RASP rules were selected from APKiD's rule set (GPL-3.0 or
commercial); they are removed together with the [A:<rule>] source map and the
vendor names. What stays: PairIP (seen in protected apps), the Play Licensing
library and the Play Integrity / SafetyNet clients (public Google package names).

New generic check (check_packed_code / Test-PackedCode, sh + ps1): components
declared in AndroidManifest.xml (Application, activities, services, receivers,
providers) that have no class in the decoded smali. >= 3 declared and >= 3
missing; >= 50% missing = medium, >= 80% = high; activity-alias and android.*
are ignored. The protector is not named. Coverage trade-off: protections that
hide nothing (e.g. a native anti-tamper SDK) are no longer reported, and the
'none' message now says so.

Validated on the 21 decoded trees of the corpus: output identical to before
(af1945 signature-vm, bulldozer license, the rest none, no false positive; a
clean app misses 0-5 of 78-179 declared classes) and on a synthetic packed tree;
sh == ps1 through Windows PowerShell 5.1. New tests/unit/t_protection.sh (26
checks); tests/run.sh quick passes. Docs updated (CLAUDE.md, SKILL.md,
neutralize.md, neutralization-guide.md).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GKSiYpKT5YKAMowGEgUwAw
2026-10-11 20:55:19 +02:00
..

tests/ — development test harness

Dev-only: nothing here ships with the plugin (the plugin lives under plugins/). It guards the "registry as single source of truth" work: registry lint, golden byte-identity of the neutralizer outputs, the consumed-registry snapshot, and (as the features land) reader/audit unit tests, sh/ps1 parity and corpus ground truth.

tests/run.sh                  # quick: lint + unit (+ parity on fixtures when PowerShell is available)
tests/run.sh full             # + golden, snapshot, corpus, bash 3.2, perf (~50 min with PowerShell + corpus)
tests/run.sh lint golden      # selected suites; single tests by name: tests/run.sh t_lint
tests/run.sh --expect-pass lint golden snapshot   # SKIP counts as FAIL for these

One line per test: PASS, FAIL, SKIP (tool or corpus missing) or SKIP(requires) (the feature the test covers is not in the tree yet: see the # requires: header of each test). Directives (# requires:, # awk-matrix) are read only from the leading comment block of a test (up to the first line that does not start with #); t_runner fails when one sits further down, where it would be ignored. A suite with no test script yet prints SKIP <suite> no tests found. Under --expect-pass every SKIP, SKIP(requires) and empty suite of the named suites/tests is a FAIL. Exit 0 = nothing failed.

Suite discovery: unit/t_*.sh, parity/t_*.sh; corpus = every corpus/*.sh (the DESIGN names groundtruth.sh, exclusions-superset.sh, marker-verify.sh) except the helper prepare-pristine.sh and perf*.sh (suite perf); bash32 = bash32/*.sh and any <dir>/bash32.sh. A single script also runs by name (tests/run.sh marker-verify).

Before committing a registry change: tests/run.sh quick, tests/run.sh golden, and bash tests/tools/registry-gaps.sh --write (then t_gaps_fresh stays green).

Path What
run.sh, lib/common.sh runner and shared helpers (env: ARE_CORPUS, ARE_JADX, ARE_GOLD, ARE_PS, BASH32, ARE_PRISTINE, ...; defaults derive from the checkout's parent directory)
lint/lint-registry.sh registry lint, rules L1-L15 of DESIGN 4.6 (--help lists them; L11 is a WARN, L14 an INFO report on the FROZEN detect-adwrapper tables; L5/L7 read the allowed keys and patterns from _schema.json), plus JSON-schema validation when python jsonschema is installed (--no-schema skips it)
golden/capture.sh, golden/compare.sh, golden/t_golden.sh registry-scan (8 apps x depth 1-3 x ads/trackers/all + synth48) and detect-adwrapper (sh with/without --registry, ps1) must stay byte-identical to the frozen baseline $ARE_GOLD/before
golden/synth48.before/ G-synth: registry-scan outputs on the synth48 fixture, captured with the product of 4c166fe
golden/snapshot.sh, golden/snapshot.before.jsonl, golden/t_snapshot.sh the registry fields today's consumers read must not change for the 48 original entries
fixtures/ generators of synthetic trees (synth48, adwrapper fixture, pitfalls) and expected outputs; no proprietary code
shims/ nojq, jq15 (fake jq 1.5), nosudo, awk-{gawk-posix,mawk,busybox} for the portability paths (with_shim with nojq or jq15 also sets HOME to a missing dir, so a real ~/.local/bin/jq cannot beat the shim)
parity/ps.sh runs a .ps1 with Windows PowerShell 5.1 from WSL (-File, Windows paths)
parity/t_parity.sh sh/ps1 byte parity (--only <section>: registry-query, adwrapper, find, each incl. the in-process case; the 8-app corpus part of adwrapper, and the badpiggies + pitfall part of find, run when the section is named with --only or under run.sh full, which exports ARE_FULL=1; find compares find-*.ps1 with find-*.sh: --tsv/--summary byte-identical, text normalized, --json semantic, on the five layouts, a drive-letter copy (also as a subst drive root), trees with an unreadable directory or symlinks, . inside sources/ and HeartStar; PowerShell runs go in parallel, ARE_PS_JOBS, default 4)
fixtures/registry-{mini,edge,bad}/, fixtures/expected/registry-query/ registry-query fixtures (clean; BOM + case-colliding keys; every integrity error) and their expected outputs
unit/ unit tests (t_*.sh; t_runner.sh covers run.sh itself; t_reader, t_pkg_parity (T-PKG-PARITY, always per awk shim), t_categories (T-CAT), t_libroots (T-LIBROOTS), t_format (T-FORMAT) cover registry-query and _audit-shared.json; t_adwrapper (fixture, jq/nojq/jq15, malformed registries, --dump-exclusions, decode-apk.sh exit code; per awk shim) and t_network_roots (T-NETROOTS: the frozen NETWORK_ROOTS/COMMON_ROOTS tables, sh == ps1, covered by the registry) cover detect-adwrapper; t_deps covers jq as a dependency: install-dep.sh jq (package manager only with sudo, else the pinned static binary via the test-only ARE_JQ_URL/ARE_JQ_SHA256 hooks), the neutralize-all soft-fail, INSTALL_OPTIONAL:jq of the bash checkers with unchanged exit codes, every INSTALL_* name being an install-dep target, and the .ps1 checkers never asking for jq)
corpus/exclusions-superset.sh I11: the .packages-based exclusions cover the legacy ones (legacy script of $ARE_BASE_COMMIT patched at test time to dump them), in the jq/nojq/jq15 modes; nojq/jq15 detect-adwrapper output on the 8 apps == G
corpus/marker-verify.sh, corpus/marker-allowlist.tsv T-MARKER (DESIGN 3.1 R4): every detection.marker_classes item has its class file in the main smali dirs of at least one corpus app where the ground truth ($ARE_GROUND_TRUTH, default registry-unification/ground-truth.json) has that SDK, else a reviewed allowlist row with a reason; corpus tree per app = $ARE_PRISTINE/<app>, else $ARE_CORPUS/<app>-decoded, else the ground truth's decoded_dir
unit/t_cli.sh, unit/t_layouts.sh, unit/t_pitfalls.sh, unit/t_tokens.sh the audit engine sdk-audit.sh behind find-trackers.sh / find-ads.sh: every historical flag, option normalization, exit codes 0/1/2 (t_cli); the five input layouts and their expected outputs fixtures/expected/layouts/ (t_layouts, fixture fixtures/make-layouts.sh; also the PowerShell twins find-*.ps1 on the same expected files, through \\wsl.localhost and a drive-letter copy, when PowerShell is reachable); the legacy false positives/negatives (t_pitfalls); T-TOKENS: every regex alternative of the legacy scripts has a resolvable home in fixtures/legacy-tokens.tsv (t_tokens)
corpus/groundtruth.sh, corpus/expected-sdkids.json, corpus/known-deviations.tsv, corpus/ep-allowlist.tsv the corpus gates of the audit on the pristine apktool decodes and the jadx corpus: G-PREC (HIGH precision 1.0), G-REC (recall, jadx package aliases), G-HS (HeartStar), G-NEW, G-FP-KNOWN (known-deviations.tsv), G-EP (Entry Points only under the reviewed ep-allowlist.tsv prefixes); ARE_JADX_EXTRA names the HeartStar / Water Sort jadx dirs
corpus/find-golden.sh, golden/find/ --tsv goldens of both tools on the 9 pristine decodes (ARE_UPDATE_EXPECTED=1 rewrites them)
tools/registry-gaps.sh, unit/t_gaps_fresh.sh dev tool (jq) that generates the computed part of skills/sdk-neutralizer/references/registry-backlog.md (DO / P / A gaps of the registry entries, between the registry-gaps markers): no option = print, --check = rc 1 when stale, --write = refresh; t_gaps_fresh (gaps-fresh) runs --check and the tool's own checks
bash32/bash32.sh (suite bash32) I18: bash -n under bash 3.2 of the scripts that promise it, then every unit test that runs product scripts through $BASH32 (and corpus/find-golden.sh) again with that bash 3.2; the binary is $BASH32, else registry-unification/scratch/bash32/bash32 next to the repo, else SKIP
corpus/perf.sh (suite perf), corpus/prepare-pristine.sh engine time budgets, WARN only (the PowerShell twin is timed too: logged, Subway beyond 60 s is a WARN); the helper that decodes the corpus originals into $ARE_PRISTINE (not a test)

Tests follow a tiny protocol (see lib/common.sh): exit 0 = PASS, 77 = SKIP, anything else = FAIL. Each test can also be run on its own: bash tests/unit/t_lint.sh.

Intended neutralization changes (rebaseline)

The golden suite and T-SNAPSHOT freeze what the neutralizer consumes. A registry change that is meant to alter neutralization (new targets, new manifest_components, a new neutralizable entry) makes them fail by design. Only additive changes are accepted without a separate review: never remove or rename an existing target, component or entry.

  1. Make the registry change; tests/run.sh quick (lint) must pass.
  2. Capture and compare: tests/golden/capture.sh --no-ps after-x, then tests/golden/compare.sh before after-x lists the differing files.
  3. Prove it is additive: tests/golden/additive-check.sh before after-x must print PURELY ADDITIVE (no line of any registry-targets.txt / registry-manifest.txt missing) and list exactly the intended added lines. Review the stdout diffs too (only MATCHED counts and UNKNOWN_PACKAGE lines that became entries may change).
  4. Dry-run neutralize.sh on the affected pristine decodes: the new targets must be applied (DRY_RUN:WOULD_*, no TARGET_SKIPPED for them).
  5. Rebaseline, keeping the old one: mv $ARE_GOLD/before $ARE_GOLD/before-<version> (and its .sha256), cp -a $ARE_GOLD/after-x/. $ARE_GOLD/before/ && rm -rf $ARE_GOLD/before/synth48, regenerate $ARE_GOLD/before.sha256; rm -rf tests/golden/synth48.before && cp -a $ARE_GOLD/after-x/synth48 tests/golden/synth48.before; tests/golden/snapshot.sh <registry> > tests/golden/snapshot.before.jsonl.
  6. tests/run.sh full must pass, then rebuild and device-test the affected apps.