Skip to content

Commit 6e5642d

Browse files
committed
stealth(leakage): static leakage checks, CI gates & release/QA policy
1 parent 32ed08d commit 6e5642d

8 files changed

Lines changed: 2768 additions & 0 deletions

docs/stealth-builds.md

Lines changed: 344 additions & 0 deletions
Large diffs are not rendered by default.

docs/stealth-leakage-checks.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Stealth Leakage Checks
2+
3+
`scripts/stealth/check_leakage.py` scans built stealth artifacts for normal
4+
Lantern identifiers. It accepts APK, AAB, ZIP-like archives, and unpacked build
5+
directories. Archive entries are scanned recursively, and string matching checks
6+
UTF-8, UTF-16LE, and UTF-16BE encodings.
7+
8+
Run the default stealth check:
9+
10+
```sh
11+
make stealth-leakage-check \
12+
STEALTH_LEAKAGE_PATHS="path/to/app.apk path/to/app.aab"
13+
```
14+
15+
Run the stricter no-VPN variant:
16+
17+
```sh
18+
make stealth-novpn-leakage-check \
19+
STEALTH_LEAKAGE_PATHS="path/to/app.apk"
20+
```
21+
22+
If the configured targets are absent, the Make targets skip successfully. This
23+
keeps normal builds and ordinary CI runs from failing on stealth-only checks.
24+
25+
## Modes
26+
27+
The forbidden-token config lives at
28+
`scripts/stealth/forbidden_tokens.json`.
29+
30+
### `stealth` (base)
31+
32+
Checks for normal Lantern package, brand, library, service, and organization
33+
identifiers; user-facing VPN strings; OAuth provider strings and method-channel
34+
entry points; billing and subscription entry points; app-link hosts, custom
35+
schemes, and deep-link paths; Lantern social/support URLs; and update feed and
36+
release URLs.
37+
38+
### `stealth-vpn`
39+
40+
Brand-only policy for the de-branded full-featured VPN variant. Checks the
41+
`lantern_identity` and `social_urls` categories only. Generic tokens such as
42+
`oauth`, `stripe`, `billing`, and `VPN` are intentionally allowed — login,
43+
payment, and VPN surfaces are retained in this variant. OAuth2 and the
44+
`lantern://` deep-link scheme are excluded at source (via `//go:build !stealth`
45+
build tags in `account/oauth.go`, `backend/oauth.go`, and `mobile_oauth.go`),
46+
so they are absent from the binary for the correct reason — not merely
47+
garble-encrypted. The `lantern_identity` tokens are case-insensitive substrings
48+
that catch all brand variants (`lantern.io`, `lantern://`, `org.getlantern.*`,
49+
etc.) without false positives on generic tokens.
50+
51+
### `stealth-novpn`
52+
53+
Extends `stealth` (full forbidden set) and also checks Android VPN/TUN surfaces
54+
such as `android.net.VpnService`, `BIND_VPN_SERVICE`, `TunOptions`, and VPN
55+
quick tile/service actions. Carries three narrow allowlist entries in
56+
`forbidden_tokens.json` for irreducible third-party strings (`oauth` from
57+
`pion/webrtc/v4` RFC 7635, `stripe` from proto field name, `/auth` from proto
58+
file descriptor path) — see the allowlist entries for full rationale.
59+
60+
## Scanner limitations
61+
62+
`check_leakage` and raw `strings | grep` scan the post-garble binary. When a
63+
Go package is in `GARBLE_GOGARBLE` scope, garble `-literals` encrypts string
64+
literals at compile time, so brand strings in compiled-in garble packages are
65+
absent from the static binary but present at runtime. A pass from
66+
`check_leakage` alone is not sufficient evidence of brand removal for garbled
67+
packages.
68+
69+
See `docs/stealth-builds.md` ("Garble string-literal obfuscation blind spot"
70+
and "Standing nm -D brand-symbol gate") for the required complementary checks.
71+
72+
## Allowlists
73+
74+
Each mode supports an `allowlist` in the JSON config. Allowlist entries can
75+
match by `token`, `category`, `location` glob, and `encoding`. Example:
76+
77+
```json
78+
{
79+
"token": "Lantern",
80+
"location": "*.SF",
81+
"reason": "example only"
82+
}
83+
```
84+
85+
Keep allowlist entries narrow and mode-specific so real leaks still fail the
86+
scan.

0 commit comments

Comments
 (0)