Six small review-list items rolled together — all CI/dev-tooling, no
runtime behaviour change.
#12 Dockerfile: pin Rust 1.95.0 and cargo-ndk 4.1.2 (was floating
`stable` + latest cargo-ndk on monthly rebuild). Versions live
in ENV vars to make the next bump a one-line edit.
#13 Add shellcheck to lint job. SC2034/SC3043 excluded — Magisk
reads SKIPUNZIP externally; Android's /system/bin/sh (mksh on
Pixel) does support `local` despite POSIX. Verified locally
that the 11 .sh files (module-side + dev tooling) pass.
shellcheck baked into the CI image via apt; inline apt-get
fallback covers the window before image rebuild.
#24 ci.yml keystore.properties: replace heredoc with `printf '%s\n'`.
Heredoc without single-quoted EOF re-expands $, backticks and
backslashes in the password — printf takes the value verbatim.
#31 scripts/release.py::patch_file now hard-fails when a regex
pattern doesn't match (was silently leaving stale versions).
#32 Split rotate_fragments_into_history into rotate + delete steps
so release.py can save_json + write_md *before* unlinking the
fragment files. If anything in between fails, fragments are
still on disk and the run is retryable.
#37 codegen-interfaces.py: emit `assert!(matches_vpn(…), msg)` /
`assert!(!matches_vpn(…), msg)` instead of
`assert_eq!(matches_vpn(…), true/false, msg)` —
clippy::bool_assert_comparison was firing on every generated
row under `cargo clippy --tests`. Both generated test modules
regenerated. CI's clippy steps now also pass `--tests` so this
class of regression is caught.
Repo had ~1800 lines of Python (kmod/build.py, scripts/*, zygisk/build.py,
portshide/build-zip.py) with no formatter or linter. Long-lived scripts
like scripts/release.py and scripts/codegen-interfaces.py benefit from
catching unused-import / undefined-name / outdated-syntax issues early.
pyproject.toml — ruff config, target-py312, line-length 100,
rules E F W I B UP SIM. Excludes zygisk/third_party,
target/, .claude/.
ci.yml — astral-sh/ruff-action@v4 for `format --check` and `check`,
ahead of the slow Rust/Gradle steps so it fails fast.
docs/development.md — add `uvx ruff …` to the local-lint snippet.
Cleanup applied (`ruff format` + `ruff check --fix`):
- reformat: kmod/build.py, scripts/{changelog_lib,codegen-interfaces,
release,stats}.py, zygisk/build.py
- I001: split multi-name imports onto separate lines after the
sys.path.insert prelude (kmod/build.py, zygisk/build.py)
- E501 manual: wrap one console.print line in scripts/release.py
Stdlib-only invariant from scripts/build_lib.py is preserved — ruff is
a dev/CI tool, not imported at runtime.
`c` iterates one character at a time over the test name string;
a single character is never the empty string, so the disjunct was
always False. The remaining `0x20 <= ord(c) < 0x7F` already
correctly rejects non-ASCII names, so behaviour is unchanged.
Both forms came in with the codegen split (#91) but no [[vpn]]
rule has ever used them — the only `suffix=` rules are `digits`
(`wlan` test vector + `if` from #93). The grammar surface paid
for itself in ~150 lines of dead C/Rust helpers + their tests.
Drop them from VALID_KINDS, the parser, the C/Rust/Kotlin
emitters, and the helper test cases. If a future rule needs
either form, reintroduce alongside the rule that needs it.
Re-ran the codegen; tests pass for all four targets.
Two follow-ups to #90 in one PR:
1. Two new match forms in data/interfaces.toml grammar:
suffix = "digits_optional" prefix + 0+ ASCII digits
suffix = "any" prefix + 1+ any chars
Needed by the upcoming whitelist (PR-B) for patterns like
`seth_lte\d*` and `v4-.+`. Not used by any current [[vpn]] rule, but
the helper functions are exercised by direct unit tests in the
generated test modules so a bug would surface before whitelist lands.
2. [[test]] vectors in data/interfaces.toml that the codegen renders
into per-language unit tests:
- zygisk + lsposed/native: #[cfg(test)] mod tests inside the
generated iface_lists.rs (run via `cargo test`)
- lsposed/app: a separate IfaceListsGeneratedTest under
src/test/kotlin (run via `:app:testDebugUnitTest`)
- kmod: a userspace test driver test_iface_lists.c — the
generated header now has __KERNEL__-guarded includes so the
same matcher compiles against libc, and a new lint step builds
and runs it via gcc.
36 fixed vectors today; trivial to grow as new rules / corner cases
come up. CI catches drift on the next push: any single matcher that
disagrees with the toml fails its job.
No production behavior change — generated matches_vpn / vpnhide_iface_is_vpn
/ IfaceLists.isVpnIface bodies are byte-identical to before; only the
helper functions and test modules grew.
The kernel module, zygisk, lsposed-native, and the LSPosed Kotlin module
each had their own hand-written list of VPN interface name prefixes,
and the four had drifted: kmod/zygisk/HookEntry knew utun/l2tp/gre
while lsposed-native and DiagnosticsScreen only knew tun/wg/ppp/tap/
ipsec/xfrm. So the self-test could PASS while the hooks were actually
hiding more interfaces.
Move the rules to data/interfaces.toml and render four matchers from it
via scripts/codegen-interfaces.py — one per language target. A new lint
job re-runs the codegen and fails if anything drifts.
The match grammar is intentionally tiny so each codegen target
implements it without depending on regex (kernel C can't):
exact / prefix / prefix+digits / contains.
Side effect: native diagnostics now agree with the hooks, so the
self-test in DiagnosticsScreen will recognize utun*, l2tp*, gre* and
*vpn* substrings as VPN tunnels (previously it would silently PASS on
those). The /proc/net/route check also moved from raw substring to
whitespace-tokenized matching, which avoids matching VPN-prefix
substrings that show up by chance inside hex-encoded IP addresses.
Existing zygisk filter unit tests still pass unchanged — public API of
is_vpn_iface_bytes / is_vpn_iface_cstr is preserved, only the body now
delegates to the generated matches_vpn().
Cargo.lock files updated incidentally (synced with Cargo.toml versions
that were already 0.7.1 in the manifests).