Inspiration
Privacy policies are long and few people read them, and even careful readers can't tell whether they're true. A site can say "we honor Global Privacy Control" while ad cookies from dozens of companies keep landing in your browser. We wanted a tool that checks the promise against what actually happens on the page, in plain language, with the policy's own words as evidence.
The method comes from PrivAudit (A Dual-Lens Auditing Framework for Website Privacy Practices under the CCPA, ACM CCS 2026, UC Santa Cruz). Its code was first made public on April 21, 2026, and its paper and data on September 9, 2026. PrivAudit compared policy disclosures with browser-observed cookies across roughly 1,000 websites and found that promises and behavior often disagree. But that finding lived in a research paper. Privany puts the method in front of the person who is about to click "I agree".
What it does
Privany is a Chrome extension with a side panel, backed by a small server that runs on your own computer.
- Checks every site automatically. As you browse, it notes which ad-tech companies the page contacts and which tracking cookies it sets, reads the site's privacy policy, and looks up PrivAudit's own 2025 audit of the site (937 sites covered). Safe sites get a green ✓; only risky sites get a warning on the page, with the reasons and the policy's own words.
- Notes under sign-up fields. Under fields like phone number or date of birth, it says what the policy does with that detail ("may be used for marketing and shared with other companies"), with a link to the exact sentence. It never reads what you type.
- Runs a full test on request. One click opens the full report. Privany visits the site in fresh browsers several times, alternating a normal browser and one sending the Global Privacy Control (GPC) opt-out signal. Each visit loads the homepage plus one inner page and scrolls both. It classifies every cookie, names the company behind it, and counts every outside domain contacted.
- Reads the policy for free. A rule-based reader, with no AI model, pulls out claims like "we don't sell your data" or "we honor GPC". Every quote is checked word for word against the policy, and unverified ones are discarded.
- Compares promise to behavior. Each claim is marked as matches, possible gap, not promised, or can't be tested from a browser.
- Tests consent and clicks. Separate visits click Reject All and Accept All on cookie banners, and click a Play button like a visitor to catch pop-up ads, which are recorded and blocked before they load.
- Applies your no-goes. You pick from 8 dealbreakers (selling data, AI training, tracking before consent, …) and Privany tells you which ones a site breaks. Your choices stay on your device.
On Allrecipes, for example, our seven checks found between 98 and 196 tracking cookies set before any consent. Its policy says it honors GPC, yet 58 to 74 outside advertising cookies were still written on every GPC visit.
How we built it
- Extension and web app. A Chrome Manifest V3 extension (side panel, background service worker, and a content script for warnings and field notes), plus a React/Vite/Tailwind web app. Both share the same report components.
- Server. Node, Express and Puppeteer run the audits. A deterministic rules engine does the comparison, with no model involved, so the same evidence always gives the same answer.
- Data. PrivAudit's released data (cookie categories and past audits of 937 sites, CC BY 4.0), the Open Cookie Database (Apache 2.0), and DuckDuckGo's Tracker Radar list for the companies behind tracker domains.
Separating signal from ad-auction noise. Ads change on every page load, so a single visit can't show what GPC does. For each setting $c$ (default or GPC), we visit $n$ times and keep only the cookies seen on every visit:
$$ S_c \;=\; \bigcap_{i=1}^{n} C_{c,i} $$
where $C_{c,i}$ is the set of cookies after visit $i$. A GPC promise is flagged as a possible gap when outside advertising cookies survive in $S_{\text{gpc}}$.
Finding the site's own policy. A page is accepted as a site's own policy only if it talks about itself and about the reader's data. With $w$ = the number of "we collect / use / share…" statements, $y$ = the number of mentions of "your personal information", $t = 1$ if the title says "Privacy Policy/Notice", and $g = 1$ if the page is about generating or templating policies:
$$ \text{score} = \min(w,20) + \min(y,20) + 6t - 40g, \qquad \text{own} \iff w \ge 3 \,\wedge\, y \ge 2 \,\wedge\, g = 0 $$
When to interrupt. Each finding is high or medium severity. With $h$ high and $m$ medium findings, a site is risky (and gets a warning) when
$$ h \ge 1 \;\lor\; m \ge 2 . $$
- Robustness. Rate limits, a job queue cap, timeouts, report caching, retries on network blips, blocked downloads, and a guard that stops the server from reaching private network addresses.
- Tests. 80 automated tests (48 server, 23 web, 9 extension), plus end-to-end runs that load the real extension into Chrome and check the badge, warnings, field notes and side panel.
Challenges we ran into
- Ad-auction noise. Our first single-visit runs contradicted themselves: one site loaded more ad cookies with GPC on than without. Repeat visits and the "seen on every visit" rule fixed it.
- Finding the right policy. Sites that sell privacy tools are full of "Privacy Policy Generator" links, and we were reading a product page as the site's policy. Link ranking plus the own-policy check above fixed it. Ranking candidates before trimming the list also fixed a bug where the real policy was the 5th link and got cut.
- Speed. Adding inner pages and scrolling pushed one check to 3.6 minutes. Running the default and GPC visits side by side brought it to about 2.
- Cookie banners. Many render inside iframes, and slow or hung ad frames could stall a visit, so we added per-frame and total time limits. Some sites have no one-click Reject on the first screen, so we report that instead of guessing at buttons.
- Reading policies with rules. Negations such as "do not collect, use, or sell … for training" initially produced a wrong "yes". We fixed it and added regression tests. The reader says "not promised" rather than guess.
- Pop-ups and bot detection. Ad networks often show a pop-up only once every few hours per connection, and some sites block automated browsers. The report says "none" doesn't rule pop-ups out.
- Honest wording. We never say "violation" or "illegal". A cookie alone doesn't prove what a company does with data, so every result carries its caveat.
- Narrow side panel. A wide cookie table pushed every section past 400px until we constrained the grid.
Accomplishments that we're proud of
- Every quote shown to the user is verified word for word, and the UI never writes quotes of its own.
- It runs with no AI model, no paid API and no account. The extension talks only to the server on your own computer and never reads what you type.
- Quiet by default: a badge on every site, a warning only on risky ones, and notes under the fields that matter.
- The no-goes turn a wall of findings into one headline, such as "Breaks 2 of your 5 no-goes".
- It brings PrivAudit's 2026 method, and its past findings, to a regular browser.
What we learned
- A policy that says one thing while the browser shows another is common, and plain-language evidence makes it understandable.
- Saying "can't be confirmed" matters as much as flagging a gap.
- Real consent banners, policy pages and ad networks vary far more than any demo suggests. Most of our hardest bugs came from real sites, not code.
- Picking the right page matters as much as reading it well.
What's next for Privany
- Opt-out and deletion email drafting, using the contact address the policy already gives.
- Parent-company grouping: "23 domains, 7 companies".
- An accuracy check of the policy reader against PrivAudit's labelled policies.
- Terms-of-service checks (arbitration, auto-renewal, content rights) and "they changed the terms" alerts.
- Firefox port, and CI to run the tests on every pull request.
Log in or sign up for Devpost to join the conversation.