Skip to main content

Security

How the Norway Data API and its reference page are protected, and the settings that have to be made by hand. The full threat model and every control are in API security controls. This page summarizes it and covers this site's side.

Quick reference

  • The API (in its own repo): https-only links, a cache key limited to documented parameters, security headers on every response, rate limits on both functions, minimal logging, and a locked build. The contract and smoke tests check all of it.
  • The reference page (this repo): Scalar is pinned with an integrity hash, loads only on /api-reference, and has its proxy, telemetry, AI agent, and remote fonts turned off.
  • This site: security headers and a Content Security Policy on every page, and hardened CI. See Security headers and Repository and CI security.
  • By hand: a few Netlify and DNS settings, listed under Settings to make by hand.

What's being protected​

The API is public and read-only, with no accounts or user data, so the risks aren't data theft. They're:

  • The Netlify bill: floods of requests that run functions.
  • Response integrity: tampered responses served to other clients from the cache.
  • Visitors' browsers: the reference page runs a large third-party script and renders content from another domain.
  • The deployed code: a compromised dependency or CI step changing what goes live.
  • The accounts and domain: GitHub, Netlify, and DNS, which control everything else.

The API​

ControlWhat it stops
Links always use https (except on localhost), never the request's schemeCache poisoning through the X-Forwarded-Proto header
Netlify-Vary: query=category|place|limit|offsetIgnored parameters creating cache entries or bypassing the cache
Rate limit on the API function (60 per minute per IP), rewritten to a problem details 429Floods from one IP address
Separate rate limit on the 429 functionCalling /_rate-limited directly to run a function without limit
CSP default-src 'none'; frame-ancestors 'none', nosniff, Referrer-Policy, CORP, HSTS on every response and the specResponses used as a page, framed, or sniffed as script
Strict validation, clipped echoes, generic 500sMalformed input, oversized responses, leaked internals
Logs with only documented parameters, no IP addressesPersonal data or tokens stored from URLs
Lockfile installs with --ignore-scripts, exact build tools, SHA-pinned Actions, a deploy gateTampered dependencies or CI steps reaching production

The smoke test checks the headers, the https links (by sending X-Forwarded-Proto: http to a unique URL), and both rate limits against production after every push.

The reference page​

src/pages/api-reference.js loads Scalar's script (its URL and integrity hash are in src/data/scalar.mjs) and points it at the spec. Its security settings:

src/pages/api-reference.js
Scalar.createApiReference(container.current, {
url: SPEC_URL,
hideDarkModeToggle: true,
proxyUrl: '',
telemetry: false,
agent: { disabled: true },
withDefaultFonts: false,
});
SettingWhy
Pinned version and integrity hashThe browser refuses the script if the CDN's copy changes. The hash matches the file in Scalar's npm package.
Loaded only on this pageThe old plugin loaded the 4.5 MB script on every page of the site.
Allowed as one exact file in the CSPThe Content Security Policy allows Scalar's exact URL, not all of cdn.jsdelivr.net, which serves every npm package.
proxyUrl: ''By default, every Test Request goes through proxy.scalar.com, so a third party sees visitors' requests and IP addresses. The API allows any origin, so the proxy isn't needed. Scalar skips the proxy when this is empty.
telemetry: falseScalar's usage telemetry is on by default.
agent: { disabled: true }Turns off Scalar's "Ask AI" agent, which sends the spec to Scalar's AI service.
withDefaultFonts: falseStops loading fonts from fonts.scalar.com. The site's own fonts are used.

These options were found by reading Scalar's bundled code, since its defaults aren't all obvious from its docs. When upgrading Scalar, check that the options still exist (see Upgrade Scalar).

Accepted risks​

  • npm audit alerts in the API repo (4 high, all the same braces issue in Netlify's bundler). The bundler only processes the repo's own fixed patterns at build time, and npm's only fix is a seven-major-version downgrade.
  • No global rate limit. A global cap would let one attacker block everyone. Usage alerts and caching cover cost instead.
  • 'unsafe-inline' styles in this site's CSP. React and Scalar set inline styles, which can't be hashed. Scripts, the bigger risk, are limited to hashes and named domains.
  • Google Analytics runs on every page, including /api-reference.
  • No DNSSEC. Netlify DNS doesn't offer it for this domain. Without it, an attacker who can forge DNS answers could send some visitors to another server, but the site and API are HTTPS-only with HSTS, so browsers would reject that server's certificate, and the CAA record makes it harder to get a valid one. Getting DNSSEC would mean moving the domain's DNS away from Netlify, which isn't worth it for this site.

Settings to make by hand​

These can't be set from the repos. Check them off once, and again after any change to the accounts or domain.

Netlify

  • Two-factor authentication on every account with access to the Netlify team, preferably with a security key or passkey. On as of 11 October 2026.
  • Usage notifications for function invocations and bandwidth, in the team's billing and usage settings, so a flood on the norway-data-api site shows up before the bill.
  • Deploy previews: on both sites, in the deploy preview settings under continuous deployment, require approval before building pull requests from outside contributors, so strangers' code doesn't run in the build or get published.

DNS (Netlify DNS, marlasowards.com)

  • A CAA record: name marlasowards.com, flag 0, tag issue, value letsencrypt.org. It lets only Let's Encrypt, which Netlify uses, issue certificates for the domain. Added 11 October 2026; it covers www and api too.
  • DNSSEC: Netlify DNS doesn't offer it for this domain (checked 11 October 2026), so it's an accepted risk. See Accepted risks.
  • If a Netlify site that uses a subdomain is ever deleted, delete its DNS record too, so nobody else can claim the subdomain.

GitHub

  • Two-factor authentication on the cedar-ave account, preferably with a security key or passkey. On as of 11 October 2026.

This site's CSP

  • Check the site for report-only CSP violations, then enforce the full policy. Enforced as of 11 October 2026. See How it was turned on.

Reporting a problem​

Both repos have private vulnerability reporting turned on: use Security > Report a vulnerability in the API repo or this one.