Omar-Ramadan24/PrivacyTotal

LLM Assisted Privacy Analysis Tool

0

stars

1

commits

Python

primary language

Apr 22, 2026

updated

README

PrivacyTotal: Final Year Project Deliverable

Omar Ramadan – C00286349 | BSc (Hons) Cybercrime and IT Security | Supervisor: Mark Cummins South East Technological University | April 2026


Deliverables Checklist

DeliverableFile / LocationStatus
Research ReportResearch Report.pdfSubmitted
Project ReportProjectReport.pdf, ProjectReport.docxSubmitted
Web Applicationwebapp/web_app.py (run: python webapp/web_app.py)Complete
Training Datadata/processed/gap_dataset_v2.jsonl5,154 examples
Trained Modelsrc/models/mobilellama_gap_scratch/F1 = 0.930
Evaluation Scripteval_f1.pyF1=0.930, P=0.892, R=0.971
Requirementsrequirements.txtComplete

Table of Contents

  1. Project Overview
  2. Directory Structure
  3. Quick Start
  4. Research Report vs. Implementation: In-Depth Comparison
  5. Final Results Summary
  6. Key Deviations and Rationale

Project Overview

PrivacyTotal is a static analysis tool for Android applications that automatically compares an app's declared APK permissions against its published privacy policy. Using a fine-tuned MobileLLaMA 2.7B language model, the system determines whether each permission is disclosed in the policy, assigns a Privacy Health Score (PHS), and presents the results through a web application.

The tool was motivated by an RTÉ PrimeTime investigation (September 2025) revealing that precise location data from tens of thousands of Irish smartphones was being sold by data brokers, highlighting that most users blindly accept privacy policies without reading them.


Directory Structure

PrivacyTotal/
├── README.md                         This document
├── LICENSE                           MIT licence
├── Research Report.pdf               Final research report
├── ProjectReport.pdf                 Final project report
├── requirements.txt                  Python dependencies
├── eval_f1.py                        F1 evaluation script (keyword-oracle)
│
├── src/
│   ├── end_to_end_gap_analysis.py    Main pipeline: APK download → permission
│   │                                 extraction → policy scraping → LLM gap
│   │                                 analysis → PHS → DB save
│   ├── batch_runner.py               Batch runner for sequential multi-app analysis
│   ├── database.py                   SQLite layer (gap_analysis.db schema, CRUD)
│   ├── acquire_app_artifacts.py      Standalone APK + metadata acquisition script
│   ├── train_gap_scratch.py          QLoRA training script (final working version)
│   ├── prepare_gap_training_v2.py    Builds gap_dataset_v2.jsonl from OPP-115 data
│   │
│   ├── models/
│   │   └── mobilellama_gap_scratch/  Working LoRA adapter (trained from scratch)
│   │       ├── adapter_config.json
│   │       ├── adapter_model.bin     ~20MB LoRA weights
│   │       ├── tokenizer.json
│   │       ├── tokenizer.model
│   │       └── tokenizer_config.json
│   │
│   └── acquired/                     Per-app result folders (117 apps)
│       └── com.example.app/
│           └── YYYYMMDD_HHMMSS/
│               └── gap_report.csv    Permission × coverage × evidence × PHS
│
├── data/
│   ├── gap_analysis.db               SQLite database (117 apps, 175 runs)
│   └── processed/
│       └── gap_dataset_v2.jsonl      Training dataset (5,154 examples)
│
└── webapp/
    ├── web_app.py                    Flask application (PrivacyTotal)
    └── templates/
        ├── base.html                 Shared layout, navigation, CSS
        ├── index.html                Home: Analyze App / Browse DB / Research Report
        ├── app.html                  Per-app: PHS gauge, coverage donut, permission table
        ├── run.html                  Single run view with gap report details
        ├── diff.html                 Policy text diff between two runs
        └── job.html                  Live job status / log streaming

Quick Start

Prerequisites

  • Python 3.11
  • NVIDIA GPU with 8GB+ VRAM (RTX 2060 Super or equivalent)
  • Windows or Linux with Git Bash

Setup

# Create virtual environment
python -m venv venv311
source venv311/Scripts/activate  # Windows Git Bash
pip install -r requirements.txt

# Download base model (MobileLLaMA-2.7B-Chat) from HuggingFace
# The LoRA adapter in src/models/mobilellama_gap_scratch/ is already trained

Run the Web Application

cd PrivacyTotal/
source venv311/Scripts/activate
python webapp/web_app.py
# Opens at http://localhost:5000

Analyze a Single App

python src/end_to_end_gap_analysis.py --package-id com.example.app

Run Batch Analysis

python src/batch_runner.py
# Edit CANDIDATES list in batch_runner.py to specify apps

Evaluate F1

PYTHONIOENCODING=utf-8 python eval_f1.py

Research Report vs. Implementation: In-Depth Comparison

1. Scope: Number of Applications

Research ReportImplemented
Target1,000 applications117 applications
Categories10 distinct categories × 100 apps each10 categories (Social Media, Gaming, Productivity, Finance, Entertainment, Navigation, Health, Shopping, Communication, Security/VPN)
Sampling methodStratified from Google Play "Top Free" charts (October 2025)Top free apps by download count, sourced across multiple APK mirrors
Selection criteria≥1M downloads, updated ≤12 months, English policy, free appSame criteria applied; some apps excluded where no APK was obtainable from any source (e.g., Uber, Roblox)

Outcome: The research report outlined 1,000 applications but the implemented study analyzed 117 applications across all 10 planned categories. This reduction was driven by practical constraints: APK acquisition is rate-limited, Cloudflare-gated, and time-intensive. The 117-app dataset still represents a statistically meaningful cross-section and satisfies the original intent of cross-category comparison. The tool remains fully capable of scaling to 1,000 apps with additional runtime; all infrastructure is in place.


2. Model Training Strategy

The research report outlined a three-phase sequential training curriculum:

PhasePlannedImplemented
Phase 1Domain adaptation on Princeton-Leuven Corpus (50,000 sampled privacy policies, unsupervised causal LM)Not used
Phase 2Supervised fine-tuning on OPP-115 + GDPR NER dataset (token classification, entity extraction)partially attempted, caused failure
Phase 3Instruction tuning on PolicyQA + PrivacyQA + synthetic instructions (response generation)attempted, caused catastrophic forgetting
Final approachn/aSingle-phase scratch training on a custom gap_dataset_v2.jsonl (5,154 examples) targeting the specific covered/not_mentioned classification task

Why the planned approach was abandoned:

Phases 1 and 2 were implemented during development. The Princeton-Leuven pre-training taught the model to generate policy-style text (domain vocabulary), and OPP-115 fine-tuning reinforced verbatim policy quoting. When Phase 3 instruction tuning was applied on top (using mobilellama_stage3_final), the model suffered catastrophic forgetting; it ignored all input instructions entirely and generated hallucinated boilerplate regardless of the query. Root causes identified:

  • Learning rate (2e-4) was too aggressive for Phase 3, causing the adapter to overwrite earlier learning
  • Training responses in Phase 3 were verbatim policy text excerpts, not analytical gap assessments
  • Stacking three sequential adapters compounded representation drift

Solution (training from scratch): A custom training dataset (gap_dataset_v2.jsonl) was built that pairs each Android permission group with policy text excerpts and produces a clean single-line analytical output: "Mentioned: [explanation]" or "Not mentioned: [explanation]". Training directly on this from the base MobileLLaMA model (without any intermediate phases) produced a stable, well-behaved adapter in 3 epochs (~2.5 hours) with training loss converging from 2.91 → 0.34.


3. Training Dataset

Research ReportImplemented
Primary sourceOPP-115 Corpus (23,000 labels across 115 policies)OPP-115 annotations used as input signal for building gap_dataset_v2.jsonl
Supporting datasetsPolicyQA (25,017 QA pairs), PrivacyQA (1,750 queries), GDPR NER (44 EU policies)GDPR NER and PolicyQA/PrivacyQA were explored but not included in final training
Dataset size18,000–25,000 instruction-response pairs estimated5,154 examples in gap_dataset_v2.jsonl
FormatClassification, extraction, summarization, risk assessment tasksSingle analytical task: classify permission coverage against policy text (covered / not_mentioned / partially_covered)
Princeton-Leuven rolePhase 1 unsupervised domain adaptationNot used in final training

Outcome: The final dataset is smaller than planned (5,154 vs 18,000–25,000) but purpose-built for the exact task. As noted by Firecrawl (2025), 5,000–10,000 well-formed instruction pairs works well for focused fine-tuning. The higher-volume multi-task approach outlined in the report was superseded by a leaner, task-focused dataset that produced better results for this specific gap-analysis classification problem.


4. Training Configuration & Resources

Research ReportImplemented
HardwareRTX 2060 Super (8GB VRAM)RTX 2060 Super (8GB VRAM) ✓
MethodQLoRA via bitsandbytes + PEFTQLoRA via bitsandbytes + PEFT ✓
LoRA rankr=8r=8 ✓
Alphaα=16α=16 ✓
Learning rate2e-45e-5 (reduced from planned 2e-4 to avoid catastrophic forgetting)
Epochs33 ✓
Adapter size~16MB estimated~20MB actual (adapter_model.bin) ✓
Training time50–60 hours~2.5 hours (due to smaller, focused dataset)
Gradient accumulation16 steps4 steps (balanced for dataset size)

Key deviation: The learning rate was reduced from 2e-4 to 5e-5. The report's recommendation of 2e-4 follows Raschka (2023) for general LoRA fine-tuning, but for this specific classification task on a 2.7B model, 2e-4 proved too aggressive and was the root cause of the catastrophic forgetting observed in the mobilellama_stage3_final adapter. At 5e-5 with cosine decay, training converged stably across 3 epochs.


5. APK & Data Acquisition Pipeline

Research ReportImplemented
APK sourceAPKMirror only5-source fallback chain: APKMirror → APKPure → APKCombo → APKMonk → Uptodown
Scraping technologySelenium WebDriver + BeautifulSoupPlaywright (replaced Selenium) for JavaScript-heavy pages + requests for simpler sources
Privacy policy retrievalGoogle Play Store (Selenium headless Chrome)Google Play Store via requests-html / httpx with HTML stripping
Cloudflare mitigationNot addressed in reportTLS fingerprint masking (10 signals), direct URL construction bypassing search pages, 7-retry backoff with randomised delays
APK verificationHash/signature checkingPackage name verification via AndroidManifest.xml binary inspection (ASCII + UTF-16-LE); rejects wrong-app CDN responses
APK storageDeleted post-processing (ethical consideration)APKs retained in src/acquired/ during session; deletion is manual

Key upgrades over the report:

  1. Playwright over Selenium: Playwright was mentioned in the report as a faster alternative but was deemed to have less documentation for legacy pages. In practice, Playwright's expect_download() context manager and response event interception proved essential for capturing APKMirror's CDN download URLs, making it the better choice.

  2. 5-source fallback: APKMirror alone is insufficient because Cloudflare blocks automated search requests. The fallback chain (APKPure, APKCombo, APKMonk, Uptodown) enables acquisition of high-profile apps (WhatsApp, Instagram, TikTok, YouTube) that APKMirror's search would block.

  3. Direct URL bypass: A _DEV_SLUG_MAP of ~60 publisher-to-developer-slug mappings enables constructing APKMirror app URLs directly (/apk/{dev-slug}/{app-slug}/) without triggering the Cloudflare-gated search page, unlocking apps like Homescapes, Township, Hulu, and Azure.

  4. Post-download verification: The Uptodown source occasionally served wrong-app CDN responses (e.g., Roblox's slot returned Instagram's APK). Package name verification reads the binary AndroidManifest.xml from the downloaded ZIP and checks for the expected package ID as both ASCII and UTF-16-LE byte patterns, rejecting mismatches before analysis proceeds.


6. Privacy Health Score (PHS) Algorithm

Research ReportImplemented
Base score100100 ✓
High-risk mismatch penalty−15 per undisclosed dangerous permission−15 per not_mentioned dangerous permission ✓
Medium-risk mismatch penalty−10 per undisclosed hardware-access permission−10 per not_mentioned hardware permission ✓
Vagueness index−2 per hedge word detected ("may", "some", "might")Vagueness index computed (hedge word frequency in policy); stored in DB but weighted differently in final PHS formula
MinimumCapped at 0Capped at 0 ✓
Risk labelsCritical Risk (<50)Low (<30), Moderate (30–59), High (60–79), Critical (≥80); the scale is inverted so higher PHS means less risk
Additional signalNot describedKeyword score: LLM output corroborated by keyword matching across 14 permission-specific keyword sets; influences coverage classification

Risk label note: The report defines "Critical Risk" as PHS < 50. The implementation uses a 0–100 scale where 100 = fully disclosed (Low Risk) and 0 = fully undisclosed (Critical Risk), consistent with the report's intent. The risk labels in the implementation are: Low Risk (≥80), Moderate Risk (60–79), High Risk (30–59), Critical Risk (<30), refined from the report's single threshold.


7. Web Application

Research ReportImplemented
NameNot namedPrivacyTotal
InterfaceWeb-based, accepts app name/developer, returns plain-language summaries of data practices, third-party sharing, permission mismatchesFlask web app on localhost:5000
Input methodApp name/identifierPackage ID (e.g., com.instagram.android) or app name search
OutputPlain-language summaries, permission mismatches, PHSFull coverage donut chart, PHS gauge, per-permission table (covered/not_mentioned + evidence text), run history, policy diff between versions
Research report displayPosted alongside the toolFull formatted research report displayed in the web app (Abstract, Methodology, Model, Results, Findings tabs)
DatabaseNot specifiedSQLite (gap_analysis.db) with all 117 apps, 175 runs, policy snapshots
Policy change detectionNot specifiedPolicy text hash comparison; /diff/<old_id>/<new_id> endpoint shows side-by-side diff of policy changes between runs
Live analysisNot specifiedBackground job system with live log streaming via /job/<job_id> endpoint

Additional capabilities not in report:

  • Browse Database tab: Live-search across all 117 analyzed apps with sortable PHS scores and coverage bars
  • Policy diff view: Detects when an app's privacy policy text changed between runs and shows highlighted additions/removals
  • Run history: Each app page shows all historical analysis runs, enabling longitudinal tracking
  • Job status page: Real-time progress display as the pipeline runs, including LLM per-permission classification progress

8. Evaluation & Validation

Research ReportImplemented
MethodManual human review of 20% of apps (200 apps across 10 categories) to establish ground truthAutomated keyword-oracle F1 (eval_f1.py): keyword matching as ground-truth proxy
MetricsPrecision, Recall, F1-ScorePrecision, Recall, F1-Score, Accuracy, Macro-avg F1, Weighted-avg F1 ✓
TargetF1 > 80Achieved F1 = 0.930
Precision targetNot specifiedAchieved Precision = 0.892
Recall targetNot specifiedAchieved Recall = 0.971
Coverage1,000 apps117 apps, 3,483 rows, 121 unique permissions, 175 runs
LLM agreementNot specified92.5% (2,931 / 3,167 non-ambiguous responses)

Validation approach deviation: The research report planned manual review of 200 apps by a human reviewer examining each privacy policy. This would be comprehensive but labour-intensive and non-reproducible. The implementation uses a deterministic keyword-oracle approach: for each permission group, a curated set of 8–14 domain keywords is matched against the policy text to establish a reproducible ground truth label. The LLM's classification is then compared against this keyword oracle. This approach:

  • Is fully reproducible (re-run eval_f1.py at any time)
  • Produces consistent results not subject to human reviewer variability
  • Scales to any number of apps automatically

The trade-off is that the keyword oracle may have its own false positives/negatives. However, an F1 of 0.930 against this oracle, with Precision of 0.892, provides meaningful evidence that the LLM classifications track the keyword retriever closely, though the oracle and the retriever share signal, so the figure is best read as a consistency check rather than an independent accuracy measure.


9. Ethical Considerations

Research ReportImplemented
Data types analyzedOpen-source programs and public legal texts onlyPublic APKs and publicly available privacy policies only ✓
Personal dataNo personal profiling, no confidential details in outputNo personal data collected or stored ✓
Rate limitingDelays between requests to respect robots.txtRandomised delays, rotating user agents, TLS fingerprint rotation ✓
APK retentionAPKs deleted post-processingAPKs are stored in src/acquired/ for reproducibility; should be deleted per the ethical commitment before final submission
ScopeAnalysis onlyAnalysis only; no modification or redistribution of APKs ✓

Note on APK retention: The research report commits to deleting APKs post-processing. The current src/acquired/ directory retains downloaded APKs alongside the CSV reports (total ~12GB). For compliance with this commitment, APK files (*.apk, *.xapk) within src/acquired/ should be deleted before final submission; only the gap_report.csv and metadata files are required for reproducibility.


10. Tools & Frameworks

ComponentResearch ReportImplemented
LLMMobileLLaMA 2.7BMobileLLaMA 2.7B ✓
Fine-tuningQLoRA + bitsandbytes + PEFTQLoRA + bitsandbytes + PEFT ✓
Static analysisAndroguardAndroguard ✓
Web scrapingSelenium + BeautifulSoupPlaywright (primary) + requests + BeautifulSoup ✓
Web frameworkNot specifiedFlask
DatabaseNot specifiedSQLite via Python sqlite3
APK sourcesAPKMirrorAPKMirror + APKPure + APKCombo + APKMonk + Uptodown
Alternatives consideredMistral 7B (too large), TinyLLaMA (too weak), MobSF (GUI-only), Frida (dynamic), Scrapy (no JS)Same alternatives considered and dismissed for same reasons ✓

Final Results Summary

MetricValue
Applications analyzed117
Total permission rows3,483
Unique permissions observed121
Analysis runs completed175
Permissions covered in policy2,272 (65.2%)
Permissions not mentioned895 (25.7%)
Partially covered / unclear316 (9.1%)
F1 Score0.930
Precision0.892
Recall0.971
Accuracy0.912
LLM agreement rate92.5%
Model training time~2.5 hours
Model adapter size~20MB
Training loss (start → end)2.91 → 0.34
Training dataset size5,154 examples

App Risk Distribution across 117 apps:

Risk LevelPHS RangeCount
Low Risk≥80~52 apps
Moderate Risk60–79~14 apps
High Risk30–59~21 apps
Critical Risk<30~13 apps

Notable findings:

  • Gaming apps (Rovio, Outfit7, Supercell, EA) frequently request NFC, WRITE_CALENDAR, and READ_PHONE_STATE permissions without policy disclosure
  • Social media apps (Instagram, Spotify) showed high PHS (100) due to comprehensive policy coverage despite many permissions
  • Productivity apps (Microsoft suite, Google Workspace) scored 94–96 consistently, disclosing almost all permissions
  • Finance/payment apps (Revolut, TransferWise) scored low (20–39), failing to disclose several data access permissions

Key Deviations and Rationale

What the Report SaidWhat Was DoneWhy
1,000 apps117 appsAPK acquisition rate-limiting and session time constraints; all infrastructure scales to 1,000
3-phase training curriculumSingle-phase scratch trainingPhases 1–3 caused catastrophic forgetting; scratch training on task-specific data produced superior results (F1=0.930 vs broken output)
18,000–25,000 instruction pairs5,154 examplesSmaller focused dataset outperformed larger unfocused dataset for this classification task
Selenium for scrapingPlaywright (primary)Playwright's async model and download interception were essential for APKMirror's CDN URLs
APKMirror only5-source fallbackCloudflare blocks APKMirror search; fallback chain enables acquisition of high-profile apps
Manual 20% validationAutomated keyword-oracle F1Fully reproducible, scalable, consistent; keyword oracle provides deterministic ground truth
LR = 2e-4LR = 5e-52e-4 caused catastrophic forgetting in early training; 5e-5 converged stably

PrivacyTotal was built to empower users to understand what data applications collect and whether they are honest about it. The tool bridges the gap between legal privacy policies and user comprehension, exposing the "Privacy Paradox" through automated, evidence-based gap analysis at scale.

Contributors

Omar-Ramadan24/PrivacyTotal

LLM Assisted Privacy Analysis Tool

0

stars

1

commits

Python

primary language

Apr 22, 2026

updated

README

PrivacyTotal: Final Year Project Deliverable

Omar Ramadan – C00286349 | BSc (Hons) Cybercrime and IT Security | Supervisor: Mark Cummins South East Technological University | April 2026


Deliverables Checklist

DeliverableFile / LocationStatus
Research ReportResearch Report.pdfSubmitted
Project ReportProjectReport.pdf, ProjectReport.docxSubmitted
Web Applicationwebapp/web_app.py (run: python webapp/web_app.py)Complete
Training Datadata/processed/gap_dataset_v2.jsonl5,154 examples
Trained Modelsrc/models/mobilellama_gap_scratch/F1 = 0.930
Evaluation Scripteval_f1.pyF1=0.930, P=0.892, R=0.971
Requirementsrequirements.txtComplete

Table of Contents

  1. Project Overview
  2. Directory Structure
  3. Quick Start
  4. Research Report vs. Implementation: In-Depth Comparison
  5. Final Results Summary
  6. Key Deviations and Rationale

Project Overview

PrivacyTotal is a static analysis tool for Android applications that automatically compares an app's declared APK permissions against its published privacy policy. Using a fine-tuned MobileLLaMA 2.7B language model, the system determines whether each permission is disclosed in the policy, assigns a Privacy Health Score (PHS), and presents the results through a web application.

The tool was motivated by an RTÉ PrimeTime investigation (September 2025) revealing that precise location data from tens of thousands of Irish smartphones was being sold by data brokers, highlighting that most users blindly accept privacy policies without reading them.


Directory Structure

PrivacyTotal/
├── README.md                         This document
├── LICENSE                           MIT licence
├── Research Report.pdf               Final research report
├── ProjectReport.pdf                 Final project report
├── requirements.txt                  Python dependencies
├── eval_f1.py                        F1 evaluation script (keyword-oracle)
│
├── src/
│   ├── end_to_end_gap_analysis.py    Main pipeline: APK download → permission
│   │                                 extraction → policy scraping → LLM gap
│   │                                 analysis → PHS → DB save
│   ├── batch_runner.py               Batch runner for sequential multi-app analysis
│   ├── database.py                   SQLite layer (gap_analysis.db schema, CRUD)
│   ├── acquire_app_artifacts.py      Standalone APK + metadata acquisition script
│   ├── train_gap_scratch.py          QLoRA training script (final working version)
│   ├── prepare_gap_training_v2.py    Builds gap_dataset_v2.jsonl from OPP-115 data
│   │
│   ├── models/
│   │   └── mobilellama_gap_scratch/  Working LoRA adapter (trained from scratch)
│   │       ├── adapter_config.json
│   │       ├── adapter_model.bin     ~20MB LoRA weights
│   │       ├── tokenizer.json
│   │       ├── tokenizer.model
│   │       └── tokenizer_config.json
│   │
│   └── acquired/                     Per-app result folders (117 apps)
│       └── com.example.app/
│           └── YYYYMMDD_HHMMSS/
│               └── gap_report.csv    Permission × coverage × evidence × PHS
│
├── data/
│   ├── gap_analysis.db               SQLite database (117 apps, 175 runs)
│   └── processed/
│       └── gap_dataset_v2.jsonl      Training dataset (5,154 examples)
│
└── webapp/
    ├── web_app.py                    Flask application (PrivacyTotal)
    └── templates/
        ├── base.html                 Shared layout, navigation, CSS
        ├── index.html                Home: Analyze App / Browse DB / Research Report
        ├── app.html                  Per-app: PHS gauge, coverage donut, permission table
        ├── run.html                  Single run view with gap report details
        ├── diff.html                 Policy text diff between two runs
        └── job.html                  Live job status / log streaming

Quick Start

Prerequisites

  • Python 3.11
  • NVIDIA GPU with 8GB+ VRAM (RTX 2060 Super or equivalent)
  • Windows or Linux with Git Bash

Setup

# Create virtual environment
python -m venv venv311
source venv311/Scripts/activate  # Windows Git Bash
pip install -r requirements.txt

# Download base model (MobileLLaMA-2.7B-Chat) from HuggingFace
# The LoRA adapter in src/models/mobilellama_gap_scratch/ is already trained

Run the Web Application

cd PrivacyTotal/
source venv311/Scripts/activate
python webapp/web_app.py
# Opens at http://localhost:5000

Analyze a Single App

python src/end_to_end_gap_analysis.py --package-id com.example.app

Run Batch Analysis

python src/batch_runner.py
# Edit CANDIDATES list in batch_runner.py to specify apps

Evaluate F1

PYTHONIOENCODING=utf-8 python eval_f1.py

Research Report vs. Implementation: In-Depth Comparison

1. Scope: Number of Applications

Research ReportImplemented
Target1,000 applications117 applications
Categories10 distinct categories × 100 apps each10 categories (Social Media, Gaming, Productivity, Finance, Entertainment, Navigation, Health, Shopping, Communication, Security/VPN)
Sampling methodStratified from Google Play "Top Free" charts (October 2025)Top free apps by download count, sourced across multiple APK mirrors
Selection criteria≥1M downloads, updated ≤12 months, English policy, free appSame criteria applied; some apps excluded where no APK was obtainable from any source (e.g., Uber, Roblox)

Outcome: The research report outlined 1,000 applications but the implemented study analyzed 117 applications across all 10 planned categories. This reduction was driven by practical constraints: APK acquisition is rate-limited, Cloudflare-gated, and time-intensive. The 117-app dataset still represents a statistically meaningful cross-section and satisfies the original intent of cross-category comparison. The tool remains fully capable of scaling to 1,000 apps with additional runtime; all infrastructure is in place.


2. Model Training Strategy

The research report outlined a three-phase sequential training curriculum:

PhasePlannedImplemented
Phase 1Domain adaptation on Princeton-Leuven Corpus (50,000 sampled privacy policies, unsupervised causal LM)Not used
Phase 2Supervised fine-tuning on OPP-115 + GDPR NER dataset (token classification, entity extraction)partially attempted, caused failure
Phase 3Instruction tuning on PolicyQA + PrivacyQA + synthetic instructions (response generation)attempted, caused catastrophic forgetting
Final approachn/aSingle-phase scratch training on a custom gap_dataset_v2.jsonl (5,154 examples) targeting the specific covered/not_mentioned classification task

Why the planned approach was abandoned:

Phases 1 and 2 were implemented during development. The Princeton-Leuven pre-training taught the model to generate policy-style text (domain vocabulary), and OPP-115 fine-tuning reinforced verbatim policy quoting. When Phase 3 instruction tuning was applied on top (using mobilellama_stage3_final), the model suffered catastrophic forgetting; it ignored all input instructions entirely and generated hallucinated boilerplate regardless of the query. Root causes identified:

  • Learning rate (2e-4) was too aggressive for Phase 3, causing the adapter to overwrite earlier learning
  • Training responses in Phase 3 were verbatim policy text excerpts, not analytical gap assessments
  • Stacking three sequential adapters compounded representation drift

Solution (training from scratch): A custom training dataset (gap_dataset_v2.jsonl) was built that pairs each Android permission group with policy text excerpts and produces a clean single-line analytical output: "Mentioned: [explanation]" or "Not mentioned: [explanation]". Training directly on this from the base MobileLLaMA model (without any intermediate phases) produced a stable, well-behaved adapter in 3 epochs (~2.5 hours) with training loss converging from 2.91 → 0.34.


3. Training Dataset

Research ReportImplemented
Primary sourceOPP-115 Corpus (23,000 labels across 115 policies)OPP-115 annotations used as input signal for building gap_dataset_v2.jsonl
Supporting datasetsPolicyQA (25,017 QA pairs), PrivacyQA (1,750 queries), GDPR NER (44 EU policies)GDPR NER and PolicyQA/PrivacyQA were explored but not included in final training
Dataset size18,000–25,000 instruction-response pairs estimated5,154 examples in gap_dataset_v2.jsonl
FormatClassification, extraction, summarization, risk assessment tasksSingle analytical task: classify permission coverage against policy text (covered / not_mentioned / partially_covered)
Princeton-Leuven rolePhase 1 unsupervised domain adaptationNot used in final training

Outcome: The final dataset is smaller than planned (5,154 vs 18,000–25,000) but purpose-built for the exact task. As noted by Firecrawl (2025), 5,000–10,000 well-formed instruction pairs works well for focused fine-tuning. The higher-volume multi-task approach outlined in the report was superseded by a leaner, task-focused dataset that produced better results for this specific gap-analysis classification problem.


4. Training Configuration & Resources

Research ReportImplemented
HardwareRTX 2060 Super (8GB VRAM)RTX 2060 Super (8GB VRAM) ✓
MethodQLoRA via bitsandbytes + PEFTQLoRA via bitsandbytes + PEFT ✓
LoRA rankr=8r=8 ✓
Alphaα=16α=16 ✓
Learning rate2e-45e-5 (reduced from planned 2e-4 to avoid catastrophic forgetting)
Epochs33 ✓
Adapter size~16MB estimated~20MB actual (adapter_model.bin) ✓
Training time50–60 hours~2.5 hours (due to smaller, focused dataset)
Gradient accumulation16 steps4 steps (balanced for dataset size)

Key deviation: The learning rate was reduced from 2e-4 to 5e-5. The report's recommendation of 2e-4 follows Raschka (2023) for general LoRA fine-tuning, but for this specific classification task on a 2.7B model, 2e-4 proved too aggressive and was the root cause of the catastrophic forgetting observed in the mobilellama_stage3_final adapter. At 5e-5 with cosine decay, training converged stably across 3 epochs.


5. APK & Data Acquisition Pipeline

Research ReportImplemented
APK sourceAPKMirror only5-source fallback chain: APKMirror → APKPure → APKCombo → APKMonk → Uptodown
Scraping technologySelenium WebDriver + BeautifulSoupPlaywright (replaced Selenium) for JavaScript-heavy pages + requests for simpler sources
Privacy policy retrievalGoogle Play Store (Selenium headless Chrome)Google Play Store via requests-html / httpx with HTML stripping
Cloudflare mitigationNot addressed in reportTLS fingerprint masking (10 signals), direct URL construction bypassing search pages, 7-retry backoff with randomised delays
APK verificationHash/signature checkingPackage name verification via AndroidManifest.xml binary inspection (ASCII + UTF-16-LE); rejects wrong-app CDN responses
APK storageDeleted post-processing (ethical consideration)APKs retained in src/acquired/ during session; deletion is manual

Key upgrades over the report:

  1. Playwright over Selenium: Playwright was mentioned in the report as a faster alternative but was deemed to have less documentation for legacy pages. In practice, Playwright's expect_download() context manager and response event interception proved essential for capturing APKMirror's CDN download URLs, making it the better choice.

  2. 5-source fallback: APKMirror alone is insufficient because Cloudflare blocks automated search requests. The fallback chain (APKPure, APKCombo, APKMonk, Uptodown) enables acquisition of high-profile apps (WhatsApp, Instagram, TikTok, YouTube) that APKMirror's search would block.

  3. Direct URL bypass: A _DEV_SLUG_MAP of ~60 publisher-to-developer-slug mappings enables constructing APKMirror app URLs directly (/apk/{dev-slug}/{app-slug}/) without triggering the Cloudflare-gated search page, unlocking apps like Homescapes, Township, Hulu, and Azure.

  4. Post-download verification: The Uptodown source occasionally served wrong-app CDN responses (e.g., Roblox's slot returned Instagram's APK). Package name verification reads the binary AndroidManifest.xml from the downloaded ZIP and checks for the expected package ID as both ASCII and UTF-16-LE byte patterns, rejecting mismatches before analysis proceeds.


6. Privacy Health Score (PHS) Algorithm

Research ReportImplemented
Base score100100 ✓
High-risk mismatch penalty−15 per undisclosed dangerous permission−15 per not_mentioned dangerous permission ✓
Medium-risk mismatch penalty−10 per undisclosed hardware-access permission−10 per not_mentioned hardware permission ✓
Vagueness index−2 per hedge word detected ("may", "some", "might")Vagueness index computed (hedge word frequency in policy); stored in DB but weighted differently in final PHS formula
MinimumCapped at 0Capped at 0 ✓
Risk labelsCritical Risk (<50)Low (<30), Moderate (30–59), High (60–79), Critical (≥80); the scale is inverted so higher PHS means less risk
Additional signalNot describedKeyword score: LLM output corroborated by keyword matching across 14 permission-specific keyword sets; influences coverage classification

Risk label note: The report defines "Critical Risk" as PHS < 50. The implementation uses a 0–100 scale where 100 = fully disclosed (Low Risk) and 0 = fully undisclosed (Critical Risk), consistent with the report's intent. The risk labels in the implementation are: Low Risk (≥80), Moderate Risk (60–79), High Risk (30–59), Critical Risk (<30), refined from the report's single threshold.


7. Web Application

Research ReportImplemented
NameNot namedPrivacyTotal
InterfaceWeb-based, accepts app name/developer, returns plain-language summaries of data practices, third-party sharing, permission mismatchesFlask web app on localhost:5000
Input methodApp name/identifierPackage ID (e.g., com.instagram.android) or app name search
OutputPlain-language summaries, permission mismatches, PHSFull coverage donut chart, PHS gauge, per-permission table (covered/not_mentioned + evidence text), run history, policy diff between versions
Research report displayPosted alongside the toolFull formatted research report displayed in the web app (Abstract, Methodology, Model, Results, Findings tabs)
DatabaseNot specifiedSQLite (gap_analysis.db) with all 117 apps, 175 runs, policy snapshots
Policy change detectionNot specifiedPolicy text hash comparison; /diff/<old_id>/<new_id> endpoint shows side-by-side diff of policy changes between runs
Live analysisNot specifiedBackground job system with live log streaming via /job/<job_id> endpoint

Additional capabilities not in report:

  • Browse Database tab: Live-search across all 117 analyzed apps with sortable PHS scores and coverage bars
  • Policy diff view: Detects when an app's privacy policy text changed between runs and shows highlighted additions/removals
  • Run history: Each app page shows all historical analysis runs, enabling longitudinal tracking
  • Job status page: Real-time progress display as the pipeline runs, including LLM per-permission classification progress

8. Evaluation & Validation

Research ReportImplemented
MethodManual human review of 20% of apps (200 apps across 10 categories) to establish ground truthAutomated keyword-oracle F1 (eval_f1.py): keyword matching as ground-truth proxy
MetricsPrecision, Recall, F1-ScorePrecision, Recall, F1-Score, Accuracy, Macro-avg F1, Weighted-avg F1 ✓
TargetF1 > 80Achieved F1 = 0.930
Precision targetNot specifiedAchieved Precision = 0.892
Recall targetNot specifiedAchieved Recall = 0.971
Coverage1,000 apps117 apps, 3,483 rows, 121 unique permissions, 175 runs
LLM agreementNot specified92.5% (2,931 / 3,167 non-ambiguous responses)

Validation approach deviation: The research report planned manual review of 200 apps by a human reviewer examining each privacy policy. This would be comprehensive but labour-intensive and non-reproducible. The implementation uses a deterministic keyword-oracle approach: for each permission group, a curated set of 8–14 domain keywords is matched against the policy text to establish a reproducible ground truth label. The LLM's classification is then compared against this keyword oracle. This approach:

  • Is fully reproducible (re-run eval_f1.py at any time)
  • Produces consistent results not subject to human reviewer variability
  • Scales to any number of apps automatically

The trade-off is that the keyword oracle may have its own false positives/negatives. However, an F1 of 0.930 against this oracle, with Precision of 0.892, provides meaningful evidence that the LLM classifications track the keyword retriever closely, though the oracle and the retriever share signal, so the figure is best read as a consistency check rather than an independent accuracy measure.


9. Ethical Considerations

Research ReportImplemented
Data types analyzedOpen-source programs and public legal texts onlyPublic APKs and publicly available privacy policies only ✓
Personal dataNo personal profiling, no confidential details in outputNo personal data collected or stored ✓
Rate limitingDelays between requests to respect robots.txtRandomised delays, rotating user agents, TLS fingerprint rotation ✓
APK retentionAPKs deleted post-processingAPKs are stored in src/acquired/ for reproducibility; should be deleted per the ethical commitment before final submission
ScopeAnalysis onlyAnalysis only; no modification or redistribution of APKs ✓

Note on APK retention: The research report commits to deleting APKs post-processing. The current src/acquired/ directory retains downloaded APKs alongside the CSV reports (total ~12GB). For compliance with this commitment, APK files (*.apk, *.xapk) within src/acquired/ should be deleted before final submission; only the gap_report.csv and metadata files are required for reproducibility.


10. Tools & Frameworks

ComponentResearch ReportImplemented
LLMMobileLLaMA 2.7BMobileLLaMA 2.7B ✓
Fine-tuningQLoRA + bitsandbytes + PEFTQLoRA + bitsandbytes + PEFT ✓
Static analysisAndroguardAndroguard ✓
Web scrapingSelenium + BeautifulSoupPlaywright (primary) + requests + BeautifulSoup ✓
Web frameworkNot specifiedFlask
DatabaseNot specifiedSQLite via Python sqlite3
APK sourcesAPKMirrorAPKMirror + APKPure + APKCombo + APKMonk + Uptodown
Alternatives consideredMistral 7B (too large), TinyLLaMA (too weak), MobSF (GUI-only), Frida (dynamic), Scrapy (no JS)Same alternatives considered and dismissed for same reasons ✓

Final Results Summary

MetricValue
Applications analyzed117
Total permission rows3,483
Unique permissions observed121
Analysis runs completed175
Permissions covered in policy2,272 (65.2%)
Permissions not mentioned895 (25.7%)
Partially covered / unclear316 (9.1%)
F1 Score0.930
Precision0.892
Recall0.971
Accuracy0.912
LLM agreement rate92.5%
Model training time~2.5 hours
Model adapter size~20MB
Training loss (start → end)2.91 → 0.34
Training dataset size5,154 examples

App Risk Distribution across 117 apps:

Risk LevelPHS RangeCount
Low Risk≥80~52 apps
Moderate Risk60–79~14 apps
High Risk30–59~21 apps
Critical Risk<30~13 apps

Notable findings:

  • Gaming apps (Rovio, Outfit7, Supercell, EA) frequently request NFC, WRITE_CALENDAR, and READ_PHONE_STATE permissions without policy disclosure
  • Social media apps (Instagram, Spotify) showed high PHS (100) due to comprehensive policy coverage despite many permissions
  • Productivity apps (Microsoft suite, Google Workspace) scored 94–96 consistently, disclosing almost all permissions
  • Finance/payment apps (Revolut, TransferWise) scored low (20–39), failing to disclose several data access permissions

Key Deviations and Rationale

What the Report SaidWhat Was DoneWhy
1,000 apps117 appsAPK acquisition rate-limiting and session time constraints; all infrastructure scales to 1,000
3-phase training curriculumSingle-phase scratch trainingPhases 1–3 caused catastrophic forgetting; scratch training on task-specific data produced superior results (F1=0.930 vs broken output)
18,000–25,000 instruction pairs5,154 examplesSmaller focused dataset outperformed larger unfocused dataset for this classification task
Selenium for scrapingPlaywright (primary)Playwright's async model and download interception were essential for APKMirror's CDN URLs
APKMirror only5-source fallbackCloudflare blocks APKMirror search; fallback chain enables acquisition of high-profile apps
Manual 20% validationAutomated keyword-oracle F1Fully reproducible, scalable, consistent; keyword oracle provides deterministic ground truth
LR = 2e-4LR = 5e-52e-4 caused catastrophic forgetting in early training; 5e-5 converged stably

PrivacyTotal was built to empower users to understand what data applications collect and whether they are honest about it. The tool bridges the gap between legal privacy policies and user comprehension, exposing the "Privacy Paradox" through automated, evidence-based gap analysis at scale.

Contributors

Languages

Python

74.0%

HTML

24.4%

CSS

1.7%