Security

Our security model in one sentence: there's nothing on a server to attack. Everything below is falsifiable from your DevTools.

Architecture

The generator is a static single-page application served from Cloudflare Pages: no user accounts, no authentication, and no backend code path anywhere in the generation flow. Every QR generation, encoding, scanning, and rendering operation runs entirely inside your browser. One small serverless endpoint sits beside it and is listed here rather than hidden: /api/contact (the contact form, which stores what you submit in Cloudflare D1 and emails it to us). It is not called while you make a QR code; the URL safety checker is a separate product at check.qr.abundera.ai. If you want to verify a QR code you've received before scanning it, use our Check if a QR code is safe tool.

Threat model

Because the generator collects, stores, and transmits no user data (the two endpoints above handle only what you explicitly send them), the most common web-app threats, credential theft, database breach, session hijacking, server-side injection, do not apply to it. The remaining attack surface is the static asset bundle (HTML, CSS, JavaScript) served from our origin. We design assuming:

Content Security Policy, by directive

The policy below is read from the deployed _headers file when this page is built, so it cannot drift from what the edge sends. Verify it in the response headers of any request.

Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://abundera.ai 'wasm-unsafe-eval' https://challenges.cloudflare.com;
  worker-src 'self' blob:;
  style-src 'self' 'unsafe-inline' https://abundera.ai;
  font-src 'self';
  img-src 'self' data: blob: https:;
  connect-src 'self' https: https://challenges.cloudflare.com;
  frame-src 'self' https://challenges.cloudflare.com;
  frame-ancestors 'none';
  base-uri 'self';
  form-action 'self'

What each directive lets us do and where it compromises:

What CSP cannot do

CSP limits where the browser will load code from. It does not vet what loaded code does. If our origin were compromised, the first item in the threat model, the attacker's script would run with the same permissions as ours, including the connect-src and img-src allowances above. Our defences against that sit upstream of CSP: deploys go out through one Cloudflare account with scoped tokens, asset filenames are content-hashed so a swapped file changes every URL that references it, and the service worker caches only same-origin GET responses. The manifesto describes the site as shipped. It is not a claim that no web page could ever send data somewhere, and we would rather you hold us to the narrower, checkable statement.

Different CSP policies apply to /bio/* (relaxed img-src for user-supplied avatars) and /embed/* (relaxed frame-ancestors for intentional embedding). Both are documented in site/_headers.

Transport + framing headers

Service worker

Our service worker (site/sw.js) caches only same-origin assets. The fetch handler explicitly rejects cross-origin requests and non-GET methods, you can read the logic in /sw.js (the source ships unminified enough to follow). Cache writes are wrapped in event.waitUntil() so they can't be dropped mid-navigation.

Input sanitization

Every rendering path that accepts user input treats it as untrusted text:

Cross-origin image fetching

When a user pastes a https: URL as a vCard photo or logo, the browser fetches it subject to CORS and our CSP's img-src allowlist. The image renders into a canvas. It never becomes live DOM, never runs as code, and never reaches our origin, the fetch is browser → remote image, and the result is painted client-side. An attacker controlling a remote image URL can track that the URL was loaded (a log line on their own server) but cannot exfiltrate anything from our page.

Subresource Integrity (SRI)

All JavaScript and CSS we ship is same-origin. We do not load third-party scripts or stylesheets, so SRI hashes are not applicable. If we ever load a third-party asset, we'll ship an SRI integrity attribute on it and document the hash-update process in this page.

Reporting a vulnerability

If you discover a security issue affecting Abundera QR, whether in our code, our deployment, or in a dependency we ship, please report it privately to security@abundera.ai. We aim to triage within 72 hours. You can also reach us via the contact details in our /.well-known/security.txt file.

No bug bounty (yet)

We do not currently offer paid bounties, but every confirmed valid report receives credit in the changelog and our public thanks.

Verify any of the above

Every claim on this page is falsifiable from your browser's DevTools without trusting us:

Contact

Security disclosures: security@abundera.ai

Last updated: 2026-09-03. Next review: 2026-12-03.