Inspiration
Every Android app ships a privacy policy, and Google Play shows a Data Safety label the developer fills in themselves. Nobody checks either one against the thing that actually runs.
The numbers are ugly. Android is on roughly 69% of mobile devices (StatCounter, September 2026), and Mozilla's 2023 review of 40 top apps found nearly 80% of their Data Safety labels were false or misleading. The pattern is worse outside the top charts. Predatory loan apps in Kenya, Nigeria, India, Brazil, and the Philippines routinely ask for contacts and SMS while the policy claims they collect almost nothing.
The tooling splits the problem in half. Exodus Privacy, MobSF, and APK Auditor read the code. Policy analyzers read the text. Nothing puts the two side by side and asks whether they agree. That is the gap FinePrint fills.
What it does
FinePrint is a static Android privacy auditor. Give it an APK and a policy URL, or paste the policy text, and it returns a receipt.
What it reads:
- Manifest: permissions, exported components, cleartext flag, signing certificate
- DEX: classes, strings, third-party SDK signatures, external endpoints
- Policy: explicit claims, each stored as an exact substring with character offsets
Then a rule engine compares them. A policy that says "we do not share or sell" plus five ad and analytics SDKs is a contradiction. A policy that limits collection to name and
email plus READ_CONTACTS is a contradiction. "Encrypted in transit" plus usesCleartextTraffic is a contradiction.
Every finding pairs the policy quote with the code artifact that contradicts it: the permission name, the SDK prefix, the domain, the manifest flag, or the certificate field. Severity, a trust score, the APK SHA-256 and the certificate SHA-256 sit on the same page.
If no policy is supplied, FinePrint looks up the public Play Store listing, finds the developer's privacy link, and fetches it. If the link is missing, ambiguous, or dead, the report is marked incomplete rather than clean, and pasting the text finishes the comparison.
The landing page ships a sample receipt for a fictional app, PocketLoan 3.4.1, with five promises and six of them broken. It runs offline and is generated by the same engine as a real scan.
How we built it
Backend: FastAPI, androguard for APK and DEX parsing, httpx and BeautifulSoup for policy discovery, Pydantic for the report schema. Frontend: React 19, Vite 8, Tailwind 4 with CSS-first config, TypeScript 7. Tests: 14 pytest tests covering the deterministic extractor, policy discovery, the rule engine, score ordering, the DEX scan cap, completeness metadata, and the upload contract.
Two rules shaped everything:
- The model extracts, the rules decide. An LLM can read a policy and pull out claims under a strict JSON schema, but it never decides whether a contradiction exists. The rule engine does, so identical inputs produce an identical receipt. Without an API key a deterministic pattern extractor takes over and the report still works.
- Quotes must be verifiable. A claim is only accepted if it is an exact substring of the policy, with start and end offsets stored in the report. If the model paraphrases, the claim is dropped. A receipt built on paraphrase is not a receipt.
The APK is parsed as an archive. It is never installed or executed.
Challenges we ran into
DEX scanning. androguard's get_all_dex() is a generator, and a real APK's string table is enormous. Scanning all of it turned a demo into a timeout. We capped the scan with
islice and, more importantly, made the report state that the scan was capped. Silently truncated evidence would be worse than a slow scan.
Making quotes exact. Models paraphrase by default. Every extracted claim is validated against the source text and discarded if it does not match character for character, which meant designing the prompt and the schema around offsets instead of prose.
Policy discovery. Play Store markup changes, some apps publish no privacy link, links go dead (we hit a real 404 during development), and some policies live on a third-party host. When two candidate links are plausible, FinePrint refuses to choose and asks for text instead. Guessing would produce a confident report about the wrong document.
Incomplete is not clean. Early on, an app with no extractable claims scored well because it had few findings. That is exactly backwards. A capability-only scan now has its own state and is never presented as low risk.
The build itself. Tailwind 4 moved to CSS-first configuration, and Vite 8 type-checks the production build with TypeScript 7, so the UI had to be clean before it would ship at all.
Accomplishments that we're proud of
- Every finding is auditable. A quote with offsets on one side, a permission, SDK, domain, or hash on the other. A human can check the work.
- The verdict is deterministic. The model never decides what is true.
- The sample receipt is not hand-written. It comes out of the same rule engine that handles real APKs, so the demo cannot drift from the product.
- Degraded mode is real. No API key, no network, no APK: the sample still renders a full receipt.
- 14 tests pass, including the boring ones: score ordering, the DEX cap, and the incomplete flag.
What we learned
Self-reported privacy labels and binary behavior diverge structurally, not occasionally. The mismatch is not a handful of bad apps, it is what happens when the claim and the artifact are never compared.
The most useful architectural decision was refusing to let the model judge. Extraction is a reading task and models are good at it. Judgment has to be reproducible, and a rule engine gives you that.
Security tools need to be honest about absence. "No SDK signature found" is not "no SDK." We spent as much time on the limitations section as on the findings, and the report reads better for it.
What's next for FinePrint
- Split APK and XAPK support
- Version diffing: what did the update add?
- Parse the Play Data Safety declaration and diff it against the APK
- On-device WASM mode for sensitive APKs
- A signed JSON evidence bundle for journalists and researchers
- Batch mode across an app category
- More SDK signatures, with a public contribution format
FinePrint. Apps make promises. We check the code.
Built With
- androguard
- apk
- beautiful-soup
- docker
- fastapi
- httpx
- openai
- privacy
- pydantic
- pytest
- python
- react
- security
- static-analysis
- tailwind-css
- typescript
- vite
Log in or sign up for Devpost to join the conversation.