From grimoire
Verifies CORS policy enforcement by testing origin reflection, null origin, subdomain bypass, wildcard-with-credentials, and preflight correctness using targeted curl and browser tests.
How this skill is triggered — by the user, by Claude, or both
Slash command
/grimoire:audit-cors-configurationThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Probe your CORS policy with targeted `curl` and browser tests to confirm it blocks what it should — misconfigured CORS is common because developers add the policy but never verify it.
Probe your CORS policy with targeted curl and browser tests to confirm it blocks what it should — misconfigured CORS is common because developers add the policy but never verify it.
Adopted by: OWASP Testing Guide includes CORS as a required audit item (OTG-CLIENT-007). Portswigger Web Security Academy documents CORS misconfiguration as a systematic exploitable class. Bug bounty programmes at Netflix, Coinbase, and dozens of fintechs have paid critical/high bounties for CORS issues — almost all were configurations that looked correct but were not verified.
Impact: James Kettle's 2017 research found CORS misconfigurations on hundreds of the Alexa top 1000 sites, including one that would have allowed theft of Bitcoin from an exchange's authenticated API. The pattern was always the same: CORS was configured but never tested. curl does not enforce the same-origin policy — tests using curl or Postman will show the header is present but will not reveal whether the policy actually restricts cross-origin reads.
Why best: Configuring CORS and auditing CORS are different activities. A middleware can be correctly installed but misconfigured. A regex can match too broadly. An environment variable can be set to the wrong value in production. Auditing closes the gap between intent and reality.
Sources: Portswigger CORS research; OWASP Testing Guide OTG-CLIENT-007; CWE-942; Kettle, AppSec EU 2017
Send a clearly untrusted origin. A correct implementation returns no Access-Control-Allow-Origin header. A misconfigured one reflects it back.
curl -s -I \
-H "Origin: https://evil.com" \
https://api.example.com/v1/user
# FAIL — server reflects arbitrary origin:
# Access-Control-Allow-Origin: https://evil.com
# PASS — no ACAO header returned, or returns only for trusted origins
If the endpoint requires authentication, include a valid session cookie or token — the dangerous case is reflection on authenticated endpoints:
curl -s -I \
-H "Origin: https://evil.com" \
-H "Authorization: Bearer <valid_token>" \
https://api.example.com/v1/user
Sandboxed iframes, data: URIs, and redirected cross-origin requests send Origin: null. Allowing null permits any page to embed your API in a sandboxed iframe and read responses.
curl -s -I \
-H "Origin: null" \
https://api.example.com/v1/user
# FAIL:
# Access-Control-Allow-Origin: null
# PASS: no ACAO header, or no match
If your allowlist uses in / contains / a substring regex, test variants that embed your domain inside an attacker-controlled domain:
# Tests for: if 'example.com' in origin — matches both
curl -s -I -H "Origin: https://evil-example.com" https://api.example.com/v1/user
curl -s -I -H "Origin: https://notexample.com" https://api.example.com/v1/user
curl -s -I -H "Origin: https://example.com.evil.io" https://api.example.com/v1/user
# All three should return no ACAO header
Access-Control-Allow-Origin: * with Access-Control-Allow-Credentials: true is blocked by browsers per spec — but some servers produce this combination, which means they will work with non-browser clients and also signals a confused implementation.
curl -s -I https://api.example.com/v1/public
# FAIL — dangerous combination:
# Access-Control-Allow-Origin: *
# Access-Control-Allow-Credentials: true
# PASS for public endpoint:
# Access-Control-Allow-Origin: *
# (no credentials header, or credentials: false)
OPTIONS preflights must restrict methods and headers to only what your API uses. An overly permissive preflight defeats the purpose of restricting actual requests.
curl -s -I \
-X OPTIONS \
-H "Origin: https://app.example.com" \
-H "Access-Control-Request-Method: DELETE" \
-H "Access-Control-Request-Headers: X-Custom-Dangerous-Header" \
https://api.example.com/v1/user
# FAIL:
# Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS
# Access-Control-Allow-Headers: *
# PASS:
# Access-Control-Allow-Methods: GET, POST
# Access-Control-Allow-Headers: Content-Type, Authorization
# (method/header not in list = browser will block the actual request)
Vary: Origin is set when reflecting dynamicallyWithout Vary: Origin, a CDN or proxy caches one origin's CORS response and serves it to all others — including attacker.com.
curl -s -I \
-H "Origin: https://app.example.com" \
https://api.example.com/v1/resource
# Required when ACAO reflects the request origin:
# Vary: Origin
curl and Postman bypass the same-origin policy — they cannot test whether the browser actually allows or blocks the request. Use a browser fetch from a different origin.
Option A — test from browser console on a trusted page:
// Paste into DevTools console on https://app.example.com
fetch('https://api.example.com/v1/user', { credentials: 'include' })
.then(r => r.json())
.then(console.log)
.catch(console.error);
// Expected: data returned (allowed origin)
Option B — test from a controlled test page on a different origin:
<!-- host this on https://test.yourlab.com, not on example.com -->
<script>
fetch('https://api.example.com/v1/user', { credentials: 'include' })
.then(r => console.log('FAIL — cross-origin read succeeded:', r.status))
.catch(e => console.log('PASS — blocked:', e.message));
</script>
CORS misconfigurations commonly exist on endpoints that developers considered internal or low-risk. Enumerate all routes and test each:
# Collect all routes (example for Express/Node)
grep -r "app\.\(get\|post\|put\|delete\|patch\)" src/ | grep -oP "['\"](/[^'\"]+)" | sort -u
# Or check your OpenAPI/Swagger spec
cat openapi.yaml | grep -oP "(?<=^ /)[\w/{}]+"
Run Steps 1–3 against each endpoint. Pay particular attention to any endpoint that returns user data, session tokens, or account information.
curl output alone — curl has no same-origin policy and will always receive the response regardless of CORS headers.* are often intentional and safe; the dangerous case is always authenticated endpoints.Trusting that the CORS library handles it correctly without verifying the configuration passed to it: CORS(app, origins='*') is one argument away from CORS(app, origins=['https://app.example.com']). Library is correct; configuration is wrong.
Only testing from the same origin as the server during development: localhost:3000 calling localhost:8080 does not trigger same-origin policy in all environments. Test from a genuinely different origin.
Concluding CORS is blocking requests because the browser shows a CORS error: A CORS error means the browser blocked the read — the request was still sent and processed by the server. CORS is not a firewall; it controls what the browser exposes to JavaScript.
Not testing with credentials: fetch(url) (no credentials) has different CORS rules than fetch(url, { credentials: 'include' }). Test both, since the dangerous case always involves credentials.
Quick audit of a production API before launch:
BASE=https://api.example.com
for path in /v1/user /v1/account /v1/payment/methods; do
echo "=== $path ==="
curl -s -I -H "Origin: https://evil.com" -H "Authorization: Bearer $TOKEN" "$BASE$path" \
| grep -i "access-control\|vary"
echo ""
done
Automated CI check (fail if arbitrary origin reflected):
#!/usr/bin/env bash
RESPONSE=$(curl -s -I -H "Origin: https://evil-corp.com" https://api.example.com/v1/user)
if echo "$RESPONSE" | grep -qi "access-control-allow-origin: https://evil-corp.com"; then
echo "FAIL: CORS reflects arbitrary origin"
exit 1
fi
echo "PASS"
npx claudepluginhub jeffreytse/grimoire --plugin grimoireValidates CORS configurations in web apps/APIs for security misconfigurations like wildcard origins, origin reflection, permissive methods/headers.
Tests CORS misconfigurations including origin reflection, null-origin trust, subdomain regex bypasses, pre-flight gating bypass, and postMessage origin checks. Use when auditing API endpoints or SPAs with Access-Control-* headers.
Identifies and exploits CORS misconfigurations in web APIs using curl, Burp Suite, and browser tests to detect unauthorized cross-origin access and credential theft during pentests.