Command-line tools for finding Cross-Origin Resource Sharing (CORS) misconfigurations in web applications — and, importantly, for judging whether a weak policy is actually exploitable rather than just flagging headers.
The tools probe an endpoint's CORS policy with a battery of crafted Origin
headers, assess whether a weakness can really be abused (credential
rideability, SameSite, data sensitivity, CSRF-token exposure,
attacker-controllability), and produce a findings report with description,
impact, evidence, and remediation for each issue. They work against a single
endpoint or can spider an entire site.
There are three tools: cors_tester.py (test one endpoint, or drive the others),
cors_spider.py (crawl a site and test everything it finds), and cors_report.py
(the reporting engine). A small deliberately-vulnerable lab is bundled as well —
not the point of the project, just a safe local target so you can watch the tools
run and see every check fire before pointing them at a real system.
⚠️ Authorized testing only. These tools send crafted, attack-style requests to a target. Only run them against systems you own or have explicit written permission to test. The bundled lab is deliberately vulnerable — keep it local.
These tools actively probe a target: they send requests carrying forged Origin
headers, crawl linked pages and forms, and inspect authenticated responses. That
is attack-style traffic. Running it against a system generates logs, may trip
security monitoring, and — without authorization — may be unlawful.
By using this project you agree that:
- You will only test systems you own or for which you hold explicit, written permission. Unauthorized testing of third-party systems may violate the U.S. Computer Fraud and Abuse Act, the UK Computer Misuse Act, and equivalent laws elsewhere. Bug-bounty participation does not imply permission beyond the program's stated scope.
- You are solely responsible for how you use these tools and for staying within the law and any applicable rules of engagement.
- The deliberately vulnerable lab (
cors-lab/) is for local use only. It exposes fake sensitive data to any origin by design; never deploy it anywhere reachable from an untrusted network. - The software is provided "as is", without warranty of any kind, and the authors accept no liability for any damage or loss arising from its use, as set out in the LICENSE.
If you are not certain you are authorized to test a given target, you are not authorized. Stop and get written permission first.
cors-project/
├── cors_tester.py # single-endpoint console tester; also --report and --spider
├── cors_report.py # multi-endpoint report engine + finding knowledge base
├── cors_spider.py # site crawler; also a library (run_spider) used by the tester
└── cors-lab/ # deliberately vulnerable Flask app in Docker (see its README)
├── app.py
├── Dockerfile, docker-compose.yml, requirements.txt
├── run_tests.sh, generate_report.sh
├── README.md # lab-specific: endpoints, how to exercise each check
└── CORS_Report.md # a sample report from a full spider run
The two Python tools that scripts reference (cors_tester.py, cors_report.py)
and the crawler (cors_spider.py) live at the top level; the lab's helper
scripts resolve them at ../. Keep this layout.
- Python 3.8+ with the
requestslibrary:pip install requests - Docker + Docker Compose (only for running the lab)
No other dependencies — parsing and crawling use the standard library.
The tools run against any target you're authorized to test. For a real target it is just the tool plus your URL (and, for authenticated endpoints, your session):
# Test one endpoint
python3 cors_tester.py https://app.example.com/api/account -o https://app.example.com
# Crawl the whole site and write a consolidated report
python3 cors_tester.py https://app.example.com/ --spider \
-o https://app.example.com -H "Cookie: session=..." --report report.mdThe fastest way to try them out — and to see every check fire safely — is against
the bundled lab. From cors-lab/:
docker compose up --build # serves http://localhost:8080# single endpoint
python3 ../cors_tester.py http://localhost:8080/vuln/reflect -o https://trusted.example.com
# spider the lab site into one report
python3 ../cors_tester.py http://localhost:8080/site --spider \
-o https://trusted.example.com --report site_report.mdThe lab's own README.md documents every endpoint and how to exercise each
specific check, including the authenticated runs that demonstrate the
mitigating-factor analysis.
Most of the crafted bypass payloads are built from the origin the application
legitimately trusts — which, in a real assessment, is usually not the host
you connect to (you might hit an IP or a staging URL while the app trusts
https://app.example.com). Pass that trusted origin with -o so the
prefix/suffix/subdomain/regex/normalization payloads are meaningful. Without it,
the tools fall back to the target's own host, which still catches blanket
reflection and null-origin trust but won't exercise host-pattern bypasses.
Probes one URL and prints a graded result per test, a preflight breakdown, and a
mitigating-factors summary. It's also the front door for the other two tools via
--report and --spider.
# Basic console test
python3 cors_tester.py https://target/api/account -o https://app.example.com
# With an authenticated session, and inspect the cookie for SameSite
python3 cors_tester.py https://target/api/account -o https://app.example.com \
-H "Cookie: session=..." --session-url https://target/login -v
# Also write a Markdown findings report for this endpoint
python3 cors_tester.py https://target/api/account -o https://app.example.com \
--report account_report.md
# Spider mode: crawl from a seed and test everything discovered
python3 cors_tester.py https://target/ --spider -o https://app.example.com \
--report site_report.mdKey options: -o/--trusted-origin, -H/--header (repeatable; for cookies or
tokens), --session-url (for SameSite/cookie inspection), -m/--method,
-t/--timeout, -k/--insecure, -v/--verbose, --report [FILE], and the
spider options below.
Crawls from a seed URL, discovers endpoints from anchor links, form actions,
and URLs referenced in inline <script> blocks, then runs the full scan +
mitigating-factor analysis on each and consolidates into one report.
python3 cors_spider.py https://target/ -o https://app.example.com --out report.mdIt is also importable as a library — cors_tester.py --spider uses it this way:
from cors_spider import run_spider
result = run_spider("https://target/", trusted_origin="https://app.example.com",
base_headers={"Cookie": "session=..."}, max_pages=80)
# result -> {"targets", "findings", "raw_by_url", "contexts", "pages", ...}Spider options (available on both cors_spider.py and cors_tester.py --spider):
--max-pages (50), --max-depth (3), --max-endpoints (60), a politeness delay
(--delay on the spider, --spider-delay on the tester; 0.2s),
--include-subdomains, --scope-host, --no-forms, --no-js, and
--allow-dangerous.
Safety rails. A crawler pointed at a live app can do damage, so by default the
spider: limits scope to the seed host; caps pages/depth/endpoints and delays
between requests; never submits forms (it only probes the action URL's CORS
headers); and skips state-changing paths (logout, delete, reset, revoke,
deactivate, …) so it never GETs something destructive. --allow-dangerous
overrides the last — don't use it on a live authenticated session, or the crawler
will log you out (or worse).
Runs the scan against one or more explicit URLs and writes the report. This is the engine the other two tools call for rendering; use it directly when you have a known list of endpoints rather than a site to crawl.
python3 cors_report.py https://target/api/a https://target/api/b \
-o https://app.example.com -H "Cookie: session=..." \
--session-url https://target/login --out report.mdDetecting a permissive header is only half the job; the tools also estimate whether it's exploitable, and every finding carries both a base severity (the header issue in isolation) and an effective severity adjusted for the mitigating factors observed:
- Credential rideability — ambient cookies ride cross-origin requests; bearer
tokens in
Authorizationdo not, which largely neutralises reflection. SameSite— aLax/Strictsession cookie won't ride a cross-sitefetch, mitigating credentialed reflection;Noneleaves it exploitable.- Data sensitivity — an authenticated-vs-unauthenticated differential: if the response is identical with and without a session, there's nothing to steal.
- Attacker-controllability — whether an attacker can actually obtain a qualifying origin. Reflection of an arbitrary origin is real; a suffix under the target's own parent domain is latent; protocol-downgrade needs a network position. These are rated conditional/latent accordingly.
The same reflection bug can therefore land as Critical, High, or Low depending on context. These effective severities are heuristics to guide manual confirmation, not a verdict — the base severity is always shown alongside so nothing is silently downgraded out of view.
The tools test each endpoint for the following 17 weaknesses. The base severity shown is the header issue in isolation; the effective severity in a report is adjusted per target by the mitigating factors (see How findings are rated).
| Check | Base | What it detects |
|---|---|---|
| Arbitrary origin reflected with credentials | High | Any Origin reflected in ACAO with ACAC: true — no allow-list |
null origin trusted with credentials |
High | ACAO: null returned (reachable from sandboxed iframes / data: URLs) |
| Prefix-match bypass | High | An origin beginning with the trusted origin passes (startsWith) |
| Suffix-match bypass | High | An attacker host ending in the trusted host passes (endsWith) |
| Unescaped-regex bypass | High | Origin regex with unescaped dots (trustedxexample.com matches) |
| Overly broad subdomain trust | Medium | Any *.trusted.com trusted, including attacker-controllable subdomains |
| Protocol downgrade | Medium | The trusted host over plaintext http:// is accepted |
| Sensitive response headers exposed | Medium | Access-Control-Expose-Headers makes headers readable cross-origin |
| Permissive cross-origin methods | Medium | Wildcard Allow-Methods, or PUT/DELETE/PATCH allowed cross-origin |
| Private Network Access to public origins | Medium | Access-Control-Allow-Private-Network: true granted to a public origin |
localhost / loopback trust |
Medium | http://localhost or http://127.0.0.1 reflected (DNS-rebinding reachable) |
Wildcard ACAO |
Low | Access-Control-Allow-Origin: * (only a leak for sensitive data) |
| Arbitrary origin reflected without credentials | Low | Any origin reflected, but credentials not allowed |
Missing Vary: Origin |
Low | A reflected origin without Vary: Origin (cache-poisoning risk) |
| Loose origin normalization | Low | Case, trailing-dot, or non-standard-port variants accepted |
Excessive preflight Max-Age |
Low | Preflight decision cached for an excessive period |
| Loose origin matching (parser confusion) | Low | Userinfo @, backslash, or substring payloads accepted |
The preflight (OPTIONS) request is parsed in full
(Allow-Methods/Allow-Headers/Expose-Headers/Max-Age/Allow-Private-Network).
Reflecting the trusted origin is treated as correct and never reported.
Each finding states its detection logic in plain language and shows the contrast that justifies it — the rejected arbitrary-origin baseline next to the accepted crafted probe — so a "suffix bypass" is visibly a specific bypass rather than blanket reflection.
The tools also detect CSRF tokens (in response bodies, headers, and cookies).
Where a token sits somewhere cross-origin script could read it (a body field or
header) and a permissive, credentialed CORS policy is present, this is flagged as
an amplifier: the CORS flaw would let an attacker read the token and defeat the
app's CSRF protection. Cookie-based tokens (not readable via fetch) are noted as
a mitigating factor instead.
Alongside the weaknesses above, the tools actively check for the protective controls and context that decide whether a weakness is exploitable, and report each in the finding's exploitability notes and the report's "Testing Context" section:
- Session cookie attributes —
SameSite(None/Lax/Strict), plusSecureandHttpOnly, read from aSet-Cookieon the target or a--session-urlyou supply.Lax/Strictis treated as a mitigation because the cookie won't ride a cross-sitefetch. - Authentication mechanism — whether the endpoint uses an ambient cookie
(rides cross-origin requests) or a bearer token in
Authorization(does not), which decides whether reflection can reach authenticated data at all. - Data sensitivity — an authenticated-vs-unauthenticated differential: if the response is identical with and without a session, there is nothing session- scoped to steal and the severity drops.
- CSRF tokens — presence and location (body / header / cookie) and whether each is readable cross-origin, treated as an amplifier or a mitigation as described above.
Vary: Origin— whether a reflecting response is safe from cache poisoning.- Attacker-controllability — whether an attacker can actually obtain an origin that satisfies the bypass, so latent and network-position-dependent cases are rated conditional rather than as live findings.
These feed the base-vs-effective severity model described under How findings are rated; a mitigation being present lowers the effective severity (and is stated as the reason), while its absence is noted too, so a clean run doesn't quietly assume a protection that isn't there.
The effective severities and the differential's "sensitive data" flag are
heuristics; confirm them manually. The spider's JS-endpoint extraction is
deliberately conservative (quoted URLs in inline scripts), so endpoints built
dynamically or loaded from external .js files won't all be found — it's a strong
first pass, not guaranteed full coverage. And a "weak regex" or suffix finding may
require a domain an attacker can't actually register; treat those as "the check is
loose, confirm exploitability" rather than proven bugs.
- OWASP — Testing Cross Origin Resource Sharing (WSTG-CLNT-07)
- PortSwigger Web Security Academy — CORS vulnerabilities
- MDN — Cross-Origin Resource Sharing (CORS)