Get unstuck

Hint 1: Reflecting an origin is not a check

Copying the Origin into Allow-Origin with credentials trusts every site. Compare the origin against the configured list with includes, and grant nothing otherwise.

Hint 2: The answer depends on the Origin header

Set Vary: Origin on every response, allowed or not. The Fetch standard asks for it whenever Access-Control-Allow-Origin depends on the Origin: without it a shared cache can replay a stored refusal to an allowed origin, or one site’s grant to another.

Reveal the reference patch and explanation
Fixedcors-policy.mjsJavaScript
export function corsHeaders(method, origin, allowedOrigins) {
  const headers = {Vary: 'Origin'};
  if (!origin || !allowedOrigins.includes(origin)) return headers;
  headers['Access-Control-Allow-Origin'] = origin;
  headers['Access-Control-Allow-Credentials'] = 'true';
  if (method === 'OPTIONS') {
    headers['Access-Control-Allow-Methods'] = 'GET, POST';
    headers['Access-Control-Allow-Headers'] = 'Content-Type, X-CSRF-Token';
    headers['Access-Control-Max-Age'] = '600';
  }
  return headers;
}

For https://portal.example.test a GET returns Vary, Allow-Origin set to that origin and Allow-Credentials true; an OPTIONS adds the methods, headers and 600-second max-age. https://reports.example.test, the second configured origin, is granted too. https://evil.test, http://portal.example.test (wrong scheme), https://portal.example.test.evil.test (suffix), https://evil-portal.example.test (substring), the literal "null" and a missing origin all receive only Vary, with no Allow-Origin. The wildcard "*" is never produced, so it can never combine with credentials.

Compare it with your own fix, then rerun the tests. The editor is not changed when you open this explanation.

Inspect the actual test harness

The browser and the lab download run the same cases, including cases that catch common wrong fixes, such as comparing with one fixture value instead of the real rule. The download includes the complete test adapter and cryptographic fixtures.

Download cases.mjs
How the browser sandbox runs your code

Your code runs in QuickJS 2025-09-13 (quickjs-emscripten 0.32.0) inside a Web Worker, isolated from the page, with a 32 MiB memory limit and a 2.5-second time limit. The test cases and the pass or fail verdict run outside that sandbox, so changing built-ins such as Array.prototype.push cannot change the result. The lab download runs the same cases under Node.js, where your code runs directly.

The same as in current Node.js:

  • ES2025 syntax and built-ins, including Set methods such as intersection, iterator helpers, Promise.try, RegExp.escape and the RegExp v flag

Added to the sandbox so that it behaves like Node.js:

  • structuredClone, Array.fromAsync, queueMicrotask, atob and btoa
  • URL and URLSearchParams, parsed by your browser’s own URL implementation
  • TextEncoder and TextDecoder for UTF-8
  • Intl.Collator and a locale-aware String.prototype.localeCompare
  • console.log and related methods, whose output appears with your results

Not available in the browser sandbox:

  • Timers (setTimeout, setInterval), fetch and other network or storage APIs; the exercises are deterministic and offline
  • Node.js modules and globals: import, require, process and Buffer
  • Web Crypto (crypto.getRandomValues, crypto.subtle); each workshop supplies the cryptographic operations it needs
  • Intl other than Collator: toLocaleString and toLocaleDateString ignore the locale, so numbers have no digit grouping and dates use one fixed format in the device’s time zone
  • using declarations, Symbol.dispose and DisposableStack (explicit resource management)
  • Uint8Array.fromBase64 and toBase64, JSON.rawJSON, Atomics and Error.captureStackTrace

Use the lesson in your own system

This is the CORS header policy, not the whole request path. CORS is a browser read-permission decision: it does not authenticate the user, authorize the object or stop CSRF, which still need cookies with SameSite, a CSRF token and server-side checks on the actual request. The lab uses exact string origins and a fixed method and header list; a real API also validates the requested method and headers on the preflight and sets an appropriate cache lifetime.

Your next task: Which of your API responses set Access-Control-Allow-Origin by reflecting the request, and do any set it together with Allow-Credentials?

MDN: Cross-Origin Resource Sharing (CORS)

Related guide: Configure CORS and CSP around the browser capabilities you need