Security

How Novus Learn is built to be safe to use.

The security baseline of the shipped build: browser policy, untrusted-input handling, SSRF hardening, on-device execution, and how to report a vulnerability.

Applies to Novus Learn 0.1.0

Report a vulnerability

Copy link

Email learn@novusstreamsolutions.com with “Novus Learn security” in the subject line. Include the URL, what you did, and what you observed. This is the monitored route until a dedicated disclosure channel is formally published; there is no bug bounty programme at this time.

Please do not include credentials or private files in a report. Novus Learn has no accounts, so there is no password we would ever need.

Browser policy

Copy link
  • The Content Security Policy defaults to same-origin scripts, connections, forms, and base URLs.
  • Images and media allow only same-origin data/blob content and upload.wikimedia.org.
  • Object embedding is denied and frame-ancestors is 'none', so the app cannot be framed.
  • Camera, microphone, geolocation, browsing topics, payment, and USB are disabled by permissions policy.
  • script-src allows 'wasm-unsafe-eval' and never general 'unsafe-eval' in production; the WebAssembly allowance exists only for the on-device transcription runtime.

Nothing third-party loads before you allow it

Copy link

Google Analytics loads only after the Analytics category is accepted. Advertising sits behind the Marketing category, and Adsterra is the active provider for the current month. Analytics and advertising code run in sandboxed frames with opaque origins, so their scripts cannot read Learn's IndexedDB, localStorage, or page DOM. Their rotating hosts and compatibility allowances are absent from the Learn document's CSP. Withdrawing consent unmounts those frames and prevents them from loading again.

Untrusted input: share tokens

Copy link
  • A share token is decoded and validated with a schema before anything renders; a tampered, truncated, or wrongly-prefixed token is rejected and shown as a clear “unavailable” state.
  • Every string and array in a share payload is length-bounded. Links must be plain http(s) URLs without embedded credentials — a crafted token cannot inject a javascript: or data: href.
  • A codec size guard bounds token length on both encode and decode, limiting URL-based denial of service.
  • A token whose encoded artifact kind does not match the route kind is rejected, so it cannot be replayed under a different kind.
  • Share pages are noindex and /share/ is disallowed in robots.txt.

Untrusted input: files and URLs

Copy link
  • Uploads are validated before parsing: an extension allow-list; 10 MB text, 40 MB binary/codebase, and 2 MB individual source-code bounds; and a NUL/control-byte signature check that rejects binaries mislabelled as text.
  • Parsers cap total blocks and per-block length, and regex quantifiers are bounded, so a pathological file cannot exhaust memory.
  • Uploaded text is rendered as escaped React text, never as HTML, and the generated project lives only in IndexedDB behind noindex routes.
  • The URL reader, sitemap indexer, and journal fetcher accept only public http(s) URLs; javascript:, data:, and userinfo URLs are rejected before any request is made.
  • DNS is resolved and validated at connect time and re-checked on every redirect hop, so a name resolving to a private, loopback, or link-local address — including ::ffff:-mapped, ::1, and NAT64 bypasses — is blocked. Requests are size-, time-, and redirect-bounded.
  • CSV exports neutralise spreadsheet formula injection, and ZIP intake caps per-entry decompressed size to stop decompression bombs.

Execution stays on your device

Copy link

Speech-to-text runs a self-hosted Whisper model entirely in the browser through a same-origin worker; audio and video are never uploaded and the CSP stays connect-src 'self'. The runtime is vendored, so no CDN is contacted at runtime.

The code sandbox runs snippets in a QuickJS WebAssembly interpreter isolated from the DOM and the network. It is a computation sandbox, not a bridge to page or host APIs.

Source access and storage

Copy link
  • IndexedDB and localStorage are scoped to the entire Learn origin, not an individual route. Third-party JavaScript therefore stays in opaque-origin sandboxes, and persisted project, backup, link, and navigation values are validated again when read.
  • Language codes and page IDs are validated before a Wikimedia host is constructed; upstream origins are fixed in code and user input cannot supply an arbitrary URL.
  • Server requests identify Novus Learn, use timeouts and bounded retries, respect Retry-After, and avoid high concurrency.
  • Search snippets and Commons metadata are reduced to plain text; the production UI does not inject upstream HTML.
  • The optional database is a capability: with no credentials configured the client is never loaded and every store call is a safe no-op. All queries are parameterised, only a hash of a share token is stored, and no snapshot bytes or per-user rows are persisted.

Known and tracked

Copy link

Production dependencies are checked with npm audit. The 27 August 2026 review records owner upgrade targets for Next, PDF.js, Undici, PostCSS, and Sharp in docs/SECURITY.md. Embedded PDF scripting and eval are disabled immediately; the remaining updates must be evaluated deliberately against the customized Next build rather than force-applied, and are recorded openly instead of being marked resolved.

Consent version 2026-08-21.1

Cookie preferences