Skip to content

Add tracker/company reverse lookup, methodology page, sitemap and social metadata - #16

Draft
kasnder wants to merge 3 commits into
mainfrom
claude/website-journalists-usefulness-yxhq1y
Draft

Add tracker/company reverse lookup, methodology page, sitemap and social metadata#16
kasnder wants to merge 3 commits into
mainfrom
claude/website-journalists-usefulness-yxhq1y

Conversation

@kasnder

@kasnder kasnder commented Aug 11, 2026

Copy link
Copy Markdown
Member

Makes the site usable by journalists and researchers, who arrive with the opposite question to the one the site answered ("which apps contain this tracker?", not "what is in this app?") and who need to check how a number was produced before quoting it.

Reverse lookup

lib/reverseIndex.js builds an inverted index — tracker → apps and company → apps — from the stored analyses. It is normalised (app metadata stored once, referenced by bundle ID), cached under CACHE_DIR in its own entry, and rebuilt on the same database signature as the aggregate site data, so requests do no extra work and the homepage cache stays small.

New pages:

  • /trackers and /companies — directories of everything seen in an analysed app, with a client-side filter (public/js/filter.js, external file so it passes the existing CSP).
  • /tracker/:slug and /company/:slug — the apps a tracker or company was found in, most reviewed first, 50 per page.

Slugs are derived from names, deduplicated deterministically so a collision (Mob.com vs Mob com) resolves the same way on every rebuild. Lookups use own-property checks, so /tracker/__proto__ 404s rather than reaching an inherited property.

App reports and the statistics tables now link into these pages, so a reader can go from an app to a tracker to every other app carrying it.

Apple system APIs the analyser reports (AdID access, Get device information) are labelled as system APIs rather than being listed as unattributed trackers; jurisdiction.isSystemSignature is exported for that.

Methodology page

/methodology documents what a figure on this site means:

  • The sample is free apps on the UK App Store, queued on demand and ordered by review count — explicitly not a random sample, which is the caveat most likely to be lost in a story.
  • Detection is static: a code signature is present in the binary. That is not evidence of transmission, and an empty result is not evidence of no tracking.
  • Jurisdiction classification rules, counting rules, limitations (substring name matching, ageing ownership data, staleness, storefront limits), what the data does and does not support, verification contact, and how to cite.

Linked from the nav and from /about.

Discoverability

  • Canonical links plus Open Graph and Twitter card metadata on every page, with the App Store icon as the card image on report pages.
  • /sitemap.xml re-enabled (was commented out) and extended to lookup and reference pages, with real lastmod timestamps instead of "now" for every URL.
  • /robots.txt pointing at the sitemap.
  • SITE_URL pins the public origin so these URLs are https:// behind a TLS-terminating proxy; it falls back to the request otherwise.

Incidental

jurisdiction.resolveTrackerName is memoised. Its substring scan over the company database now runs once per distinct tracker name instead of once per app occurrence, which is what makes building the index over the whole corpus cheap.

Testing

npm test — 57 tests pass (was 35).

  • test/reverseIndex.test.js: counting and denominators, ordering by popularity, company grouping, system-signature flagging, slug collisions, rejection of invalid and inherited-property slugs, JSON cache round-trip, pagination clamping.
  • test/lookupPages.test.js: boots the server against a stubbed database and asserts the new pages render, unknown slugs 404, trackers link from app reports and statistics, and the sitemap, robots.txt and social metadata contain what they should.

Both suites run without a database. Rendered output was also checked by hand for markup and spacing.

Notes for review

  • Nav now has five items (Home / Trackers / Statistics / Methodology / About); worth a look on a narrow viewport.
  • /statistics on a cold cache builds both the site data and the reverse index, so it reads all apps twice once. Both are cached afterwards. Say the word if you would rather they share one read.
  • The methodology text makes factual claims about the pipeline and about the operator; please check the wording before this leaves draft.

Generated by Claude Code

kasnder commented Aug 11, 2026

Copy link
Copy Markdown
Member Author

CI was red on the npm audit step, not on the tests — all 57 tests passed. The failure was not caused by this branch: its package.json and package-lock.json were byte-identical to main, and main's last CI run was on 13 July, before these two advisories were published. npm audit queries the live advisory database, so main would fail the same way if re-run today.

Pushed a lockfile-only fix in a separate commit (671e791):

  • body-parser 1.20.5 → 1.20.6 — GHSA-v422-hmwv-36x6, invalid limit values silently disabling size enforcement. Worth having here, since body size limits are configured from environment variables.
  • brace-expansion 5.0.6 → 5.0.9 — GHSA-3jxr-9vmj-r5cp and two related, transitive.

Both are patch bumps inside the existing semver ranges (express depends on body-parser@~1.20.5), so package.json is unchanged. Afterwards npm audit reports no vulnerabilities and the full suite still passes, including the body-limit tests that exercise the changed body-parser behaviour.

It is a separate commit so it can be dropped cleanly if Dependabot is already handling these, or if you would rather keep dependency changes out of a feature PR.


Generated by Claude Code

claude added 2 commits August 11, 2026 13:15
…ial metadata

The site could only answer "what is in this app?". Reporters and researchers
normally arrive with the opposite question, and with a need to check how a
number was produced before quoting it.

Reverse lookup: lib/reverseIndex.js builds an inverted tracker -> apps and
company -> apps index from the stored analyses, cached and rebuilt on the same
signature as the aggregate site data so requests do no extra work. New pages
/trackers, /companies, /tracker/:slug and /company/:slug list every tracker and
company with the apps they were found in, most reviewed first and paginated.
App reports and the statistics tables now link into them.

Methodology: /methodology documents the sample (free UK App Store apps, queued
on demand and ordered by popularity, so not a random sample), how detection
works and what a detection does and does not mean, the jurisdiction
classification rules, counting rules, limitations and citation guidance.

Discoverability: canonical links, Open Graph and Twitter card metadata per
page, with the app icon as the card image on reports; /sitemap.xml re-enabled
and extended to the lookup and reference pages with real lastmod timestamps;
/robots.txt points at it. SITE_URL pins the public origin behind a TLS proxy.

Also memoises jurisdiction.resolveTrackerName, whose substring scan over the
company database now runs once per distinct tracker name instead of once per
app occurrence.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011VVgEEw2J1LensebivHrwW
CI runs `npm audit` as a hard gate. Two advisories published since main last
ran CI now fail it on any branch, including this one, whose dependency files
are otherwise identical to main:

- body-parser <1.20.6 (GHSA-v422-hmwv-36x6), reachable here because the app
  configures body size limits from environment variables
- brace-expansion 3.0.0-5.0.8 (GHSA-3jxr-9vmj-r5cp and two related), transitive

Both are patch bumps within the existing semver ranges (express depends on
body-parser ~1.20.5), so package.json is unchanged. `npm audit` reports no
vulnerabilities afterwards and the full suite still passes, including the body
limit tests that exercise the affected body-parser behaviour.

Kept as a separate commit so it can be dropped if these are handled elsewhere.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011VVgEEw2J1LensebivHrwW
@kasnder
kasnder force-pushed the claude/website-journalists-usefulness-yxhq1y branch from 671e791 to 104811d Compare August 11, 2026 11:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants