Showing preview only (224K chars total). Download the full file or copy to clipboard to get everything.
Repository: sandrogauci/wafw00f
Branch: master
Commit: 69fbe3956bba
Files: 202
Total size: 186.3 KB
Directory structure:
gitextract_69mvkwm9/
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ └── bug_report.md
│ └── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── CLAUDE.md
├── CODE_OF_CONDUCT.md
├── CREDITS.txt
├── Dockerfile
├── LICENSE
├── MANIFEST.in
├── Makefile
├── README.md
├── docs/
│ ├── Makefile
│ ├── conf.py
│ ├── index.rst
│ └── wafw00f.8
├── pyproject.toml
├── tests/
│ ├── __init__.py
│ ├── conftest.py
│ ├── test_detection.py
│ ├── test_evillib.py
│ ├── test_manager.py
│ └── test_matching.py
└── wafw00f/
├── __init__.py
├── lib/
│ ├── __init__.py
│ ├── asciiarts.py
│ └── evillib.py
├── main.py
├── manager.py
├── plugins/
│ ├── __init__.py
│ ├── aesecure.py
│ ├── airee.py
│ ├── airlock.py
│ ├── alertlogic.py
│ ├── aliyundun.py
│ ├── anquanbao.py
│ ├── anubis.py
│ ├── anyu.py
│ ├── applicationgateway.py
│ ├── approach.py
│ ├── armor.py
│ ├── arvancloud.py
│ ├── aspa.py
│ ├── aspnetgen.py
│ ├── astra.py
│ ├── awswaf.py
│ ├── azion.py
│ ├── baffinbay.py
│ ├── baidu.py
│ ├── barikode.py
│ ├── barracuda.py
│ ├── bekchy.py
│ ├── beluga.py
│ ├── binarysec.py
│ ├── bitninja.py
│ ├── blockdos.py
│ ├── bluedon.py
│ ├── bulletproof.py
│ ├── cachefly.py
│ ├── cachewall.py
│ ├── cdnns.py
│ ├── cerber.py
│ ├── chinacache.py
│ ├── chuangyu.py
│ ├── ciscoacexml.py
│ ├── cloudbric.py
│ ├── cloudflare.py
│ ├── cloudfloordns.py
│ ├── cloudfront.py
│ ├── cloudprotector.py
│ ├── comodo.py
│ ├── crawlprotect.py
│ ├── ddosguard.py
│ ├── denyall.py
│ ├── distil.py
│ ├── dosarrest.py
│ ├── dotdefender.py
│ ├── dynamicweb.py
│ ├── edgecast.py
│ ├── eisoo.py
│ ├── envoy.py
│ ├── expressionengine.py
│ ├── f5bigipapm.py
│ ├── f5bigipasm.py
│ ├── f5bigipltm.py
│ ├── f5firepass.py
│ ├── f5trafficshield.py
│ ├── fastly.py
│ ├── fortigate.py
│ ├── fortiguard.py
│ ├── fortiweb.py
│ ├── frontdoor.py
│ ├── gcparmor.py
│ ├── godaddy.py
│ ├── greywizard.py
│ ├── huaweicloud.py
│ ├── hyperguard.py
│ ├── ibmdatapower.py
│ ├── imunify360.py
│ ├── incapsula.py
│ ├── indusguard.py
│ ├── instartdx.py
│ ├── isaserver.py
│ ├── janusec.py
│ ├── jiasule.py
│ ├── kemp.py
│ ├── keycdn.py
│ ├── knownsec.py
│ ├── kona.py
│ ├── limelight.py
│ ├── link11.py
│ ├── litespeed.py
│ ├── malcare.py
│ ├── maxcdn.py
│ ├── missioncontrol.py
│ ├── modsecurity.py
│ ├── naxsi.py
│ ├── nemesida.py
│ ├── netcontinuum.py
│ ├── netscaler.py
│ ├── nevisproxy.py
│ ├── newdefend.py
│ ├── nexusguard.py
│ ├── ninja.py
│ ├── nsfocus.py
│ ├── nullddos.py
│ ├── onmessage.py
│ ├── openresty.py
│ ├── oraclecloud.py
│ ├── paloalto.py
│ ├── panyun360.py
│ ├── pentawaf.py
│ ├── perimeterx.py
│ ├── pksec.py
│ ├── powercdn.py
│ ├── profense.py
│ ├── ptaf.py
│ ├── puhui.py
│ ├── qcloud.py
│ ├── qiniu.py
│ ├── qrator.py
│ ├── radware.py
│ ├── reblaze.py
│ ├── reflected.py
│ ├── rsfirewall.py
│ ├── rvmode.py
│ ├── sabre.py
│ ├── safe3.py
│ ├── safedog.py
│ ├── safeline.py
│ ├── scutum.py
│ ├── secking.py
│ ├── secupress.py
│ ├── secureentry.py
│ ├── secureiis.py
│ ├── securesphere.py
│ ├── senginx.py
│ ├── serverdefender.py
│ ├── shadowd.py
│ ├── shieldon.py
│ ├── shieldsecurity.py
│ ├── siteground.py
│ ├── siteguard.py
│ ├── sitelock.py
│ ├── sonicwall.py
│ ├── sophos.py
│ ├── squarespace.py
│ ├── squidproxy.py
│ ├── stackpath.py
│ ├── sucuri.py
│ ├── tencent.py
│ ├── teros.py
│ ├── threatx.py
│ ├── transip.py
│ ├── uewaf.py
│ ├── urlmaster.py
│ ├── urlscan.py
│ ├── variti.py
│ ├── varnish.py
│ ├── vercel.py
│ ├── viettel.py
│ ├── virusdie.py
│ ├── wallarm.py
│ ├── watchguard.py
│ ├── webarx.py
│ ├── webknight.py
│ ├── webland.py
│ ├── webray.py
│ ├── webseal.py
│ ├── webtotem.py
│ ├── west263cdn.py
│ ├── wordfence.py
│ ├── wpmudev.py
│ ├── wts.py
│ ├── wzb360.py
│ ├── xlabssecuritywaf.py
│ ├── xuanwudun.py
│ ├── yundun.py
│ ├── yunsuo.py
│ ├── yxlink.py
│ ├── zenedge.py
│ └── zscaler.py
└── wafprio.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: sandrogauci, 0xInfection
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Command that reproduces the issue. e.g. `wafw00f http://example.org -a -vv`
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. Windows, Linux]
- OS version, distribution:
- Python version: [e.g. python 3.2]
**Debug output**
Paste the output that you get when passing `-vv` to wafw00f. Example:
```
[*] Checking http://www.example.com
INFO:wafw00f:starting wafw00f on http://www.example.com
INFO:wafw00f:Request Succeeded
INFO:wafw00f:Request Succeeded
INFO:wafw00f:Checking for ACE XML Gateway (Cisco)
INFO:wafw00f:Checking for aeSecure (aeSecure)
INFO:wafw00f:Checking for AireeCDN (Airee)
INFO:wafw00f:Checking for Airlock (Phion/Ergon)
INFO:wafw00f:Checking for Alert Logic (Alert Logic)
INFO:wafw00f:Checking for AliYunDun (Alibaba Cloud Computing)
INFO:wafw00f:Checking for Anquanbao (Anquanbao)
INFO:wafw00f:Checking for AnYu (AnYu Technologies)
INFO:wafw00f:Checking for Approach (Approach)
INFO:wafw00f:Checking for AppWall (Radware)
INFO:wafw00f:Checking for Armor Defense (Armor)
INFO:wafw00f:Checking for ArvanCloud (ArvanCloud)
INFO:wafw00f:Checking for ASP.NET Generic (Microsoft)
INFO:wafw00f:Checking for ASPA Firewall (ASPA Engineering Co.)
INFO:wafw00f:Checking for Astra (Czar Securities)
INFO:wafw00f:Checking for AzionCDN (AzionCDN)
INFO:wafw00f:Checking for Barikode (Ethic Ninja)
INFO:wafw00f:Checking for Barracuda (Barracuda Networks)
INFO:wafw00f:Checking for Bekchy (Faydata Technologies Inc.)
INFO:wafw00f:Checking for Beluga CDN (Beluga)
INFO:wafw00f:Checking for BIG-IP Local Traffic Manager (F5 Networks)
INFO:wafw00f:Checking for BinarySec (BinarySec)
INFO:wafw00f:Checking for BitNinja (BitNinja)
INFO:wafw00f:Checking for BlockDoS (BlockDoS)
INFO:wafw00f:Checking for Bluedon (Bluedon IST)
INFO:wafw00f:Checking for BulletProof Security Pro (AITpro Security)
INFO:wafw00f:Checking for CacheWall (Varnish)
INFO:wafw00f:Checking for CacheFly CDN (CacheFly)
INFO:wafw00f:Checking for Comodo cWatch (Comodo CyberSecurity)
INFO:wafw00f:Checking for CdnNS Application Gateway (CdnNs/WdidcNet)
INFO:wafw00f:Checking for ChinaCache Load Balancer (ChinaCache)
INFO:wafw00f:Checking for Chuang Yu Shield (Yunaq)
INFO:wafw00f:Checking for Cloudbric (Penta Security)
INFO:wafw00f:Checking for Cloudflare (Cloudflare Inc.)
INFO:wafw00f:Checking for Cloudfloor (Cloudfloor DNS)
INFO:wafw00f:Checking for Cloudfront (Amazon)
INFO:wafw00f:Checking for CrawlProtect (Jean-Denis Brun)
INFO:wafw00f:Checking for DataPower (IBM)
INFO:wafw00f:Checking for DenyALL (Rohde & Schwarz CyberSecurity)
INFO:wafw00f:Checking for Distil (Distil Networks)
INFO:wafw00f:Checking for DOSarrest (DOSarrest Internet Security)
INFO:wafw00f:Checking for DotDefender (Applicure Technologies)
INFO:wafw00f:Checking for DynamicWeb Injection Check (DynamicWeb)
INFO:wafw00f:Checking for Edgecast (Verizon Digital Media)
INFO:wafw00f:Identified WAF: ['Edgecast (Verizon Digital Media)']
[+] The site http://www.example.com is behind Edgecast (Verizon Digital Media) WAF.
[~] Number of requests: 2
```
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
#### Which category is this pull request?
<!-- Check the boxes with 'x' like '[x]' -->
- [ ] A new feature/enhancement.
- [ ] Fix an issue/feature-request.
- [ ] An improvement to existing modules.
- [ ] Other (Please mention below).
#### Where has this been tested?
<!-- Check the boxes with 'x' like '[x]' -->
- Python Version
- [ ] v3.x
- [ ] v2.x
- Operating System:
- [ ] Linux (Please specify distro)
- [ ] Windows
- [ ] MacOS
#### Does this close any currently open issues?
[Mention any issue which this PR closes]
#### Does this add any new dependency?
[Mention if this PR includes any new library]
#### Does this add any new command line switch/argument?
[Mention if the changes add any new arguments like `--arg`]
#### Any other comments you would like to make?
[Anything more you'd want the reviewer to know]
================================================
FILE: .gitignore
================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*.swp
# C extensions
*.so
# Distribution / packaging
.Python
env/
bin/
build/
develop-eggs/
dist/
eggs/
include/
local/
lib64/
man/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.cache
nosetests.xml
coverage.xml
# Translations
*.mo
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
# Rope
.ropeproject
# Django stuff:
*.log
*.pot
# Sphinx documentation
docs/_build/
*.csv
*.json
.idea/*
.vscode/*
.pypirc
.venv/
================================================
FILE: CLAUDE.md
================================================
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
WAFW00F is a Web Application Firewall (WAF) fingerprinting and detection tool written in Python. It identifies WAF products protecting web applications through HTTP response analysis and behavioral testing.
## Development Commands
### Testing
```bash
# Run all tests
pytest
# Run tests with verbose output
pytest -v
# Run specific test file
pytest tests/test_evillib.py
# Run specific test
pytest tests/test_evillib.py::TestWafToolsEngine::test_default_timeout
# Run tests with coverage
pytest --cov=wafw00f --cov-report=term-missing
```
### Linting
```bash
# Run linter (prospector)
prospector wafw00f --strictness veryhigh
```
### Building and Installing
```bash
# Install in development mode with dev dependencies
pip install -e .[dev,docs]
# Build distribution packages
python setup.py sdist bdist_wheel
# Clean build artifacts
make clean
```
### Publishing New Release
```bash
# 1. Update version in wafw00f/__init__.py
# 2. Update version in README.md (badge and ASCII art examples - 3 places)
# 3. Run tests
pytest
# 4. Build package
python setup.py sdist bdist_wheel
# 5. Upload to PyPI
twine upload dist/*
# 6. Create GitHub release
gh release create vX.Y.Z --title "vX.Y.Z" --notes "Release notes here"
# 7. Commit version changes
git add wafw00f/__init__.py README.md
git commit -m "Bump version to X.Y.Z"
git push
```
## Architecture
### Core Components
**main.py** - Entry point and orchestration
- `WAFW00F` class extends `waftoolsengine` and orchestrates the detection workflow
- Contains attack payload definitions (XSS, SQLi, LFI, XXE, RCE)
- Implements detection methods: `matchHeader()`, `matchStatus()`, `matchCookie()`, `matchContent()`, `matchReason()`
- Provides attack request generators: `normalRequest()`, `xssAttack()`, `sqliAttack()`, `centralAttack()`, etc.
- Two detection modes:
- `identwaf()`: Plugin-based detection using WAF-specific signatures
- `genericdetect()`: Behavioral detection when no plugin matches
**manager.py** - Plugin loader
- Dynamically discovers and loads all Python files from `wafw00f/plugins/`
- Uses `importlib.util` for runtime module loading
- Returns dictionary: `{plugin_name: plugin_module}`
**wafprio.py** - Detection priority
- Ordered list of 182 WAF names defining which plugins to check first
- Optimization: fast header/cookie checks before complex logic
- Plugins not in the list are still checked but after prioritized ones
**evillib.py** - HTTP request engine
- `waftoolsengine` class wraps the `requests` library
- Enforces 100KB max response size to prevent hanging on streaming responses
- Enforces timeout during response body reading (not just connection)
- Default timeout: 7 seconds (configurable)
- Disables SSL warnings for testing self-signed certificates
- Streams responses in 8KB chunks
### Detection Flow
1. **Normal request**: Baseline HTTP request to establish normal behavior
2. **Attack request**: `centralAttack()` sends combined XSS+SQLi+LFI payload
3. **Plugin detection**: Iterate through prioritized plugins, each calling `is_waf(self)`
4. **Generic detection**: If no plugin matches, analyze behavioral differences (status codes, headers, blocking)
### Plugin System
Plugins are minimal Python modules in `wafw00f/plugins/` with exactly 2 requirements:
```python
NAME = 'WAF Name (Manufacturer)'
def is_waf(self):
# 'self' is the WAFW00F instance
# Access: self.rq (normal response), self.attackres (attack response)
# Available methods: matchHeader, matchCookie, matchContent, matchStatus, matchReason
if self.matchHeader(('server', 'cloudflare')):
return True
return False
```
**Common detection patterns:**
1. **Simple single-check** (e.g., `cloudflare.py`):
- Check for specific header, cookie, or content pattern
2. **Multiple checks** (e.g., `incapsula.py`):
- Try several different signatures (OR logic)
3. **Schema-based** (e.g., `modsecurity.py`):
- Multiple helper functions checking combinations of conditions (AND logic)
- Example: `check_schema_02()` requires both 403 status AND "ModSecurity Action" reason
**Detection methods available to plugins:**
- `matchHeader((name, pattern), attack=False)` - Regex match on header
- `matchCookie(pattern, attack=False)` - Shortcut for Set-Cookie header
- `matchContent(regex, attack=True)` - Regex match on response body
- `matchStatus(code, attack=True)` - Match HTTP status code
- `matchReason(phrase, attack=True)` - Match HTTP reason phrase
## Adding New WAF Detection
1. Create `wafw00f/plugins/newwaf.py`
2. Define `NAME` constant with "WAF Name (Manufacturer)" format
3. Define `is_waf(self)` function returning True/False
4. Optionally add WAF name to `wafprio.py` for priority detection
5. Add test to `tests/test_detection.py`:
```python
@responses.activate
def test_detect_newwaf_by_header(self):
responses.add(responses.GET, 'https://example.com',
headers={'Server': 'NewWAF'}, status=200)
engine = WAFW00F('https://example.com')
assert 'NewWAF' in engine.identwaf()
```
## Important Notes for Development
### Timeout Handling (Issue #246)
The timeout parameter must be enforced during both:
1. Connection establishment (handled by requests library)
2. Response body reading (enforced manually in `evillib.py`)
When modifying request logic, ensure timeouts are respected during streaming to prevent hangs on slow servers.
### Response Size Limiting
Always use `stream=True` with requests and enforce `MAX_RESPONSE_SIZE` (100KB) to prevent memory issues and hanging on:
- Streaming media servers (audio/video)
- Infinite response generators
- Large file downloads
### Version Updates
When bumping version, update **3 locations**:
1. `wafw00f/__init__.py` - `__version__` variable
2. `README.md` - Badge (line 18)
3. `README.md` - ASCII art examples (lines 53 and 253)
### Commit Messages
Follow conventional format:
- "Fix X" for bug fixes
- "Add X" for new features
- "Update X" for enhancements
- Include issue references: "Fix timeout enforcement (issue #246)"
### Testing WAF Plugins
When testing plugin detection:
- Use `@responses.activate` decorator
- Mock HTTP responses with specific headers/content/status
- Test both positive (WAF detected) and negative (not detected) cases
- Check against actual attack responses when possible
### Git Workflow
- Main branch: `master`
- Always run tests before committing
- Push releases to both GitHub and PyPI
- Create GitHub releases using `gh release create`
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at code@enablesecurity.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
================================================
FILE: CREDITS.txt
================================================
===================
THE WAFW00F PROJECT
===================
$ AUTHORS
=======
* Current Maintainers :-
- Sandro Gauci <sandro [at] enablesecurity [dot] com>
- Pinaki Mondal <0xinfection [at] gmail [dot] com>
* Original Code by :-
- Sandro Gauci <sandro [at] enablesecurity [dot] com>
- Wendel G. Henrique <whenrique [at] trustwave [dot] com>
$ CONTRIBUTORS
============
A number of people contributed in the past (in no particular order):
- Sebastien Gioria <https://github.com/SPoint42>
- W3AF (or Andres Riancho) <https://github.com/andresriancho>
- Charlie Campbell <https://github.com/sinnur>
- @j0eMcCray <https://github.com/joemccray>
- Mathieu Dessus
- David S. Langlands
- Nmap's http-waf-fingerprint.nse / Hani Benhabiles
- Denis Kolegov <https://github.com/dnkolegov>
- kun a <https://github.com/akun>
- Louis-Philippe Huberdeau <https://github.com/lphuberdeau>
- Brendan Coles <https://github.com/bcoles>
- Matt Foster <https://github.com/mattfoster>
- g0tmi1k (?) <https://github.com/g0tmi1k>
- MyKings <https://github.com/MyKings>
If you did contribute and somehow I didn't put your name in there, please do
let me know at: <sandro [at] enablesecurity [dot] com>.
================================================
FILE: Dockerfile
================================================
FROM python:3.11.9-alpine
WORKDIR /usr/src/app
COPY . .
RUN pip install .
ENTRYPOINT [ "wafw00f" ]
================================================
FILE: LICENSE
================================================
Copyright (c) 2009-2026, WAFW00F Developers
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the {organization} nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: MANIFEST.in
================================================
include CREDITS.txt
include LICENSE
include MANIFEST.in
include README.md
include wafw00f/__init__.py
include wafw00f/bin/wafw00f
================================================
FILE: Makefile
================================================
SRC_DIR = wafw00f
DOC_DIR = docs
MAKE = make
all:
make install
make test
make html
make clean
install:
pip install -q -e .[dev,docs]
test:
pytest
test-verbose:
pytest -v --tb=short
test-coverage:
pytest --cov=$(SRC_DIR) --cov-report=term-missing
lint:
prospector $(SRC_DIR) --strictness veryhigh
testall:
tox
html:
cd $(DOC_DIR) && $(MAKE) html
clean:
rm -rf *.egg-info build dist .coverage
find $(SRC_DIR) -name "*.pyc" | xargs rm -rf
================================================
FILE: README.md
================================================
<h1 align="center">
<a href="https://github.com/enablesecurity/wafw00f"><img src="https://i.imgur.com/uAgp49o.png" alt="wafw00f"/></a>
<br>
WAFW00F
</h1>
<p align="center">
<b>The Web Application Firewall Fingerprinting Tool.</b>
<br>
<b>
— From <a href="https://enablesecurity.com">Enable Security</a>
</b>
</p>
<p align="center">
<a href="https://docs.python.org/3/download.html">
<img src="https://img.shields.io/badge/Python-3.10+-green.svg">
</a>
<a href="https://github.com/EnableSecurity/wafw00f/releases">
<img src="https://img.shields.io/badge/Version-2.4.2%20(stable)-blue.svg">
</a>
<a href="https://github.com/EnableSecurity/wafw00f/blob/master/LICENSE">
<img src="https://img.shields.io/badge/License-BSD%203%20Clause-orange.svg">
</a>
</p>
## How does it work?
To do its magic, WAFW00F does the following:
- Sends a _normal_ HTTP request and analyses the response; this identifies a
number of WAF solutions.
- If that is not successful, it sends a number of (potentially malicious) HTTP
requests and uses simple logic to deduce which WAF it is.
- If that is also not successful, it analyses the responses previously
returned and uses another simple algorithm to guess if a WAF or security
solution is actively responding to our attacks.
For further details, check out the source code on our [main repository](https://github.com/EnableSecurity/wafw00f).
## What does it detect?
WAFW00F can detect a number of firewalls, a list of which is as below:
```
$ wafw00f -l
? ,. ( . ) . "
__ ?? (" ) )' ,' ) . (` '`
(___()'`; ??? .; ) ' (( (" ) ;(, (( ( ;) " )")
/,___ /` _"., ,._'_.,)_(..,( . )_ _' )_') (. _..( ' )
\\ \\ |____|____|____|____|____|____|____|____|____|
~ WAFW00F : v2.4.2 ~
~ Sniffing Web Application Firewalls since 2009 ~
[+] Can test for these WAFs:
WAF Name Manufacturer
-------- ------------
360PanYun 360 Technologies
360WangZhanBao 360 Technologies
ACE XML Gateway Cisco
ASP.NET Generic Microsoft
ASPA Firewall ASPA Engineering Co.
AWS Elastic Load Balancer Amazon
AireeCDN Airee
Airlock Phion/Ergon
Alert Logic Alert Logic
AliYunDun Alibaba Cloud Computing
AnYu AnYu Technologies
Anquanbao Anquanbao
Anubis Techaro
AppWall Radware
Approach Approach
Armor Defense Armor
ArvanCloud ArvanCloud
Astra Czar Securities
Azion Edge Firewall Azion
Azure Application Gateway Microsoft
Azure Front Door Microsoft
BIG-IP AP Manager F5 Networks
BIG-IP AppSec Manager F5 Networks
BIG-IP Local Traffic Manager F5 Networks
Barikode Ethic Ninja
Barracuda Barracuda Networks
Baffin Bay Mastercard
Bekchy Faydata Technologies Inc.
Beluga CDN Beluga
BinarySec BinarySec
BitNinja BitNinja
BlockDoS BlockDoS
Bluedon Bluedon IST
BulletProof Security Pro AITpro Security
CacheFly CDN CacheFly
CacheWall Varnish
CdnNS Application Gateway CdnNs/WdidcNet
ChinaCache Load Balancer ChinaCache
Chuang Yu Shield Yunaq
Cloud Protector Rohde & Schwarz CyberSecurity
Cloudbric Penta Security
Cloudflare Cloudflare Inc.
Cloudfloor Cloudfloor DNS
Cloudfront Amazon
Comodo cWatch Comodo CyberSecurity
CrawlProtect Jean-Denis Brun
DDoS-GUARD DDOS-GUARD CORP.
DOSarrest DOSarrest Internet Security
DataPower IBM
DenyALL Rohde & Schwarz CyberSecurity
Distil Distil Networks
DotDefender Applicure Technologies
DynamicWeb Injection Check DynamicWeb
Edgecast Verizon Digital Media
Eisoo Cloud Firewall Eisoo
Envoy EnvoyProxy
Expression Engine EllisLab
Fastly Fastly CDN
FirePass F5 Networks
FortiGate Fortinet
FortiGuard Fortinet
FortiWeb Fortinet
GoDaddy Website Protection GoDaddy
Google Cloud App Armor Google Cloud
Greywizard Grey Wizard
Huawei Cloud Firewall Huawei
HyperGuard Art of Defense
ISA Server Microsoft
Imunify360 CloudLinux
Incapsula Imperva Inc.
IndusGuard Indusface
Instart DX Instart Logic
Janusec Application Gateway Janusec
Jiasule Jiasule
KS-WAF KnownSec
Kemp LoadMaster Progress Software
KeyCDN KeyCDN
Kona SiteDefender Akamai
LimeLight CDN LimeLight
Link11 WAAP Link11
LiteSpeed LiteSpeed Technologies
Malcare Inactiv
MaxCDN MaxCDN
Mission Control Shield Mission Control
ModSecurity SpiderLabs
NAXSI NBS Systems
NSFocus NSFocus Global Inc.
Nemesida PentestIt
NetContinuum Barracuda Networks
NetScaler AppFirewall Citrix Systems
NevisProxy AdNovum
Newdefend NewDefend
NexusGuard Firewall NexusGuard
NinjaFirewall NinTechNet
NullDDoS Protection NullDDoS
OnMessage Shield BlackBaud
Open-Resty Lua Nginx FLOSS
Oracle Cloud Oracle
PT Application Firewall Positive Technologies
Palo Alto Next Gen Firewall Palo Alto Networks
PentaWAF Global Network Services
PerimeterX PerimeterX
PowerCDN PowerCDN
Profense ArmorLogic
Puhui Puhui
Qcloud Tencent Cloud
Qiniu Qiniu CDN
Qrator Qrator
RSFirewall RSJoomla!
RayWAF WebRay Solutions
Reblaze Reblaze
Reflected Networks Reflected Networks
RequestValidationMode Microsoft
SEnginx Neusoft
Sabre Firewall Sabre
Safe3 Web Firewall Safe3
Safedog SafeDog
Safeline Chaitin Tech.
Scutum Secure Sky Technology Inc.
SecKing SecKing
SecuPress WP Security SecuPress
Secure Entry United Security Providers
SecureSphere Imperva Inc.
ServerDefender VP Port80 Software
Shadow Daemon Zecure
Shield Security One Dollar Plugin
SiteGround SiteGround
SiteGuard EG Secure Solutions Inc.
Sitelock TrueShield
SonicWall Dell
Squarespace Squarespace
SquidProxy IDS SquidProxy
StackPath StackPath
Sucuri CloudProxy Sucuri Inc.
Tencent Cloud Firewall Tencent Technologies
Teros Citrix Systems
ThreatX A10 Networks
Trafficshield F5 Networks
TransIP Web Firewall TransIP
UEWaf UCloud
URLMaster SecurityCheck iFinity/DotNetNuke
URLScan Microsoft
UTM Web Protection Sophos
Variti Variti
Varnish OWASP
Vercel WAF Vercel
Viettel Cloudrity
VirusDie VirusDie LLC
WP Cerber Security Cerber Tech
WTS-WAF WTS
Wallarm Wallarm Inc.
WatchGuard WatchGuard Technologies
WebARX WebARX Security Solutions
WebKnight AQTRONIX
WebLand WebLand
WebSEAL IBM
WebTotem WebTotem
West263 CDN West263CDN
Wordfence Defiant
XLabs Security WAF XLabs
Xuanwudun Xuanwudun
YXLink YxLink Technologies
Yundun Yundun
Yunjiasu Baidu Cloud Computing
Yunsuo Yunsuo
ZScaler Accenture
Zenedge Zenedge
aeSecure aeSecure
eEye SecureIIS BeyondTrust
pkSecurity IDS pkSec
wpmudev WAF Incsub
Shieldon Firewall Shieldon.io
```
## How do I use it?
First, install the tools as described [here](#how-do-i-install-it).
For help you can make use of the `--help` option. The basic usage is to pass
an URL as an argument. Example:
```
$ wafw00f https://example.org
______
/ \
( Woof! )
\ ____/ )
,, ) (_
.-. - _______ ( |__|
()``; |==|_______) .)|__|
/ (' /|\ ( |__|
( / ) / | \ . |__|
\(_)_)) / | \ |__|
~ WAFW00F : v2.4.2 ~
The Web Application Firewall Fingerprinting Toolkit
[*] Checking https://example.org
[+] The site https://example.org is behind Edgecast (Verizon Digital Media) WAF.
[~] Number of requests: 2
```
## How do I install it?
### Install from PyPI (recommended)
Run:
```
python3 -m pip install wafw00f
```
or
```
pip3 install wafw00f
```
### Via Docker
It is also possible to run it within a docker container. Clone this repository first and build the Docker image using:
```
docker build . -t wafw00f
```
Now you can run:
```
docker run --rm -it wafw00f https://example.com
```
### From source
> NOTE: Be careful to not break your system packages while installing wafw00f. Use venv as and when required.
Clone the repository:
```
git clone https://github.com/enablesecurity/wafw00f.git
```
Then:
```
cd wafw00f/
python3 -m pip install .
```
Or, by using pipx directly:
```
pipx install git+https://github.com/EnableSecurity/wafw00f.git
```
## Final Words
__Questions?__ Pull up an [issue on GitHub Issue Tracker](https://github.com/enablesecurity/wafw00f/issues/new) or contact [me](mailto:sandro@enablesecurity.com).
[Pull requests](https://github.com/enablesecurity/wafw00f/pulls), [ideas and issues](https://github.com/enablesecurity/wafw00f/issues) are highly welcome.
Some useful links:
- [Documentation/Wiki](https://github.com/enablesecurity/wafw00f/wiki/)
- [Pypi Package Repository](https://pypi.org/project/wafw00f)
Presently being developed and maintained by:
- Sandro Gauci ([@SandroGauci](https://twitter.com/sandrogauci))
- Pinaki Mondal ([@0xInfection](https://twitter.com/0xinfection))
================================================
FILE: docs/Makefile
================================================
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# User-friendly check for sphinx-build
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
endif
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " xml to make Docutils-native XML files"
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/wafw00f.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/wafw00f.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/wafw00f"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/wafw00f"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
latexpdfja:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through platex and dvipdfmx..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
xml:
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
@echo
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
pseudoxml:
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
@echo
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
================================================
FILE: docs/conf.py
================================================
# -*- coding: utf-8 -*-
#
# wafw00f documentation build configuration file, created by
# sphinx-quickstart on Thu May 15 20:04:22 2014.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import os
import sys
BASE_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
sys.path.insert(0, BASE_DIR)
from wafw00f import __version__
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = []
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'wafw00f'
copyright = u'2020, WAFW00F Developers'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = __version__
# The full version, including alpha/beta/rc tags.
release = __version__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#html_extra_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'wafw00fdoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
('index', 'wafw00f.tex', u'wafw00f Documentation',
u'sandrogauci', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'wafw00f', u'wafw00f Documentation',
[u'sandrogauci'], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'wafw00f', u'wafw00f Documentation',
u'sandrogauci', 'wafw00f', 'One line description of project.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
================================================
FILE: docs/index.rst
================================================
.. wafw00f documentation master file, created by
sphinx-quickstart on Thu May 15 20:04:22 2014.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to wafw00f's documentation!
===================================
Contents:
.. toctree::
:maxdepth: 2
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
================================================
FILE: docs/wafw00f.8
================================================
.TH WAFW00F "8" "October 2020" "wafw00f " "User Commands"
.SH NAME
WAFW00F \- Identify and fingerprint Web Application Firewall products
.SH SYNOPSIS
.B wafw00f \fI\,url1 \/\fR[\fI\,url2 \/\fR[\fI\,url3 \/\fR... ]]
.SH DESCRIPTION
.TP
The Web Application Firewall Identification and Fingerprinting Tool.
.TP
.TP
To do its magic, WAFW00F does the following:
Sends a normal HTTP request and analyses the response; this identifies a number of WAF solutions.
If that is not successful, it sends a number of (potentially malicious) HTTP requests and uses simple logic to deduce which WAF it is.
If that is also not successful, it analyses the responses previously returned and uses another simple algorithm to guess if a WAF or security solution is active>
.SH OPTIONS
.TP
\fB\-h\fR, \fB\-\-help\fR
Show available options.
.TP
\fB\-v\fR, \fB\-\-verbose\fR
Enable verbosity \- multiple \fB\-v\fR options increase verbosity.
.TP
\fB\-a\fR, \fB\-\-findall\fR
Find all WAFs, do not stop testing on the first one.
.TP
\fB\-r\fR, \fB\-\-noredirect\fR
Do not follow redirections given by 3xx responses.
.TP
\fB\-t\fR WAF, \fB\-\-test\fR=\fI\,WAF\/\fR
Test for one specific WAF product.
.TP
\fB\-o\fR OUTPUT, \fB\-\-output\fR=\fI\,OUTPUT\/\fR
Write output to csv, json or text file depending on file extension. For stdout, specify - as filename.
.TP
\fB\-f\fR, \fB\-\-format\fR=\fI\,FORMAT\/\fR
Force output format to csv, json or text.
.TP
\fB\-i\fR INPUT, \fB\-\-input\fR=\fI\,INPUT\/\fR
Read targets from a file. Input format can be csv, json or text. For csv and json, a `url` column name or element is required.
.TP
\fB\-l\fR, \fB\-\-list\fR
List all the WAFs that WAFW00F is able to detect.
.TP
\fB\-p\fR PROXY, \fB\-\-proxy\fR=\fI\,PROXY\/\fR
Use an HTTP proxy to perform requests, example: http://hostname:8080, socks5://hostname:1080.
.TP
\fB\-V\fR, \fB\-\-version\fR
Print out the version.
.TP
\fB\-H\fR FILE, \fB\-\-headers\fR=\fI\,FILE\/\fR
Pass custom headers, for example to overwrite the default user\-agent string.
.TP
\fB\-T\fR TIMEOUT, \fB\-\-timeout\fR=\fI\,TIMEOUT\/\fR
Set the timeout for the requests.
.TP
\fB\-\-no\-colors\fR
Disable ANSI colors in output.
.SH AUTHORS
Sandro Gauci (@SandroGauci)
.br
Pinaki Mondal (@0xInfection)
.SH REPORTING BUGS
You can report bugs at the project homepage issue tracker: <https://github.com/EnableSecurity/wafw00f/issues/>.
.SH COPYRIGHT
Copyright (C) 2009-2022 WAFW00F Developers. License: BSD 3-Clause <https://opensource.org/licenses/BSD-3-Clause>.
.br
This is free software: you are free to modify and distribute under the terms as permitted by the license provided alongwith.
.SH SEE ALSO
Full documentation is available at: <https://github.com/EnableSecurity/wafw00f/wiki/>.
.PP
================================================
FILE: pyproject.toml
================================================
[build-system]
requires = ["setuptools>=64", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "wafw00f"
dynamic = ["version"]
description = "The Web Application Firewall Fingerprinting Toolkit"
readme = "README.md"
license = "BSD-3-Clause"
authors = [
{name = "Sandro Gauci", email = "sandro@enablesecurity.com"}
]
keywords = ["waf", "firewall", "detector", "fingerprint"]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: System Administrators",
"Intended Audience :: Information Technology",
"Topic :: Internet",
"Topic :: Security",
"Topic :: System :: Networking :: Firewalls",
"Programming Language :: Python :: 3",
"Operating System :: OS Independent",
]
requires-python = ">=3.10"
dependencies = [
"requests",
"requests[socks]",
]
[project.optional-dependencies]
dev = ["prospector", "pytest", "pytest-mock", "responses"]
docs = ["Sphinx"]
[project.urls]
Homepage = "https://github.com/enablesecurity/wafw00f"
"Bug Tracker" = "https://github.com/EnableSecurity/wafw00f/issues"
Documentation = "https://github.com/EnableSecurity/wafw00f/wiki"
"Source Code" = "https://github.com/EnableSecurity/wafw00f/tree/master"
[project.scripts]
wafw00f = "wafw00f.main:main"
[tool.setuptools.dynamic]
version = {attr = "wafw00f.__version__"}
[tool.setuptools.packages.find]
include = ["wafw00f*"]
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_functions = ["test_*"]
addopts = "-v"
================================================
FILE: tests/__init__.py
================================================
# wafw00f tests
================================================
FILE: tests/conftest.py
================================================
"""Shared pytest fixtures for wafw00f tests."""
import pytest
from unittest.mock import MagicMock
class MockResponse:
"""Mock HTTP response for testing."""
def __init__(self, status_code=200, headers=None, text='', reason='OK'):
self.status_code = status_code
self.headers = headers or {}
self.text = text
self.reason = reason
self._content = text.encode('utf-8') if isinstance(text, str) else text
@property
def content(self):
return self._content
@pytest.fixture
def mock_response():
"""Factory fixture to create mock responses."""
def _make_response(status_code=200, headers=None, text='', reason='OK'):
return MockResponse(status_code, headers, text, reason)
return _make_response
@pytest.fixture
def wafw00f_instance():
"""Create a WAFW00F instance for testing."""
from wafw00f.main import WAFW00F
return WAFW00F(target='https://example.com')
================================================
FILE: tests/test_detection.py
================================================
"""Integration tests for WAF detection."""
import pytest
import responses
from wafw00f.main import WAFW00F
class TestCloudflareDetection:
"""Tests for Cloudflare WAF detection."""
@responses.activate
def test_detect_cloudflare_by_header(self):
"""Test detecting Cloudflare by server header."""
responses.add(
responses.GET,
'https://example.com',
headers={'Server': 'cloudflare'},
status=200
)
responses.add(
responses.GET,
'https://example.com',
headers={'Server': 'cloudflare'},
status=200
)
waf = WAFW00F(target='https://example.com')
waf.rq = waf.Request()
waf.attackres = waf.Request()
from wafw00f.plugins import cloudflare
assert cloudflare.is_waf(waf)
@responses.activate
def test_detect_cloudflare_by_cf_ray(self):
"""Test detecting Cloudflare by CF-RAY header."""
responses.add(
responses.GET,
'https://example.com',
headers={'CF-RAY': '1234567890abcdef-LAX'},
status=200
)
responses.add(
responses.GET,
'https://example.com',
headers={'CF-RAY': '1234567890abcdef-LAX'},
status=200
)
waf = WAFW00F(target='https://example.com')
waf.rq = waf.Request()
waf.attackres = waf.Request()
from wafw00f.plugins import cloudflare
assert cloudflare.is_waf(waf)
class TestFastlyDetection:
"""Tests for Fastly detection."""
@responses.activate
def test_detect_fastly_by_request_id(self):
"""Test detecting Fastly by X-Fastly-Request-ID header."""
responses.add(
responses.GET,
'https://example.com',
headers={'X-Fastly-Request-ID': 'abc123def456'},
status=200
)
responses.add(
responses.GET,
'https://example.com',
headers={'X-Fastly-Request-ID': 'abc123def456'},
status=200
)
waf = WAFW00F(target='https://example.com')
waf.rq = waf.Request()
waf.attackres = waf.Request()
from wafw00f.plugins import fastly
assert fastly.is_waf(waf)
@responses.activate
def test_detect_fastly_by_served_by(self):
"""Test detecting Fastly by X-Served-By header."""
responses.add(
responses.GET,
'https://example.com',
headers={'X-Served-By': 'cache-sjc10049-SJC'},
status=200
)
responses.add(
responses.GET,
'https://example.com',
headers={'X-Served-By': 'cache-sjc10049-SJC'},
status=200
)
waf = WAFW00F(target='https://example.com')
waf.rq = waf.Request()
waf.attackres = waf.Request()
from wafw00f.plugins import fastly
assert fastly.is_waf(waf)
class TestAWSWAFDetection:
"""Tests for AWS WAF detection."""
@responses.activate
def test_detect_awswaf_by_header(self):
"""Test detecting AWS WAF by X-AMZ-ID header."""
responses.add(
responses.GET,
'https://example.com',
headers={'X-AMZ-ID': 'abc123xyz'},
status=200
)
responses.add(
responses.GET,
'https://example.com',
headers={'X-AMZ-ID': 'abc123xyz'},
status=200
)
waf = WAFW00F(target='https://example.com')
waf.rq = waf.Request()
waf.attackres = waf.Request()
from wafw00f.plugins import awswaf
assert awswaf.is_waf(waf)
class TestNoWAFDetection:
"""Tests for when no WAF is detected."""
@responses.activate
def test_no_waf_plain_response(self):
"""Test that plain response doesn't trigger false positive."""
responses.add(
responses.GET,
'https://example.com',
headers={'Server': 'nginx'},
body='<html><body>Hello World</body></html>',
status=200
)
responses.add(
responses.GET,
'https://example.com',
headers={'Server': 'nginx'},
body='<html><body>Hello World</body></html>',
status=200
)
waf = WAFW00F(target='https://example.com')
waf.rq = waf.Request()
waf.attackres = waf.Request()
# Test some common WAF plugins don't trigger
from wafw00f.plugins import cloudflare, fastly, awswaf
assert not cloudflare.is_waf(waf)
assert not fastly.is_waf(waf)
assert not awswaf.is_waf(waf)
class TestAnubisDetection:
"""Tests for Anubis bot protection detection."""
@responses.activate
def test_detect_anubis_by_cookie(self):
"""Test detecting Anubis by cookie pattern."""
responses.add(
responses.GET,
'https://example.com',
headers={'Set-Cookie': 'site-anubis-auth=token123; path=/'},
status=200
)
responses.add(
responses.GET,
'https://example.com',
headers={'Set-Cookie': 'site-anubis-auth=token123; path=/'},
status=200
)
waf = WAFW00F(target='https://example.com')
waf.rq = waf.Request()
waf.attackres = waf.Request()
from wafw00f.plugins import anubis
assert anubis.is_waf(waf)
@responses.activate
def test_detect_anubis_by_content(self):
"""Test detecting Anubis by page content."""
anubis_page = '''
<html>
<head><script id="anubis_version">v1.0</script></head>
<body>Protected by Anubis From Techaro</body>
</html>
'''
responses.add(
responses.GET,
'https://example.com',
body=anubis_page,
status=200
)
responses.add(
responses.GET,
'https://example.com',
body=anubis_page,
status=200
)
waf = WAFW00F(target='https://example.com')
waf.rq = waf.Request()
waf.attackres = waf.Request()
from wafw00f.plugins import anubis
assert anubis.is_waf(waf)
================================================
FILE: tests/test_evillib.py
================================================
"""Tests for the evillib module."""
import pytest
import responses
from wafw00f.lib.evillib import waftoolsengine, MAX_RESPONSE_SIZE
class TestWafToolsEngine:
"""Tests for the waftoolsengine class."""
def test_default_timeout(self):
"""Test default timeout is set."""
engine = waftoolsengine(target='https://example.com')
assert engine.timeout == 7
def test_custom_timeout(self):
"""Test custom timeout can be set."""
engine = waftoolsengine(target='https://example.com', timeout=30)
assert engine.timeout == 30
def test_default_headers(self):
"""Test default headers are set."""
engine = waftoolsengine(target='https://example.com')
assert 'User-Agent' in engine.headers
assert 'Accept' in engine.headers
def test_custom_headers(self):
"""Test custom headers can be set."""
custom = {'X-Custom': 'value'}
engine = waftoolsengine(target='https://example.com', head=custom)
assert engine.headers == custom
@responses.activate
def test_request_success(self):
"""Test successful request."""
responses.add(
responses.GET,
'https://example.com',
body='Hello World',
status=200
)
engine = waftoolsengine(target='https://example.com')
resp = engine.Request()
assert resp is not None
assert resp.status_code == 200
assert engine.requestnumber == 1
@responses.activate
def test_request_increments_counter(self):
"""Test request counter increments."""
responses.add(responses.GET, 'https://example.com', status=200)
responses.add(responses.GET, 'https://example.com', status=200)
engine = waftoolsengine(target='https://example.com')
engine.Request()
engine.Request()
assert engine.requestnumber == 2
@responses.activate
def test_response_content_accessible(self):
"""Test response content is accessible after streaming read."""
test_content = 'This is test content for WAF detection'
responses.add(
responses.GET,
'https://example.com',
body=test_content,
status=200
)
engine = waftoolsengine(target='https://example.com')
resp = engine.Request()
assert resp.content == test_content.encode('utf-8')
assert resp.text == test_content
class TestResponseSizeLimit:
"""Tests for the response size limiting feature."""
def test_max_response_size_defined(self):
"""Test MAX_RESPONSE_SIZE is defined."""
assert MAX_RESPONSE_SIZE > 0
assert MAX_RESPONSE_SIZE == 100 * 1024 # 100KB
@responses.activate
def test_small_response_fully_read(self):
"""Test small responses are fully read."""
small_content = 'x' * 1000 # 1KB
responses.add(
responses.GET,
'https://example.com',
body=small_content,
status=200
)
engine = waftoolsengine(target='https://example.com')
resp = engine.Request()
assert len(resp.content) == 1000
@responses.activate
def test_large_response_truncated(self):
"""Test large responses are truncated to MAX_RESPONSE_SIZE."""
large_content = 'x' * (MAX_RESPONSE_SIZE + 50000) # 150KB
responses.add(
responses.GET,
'https://example.com',
body=large_content,
status=200
)
engine = waftoolsengine(target='https://example.com')
resp = engine.Request()
# Should be truncated to around MAX_RESPONSE_SIZE (may be slightly over due to chunk size)
assert len(resp.content) <= MAX_RESPONSE_SIZE + 8192
assert len(resp.content) >= MAX_RESPONSE_SIZE
class TestTimeoutEnforcement:
"""Tests for timeout enforcement during response reading."""
@responses.activate
def test_timeout_attribute_used(self):
"""Test timeout is properly configured and accessible.
Note: Testing actual timeout enforcement during slow streaming
requires integration tests with real servers, as the responses
mock library doesn't support time-based streaming simulation.
The timeout enforcement logic in Request() will break out of
the chunk reading loop if time.time() - start_time > self.timeout.
"""
responses.add(
responses.GET,
'https://example.com',
body='test',
status=200
)
engine = waftoolsengine(target='https://example.com', timeout=5)
resp = engine.Request()
# Verify the engine has timeout configured
assert engine.timeout == 5
assert resp is not None
class TestPathPreservation:
"""Tests that request paths are not normalized."""
@responses.activate
def test_path_traversal_not_normalized(self):
"""Test path traversal sequences are preserved."""
responses.add(responses.GET, 'https://example.com/../../etc/passwd', status=200)
engine = waftoolsengine(target='https://example.com')
engine.Request(path='../../etc/passwd')
assert '../../etc/passwd' in responses.calls[0].request.url
@responses.activate
def test_path_traversal_with_params(self):
"""Test path traversal is preserved when query params are present."""
responses.add(responses.GET, 'https://example.com/../../etc/passwd', status=200)
engine = waftoolsengine(target='https://example.com')
engine.Request(path='../../etc/passwd', params={'key': 'val'})
url = responses.calls[0].request.url
assert '../../etc/passwd' in url
assert 'key=val' in url
================================================
FILE: tests/test_manager.py
================================================
"""Tests for the plugin manager."""
import pytest
from wafw00f.manager import load_plugins
class TestLoadPlugins:
"""Tests for the load_plugins function."""
def test_load_plugins_returns_dict(self):
"""Verify load_plugins returns a dictionary."""
plugins = load_plugins()
assert isinstance(plugins, dict)
def test_load_plugins_not_empty(self):
"""Verify plugins are loaded."""
plugins = load_plugins()
assert len(plugins) > 0
def test_plugins_have_name_attribute(self):
"""Verify each plugin has a NAME attribute."""
plugins = load_plugins()
for name, plugin in plugins.items():
assert hasattr(plugin, 'NAME'), f"Plugin {name} missing NAME attribute"
def test_plugins_have_is_waf_function(self):
"""Verify each plugin has an is_waf function."""
plugins = load_plugins()
for name, plugin in plugins.items():
assert hasattr(plugin, 'is_waf'), f"Plugin {name} missing is_waf function"
assert callable(plugin.is_waf), f"Plugin {name} is_waf is not callable"
def test_known_plugins_loaded(self):
"""Verify some known plugins are loaded."""
plugins = load_plugins()
known_plugins = ['cloudflare', 'fastly', 'awswaf', 'anubis']
for plugin_name in known_plugins:
assert plugin_name in plugins, f"Expected plugin {plugin_name} not found"
def test_plugin_names_are_strings(self):
"""Verify plugin keys are strings."""
plugins = load_plugins()
for name in plugins.keys():
assert isinstance(name, str)
================================================
FILE: tests/test_matching.py
================================================
"""Tests for the WAFW00F matching functions."""
import pytest
from wafw00f.main import WAFW00F
class TestMatchHeader:
"""Tests for the matchHeader function."""
def test_match_header_exact(self, wafw00f_instance, mock_response):
"""Test matching an exact header value."""
wafw00f_instance.rq = mock_response(headers={'Server': 'cloudflare'})
assert wafw00f_instance.matchHeader(('Server', 'cloudflare'), attack=False)
def test_match_header_regex(self, wafw00f_instance, mock_response):
"""Test matching header with regex."""
wafw00f_instance.rq = mock_response(headers={'Server': 'cloudflare-nginx'})
assert wafw00f_instance.matchHeader(('Server', r'cloudflare.*'), attack=False)
def test_match_header_case_insensitive(self, wafw00f_instance, mock_response):
"""Test that header matching is case insensitive."""
wafw00f_instance.rq = mock_response(headers={'Server': 'CLOUDFLARE'})
assert wafw00f_instance.matchHeader(('Server', 'cloudflare'), attack=False)
def test_match_header_not_found(self, wafw00f_instance, mock_response):
"""Test when header doesn't match."""
wafw00f_instance.rq = mock_response(headers={'Server': 'nginx'})
assert not wafw00f_instance.matchHeader(('Server', 'cloudflare'), attack=False)
def test_match_header_missing(self, wafw00f_instance, mock_response):
"""Test when header is missing."""
wafw00f_instance.rq = mock_response(headers={})
assert not wafw00f_instance.matchHeader(('Server', 'cloudflare'), attack=False)
def test_match_header_none_response(self, wafw00f_instance):
"""Test when response is None."""
wafw00f_instance.rq = None
result = wafw00f_instance.matchHeader(('Server', 'cloudflare'), attack=False)
assert result is None
class TestMatchContent:
"""Tests for the matchContent function."""
def test_match_content_exact(self, wafw00f_instance, mock_response):
"""Test matching exact content."""
wafw00f_instance.attackres = mock_response(text='Access Denied by Cloudflare')
assert wafw00f_instance.matchContent('Access Denied')
def test_match_content_regex(self, wafw00f_instance, mock_response):
"""Test matching content with regex."""
wafw00f_instance.attackres = mock_response(text='Error 403: Forbidden by WAF')
assert wafw00f_instance.matchContent(r'Error \d+:.*WAF')
def test_match_content_case_insensitive(self, wafw00f_instance, mock_response):
"""Test that content matching is case insensitive."""
wafw00f_instance.attackres = mock_response(text='ACCESS DENIED')
assert wafw00f_instance.matchContent('access denied')
def test_match_content_not_found(self, wafw00f_instance, mock_response):
"""Test when content doesn't match."""
wafw00f_instance.attackres = mock_response(text='Welcome to our website')
assert not wafw00f_instance.matchContent('Access Denied')
def test_match_content_none_response(self, wafw00f_instance):
"""Test when response is None."""
wafw00f_instance.attackres = None
result = wafw00f_instance.matchContent('test')
assert result is None
class TestMatchCookie:
"""Tests for the matchCookie function."""
def test_match_cookie(self, wafw00f_instance, mock_response):
"""Test matching a cookie."""
wafw00f_instance.rq = mock_response(
headers={'Set-Cookie': '__cfduid=abc123; path=/'}
)
assert wafw00f_instance.matchCookie('__cfduid', attack=False)
def test_match_cookie_regex(self, wafw00f_instance, mock_response):
"""Test matching cookie with regex."""
wafw00f_instance.rq = mock_response(
headers={'Set-Cookie': 'session_id=xyz789; path=/'}
)
assert wafw00f_instance.matchCookie(r'session_id=\w+', attack=False)
def test_match_cookie_not_found(self, wafw00f_instance, mock_response):
"""Test when cookie doesn't match."""
wafw00f_instance.rq = mock_response(
headers={'Set-Cookie': 'other_cookie=value'}
)
assert not wafw00f_instance.matchCookie('__cfduid', attack=False)
class TestMatchStatus:
"""Tests for the matchStatus function."""
def test_match_status_200(self, wafw00f_instance, mock_response):
"""Test matching 200 status code."""
wafw00f_instance.attackres = mock_response(status_code=200)
assert wafw00f_instance.matchStatus(200)
def test_match_status_403(self, wafw00f_instance, mock_response):
"""Test matching 403 status code."""
wafw00f_instance.attackres = mock_response(status_code=403)
assert wafw00f_instance.matchStatus(403)
def test_match_status_mismatch(self, wafw00f_instance, mock_response):
"""Test when status code doesn't match."""
wafw00f_instance.attackres = mock_response(status_code=200)
assert not wafw00f_instance.matchStatus(403)
def test_match_status_none_response(self, wafw00f_instance):
"""Test when response is None."""
wafw00f_instance.attackres = None
result = wafw00f_instance.matchStatus(200)
assert result is None
class TestMatchReason:
"""Tests for the matchReason function."""
def test_match_reason_ok(self, wafw00f_instance, mock_response):
"""Test matching OK reason."""
wafw00f_instance.attackres = mock_response(reason='OK')
assert wafw00f_instance.matchReason('OK')
def test_match_reason_forbidden(self, wafw00f_instance, mock_response):
"""Test matching Forbidden reason."""
wafw00f_instance.attackres = mock_response(reason='Forbidden')
assert wafw00f_instance.matchReason('Forbidden')
def test_match_reason_mismatch(self, wafw00f_instance, mock_response):
"""Test when reason doesn't match."""
wafw00f_instance.attackres = mock_response(reason='OK')
assert not wafw00f_instance.matchReason('Forbidden')
================================================
FILE: wafw00f/__init__.py
================================================
#!/usr/bin/env python3
__version__ = '2.4.2'
__license__ = 'BSD 3-Clause'
================================================
FILE: wafw00f/lib/__init__.py
================================================
================================================
FILE: wafw00f/lib/asciiarts.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
from dataclasses import dataclass
from random import randint
from wafw00f import __version__
@dataclass
class Color:
"""ANSI colors."""
W: str = '\033[1;97m'
Y: str = '\033[1;93m'
G: str = '\033[1;92m'
R: str = '\033[1;91m'
B: str = '\033[1;94m'
C: str = '\033[1;96m'
E: str = '\033[0m'
@classmethod
def disable(cls):
"""Disables all colors."""
cls.W = ''
cls.Y = ''
cls.G = ''
cls.R = ''
cls.B = ''
cls.C = ''
cls.E = ''
@classmethod
def unpack(cls):
"""Unpacks and returns the color values.
Useful for brevity, e.g.:
(W,Y,G,R,B,C,E) = Color.unpack()
"""
return (
cls.W,
cls.Y,
cls.G,
cls.R,
cls.B,
cls.C,
cls.E
)
def randomArt():
# Colors for terminal
(W,Y,G,R,B,C,E) = Color.unpack()
woof = '''
'''+W+'''______
'''+W+'''/ \\
'''+W+'''( Woof! )
'''+W+r'''\ ____/ '''+R+''')
'''+W+''',, '''+R+''') ('''+Y+'''_
'''+Y+'''.-. '''+W+'''- '''+G+'''_______ '''+R+'''( '''+Y+'''|__|
'''+Y+'''()``; '''+G+'''|==|_______) '''+R+'''.)'''+Y+'''|__|
'''+Y+'''/ (' '''+G+r'''/|\ '''+R+'''( '''+Y+'''|__|
'''+Y+'''( / ) '''+G+r''' / | \ '''+R+'''. '''+Y+'''|__|
'''+Y+r'''\(_)_)) '''+G+r'''/ | \ '''+Y+'''|__|'''+E+'''
'''+C+'~ WAFW00F : '+B+'v'+__version__+''' ~'''+W+'''
The Web Application Firewall Fingerprinting Toolkit
'''+E
w00f = '''
'''+W+'''______
'''+W+'''/ \\
'''+W+'''( W00f! )
'''+W+r'''\ ____/
'''+W+''',, '''+G+'''__ '''+Y+'''404 Hack Not Found
'''+C+'''|`-.__ '''+G+'''/ / '''+R+''' __ __
'''+C+'''/" _/ '''+G+'''/_/ '''+R+r'''\ \ / /
'''+B+'''*===* '''+G+'''/ '''+R+r'''\ \_/ / '''+Y+'''405 Not Allowed
'''+C+'''/ )__// '''+R+r'''\ /
'''+C+'''/| / /---` '''+Y+'''403 Forbidden
'''+C+r'''\\/` \ | '''+R+'''/ _ \\
'''+C+r'''`\ /_\\_ '''+Y+'''502 Bad Gateway '''+R+r'''/ / \ \ '''+Y+'''500 Internal Error
'''+C+'''`_____``-` '''+R+r'''/_/ \_\\
'''+C+'~ WAFW00F : '+B+'v'+__version__+''' ~'''+W+'''
The Web Application Firewall Fingerprinting Toolkit
'''+E
wo0f = r'''
? ,. ( . ) . "
__ ?? (" ) )' ,' ) . (` '`
(___()'`; ??? .; ) ' (( (" ) ;(, (( ( ;) " )")
/,___ /` _"., ,._'_.,)_(..,( . )_ _' )_') (. _..( ' )
\\ \\ |____|____|____|____|____|____|____|____|____|
~ WAFW00F : v'''+__version__+''' ~
~ Sniffing Web Application Firewalls since 2009 ~
'''
arts = [woof, w00f, wo0f]
return arts[randint(0, len(arts)-1)]
================================================
FILE: wafw00f/lib/evillib.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
import time
import logging
from copy import copy
from urllib.parse import urlparse
import requests
import urllib3
# For requests < 2.16, this should be used.
# requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
# For requests >= 2.16, this is the convention
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
def_headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:130.0) Gecko/20100101 Firefox/130.0',
'Accept-Language': 'en-US,en;q=0.5',
'Upgrade-Insecure-Requests': '1',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'cross-site',
'Priority': 'u=0, i',
'DNT': '1',
}
proxies = {}
# Maximum response body size to read (100KB should be plenty for WAF detection)
MAX_RESPONSE_SIZE = 100 * 1024
class waftoolsengine:
def __init__(
self, target='https://example.com', debuglevel=0,
path='/', proxies=None, redir=True, head=None, timeout=7
):
self.target = target
self.debuglevel = debuglevel
self.requestnumber = 0
self.path = path
self.redirectno = 0
self.allowredir = redir
self.proxies = proxies
self.log = logging.getLogger('wafw00f')
self.timeout = timeout
if head:
self.headers = head
else:
self.headers = copy(def_headers) #copy object by value not reference. Fix issue #90
def Request(self, headers=None, path=None, params={}, delay=0):
try:
time.sleep(delay)
if not headers:
h = self.headers
else: h = headers
# Create the url manually to avoid path normalization
url = self.target if path is None else self.target.rstrip('/') + '/' + path.lstrip('/')
prepared = requests.Request('GET', url, headers=h,
params=params or {}).prepare()
parsed_url = urlparse(prepared.url)
# Ensuring trailing slash does not disappear
trailing_slash = parsed_url.path.endswith('/')
if trailing_slash and not url.endswith('/'):
url += '/'
# Preserve the original path (e.g. ../../etc/passwd)
if params:
prepared.url = url + '?' + parsed_url.query
else:
prepared.url = url
req = requests.Session().send(prepared, proxies=self.proxies, timeout=self.timeout,
allow_redirects=self.allowredir, verify=False, stream=True)
# Read only up to MAX_RESPONSE_SIZE to avoid hanging on streaming responses
# (e.g., audio streams) - see issue #246
# Also enforce timeout during reading to handle slow streaming servers
chunks = []
bytes_read = 0
start_time = time.time()
for chunk in req.iter_content(chunk_size=8192):
chunks.append(chunk)
bytes_read += len(chunk)
if bytes_read >= MAX_RESPONSE_SIZE:
break
# Check if we've exceeded the timeout during reading
if time.time() - start_time > self.timeout:
self.log.debug('Timeout reached during response body reading')
break
req._content = b''.join(chunks)
self.log.info('Request Succeeded')
self.log.debug('Headers: %s\n' % req.headers)
self.log.debug('Content: %s\n' % req.content)
self.requestnumber += 1
return req
except requests.exceptions.RequestException as e:
self.log.error('Something went wrong %s' % (e.__str__()))
================================================
FILE: wafw00f/main.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
import csv
import io
import json
import logging
import os
import random
import re
import sys
import string
import urllib.parse
from collections import defaultdict
from optparse import OptionParser
from wafw00f import __license__, __version__
from wafw00f.lib.asciiarts import Color, randomArt
from wafw00f.lib.evillib import waftoolsengine
from wafw00f.manager import load_plugins
from wafw00f.wafprio import wafdetectionsprio
class WAFW00F(waftoolsengine):
xsstring = r'<script>alert("XSS");</script>'
sqlistring = r'UNION SELECT ALL FROM information_schema AND " or SLEEP(5) or "'
lfistring = r'../../etc/passwd'
rcestring = r'/bin/cat /etc/passwd; ping 127.0.0.1; curl google.com'
xxestring = r'<!ENTITY xxe SYSTEM "file:///etc/shadow">]><pwn>&hack;</pwn>'
def __init__(self, target='www.example.com', debuglevel=0, path='/',
followredirect=True, extraheaders={}, proxies=None, timeout=7):
self.log = logging.getLogger('wafw00f')
self.attackres = None
waftoolsengine.__init__(self, target, debuglevel, path, proxies, followredirect, extraheaders, timeout)
self.knowledge = {
'generic': {
'found': False,
'reason': ''
},
'wafname': []
}
self.rq = self.normalRequest()
def normalRequest(self):
return self.Request()
def customRequest(self, headers=None):
return self.Request(
headers=headers
)
def nonExistent(self):
return self.Request(
path=self.path + str(random.randrange(100, 999)) + '.html'
)
def xssAttack(self):
return self.Request(
path=self.path,
params={
create_random_param_name(): self.xsstring
}
)
def xxeAttack(self):
return self.Request(
path=self.path,
params={
create_random_param_name(): self.xxestring
}
)
def lfiAttack(self):
return self.Request(
path=self.path + self.lfistring
)
def centralAttack(self):
return self.Request(
path=self.path,
params={
create_random_param_name(): self.xsstring,
create_random_param_name(): self.sqlistring,
create_random_param_name(): self.lfistring
}
)
def sqliAttack(self):
return self.Request(
path=self.path,
params={
create_random_param_name(): self.sqlistring
}
)
def osciAttack(self):
return self.Request(
path=self.path,
params= {
create_random_param_name(): self.rcestring
}
)
def performCheck(self, request_method):
r = request_method()
if r is None:
raise RequestBlocked()
return r, r.url
# Most common attacks used to detect WAFs
attcom = [xssAttack, sqliAttack, lfiAttack]
attacks = [xssAttack, xxeAttack, lfiAttack, sqliAttack, osciAttack]
def genericdetect(self):
reason = ''
reasons = ['Blocking is being done at connection/packet level.',
'The server header is different when an attack is detected.',
'The server returns a different response code when an attack string is used.',
'It closed the connection for a normal request.',
'The response was different when the request wasn\'t made from a browser.'
]
try:
# Testing for no user-agent response. Detects almost all WAFs out there.
resp1, _ = self.performCheck(self.normalRequest)
if 'User-Agent' in self.headers:
self.headers.pop('User-Agent') # Deleting the user-agent key from object not dict.
resp3 = self.customRequest(headers=self.headers)
if resp3 is not None and resp1 is not None:
if resp1.status_code != resp3.status_code:
self.log.info('Server returned a different response when request didn\'t contain the User-Agent header.')
reason = reasons[4]
reason += '\r\n'
reason += 'Normal response code is "%s",' % resp1.status_code
reason += ' while the response code to a modified request is "%s"' % resp3.status_code
self.knowledge['generic']['reason'] = reason
self.knowledge['generic']['found'] = True
return True
# Testing the status code upon sending a xss attack
resp2, xss_url = self.performCheck(self.xssAttack)
if resp1.status_code != resp2.status_code:
self.log.info('Server returned a different response when a XSS attack vector was tried.')
reason = reasons[2]
reason += '\r\n'
reason += 'Normal response code is "%s",' % resp1.status_code
reason += ' while the response code to cross-site scripting attack is "%s"' % resp2.status_code
self.knowledge['generic']['reason'] = reason
self.knowledge['generic']['found'] = True
return xss_url
# Testing the status code upon sending a lfi attack
resp2, lfi_url = self.performCheck(self.lfiAttack)
if resp1.status_code != resp2.status_code:
self.log.info('Server returned a different response when a directory traversal was attempted.')
reason = reasons[2]
reason += '\r\n'
reason += 'Normal response code is "%s",' % resp1.status_code
reason += ' while the response code to a file inclusion attack is "%s"' % resp2.status_code
self.knowledge['generic']['reason'] = reason
self.knowledge['generic']['found'] = True
return lfi_url
# Testing the status code upon sending a sqli attack
resp2, sqli_url = self.performCheck(self.sqliAttack)
if resp1.status_code != resp2.status_code:
self.log.info('Server returned a different response when a SQLi was attempted.')
reason = reasons[2]
reason += '\r\n'
reason += 'Normal response code is "%s",' % resp1.status_code
reason += ' while the response code to a SQL injection attack is "%s"' % resp2.status_code
self.knowledge['generic']['reason'] = reason
self.knowledge['generic']['found'] = True
return sqli_url
# Checking for the Server header after sending malicious requests
normalserver, attackresponse_server = '', ''
response = self.attackres
if 'server' in resp1.headers:
normalserver = resp1.headers.get('Server')
if response is not None and 'server' in response.headers:
attackresponse_server = response.headers.get('Server')
if attackresponse_server != normalserver:
self.log.info('Server header changed, WAF possibly detected')
self.log.debug('Attack response: %s' % attackresponse_server)
self.log.debug('Normal response: %s' % normalserver)
reason = reasons[1]
reason += '\r\nThe server header for a normal response is "%s",' % normalserver
reason += ' while the server header a response to an attack is "%s",' % attackresponse_server
self.knowledge['generic']['reason'] = reason
self.knowledge['generic']['found'] = True
return True
# If at all request doesn't go, press F
except RequestBlocked:
self.knowledge['generic']['reason'] = reasons[0]
self.knowledge['generic']['found'] = True
return True
return False
def matchHeader(self, headermatch, attack=False):
if attack:
r = self.attackres
else:
r = self.rq
if r is None:
return
header, match = headermatch
headerval = r.headers.get(header)
if headerval:
# set-cookie can have multiple headers, python gives it to us
# concatinated with a comma
if header == 'Set-Cookie':
headervals = headerval.split(', ')
else:
headervals = [headerval]
for headerval in headervals:
if re.search(match, headerval, re.I):
return True
return False
def matchStatus(self, statuscode, attack=True):
if attack:
r = self.attackres
else:
r = self.rq
if r is None:
return
if r.status_code == statuscode:
return True
return False
def matchCookie(self, match, attack=False):
return self.matchHeader(('Set-Cookie', match), attack=attack)
def matchReason(self, reasoncode, attack=True):
if attack:
r = self.attackres
else:
r = self.rq
if r is None:
return
# We may need to match multiline context in response body
if str(r.reason) == reasoncode:
return True
return False
def matchContent(self, regex, attack=True):
if attack:
r = self.attackres
else:
r = self.rq
if r is None:
return
# We may need to match multiline context in response body
if re.search(regex, r.text, re.I):
return True
return False
wafdetections = dict()
plugin_dict = load_plugins()
result_dict = {}
for plugin_module in plugin_dict.values():
wafdetections[plugin_module.NAME] = plugin_module.is_waf
# Check for prioritized ones first, then check those added externally
checklist = wafdetectionsprio
checklist += list(set(wafdetections.keys()) - set(checklist))
def identwaf(self, findall=False):
detected = list()
try:
self.attackres, xurl = self.performCheck(self.centralAttack)
except RequestBlocked:
return detected, None
for wafvendor in self.checklist:
self.log.info('Checking for %s' % wafvendor)
if self.wafdetections[wafvendor](self):
detected.append(wafvendor)
if not findall:
break
self.knowledge['wafname'] = detected
return detected, xurl
def calclogginglevel(verbosity):
default = 40 # errors are printed out
level = default - (verbosity * 10)
if level < 0:
level = 0
return level
def buildResultRecord(url, waf, evil_url=None):
result = {}
result['url'] = url
if waf:
result['detected'] = True
if waf == 'generic':
result['trigger_url'] = evil_url
result['firewall'] = 'Generic'
result['manufacturer'] = 'Unknown'
else:
result['trigger_url'] = evil_url
result['firewall'] = waf.split('(')[0].strip()
result['manufacturer'] = waf.split('(')[1].replace(')', '').strip()
else:
result['trigger_url'] = evil_url
result['detected'] = False
result['firewall'] = 'None'
result['manufacturer'] = 'None'
return result
def getTextResults(res=[]):
# leaving out some space for future possibilities of newer columns
# newer columns can be added to this tuple below
keys = ('detected')
res = [({key: ba[key] for key in ba if key not in keys}) for ba in res]
rows = []
for dk in res:
p = [str(x) for _, x in dk.items()]
rows.append(p)
for m in rows:
m[1] = '%s (%s)' % (m[1], m[2])
m.pop()
defgen = [
(max([len(str(row[i])) for row in rows]) + 3)
for i in range(len(rows[0]))
]
rwfmt = ''.join(['{:>'+str(dank)+'}' for dank in defgen])
textresults = []
for row in rows:
textresults.append(rwfmt.format(*row))
return textresults
def create_random_param_name(size=8, chars=string.ascii_lowercase):
return ''.join(random.choice(chars) for _ in range(size))
def disableStdOut():
sys.stdout = None
def enableStdOut():
sys.stdout = sys.__stdout__
def getheaders(fn):
headers = {}
if not os.path.exists(fn):
logging.getLogger('wafw00f').critical('Headers file "%s" does not exist!' % fn)
return
with io.open(fn, 'r', encoding='utf-8') as f:
for line in f.readlines():
_t = line.split(':', 2)
if len(_t) == 2:
h, v = map(lambda x: x.strip(), _t)
headers[h] = v
return headers
class RequestBlocked(Exception):
pass
def main():
parser = OptionParser(usage='%prog url1 [url2 [url3 ... ]]\r\nexample: %prog http://www.victim.org/')
parser.add_option('-v', '--verbose', action='count', dest='verbose', default=0,
help='Enable verbosity, multiple -v options increase verbosity')
parser.add_option('-a', '--findall', action='store_true', dest='findall', default=False,
help='Find all WAFs which match the signatures, do not stop testing on the first one')
parser.add_option('-r', '--noredirect', action='store_false', dest='followredirect',
default=True, help='Do not follow redirections given by 3xx responses')
parser.add_option('-t', '--test', dest='test', help='Test for one specific WAF (use --list to get names, quote names with spaces e.g. "AireeCDN (Airee)")')
parser.add_option('-o', '--output', dest='output', help='Write output to csv, json or text file depending on file extension. For stdout, specify - as filename.',
default=None)
parser.add_option('-f', '--format', dest='format', help='Force output format to csv, json or text.',
default=None)
parser.add_option('-i', '--input-file', dest='input', help='Read targets from a file. Input format can be csv, json or text. For csv and json, a `url` column name or element is required.',
default=None)
parser.add_option('-l', '--list', dest='list', action='store_true',
default=False, help='List all WAFs that WAFW00F is able to detect')
parser.add_option('-p', '--proxy', dest='proxy', default=None,
help='Use an HTTP proxy to perform requests, examples: http://hostname:8080, socks5://hostname:1080, http://user:pass@hostname:8080')
parser.add_option('--version', '-V', dest='version', action='store_true',
default=False, help='Print out the current version of WafW00f and exit.')
parser.add_option('--headers', '-H', dest='headers', action='store', default=None,
help='Pass custom headers via a text file to overwrite the default header set.')
parser.add_option('-T', '--timeout', dest='timeout', action='store', default=7, type=int,
help='Set the timeout for the requests.')
parser.add_option('--no-colors', dest='colors', action='store_false',
default=True, help='Disable ANSI colors in output.')
options, args = parser.parse_args()
logging.basicConfig(level=calclogginglevel(options.verbose))
log = logging.getLogger('wafw00f')
if options.output == '-':
disableStdOut()
# Windows based systems do not support ANSI sequences,
# hence not displaying them.
if not options.colors or 'win' in sys.platform:
Color.disable()
print(randomArt())
(W,Y,G,R,B,C,E) = Color.unpack()
if options.list:
print('[+] Can test for these WAFs:\r\n')
try:
m = [i.replace(')', '').split(' (') for i in wafdetectionsprio]
print(R+' WAF Name'+' '*24+'Manufacturer\n '+'-'*8+' '*24+'-'*12+'\n')
max_len = max(len(str(x)) for k in m for x in k)
for inner in m:
first = True
for elem in inner:
if first:
text = Y+' {:<{}} '.format(elem, max_len+2)
first = False
else:
text = W+'{:<{}} '.format(elem, max_len+2)
print(text, E, end='')
print()
sys.exit(0)
except Exception:
return
if options.version:
print('[+] The version of WAFW00F you have is %sv%s%s' % (B, __version__, E))
print('[+] WAFW00F is provided under the %s%s%s license.' % (C, __license__, E))
return
extraheaders = {}
if options.headers:
log.info('Getting extra headers from %s' % options.headers)
extraheaders = getheaders(options.headers)
if extraheaders is None:
parser.error('Please provide a headers file with colon delimited header names and values')
if len(args) == 0 and not options.input:
parser.error('No test target specified.')
#check if input file is present
if options.input:
log.debug('Loading file "%s"' % options.input)
try:
if options.input.endswith('.json'):
with open(options.input) as f:
try:
urls = json.loads(f.read())
except json.decoder.JSONDecodeError:
log.critical('JSON file %s did not contain well-formed JSON', options.input)
sys.exit(1)
log.info('Found: %s urls to check.' %(len(urls)))
targets = [ item['url'] for item in urls ]
elif options.input.endswith('.csv'):
columns = defaultdict(list)
with open(options.input) as f:
reader = csv.DictReader(f)
for row in reader:
for (k,v) in row.items():
columns[k].append(v)
targets = columns['url']
else:
with open(options.input) as f:
targets = [x for x in f.read().splitlines()]
except FileNotFoundError:
log.error('File %s could not be read. No targets loaded.', options.input)
sys.exit(1)
else:
targets = args
results = []
for target in targets:
if not target.startswith('http'):
log.info('The url %s should start with http:// or https:// .. fixing (might make this unusable)' % target)
target = 'https://' + target
print('[*] Checking %s' % target)
pret = urllib.parse.urlparse(target)
if pret is None:
log.critical('The url %s is not well formed' % target)
sys.exit(1)
log.info('starting wafw00f on %s' % target)
proxies = dict()
if options.proxy:
proxies = {
'http': options.proxy,
'https': options.proxy,
}
attacker = WAFW00F(target, debuglevel=options.verbose, path=pret.path,
followredirect=options.followredirect, extraheaders=extraheaders,
proxies=proxies, timeout=options.timeout)
if attacker.rq is None:
log.error('Site %s appears to be down' % pret.hostname)
continue
if options.test:
if options.test in attacker.wafdetections:
waf = attacker.wafdetections[options.test](attacker)
if waf:
print('[+] The site %s%s%s is behind %s%s%s WAF.' % (B, target, E, C, options.test, E))
else:
print('[-] WAF %s was not detected on %s' % (options.test, target))
else:
print('[-] WAF %s was not found in our list\r\nUse the --list option to see what is available' % options.test)
return
waf, xurl = attacker.identwaf(options.findall)
log.info('Identified WAF: %s' % waf)
if len(waf) > 0:
for i in waf:
results.append(buildResultRecord(target, i, xurl))
print('[+] The site %s%s%s is behind %s%s%s WAF.' % (B, target, E, C, (E+' and/or '+C).join(waf), E))
if (options.findall) or len(waf) == 0:
print('[+] Generic Detection results:')
generic_url = attacker.genericdetect()
if generic_url:
log.info('Generic Detection: %s' % attacker.knowledge['generic']['reason'])
print('[*] The site %s seems to be behind a WAF or some sort of security solution' % target)
print('[~] Reason: %s' % attacker.knowledge['generic']['reason'])
results.append(buildResultRecord(target, 'generic', generic_url))
else:
print('[-] No WAF detected by the generic detection')
results.append(buildResultRecord(target, None, None))
print('[~] Number of requests: %s' % attacker.requestnumber)
#print table of results
if len(results) > 0:
log.info('Found: %s matches.' % (len(results)))
if options.output:
if options.output == '-':
enableStdOut()
if options.format == 'json':
json.dump(results, sys.stdout, indent=2, sort_keys=True)
elif options.format == 'csv':
csvwriter = csv.writer(sys.stdout, delimiter=',', quotechar='"',
quoting=csv.QUOTE_MINIMAL)
count = 0
for result in results:
if count == 0:
header = result.keys()
csvwriter.writerow(header)
count += 1
csvwriter.writerow(result.values())
else:
print(os.linesep.join(getTextResults(results)))
elif options.output.endswith('.json'):
log.debug('Exporting data in json format to file: %s' % (options.output))
with open(options.output, 'w') as outfile:
json.dump(results, outfile, indent=2, sort_keys=True)
elif options.output.endswith('.csv'):
log.debug('Exporting data in csv format to file: %s' % (options.output))
with open(options.output, 'w') as outfile:
csvwriter = csv.writer(outfile, delimiter=',', quotechar='"',
quoting=csv.QUOTE_MINIMAL)
count = 0
for result in results:
if count == 0:
header = result.keys()
csvwriter.writerow(header)
count += 1
csvwriter.writerow(result.values())
else:
log.debug('Exporting data in text format to file: %s' % (options.output))
if options.format == 'json':
with open(options.output, 'w') as outfile:
json.dump(results, outfile, indent=2, sort_keys=True)
elif options.format == 'csv':
with open(options.output, 'w') as outfile:
csvwriter = csv.writer(outfile, delimiter=',', quotechar='"',
quoting=csv.QUOTE_MINIMAL)
count = 0
for result in results:
if count == 0:
header = result.keys()
csvwriter.writerow(header)
count += 1
csvwriter.writerow(result.values())
else:
with open(options.output, 'w') as outfile:
outfile.write(os.linesep.join(getTextResults(results)))
if __name__ == '__main__':
version_info = sys.version_info
if version_info.major < 3 or (version_info.major == 3 and version_info.minor < 6):
sys.stderr.write('Your version of python is way too old... please update to 3.6 or later\r\n')
main()
================================================
FILE: wafw00f/manager.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
import os
import importlib.util
def load_plugins():
here = os.path.abspath(os.path.dirname(__file__))
plugin_dir = os.path.join(here, 'plugins')
plugin_dict = {}
# Iterate over all files in the plugin directory
for filename in os.listdir(plugin_dir):
if filename.endswith('.py') and filename != '__init__.py':
plugin_name = filename[:-3] # Remove the .py extension
plugin_path = os.path.join(plugin_dir, filename)
# Load the plugin module
spec = importlib.util.spec_from_file_location(plugin_name, plugin_path)
plugin_module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(plugin_module)
# Store the loaded plugin in the dictionary
plugin_dict[plugin_name] = plugin_module
return plugin_dict
================================================
FILE: wafw00f/plugins/__init__.py
================================================
================================================
FILE: wafw00f/plugins/aesecure.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'aeSecure (aeSecure)'
def is_waf(self):
if self.matchHeader(('aeSecure-code', '.+?')):
return True
if self.matchContent(r'aesecure_denied\.png'):
return True
return False
================================================
FILE: wafw00f/plugins/airee.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'AireeCDN (Airee)'
def is_waf(self):
if self.matchHeader(('Server', 'Airee')):
return True
if self.matchHeader(('X-Cache', r'(\w+\.)?airee\.cloud')):
return True
if self.matchContent(r'airee\.cloud'):
return True
return False
================================================
FILE: wafw00f/plugins/airlock.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Airlock (Phion/Ergon)'
def is_waf(self):
# This method of detection is old (though most reliable), so we check it first
if self.matchCookie(r'^al[_-]?(sess|lb)='):
return True
if self.matchContent(r'server detected a syntax error in your request'):
return True
return False
================================================
FILE: wafw00f/plugins/alertlogic.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Alert Logic (Alert Logic)'
def is_waf(self):
if not self.matchContent(r'<(title|h\d{1})>requested url cannot be found'):
return False
if not self.matchContent(r'we are sorry.{0,10}?but the page you are looking for cannot be found'):
return False
if not self.matchContent(r'back to previous page'):
return False
if not self.matchContent(r'proceed to homepage'):
return False
if not self.matchContent(r'reference id'):
return False
return True
================================================
FILE: wafw00f/plugins/aliyundun.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'AliYunDun (Alibaba Cloud Computing)'
def is_waf(self):
if not self.matchContent(r'error(s)?\.aliyun(dun)?\.(com|net)?'):
return False
if not self.matchContent(r'alicdn\.com\/sd\-base\/static\/\d{1,2}\.\d{1,2}\.\d{1,2}\/image\/405\.png'):
return False
if not self.matchContent(r'Sorry, your request has been blocked as it may cause potential threats to the server\'s security.'):
return False
if not self.matchStatus(405):
return False
return True
================================================
FILE: wafw00f/plugins/anquanbao.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Anquanbao (Anquanbao)'
def is_waf(self):
if self.matchHeader(('X-Powered-By-Anquanbao', '.+?')):
return True
if self.matchContent(r'aqb_cc/error/'):
return True
return False
================================================
FILE: wafw00f/plugins/anubis.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Anubis (Techaro)'
def is_waf(self) -> bool:
if self.matchCookie(r'-anubis-auth='):
return True
if self.matchContent(r'/\.within\.website/x/cmd/anubis/'):
return True
if self.matchContent(r'<script id="anubis_version"'):
return True
if self.matchContent(r'<script id="anubis_challenge"'):
return True
if self.matchContent(r'Protected by.*Anubis.*From.*Techaro'):
return True
if self.matchContent(r'github\.com/TecharoHQ/anubis'):
return True
return False
================================================
FILE: wafw00f/plugins/anyu.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'AnYu (AnYu Technologies)'
def is_waf(self):
if self.matchContent(r'anyu.{0,10}?the green channel'):
return True
if self.matchContent(r'your access has been intercepted by anyu'):
return True
return False
================================================
FILE: wafw00f/plugins/applicationgateway.py
================================================
#!/usr/bin/env python
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Azure Application Gateway (Microsoft)'
def is_waf(self):
if self.matchContent(r'<center>Microsoft-Azure-Application-Gateway/v2</center>') and \
self.matchContent(r'<h1>403 Forbidden</h1>'):
return True
return False
================================================
FILE: wafw00f/plugins/approach.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Approach (Approach)'
def is_waf(self):
# This method of detection is old (though most reliable), so we check it first
if self.matchContent(r'approach.{0,10}?web application (firewall|filtering)'):
return True
if self.matchContent(r'approach.{0,10}?infrastructure team'):
return True
return False
================================================
FILE: wafw00f/plugins/armor.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Armor Defense (Armor)'
def is_waf(self):
if self.matchContent(r'blocked by website protection from armor'):
return True
if self.matchContent(r'please create an armor support ticket'):
return True
return False
================================================
FILE: wafw00f/plugins/arvancloud.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'ArvanCloud (ArvanCloud)'
def is_waf(self):
if self.matchHeader(('Server', 'ArvanCloud')):
return True
return False
================================================
FILE: wafw00f/plugins/aspa.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'ASPA Firewall (ASPA Engineering Co.)'
def is_waf(self):
if self.matchHeader(('Server', r'ASPA[\-_]?WAF')):
return True
if self.matchHeader(('ASPA-Cache-Status', r'.+?')):
return True
return False
================================================
FILE: wafw00f/plugins/aspnetgen.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'ASP.NET Generic (Microsoft)'
def is_waf(self):
if self.matchContent(r'iis (\d+.)+?detailed error'):
return True
if self.matchContent(r'potentially dangerous request querystring'):
return True
if self.matchContent(r'application error from being viewed remotely (for security reasons)?'):
return True
if self.matchContent(r'An application error occurred on the server'):
return True
return False
================================================
FILE: wafw00f/plugins/astra.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Astra (Czar Securities)'
def is_waf(self):
if self.matchCookie(r'^cz_astra_csrf_cookie'):
return True
if self.matchContent(r'astrawebsecurity\.freshdesk\.com'):
return True
if self.matchContent(r'www\.getastra\.com/assets/images'):
return True
return False
================================================
FILE: wafw00f/plugins/awswaf.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'AWS Elastic Load Balancer (Amazon)'
def is_waf(self):
if self.matchHeader(('X-AMZ-ID', '.+?')):
return True
if self.matchHeader(('X-AMZ-Request-ID', '.+?')):
return True
if self.matchCookie(r'^aws.?alb='):
return True
if self.matchHeader(('Server', r'aws.?elb'), attack=True):
return True
if self.matchHeader(('X-Blocked-By-WAF', 'Blocked_by_custom_response_for_AWSManagedRules.*')):
return True
return False
================================================
FILE: wafw00f/plugins/azion.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Azion Edge Firewall (Azion)'
def is_waf(self):
if self.matchHeader(('x-azion-edge-pop', r'.+?')):
return True
if self.matchHeader(('x-azion-request-id', r'.+?')):
return True
return False
================================================
FILE: wafw00f/plugins/baffinbay.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Baffin Bay (Mastercard)'
def is_waf(self):
if self.matchHeader(('server', 'baffin-bay-inlet')):
return True
return False
================================================
FILE: wafw00f/plugins/baidu.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Yunjiasu (Baidu Cloud Computing)'
def is_waf(self):
if self.matchHeader(('Server', r'yunjiasu.*')):
return True
if self.matchContent(r'href="/.well-known/yunjiasu-cgi/'):
return True
if self.matchContent(r"document.cookie='yjs_use_ob=0"):
return True
return False
================================================
FILE: wafw00f/plugins/barikode.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Barikode (Ethic Ninja)'
def is_waf(self):
if self.matchContent(r'<strong>barikode<.strong>'):
return True
return False
================================================
FILE: wafw00f/plugins/barracuda.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Barracuda (Barracuda Networks)'
def is_waf(self):
if self.matchCookie(r'^barra_counter_session='):
return True
if self.matchCookie(r'^BNI__BARRACUDA_LB_COOKIE='):
return True
if self.matchCookie(r'^BNI_persistence='):
return True
if self.matchCookie(r'^BN[IE]S_.*?='):
return True
if self.matchContent(r'Barracuda.Networks'):
return True
return False
================================================
FILE: wafw00f/plugins/bekchy.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Bekchy (Faydata Technologies Inc.)'
def is_waf(self):
# Both signatures are contained within response, so checking for any one of them
# Sometimes I observed that there is an XHR request being being made to submit the
# report data automatically upon page load. In those cases a missing https is causing
# false negatives.
if self.matchContent(r'Bekchy.{0,10}?Access Denied'):
return True
if self.matchContent(r'bekchy\.com/report'):
return True
return False
================================================
FILE: wafw00f/plugins/beluga.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Beluga CDN (Beluga)'
def is_waf(self):
if self.matchHeader(('Server', r'Beluga')):
return True
if self.matchCookie(r'^beluga_request_trail='):
return True
return False
================================================
FILE: wafw00f/plugins/binarysec.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'BinarySec (BinarySec)'
def is_waf(self):
if self.matchHeader(('Server', 'BinarySec')):
return True
if self.matchHeader(('x-binarysec-via', '.+')):
return True
if self.matchHeader(('x-binarysec-nocache', '.+')):
return True
return False
================================================
FILE: wafw00f/plugins/bitninja.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'BitNinja (BitNinja)'
def is_waf(self):
if self.matchContent(r'Security check by BitNinja'):
return True
if self.matchContent(r'Visitor anti-robot validation'):
return True
return False
================================================
FILE: wafw00f/plugins/blockdos.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'BlockDoS (BlockDoS)'
def is_waf(self):
if self.matchHeader(('Server', r'blockdos\.net')):
return True
return False
================================================
FILE: wafw00f/plugins/bluedon.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Bluedon (Bluedon IST)'
def is_waf(self):
# Found sample servers returning 'Server: BDWAF/2.0'
if self.matchHeader(('Server', r'BDWAF')):
return True
if self.matchContent(r'bluedon web application firewall'):
return True
return False
================================================
FILE: wafw00f/plugins/bulletproof.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'BulletProof Security Pro (AITpro Security)'
def is_waf(self):
if not self.matchContent(r'\+?bpsMessage'):
return False
if not self.matchContent(r'403 Forbidden Error Page'):
return False
if not self.matchContent(r'If you arrived here due to a search'):
return False
return True
================================================
FILE: wafw00f/plugins/cachefly.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'CacheFly CDN (CacheFly)'
def is_waf(self):
if self.matchHeader(('BestCDN', r'Cachefly')):
return True
if self.matchCookie(r'^cfly_req.*='):
return True
return False
================================================
FILE: wafw00f/plugins/cachewall.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'CacheWall (Varnish)'
def is_waf(self):
if self.matchHeader(('Server', 'Varnish')):
return True
if self.matchHeader(('X-Varnish', '.+')):
return True
if self.matchHeader(('X-Cachewall-Action', '.+?')):
return True
if self.matchHeader(('X-Cachewall-Reason', '.+?')):
return True
if self.matchContent(r'security by cachewall'):
return True
if self.matchContent(r'403 naughty.{0,10}?not nice!'):
return True
if self.matchContent(r'varnish cache server'):
return True
return False
================================================
FILE: wafw00f/plugins/cdnns.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'CdnNS Application Gateway (CdnNs/WdidcNet)'
def is_waf(self):
if self.matchContent(r'cdnnswaf application gateway'):
return True
return False
================================================
FILE: wafw00f/plugins/cerber.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'WP Cerber Security (Cerber Tech)'
def is_waf(self):
if not self.matchContent(r'your request looks suspicious or similar to automated'):
return False
if not self.matchContent(r'our server stopped processing your request'):
return False
if not self.matchContent(r'We.re sorry.{0,10}?you are not allowed to proceed'):
return False
if not self.matchContent(r'requests from spam posting software'):
return False
if not self.matchContent(r'<title>403 Access Forbidden'):
return False
return True
================================================
FILE: wafw00f/plugins/chinacache.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'ChinaCache Load Balancer (ChinaCache)'
def is_waf(self):
if self.matchHeader(('Powered-By-ChinaCache', '.+')):
return True
return False
================================================
FILE: wafw00f/plugins/chuangyu.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Chuang Yu Shield (Yunaq)'
def is_waf(self):
if self.matchContent(r'www\.365cyd\.com'):
return True
if self.matchContent(r'help\.365cyd\.com/cyd\-error\-help.html\?code=403'):
return True
return False
================================================
FILE: wafw00f/plugins/ciscoacexml.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'ACE XML Gateway (Cisco)'
def is_waf(self):
if self.matchHeader(('Server', 'ACE XML Gateway')):
return True
return False
================================================
FILE: wafw00f/plugins/cloudbric.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Cloudbric (Penta Security)'
def is_waf(self):
if self.matchContent(r'<title>Cloudbric.{0,5}?ERROR!'):
return True
if self.matchContent(r'Your request was blocked by Cloudbric'):
return True
if self.matchContent(r'please contact Cloudbric Support'):
return True
if self.matchContent(r'cloudbric\.zendesk\.com'):
return True
if self.matchContent(r'Cloudbric Help Center'):
return True
if self.matchContent(r'malformed request syntax.{0,4}?invalid request message framing.{0,4}?or deceptive request routing'):
return True
return False
================================================
FILE: wafw00f/plugins/cloudflare.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Cloudflare (Cloudflare Inc.)'
def is_waf(self):
if self.matchHeader(('server', 'cloudflare')):
return True
if self.matchHeader(('server', r'cloudflare[-_]nginx')):
return True
if self.matchHeader(('cf-ray', r'.+?')):
return True
if self.matchCookie('__cfduid'):
return True
return False
================================================
FILE: wafw00f/plugins/cloudfloordns.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Cloudfloor (Cloudfloor DNS)'
def is_waf(self):
if self.matchHeader(('Server', r'CloudfloorDNS(.WAF)?')):
return True
if self.matchContent(r'<(title|h\d{1})>CloudfloorDNS.{0,6}?Web Application Firewall Error'):
return True
if self.matchContent(r'www\.cloudfloordns\.com/contact'):
return True
return False
================================================
FILE: wafw00f/plugins/cloudfront.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Cloudfront (Amazon)'
def is_waf(self):
# This is standard detection schema, checking the server header
if self.matchHeader(('Server', 'Cloudfront')):
return True
# Found samples returning 'Via: 1.1 58bfg7h6fg76h8fg7jhdf2.cloudfront.net (CloudFront)'
if self.matchHeader(('Via', r'([0-9\.]+?)? \w+?\.cloudfront\.net \(Cloudfront\)')):
return True
# The request token is sent along with this header, eg:
# X-Amz-Cf-Id: sX5QSkbAzSwd-xx3RbJmxYHL3iVNNyXa1UIebDNCshQbHxCjVcWDww==
if self.matchHeader(('X-Amz-Cf-Id', '.+?'), attack=True):
return True
# This is another reliable fingerprint found on headers
if self.matchHeader(('X-Cache', 'Error from Cloudfront'), attack=True):
return True
# These fingerprints are found on the blockpage itself
if self.matchContent(r'Generated by cloudfront \(CloudFront\)'):
return True
return False
================================================
FILE: wafw00f/plugins/cloudprotector.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Cloud Protector (Rohde & Schwarz CyberSecurity)'
def is_waf(self):
if self.matchContent(r'Cloud Protector.*?by Rohde.{3,8}?Schwarz Cybersecurity'):
return True
if self.matchContent(r"<a href='https?:\/\/(?:www\.)?cloudprotector\.com\/'>R.{1,6}?S.Cloud Protector"):
return True
return False
================================================
FILE: wafw00f/plugins/comodo.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Comodo cWatch (Comodo CyberSecurity)'
def is_waf(self):
if self.matchHeader(('Server', r'Protected by COMODO WAF(.+)?')):
return True
return False
================================================
FILE: wafw00f/plugins/crawlprotect.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'CrawlProtect (Jean-Denis Brun)'
def is_waf(self):
if self.matchCookie(r'^crawlprotecttag='):
return True
if self.matchContent(r'<title>crawlprotect'):
return True
if self.matchContent(r'this site is protected by crawlprotect'):
return True
return False
================================================
FILE: wafw00f/plugins/ddosguard.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'DDoS-GUARD (DDOS-GUARD CORP.)'
def is_waf(self):
if self.matchCookie(r'^__ddg1.*?='):
return True
if self.matchCookie(r'^__ddg2.*?='):
return True
if self.matchCookie(r'^__ddgid.*?='):
return True
if self.matchCookie(r'^__ddgmark.*?='):
return True
if self.matchHeader(('Server', 'ddos-guard')):
return True
return False
================================================
FILE: wafw00f/plugins/denyall.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'DenyALL (Rohde & Schwarz CyberSecurity)'
def is_waf(self):
if not self.matchStatus(200):
return False
if not self.matchReason('Condition Intercepted'):
return False
return True
================================================
FILE: wafw00f/plugins/distil.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Distil (Distil Networks)'
def is_waf(self):
if self.matchContent(r'cdn\.distilnetworks\.com/images/anomaly\.detected\.png'):
return True
if self.matchContent(r'distilCaptchaForm'):
return True
if self.matchContent(r'distilCallbackGuard'):
return True
return False
================================================
FILE: wafw00f/plugins/dosarrest.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'DOSarrest (DOSarrest Internet Security)'
def is_waf(self):
if self.matchHeader(('X-DIS-Request-ID', '.+')):
return True
# Found samples of DOSArrest returning 'Server: DoSArrest/3.5'
if self.matchHeader(('Server', r'DOSarrest(.*)?')):
return True
return False
================================================
FILE: wafw00f/plugins/dotdefender.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'DotDefender (Applicure Technologies)'
def is_waf(self):
if self.matchHeader(('X-dotDefender-denied', r'.+?'), attack=True):
return True
if self.matchContent(r'dotdefender blocked your request'):
return True
if self.matchContent(r'Applicure is the leading provider of web application security'):
return True
return False
================================================
FILE: wafw00f/plugins/dynamicweb.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'DynamicWeb Injection Check (DynamicWeb)'
def is_waf(self):
if self.matchHeader(('X-403-Status-By', r'dw.inj.check'), attack=True):
return True
if self.matchContent(r'by dynamic check(.{0,10}?module)?'):
return True
return False
================================================
FILE: wafw00f/plugins/edgecast.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Edgecast (Verizon Digital Media)'
def is_waf(self):
if self.matchHeader(('Server', r'^ECD(.+)?')):
return True
if self.matchHeader(('Server', r'^ECS(.*)?')):
return True
return False
================================================
FILE: wafw00f/plugins/eisoo.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Eisoo Cloud Firewall (Eisoo)'
def is_waf(self):
if self.matchHeader(('Server', r'EisooWAF(\-AZURE)?/?')):
return True
if self.matchContent(r'<link.{0,10}?href=\"/eisoo\-firewall\-block\.css'):
return True
if self.matchContent(r'www\.eisoo\.com'):
return True
if self.matchContent(r'© \d{4} Eisoo Inc'):
return True
return False
================================================
FILE: wafw00f/plugins/envoy.py
================================================
#!/usr/bin/env python
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Envoy (EnvoyProxy)'
def is_waf(self):
if self.matchHeader(('server', 'envoy')):
return True
if self.matchHeader(('x-envoy-upstream-service-time', '.+')):
return True
if self.matchHeader(('x-envoy-downstream-service-cluster', '.+')):
return True
if self.matchHeader(('x-envoy-downstream-service-node', '.+')):
return True
if self.matchHeader(('x-envoy-external-address', '.+')):
return True
if self.matchHeader(('x-envoy-force-trace', '.+')):
return True
if self.matchHeader(('x-envoy-internal', '.+')):
return True
if self.matchHeader(('x-envoy-original-dst-host', '.+')):
return True
if self.matchHeader(('x-envoy-original-path', '.+')):
return True
if self.matchHeader(('x-envoy-local-overloaded', '.+')):
return True
return False
================================================
FILE: wafw00f/plugins/expressionengine.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Expression Engine (EllisLab)'
def is_waf(self):
# I have seen some sites use a tracking header and sets a cookie upon authentication
# 'Set-Cookie: _exp_tracking=rufyhweiuitefgcxyniercyft5-6dctuxeygfr'
if self.matchCookie(r'^exp_track.+?='):
return True
# There are traces found where cookie is returning values like:
# Set-Cookie: exp_last_query=834y8d73y94d8g983u4shn8u4shr3uh3
# Set-Cookie: exp_last_id=b342b432b1a876r8
if self.matchCookie(r'^exp_last_.+?=', attack=True):
return True
# In-page fingerprints vary a lot in different sites. Hence these are not quite reliable.
if self.matchContent(r'invalid get data'):
return True
return False
================================================
FILE: wafw00f/plugins/f5bigipapm.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'BIG-IP AP Manager (F5 Networks)'
def is_waf(self):
if check_schema_01(self):
return True
if check_schema_02(self):
return True
if check_schema_03(self):
return True
return False
def check_schema_01(self):
if not self.matchCookie('^LastMRH_Session'):
return False
if not self.matchCookie('^MRHSession'):
return False
return True
def check_schema_02(self):
if not self.matchCookie('^MRHSession'):
return False
if not self.matchHeader(('Server', r'Big([-_])?IP'), attack=True):
return False
return True
def check_schema_03(self):
if self.matchCookie('^F5_fullWT'):
return True
if self.matchCookie('^F5_fullWT'):
return True
if self.matchCookie('^F5_HT_shrinked'):
return True
return False
================================================
FILE: wafw00f/plugins/f5bigipasm.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'BIG-IP AppSec Manager (F5 Networks)'
def is_waf(self):
if check_schema_01(self):
return True
# ASM ≥ 11.4.0 → eight hex digits after “TS” - https://my.f5.com/manage/s/article/K6850
if self.matchCookie(r'TS[a-fA-F0-9]{8}=.+'):
return True
# ASM 10.0.0 – 11.3.0 → six hex digits after “TS” - https://my.f5.com/manage/s/article/K6850
if self.matchCookie(r'TS[a-fA-F0-9]{6}=.+'):
return True
return False
def check_schema_01(self):
if not self.matchContent('the requested url was rejected'):
return False
if not self.matchContent('please consult with your administrator'):
return False
return True
================================================
FILE: wafw00f/plugins/f5bigipltm.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'BIG-IP Local Traffic Manager (F5 Networks)'
def is_waf(self):
if self.matchCookie('^bigipserver'):
return True
if self.matchHeader(('X-Cnection', 'close'), attack=True):
return True
return False
================================================
FILE: wafw00f/plugins/f5firepass.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'FirePass (F5 Networks)'
def is_waf(self):
if check_schema_01(self):
return True
if check_schema_02(self):
return True
return False
def check_schema_01(self):
if not self.matchCookie('^VHOST'):
return False
if not self.matchHeader(('Location', r'\/my\.logon\.php3')):
return False
return True
def check_schema_02(self):
if not self.matchCookie(r'^F5_fire.+?'):
return False
if not self.matchCookie('^F5_passid_shrinked'):
return False
return True
================================================
FILE: wafw00f/plugins/f5trafficshield.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Trafficshield (F5 Networks)'
def is_waf(self):
if self.matchCookie('^ASINFO='):
return True
if self.matchHeader(('Server', 'F5-TrafficShield')):
return True
return False
================================================
FILE: wafw00f/plugins/fastly.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Fastly (Fastly CDN)'
def is_waf(self):
if self.matchHeader(('X-Fastly-Request-ID', r'\w+')):
return True
if self.matchHeader(('X-Served-By', r'^cache-[a-z]{3}\d+-[A-Z]{3}')):
return True
return False
================================================
FILE: wafw00f/plugins/fortigate.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'FortiGate (Fortinet)'
def is_waf(self):
if check_schema_01(self):
return True
if check_schema_02(self):
return True
return False
def check_schema_01(self):
if not self.matchContent('//globalurl.fortinet.net'):
return False
if not self.matchContent('FortiGate Application Control'):
return False
return True
def check_schema_02(self):
if not self.matchContent('Web Application Firewall'):
return False
if not self.matchContent('Event ID'):
return False
if not self.matchContent('//globalurl.fortinet.net'):
return False
return True
================================================
FILE: wafw00f/plugins/fortiguard.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'FortiGuard (Fortinet)'
def is_waf(self):
if check_schema(self):
return True
return False
def check_schema(self):
# Use OR logic with strong signatures containing explicit FortiGuard/Fortinet branding
# Generic "Web Filter" strings are avoided to prevent false positives
if self.matchContent('FortiGuard Intrusion Prevention'):
return True
if self.matchContent('//globalurl.fortinet.net'):
return True
return False
================================================
FILE: wafw00f/plugins/fortiweb.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'FortiWeb (Fortinet)'
def is_waf(self):
if check_schema_01(self):
return True
if check_schema_02(self):
return True
return False
def check_schema_01(self):
if self.matchCookie(r'^FORTIWAFSID='):
return True
if self.matchContent('.fgd_icon'):
return True
return False
def check_schema_02(self):
if not self.matchContent('fgd_icon'):
return False
if not self.matchContent('web.page.blocked'):
return False
if not self.matchContent('url'):
return False
if not self.matchContent('attack.id'):
return False
if not self.matchContent('message.id'):
return False
if not self.matchContent('client.ip'):
return False
return True
================================================
FILE: wafw00f/plugins/frontdoor.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Azure Front Door (Microsoft)'
def is_waf(self):
if self.matchHeader(('X-Azure-Ref', '.+?')):
return True
return False
================================================
FILE: wafw00f/plugins/gcparmor.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Google Cloud App Armor (Google Cloud)'
def is_waf(self):
if self.matchHeader(('Via', '1.1 google')):
return True
return False
================================================
FILE: wafw00f/plugins/godaddy.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'GoDaddy Website Protection (GoDaddy)'
def is_waf(self):
if self.matchContent(r'GoDaddy (security|website firewall)'):
return True
return False
================================================
FILE: wafw00f/plugins/greywizard.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Greywizard (Grey Wizard)'
def is_waf(self):
if self.matchHeader(('Server', 'greywizard')):
return True
if self.matchContent(r'<(title|h\d{1})>Grey Wizard'):
return True
if self.matchContent(r'contact the website owner or Grey Wizard'):
return True
if self.matchContent(r'We.ve detected attempted attack or non standard traffic from your ip address'):
return True
return False
================================================
FILE: wafw00f/plugins/huaweicloud.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Huawei Cloud Firewall (Huawei)'
def is_waf(self):
if self.matchCookie(r'^HWWAFSESID='):
return True
if self.matchHeader(('Server', r'HuaweiCloudWAF')):
return True
if self.matchContent(r'hwclouds\.com'):
return True
if self.matchContent(r'hws_security@'):
return True
return False
================================================
FILE: wafw00f/plugins/hyperguard.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'HyperGuard (Art of Defense)'
def is_waf(self):
if self.matchCookie('^WODSESSION='):
return True
return False
================================================
FILE: wafw00f/plugins/ibmdatapower.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'DataPower (IBM)'
def is_waf(self):
if self.matchHeader(('X-Backside-Transport', r'(OK|FAIL)')):
return True
return False
================================================
FILE: wafw00f/plugins/imunify360.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Imunify360 (CloudLinux)'
def is_waf(self):
if self.matchHeader(('Server', r'imunify360.{0,10}?')):
return True
if self.matchContent(r'protected.by.{0,10}?imunify360'):
return True
if self.matchContent(r'powered.by.{0,10}?imunify360'):
return True
if self.matchContent(r'imunify360.preloader'):
return True
return False
================================================
FILE: wafw00f/plugins/incapsula.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Incapsula (Imperva Inc.)'
def is_waf(self):
if self.matchCookie(r'^incap_ses.*?='):
return True
if self.matchCookie(r'^visid_incap.*?='):
return True
if self.matchContent(r'incapsula incident id'):
return True
if self.matchContent(r'powered by incapsula'):
return True
if self.matchContent(r'/_Incapsula_Resource'):
return True
return False
================================================
FILE: wafw00f/plugins/indusguard.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'IndusGuard (Indusface)'
def is_waf(self):
if self.matchHeader(('Server', r'IF_WAF')):
return True
if self.matchContent(r'This website is secured against online attacks. Your request was blocked'):
return True
return False
================================================
FILE: wafw00f/plugins/instartdx.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Instart DX (Instart Logic)'
def is_waf(self):
if check_schema_01(self):
return True
if check_schema_02(self):
return True
return False
def check_schema_01(self):
if self.matchHeader(('X-Instart-Request-ID', '.+')):
return True
if self.matchHeader(('X-Instart-Cache', '.+')):
return True
if self.matchHeader(('X-Instart-WL', '.+')):
return True
return False
def check_schema_02(self):
if not self.matchContent(r'the requested url was rejected'):
return False
if not self.matchContent(r'please consult with your administrator'):
return False
if not self.matchContent(r'your support id is'):
return False
return True
================================================
FILE: wafw00f/plugins/isaserver.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'ISA Server (Microsoft)'
def is_waf(self):
if self.matchContent(r'The.{0,10}?(isa.)?server.{0,10}?denied the specified uniform resource locator \(url\)'):
return True
return False
================================================
FILE: wafw00f/plugins/janusec.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Janusec Application Gateway (Janusec)'
def is_waf(self):
if self.matchContent(r'janusec application gateway'):
return True
return False
================================================
FILE: wafw00f/plugins/jiasule.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Jiasule (Jiasule)'
def is_waf(self):
if self.matchHeader(('Server', r'jiasule\-waf')):
return True
if self.matchCookie(r'^jsl_tracking(.+)?='):
return True
if self.matchCookie(r'__jsluid='):
return True
if self.matchContent(r'notice\-jiasule'):
return True
if self.matchContent(r'static\.jiasule\.com'):
return True
return False
================================================
FILE: wafw00f/plugins/kemp.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Kemp LoadMaster (Progress Software)'
def is_waf(self):
if self.matchHeader(('X-ServedBy', 'KEMP-LM')) and \
self.matchStatus(403) and \
self.matchContent(r'<title>403 Forbidden</title>'):
return True
return False
================================================
FILE: wafw00f/plugins/keycdn.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'KeyCDN (KeyCDN)'
def is_waf(self):
if self.matchHeader(('Server', 'KeyCDN')):
return True
return False
================================================
FILE: wafw00f/plugins/knownsec.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'KS-WAF (KnownSec)'
def is_waf(self):
if self.matchContent(r'/ks[-_]waf[-_]error\.png'):
return True
return False
================================================
FILE: wafw00f/plugins/kona.py
================================================
#!/usr/bin/env python3
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Kona SiteDefender (Akamai)'
def is_waf(self):
if self.matchHeader(('Server', 'AkamaiGHost')):
return True
if self.matchHeader(('Server', 'AkamaiGHost'), attack=True) :
return True
return False
================================================
FILE: wafw00f/plugins/limelight.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'LimeLight CDN (LimeLight)'
def is_waf(self):
if self.matchCookie(r'^limelight'):
return True
if self.matchCookie(r'^l[mg]_sessid='):
return True
return False
================================================
FILE: wafw00f/plugins/link11.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Link11 WAAP (Link11)'
def is_waf(self):
if check_schema_01(self):
return True
return False
def check_schema_01(self):
if self.matchHeader(('server', 'rhino-core-shield')):
return True
return False
================================================
FILE: wafw00f/plugins/litespeed.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'LiteSpeed (LiteSpeed Technologies)'
def is_waf(self):
if check_schema_01(self):
return True
if check_schema_02(self):
return True
return False
def check_schema_01(self):
if not self.matchHeader(('Server', 'LiteSpeed')):
return False
if not self.matchStatus(403):
return False
return True
def check_schema_02(self):
if self.matchContent(r'Proudly powered by litespeed web server'):
return True
if self.matchContent(r'www\.litespeedtech\.com/error\-page'):
return True
return False
================================================
FILE: wafw00f/plugins/malcare.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Malcare (Inactiv)'
def is_waf(self):
if self.matchContent(r'firewall.{0,15}?powered.by.{0,15}?malcare.{0,15}?pro'):
return True
if self.matchContent('blocked because of malicious activities'):
return True
return False
================================================
FILE: wafw00f/plugins/maxcdn.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'MaxCDN (MaxCDN)'
def is_waf(self):
if self.matchHeader(('X-CDN', r'maxcdn')):
return True
return False
================================================
FILE: wafw00f/plugins/missioncontrol.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Mission Control Shield (Mission Control)'
def is_waf(self):
if self.matchHeader(('Server', 'Mission Control Application Shield')):
return True
return False
================================================
FILE: wafw00f/plugins/modsecurity.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'ModSecurity (SpiderLabs)'
def is_waf(self):
if check_schema_01(self):
return True
if check_schema_02(self):
return True
if check_schema_03(self):
return True
return False
def check_schema_01(self):
if self.matchHeader(('Server', r'(mod_security|Mod_Security|NOYB)')):
return True
if self.matchContent(r'This error was generated by Mod.?Security'):
return True
if self.matchContent(r'rules of the mod.security.module'):
return True
if self.matchContent(r'mod.security.rules triggered'):
return True
if self.matchContent(r'Protected by Mod.?Security'):
return True
if self.matchContent(r'/modsecurity[\-_]errorpage/'):
return True
if self.matchContent(r'modsecurity iis'):
return True
return False
def check_schema_02(self):
if not self.matchReason('ModSecurity Action'):
return False
if not self.matchStatus(403):
return False
return True
def check_schema_03(self):
if not self.matchReason('ModSecurity Action'):
return False
if not self.matchStatus(406):
return False
return True
================================================
FILE: wafw00f/plugins/naxsi.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'NAXSI (NBS Systems)'
def is_waf(self):
if self.matchHeader(('X-Data-Origin', r'^naxsi(.+)?')):
return True
if self.matchHeader(('Server', r'naxsi(.+)?')):
return True
if self.matchContent(r'blocked by naxsi'):
return True
if self.matchContent(r'naxsi blocked information'):
return True
return False
================================================
FILE: wafw00f/plugins/nemesida.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Nemesida (PentestIt)'
def is_waf(self):
if self.matchContent(r'@?nemesida(\-security)?\.com'):
return True
if self.matchContent(r'Suspicious activity detected.{0,10}?Access to the site is blocked'):
return True
if self.matchContent(r'nwaf@'):
return True
if self.matchStatus(222):
return True
return False
================================================
FILE: wafw00f/plugins/netcontinuum.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'NetContinuum (Barracuda Networks)'
def is_waf(self):
if self.matchCookie(r'^NCI__SessionId='):
return True
return False
================================================
FILE: wafw00f/plugins/netscaler.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'NetScaler AppFirewall (Citrix Systems)'
def is_waf(self):
# This header can be obtained without attack mode
if self.matchHeader(('Via', r'NS\-CACHE')):
return True
# Cookies are set only when someone is authenticated.
# Not much reliable since wafw00f isn't authenticating.
if self.matchCookie(r'^(ns_af=|citrix_ns_id|NSC_)'):
return True
if self.matchContent(r'(NS Transaction|AppFW Session) id'):
return True
if self.matchContent(r'Violation Category.{0,5}?APPFW_'):
return True
if self.matchContent(r'Citrix\|NetScaler'):
return True
# Reliable but not all servers return this header
if self.matchHeader(('Cneonction', r'^(keep alive|close)'), attack=True):
return True
if self.matchHeader(('nnCoection', r'^(keep alive|close)'), attack=True):
return True
return False
================================================
FILE: wafw00f/plugins/nevisproxy.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'NevisProxy (AdNovum)'
def is_waf(self):
if self.matchCookie(r'^Navajo'):
return True
if self.matchCookie(r'^NP_ID'):
return True
return False
================================================
FILE: wafw00f/plugins/newdefend.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Newdefend (NewDefend)'
def is_waf(self):
# This header can be obtained without attack mode
# Most reliable fingerprint
if self.matchHeader(('Server', 'Newdefend')):
return True
# Reliable ones within blockpage
if self.matchContent(r'www\.newdefend\.com/feedback'):
return True
if self.matchContent(r'/nd\-block/'):
return True
return False
================================================
FILE: wafw00f/plugins/nexusguard.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'NexusGuard Firewall (NexusGuard)'
def is_waf(self):
if self.matchContent(r'Powered by Nexusguard'):
return True
if self.matchContent(r'nexusguard\.com/wafpage/.+#\d{3};'):
return True
return False
================================================
FILE: wafw00f/plugins/ninja.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'NinjaFirewall (NinTechNet)'
def is_waf(self):
if not self.matchContent(r'<title>NinjaFirewall.{0,10}?\d{3}.forbidden'):
return False
if not self.matchContent(r'For security reasons?.{0,10}?it was blocked and logged'):
return False
return True
================================================
FILE: wafw00f/plugins/nsfocus.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'NSFocus (NSFocus Global Inc.)'
def is_waf(self):
if self.matchHeader(('Server', 'NSFocus')):
return True
return False
================================================
FILE: wafw00f/plugins/nullddos.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'NullDDoS Protection (NullDDoS)'
def is_waf(self):
if self.matchHeader(('Server', r'NullDDoS(.System)?')):
return True
return False
================================================
FILE: wafw00f/plugins/onmessage.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'OnMessage Shield (BlackBaud)'
def is_waf(self):
if self.matchHeader(('X-Engine', 'onMessage Shield')):
return True
if self.matchContent(r'Blackbaud K\-12 conducts routine maintenance'):
return True
if self.matchContent(r'onMessage SHEILD'):
return True
if self.matchContent(r'maintenance\.blackbaud\.com'):
return True
if self.matchContent(r'status\.blackbaud\.com'):
return True
return False
================================================
FILE: wafw00f/plugins/openresty.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Open-Resty Lua Nginx (FLOSS)'
def is_waf(self):
if check_schema_01(self):
return True
if check_schema_02(self):
return True
return False
def check_schema_01(self):
if not self.matchHeader(('Server', r'^openresty/[0-9\.]+?')):
return False
if not self.matchStatus(403):
return False
return True
def check_schema_02(self):
if not self.matchContent(r'openresty/[0-9\.]+?'):
return False
if not self.matchStatus(406):
return False
return True
================================================
FILE: wafw00f/plugins/oraclecloud.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Oracle Cloud (Oracle)'
def is_waf(self):
if self.matchContent(r'<title>fw_error_www'):
return True
if self.matchContent(r'src=\"/oralogo_small\.gif\"'):
return True
if self.matchContent(r'www\.oracleimg\.com/us/assets/metrics/ora_ocom\.js'):
return True
return False
================================================
FILE: wafw00f/plugins/paloalto.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Palo Alto Next Gen Firewall (Palo Alto Networks)'
def is_waf(self):
if self.matchContent(r'Download of virus.spyware blocked'):
return True
if self.matchContent(r'Palo Alto Next Generation Security Platform'):
return True
return False
================================================
FILE: wafw00f/plugins/panyun360.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = '360PanYun (360 Technologies)'
def is_waf(self):
if self.matchHeader(('Server', r'panyun')):
return True
if self.matchHeader(('X-Panyun-Request-ID', r'.+?'), attack=True):
return True
if self.matchHeader(('X-Panyun-Error-Reason', r'.+?'), attack=True):
return True
if self.matchHeader(('X-Panyun-Error-Step', r'.+?'), attack=True):
return True
return False
================================================
FILE: wafw00f/plugins/pentawaf.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'PentaWAF (Global Network Services)'
def is_waf(self):
if self.matchHeader(('Server', r'PentaWaf(/[0-9\.]+)?')):
return True
if self.matchContent(r'Penta.?Waf/[0-9\.]+?.server'):
return True
return False
================================================
FILE: wafw00f/plugins/perimeterx.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'PerimeterX (PerimeterX)'
def is_waf(self):
if self.matchContent(r'www\.perimeterx\.(com|net)/whywasiblocked'):
return True
if self.matchContent(r'client\.perimeterx\.(net|com)'):
return True
if self.matchContent(r'denied because we believe you are using automation tools'):
return True
return False
================================================
FILE: wafw00f/plugins/pksec.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'pkSecurity IDS (pkSec)'
def is_waf(self):
if check_schema_01(self):
return True
if check_schema_02(self):
return True
return False
def check_schema_01(self):
if self.matchContent(r'pk.?Security.?Module'):
return True
if self.matchContent(r'Security.Alert'):
return True
return False
def check_schema_02(self):
if not self.matchContent(r'As this could be a potential hack attack'):
return False
if not self.matchContent(r'A safety critical (call|request) was (detected|discovered) and blocked'):
return False
if not self.matchContent(r'maximum number of reloads per minute and prevented access'):
return False
return True
================================================
FILE: wafw00f/plugins/powercdn.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'PowerCDN (PowerCDN)'
def is_waf(self):
if self.matchHeader(('Via', r'(.*)?powercdn.com(.*)?')):
return True
if self.matchHeader(('X-Cache', r'(.*)?powercdn.com(.*)?')):
return True
if self.matchHeader(('X-CDN', r'PowerCDN')):
return True
return False
================================================
FILE: wafw00f/plugins/profense.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Profense (ArmorLogic)'
def is_waf(self):
if self.matchHeader(('Server', 'Profense')):
return True
if self.matchCookie(r'^PLBSID='):
return True
return False
================================================
FILE: wafw00f/plugins/ptaf.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'PT Application Firewall (Positive Technologies)'
def is_waf(self):
if not self.matchContent(r'<h1.{0,10}?Forbidden'):
return False
if not self.matchContent(r'<pre>Request.ID:.{0,10}?\d{4}\-(\d{2})+.{0,35}?pre>'):
return False
return True
================================================
FILE: wafw00f/plugins/puhui.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Puhui (Puhui)'
def is_waf(self):
if self.matchHeader(('Server', r'Puhui[\-_]?WAF')):
return True
return False
================================================
FILE: wafw00f/plugins/qcloud.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Qcloud (Tencent Cloud)'
def is_waf(self):
if not self.matchContent(r'腾讯云Web应用防火墙'):
return False
if not self.matchStatus(403):
return False
return True
================================================
FILE: wafw00f/plugins/qiniu.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Qiniu (Qiniu CDN)'
def is_waf(self):
if self.matchHeader(('X-Qiniu-CDN', r'\d+?')):
return True
return False
================================================
FILE: wafw00f/plugins/qrator.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Qrator (Qrator)'
def is_waf(self):
if self.matchHeader(('Server', r'QRATOR')):
return True
return False
================================================
FILE: wafw00f/plugins/radware.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'AppWall (Radware)'
def is_waf(self):
if check_schema_01(self):
return True
if check_schema_02(self):
return True
return False
def check_schema_01(self):
if self.matchContent(r'CloudWebSec\.radware\.com'):
return True
if self.matchHeader(('X-SL-CompState', '.+')):
return True
return False
def check_schema_02(self):
if not self.matchContent(r'because we have detected unauthorized activity'):
return False
if not self.matchContent(r'<title>Unauthorized Request Blocked'):
return False
if not self.matchContent(r'if you believe that there has been some mistake'):
return False
if not self.matchContent(r'\?Subject=Security Page.{0,10}?Case Number'):
return False
return True
================================================
FILE: wafw00f/plugins/reblaze.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Reblaze (Reblaze)'
def is_waf(self):
if check_schema_01(self):
return True
if check_schema_02(self):
return True
return False
def check_schema_01(self):
if self.matchCookie(r'^rbzid'):
return True
if self.matchHeader(('Server', 'Reblaze Secure Web Gateway')):
return True
return False
def check_schema_02(self):
if not self.matchContent(r'current session has been terminated'):
return False
if not self.matchContent(r'do not hesitate to contact us'):
return False
if not self.matchContent(r'access denied \(\d{3}\)'):
return False
return True
================================================
FILE: wafw00f/plugins/reflected.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Reflected Networks (Reflected Networks)'
def is_waf(self):
if self.matchContent('<img class="logo loader" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAbgAAABHCAIAAAD6G8WcAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw') and \
self.matchStatus(403) and \
self.matchContent(r'content="Request is denied"') and \
self.matchContent(r'<title>Forbidden</title>'):
return True
return False
================================================
FILE: wafw00f/plugins/rsfirewall.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'RSFirewall (RSJoomla!)'
def is_waf(self):
if self.matchContent(r'com_rsfirewall_(\d{3}_forbidden|event)?'):
return True
return False
================================================
FILE: wafw00f/plugins/rvmode.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'RequestValidationMode (Microsoft)'
def is_waf(self):
if self.matchContent(r'Request Validation has detected a potentially dangerous client input'):
return True
if self.matchContent(r'ASP\.NET has detected data in the request'):
return True
if self.matchContent(r'HttpRequestValidationException'):
return True
return False
================================================
FILE: wafw00f/plugins/sabre.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Sabre Firewall (Sabre)'
def is_waf(self):
if self.matchContent(r'dxsupport\.sabre\.com'):
return True
if check_schema_01(self):
return True
return False
def check_schema_01(self):
if not self.matchContent(r'<title>Application Firewall Error'):
return False
if not self.matchContent(r'add some important details to the email for us to investigate'):
return False
return True
================================================
FILE: wafw00f/plugins/safe3.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Safe3 Web Firewall (Safe3)'
def is_waf(self):
if self.matchHeader(('Server', 'Safe3 Web Firewall')):
return True
if self.matchHeader(('X-Powered-By', r'Safe3WAF/[\.0-9]+?')):
return True
if self.matchContent(r'Safe3waf/[0-9\.]+?'):
return True
return False
================================================
FILE: wafw00f/plugins/safedog.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Safedog (SafeDog)'
def is_waf(self):
if self.matchCookie(r'^safedog\-flow\-item='):
return True
if self.matchHeader(('Server', 'Safedog')):
return True
if self.matchContent(r'safedogsite/broswer_logo\.jpg'):
return True
if self.matchContent(r'404\.safedog\.cn/sitedog_stat.html'):
return True
if self.matchContent(r'404\.safedog\.cn/images/safedogsite/head\.png'):
return True
return False
================================================
FILE: wafw00f/plugins/safeline.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Safeline (Chaitin Tech.)'
def is_waf(self):
if self.matchContent(r'safeline|<!\-\-\sevent id:'):
return True
return False
================================================
FILE: wafw00f/plugins/scutum.py
================================================
#!/usr/bin/env python3
"""
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
"""
NAME = "Scutum (Secure Sky Technology Inc.)"
def is_waf(self):
if self.matchHeader(("Server", "Scutum")):
return True
return False
================================================
FILE: wafw00f/plugins/secking.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'SecKing (SecKing)'
def is_waf(self):
if self.matchHeader(('Server', r'secking(.?waf)?')):
return True
return False
================================================
FILE: wafw00f/plugins/secupress.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'SecuPress WP Security (SecuPress)'
def is_waf(self):
if self.matchContent(r'<(title|h\d{1})>SecuPress'):
return True
return False
================================================
FILE: wafw00f/plugins/secureentry.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Secure Entry (United Security Providers)'
def is_waf(self):
if self.matchHeader(('Server', 'Secure Entry Server')):
return True
return False
================================================
FILE: wafw00f/plugins/secureiis.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'eEye SecureIIS (BeyondTrust)'
def is_waf(self):
if self.matchContent(r'SecureIIS is an internet security application'):
return True
if self.matchContent(r'Download SecureIIS Personal Edition'):
return True
if self.matchContent(r'https?://www\.eeye\.com/Secure\-?IIS'):
return True
return False
================================================
FILE: wafw00f/plugins/securesphere.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'SecureSphere (Imperva Inc.)'
def is_waf(self):
if not self.matchContent(r'<(title|h2)>Error'):
return False
if not self.matchContent(r'The incident ID is'):
return False
if not self.matchContent(r"This page can't be displayed"):
return False
if not self.matchContent(r'Contact support for additional information'):
return False
return True
================================================
FILE: wafw00f/plugins/senginx.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'SEnginx (Neusoft)'
def is_waf(self):
if self.matchContent(r'SENGINX\-ROBOT\-MITIGATION'):
return True
return False
================================================
FILE: wafw00f/plugins/serverdefender.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'ServerDefender VP (Port80 Software)'
def is_waf(self):
if self.matchHeader(('X-Pint', r'p(ort\-)?80')):
return True
return False
================================================
FILE: wafw00f/plugins/shadowd.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Shadow Daemon (Zecure)'
def is_waf(self):
if not self.matchContent(r"<h\d{1}>\d{3}.forbidden<.h\d{1}>"):
return False
if not self.matchContent(r"request forbidden by administrative rules"):
return False
return True
================================================
FILE: wafw00f/plugins/shieldon.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Shieldon Firewall (Shieldon.io)'
def is_waf(self):
if check_schema_01(self):
return True
if check_schema_02(self):
return True
if check_schema_03(self):
return True
if self.matchHeader((r'[Xx]-[Pp]rotected-[Bb]y', 'shieldon.io')):
return True
return False
def check_schema_01(self):
if not self.matchContent('Please solve CAPTCHA'):
return False
if not self.matchContent('shieldon_captcha'):
return False
if not self.matchContent('Unusual behavior detected'):
return False
if not self.matchContent('status-user-info'):
return False
return True
def check_schema_02(self):
if not self.matchContent('Access denied'):
return False
if not self.matchContent('The IP address you are using has been blocked.'):
return False
if not self.matchContent('status-user-info'):
return False
return True
def check_schema_03(self):
if not self.matchContent('Please line up'):
return False
if not self.matchContent('This page is limiting the number of people online. Please wait a moment.'):
return False
return True
================================================
FILE: wafw00f/plugins/shieldsecurity.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Shield Security (One Dollar Plugin)'
def is_waf(self):
if self.matchContent(r"You were blocked by the Shield"):
return True
if self.matchContent(r"remaining transgression\(s\) against this site"):
return True
if self.matchContent(r"Something in the URL.{0,5}?Form or Cookie data wasn\'t appropriate"):
return True
return False
================================================
FILE: wafw00f/plugins/siteground.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'SiteGround (SiteGround)'
def is_waf(self):
if self.matchContent(r"Our system thinks you might be a robot!"):
return True
if self.matchContent(r'access is restricted due to a security rule'):
return True
return False
================================================
FILE: wafw00f/plugins/siteguard.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'SiteGuard (EG Secure Solutions Inc.)'
def is_waf(self):
if self.matchContent(r"Powered by SiteGuard"):
return True
if self.matchContent(r'The server refuse to browse the page'):
return True
return False
================================================
FILE: wafw00f/plugins/sitelock.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Sitelock (TrueShield)'
# Well this is confusing, Sitelock itself uses Incapsula from Imperva
# So the fingerprints obtained on blockpage are similar to those of Incapsula.
def is_waf(self):
if self.matchContent(r"SiteLock will remember you"):
return True
if self.matchContent(r"Sitelock is leader in Business Website Security Services"):
return True
if self.matchContent(r"sitelock[_\-]shield([_\-]logo|[\-_]badge)?"):
return True
if self.matchContent(r'SiteLock incident ID'):
return True
return False
================================================
FILE: wafw00f/plugins/sonicwall.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'SonicWall (Dell)'
def is_waf(self):
if self.matchHeader(('Server', 'SonicWALL')):
return True
if self.matchContent(r"<(title|h\d{1})>Web Site Blocked"):
return True
if self.matchContent(r'\+?nsa_banner'):
return True
return False
================================================
FILE: wafw00f/plugins/sophos.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'UTM Web Protection (Sophos)'
def is_waf(self):
if check_schema_01(self):
return True
if check_schema_02(self):
return True
return False
def check_schema_01(self):
if self.matchContent(r'www\.sophos\.com'):
return True
if self.matchContent(r'Powered by.?(Sophos)? UTM Web Protection'):
return True
return False
def check_schema_02(self):
if not self.matchContent(r'<title>Access to the requested URL was blocked'):
return False
if not self.matchContent(r'Access to the requested URL was blocked'):
return False
if not self.matchContent(r'incident was logged with the following log identifier'):
return False
if not self.matchContent(r'Inbound Anomaly Score exceeded'):
return False
if not self.matchContent(r'Your cache administrator is'):
return False
return True
================================================
FILE: wafw00f/plugins/squarespace.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Squarespace (Squarespace)'
def is_waf(self):
if self.matchHeader(('Server', 'Squarespace')):
return True
if self.matchCookie(r'^SS_ANALYTICS_ID='):
return True
if self.matchCookie(r'^SS_MATTR='):
return True
if self.matchCookie(r'^SS_MID='):
return True
if self.matchCookie(r'SS_CVT='):
return True
if self.matchContent(r'status\.squarespace\.com'):
return True
if self.matchContent(r'BRICK\-\d{2}'):
return True
return False
================================================
FILE: wafw00f/plugins/squidproxy.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'SquidProxy IDS (SquidProxy)'
def is_waf(self):
if not self.matchHeader(('Server', r'squid(/[0-9\.]+)?')):
return False
if not self.matchContent(r'Access control configuration prevents your request'):
return False
return True
================================================
FILE: wafw00f/plugins/stackpath.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'StackPath (StackPath)'
def is_waf(self):
if check_schema_01(self):
return True
if check_schema_02(self):
return True
return False
def check_schema_01(self):
if self.matchContent(r'<title>StackPath[^<]+</title>'):
return True
if self.matchContent(r'Protected by <a href="https?:\/\/(?:www\.)?stackpath\.com\/"[^>]+>StackPath'):
return True
return False
def check_schema_02(self):
if not self.matchContent(r"is using a security service for protection against online attacks"):
return False
if not self.matchContent(r'An action has triggered the service and blocked your request'):
return False
return True
================================================
FILE: wafw00f/plugins/sucuri.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Sucuri CloudProxy (Sucuri Inc.)'
def is_waf(self):
if self.matchHeader(('X-Sucuri-ID', r'.+?')):
return True
if self.matchHeader(('X-Sucuri-Cache', r'.+?')):
return True
if self.matchHeader(('Server', r'Sucuri(\-Cloudproxy)?')):
return True
if self.matchHeader(('X-Sucuri-Block', r'.+?'), attack=True):
return True
if self.matchContent(r"Access Denied.{0,6}?Sucuri Website Firewall"):
return True
if self.matchContent(r"<title>Sucuri WebSite Firewall.{0,6}?(CloudProxy)?.{0,6}?Access Denied"):
return True
if self.matchContent(r"sucuri\.net/privacy\-policy"):
return True
if self.matchContent(r"cdn\.sucuri\.net/sucuri[-_]firewall[-_]block\.css"):
return True
if self.matchContent(r'cloudproxy@sucuri\.net'):
return True
return False
================================================
FILE: wafw00f/plugins/tencent.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Tencent Cloud Firewall (Tencent Technologies)'
def is_waf(self):
if self.matchContent(r'waf\.tencent\-?cloud\.com/'):
return True
if self.matchContent(r'window\.location\.href.{1,3}?https?://waf.tencent(?:-?cloud)?.com/(?:403|501)page\.html'):
return True
return False
================================================
FILE: wafw00f/plugins/teros.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Teros (Citrix Systems)'
def is_waf(self):
if self.matchCookie(r'^st8id='):
return True
return False
================================================
FILE: wafw00f/plugins/threatx.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'ThreatX (A10 Networks)'
def is_waf(self):
if self.matchHeader(('X-Request-Id', r'.*')) and \
self.matchContent(r"^Forbidden - ID: ([a-fA-F0-9]{32})$") and \
self.matchStatus(403):
return True
return False
================================================
FILE: wafw00f/plugins/transip.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'TransIP Web Firewall (TransIP)'
def is_waf(self):
if self.matchHeader(('X-TransIP-Backend', '.+')):
return True
if self.matchHeader(('X-TransIP-Balancer', '.+')):
return True
return False
================================================
FILE: wafw00f/plugins/uewaf.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'UEWaf (UCloud)'
def is_waf(self):
if self.matchHeader(('Server', r'uewaf(/[0-9\.]+)?')):
return True
if self.matchContent(r'/uewaf_deny_pages/default/img/'):
return True
if self.matchContent(r'ucloud\.cn'):
return True
return False
================================================
FILE: wafw00f/plugins/urlmaster.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'URLMaster SecurityCheck (iFinity/DotNetNuke)'
def is_waf(self):
if check_schema_01(self):
return True
if check_schema_02(self):
return True
return False
def check_schema_01(self):
if self.matchHeader(('X-UrlMaster-Debug', '.+')):
return True
if self.matchHeader(('X-UrlMaster-Ex', '.+')):
return True
return False
def check_schema_02(self):
if not self.matchContent(r"Ur[li]RewriteModule"):
return False
if not self.matchContent(r'SecurityCheck'):
return False
return True
================================================
FILE: wafw00f/plugins/urlscan.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'URLScan (Microsoft)'
def is_waf(self):
if self.matchContent(r"Rejected[-_]By[_-]UrlScan"):
return True
if self.matchContent(r'A custom filter or module.{0,4}?such as URLScan'):
return True
return False
================================================
FILE: wafw00f/plugins/variti.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Variti (Variti)'
def is_waf(self):
if self.matchHeader(('Server', r'Variti(?:\/[a-z0-9\.\-]+)?')):
return True
return False
================================================
FILE: wafw00f/plugins/varnish.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Varnish (OWASP)'
def is_waf(self):
if self.matchContent(r'Request rejected by xVarnish\-WAF'):
return True
return False
================================================
FILE: wafw00f/plugins/vercel.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Vercel WAF (Vercel)'
def is_waf(self):
if self.matchContent(r'<title>Vercel Security Checkpoint</title>'):
return True
if self.matchContent(r'/vercel/security/'):
return True
return False
================================================
FILE: wafw00f/plugins/viettel.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Viettel (Cloudrity)'
def is_waf(self):
if self.matchContent(r"Access Denied.{0,10}?Viettel WAF"):
return True
if self.matchContent(r"cloudrity\.com\.(vn)?/"):
return True
if self.matchContent(r"Viettel WAF System"):
return True
return False
================================================
FILE: wafw00f/plugins/virusdie.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'VirusDie (VirusDie LLC)'
def is_waf(self):
if self.matchContent(r"cdn\.virusdie\.ru/splash/firewallstop\.png"):
return True
if self.matchContent(r'copy.{0,10}?Virusdie\.ru'):
return True
return False
================================================
FILE: wafw00f/plugins/wallarm.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'Wallarm (Wallarm Inc.)'
def is_waf(self):
if self.matchHeader(('Server', r'nginx[\-_]wallarm')):
return True
return False
================================================
FILE: wafw00f/plugins/watchguard.py
================================================
#!/usr/bin/env python3
'''
Copyright (C) 2026, WAFW00F Developers.
See the LICENSE file for copying permission.
'''
NAME = 'WatchGuard
gitextract_69mvkwm9/
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ └── bug_report.md
│ └── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── CLAUDE.md
├── CODE_OF_CONDUCT.md
├── CREDITS.txt
├── Dockerfile
├── LICENSE
├── MANIFEST.in
├── Makefile
├── README.md
├── docs/
│ ├── Makefile
│ ├── conf.py
│ ├── index.rst
│ └── wafw00f.8
├── pyproject.toml
├── tests/
│ ├── __init__.py
│ ├── conftest.py
│ ├── test_detection.py
│ ├── test_evillib.py
│ ├── test_manager.py
│ └── test_matching.py
└── wafw00f/
├── __init__.py
├── lib/
│ ├── __init__.py
│ ├── asciiarts.py
│ └── evillib.py
├── main.py
├── manager.py
├── plugins/
│ ├── __init__.py
│ ├── aesecure.py
│ ├── airee.py
│ ├── airlock.py
│ ├── alertlogic.py
│ ├── aliyundun.py
│ ├── anquanbao.py
│ ├── anubis.py
│ ├── anyu.py
│ ├── applicationgateway.py
│ ├── approach.py
│ ├── armor.py
│ ├── arvancloud.py
│ ├── aspa.py
│ ├── aspnetgen.py
│ ├── astra.py
│ ├── awswaf.py
│ ├── azion.py
│ ├── baffinbay.py
│ ├── baidu.py
│ ├── barikode.py
│ ├── barracuda.py
│ ├── bekchy.py
│ ├── beluga.py
│ ├── binarysec.py
│ ├── bitninja.py
│ ├── blockdos.py
│ ├── bluedon.py
│ ├── bulletproof.py
│ ├── cachefly.py
│ ├── cachewall.py
│ ├── cdnns.py
│ ├── cerber.py
│ ├── chinacache.py
│ ├── chuangyu.py
│ ├── ciscoacexml.py
│ ├── cloudbric.py
│ ├── cloudflare.py
│ ├── cloudfloordns.py
│ ├── cloudfront.py
│ ├── cloudprotector.py
│ ├── comodo.py
│ ├── crawlprotect.py
│ ├── ddosguard.py
│ ├── denyall.py
│ ├── distil.py
│ ├── dosarrest.py
│ ├── dotdefender.py
│ ├── dynamicweb.py
│ ├── edgecast.py
│ ├── eisoo.py
│ ├── envoy.py
│ ├── expressionengine.py
│ ├── f5bigipapm.py
│ ├── f5bigipasm.py
│ ├── f5bigipltm.py
│ ├── f5firepass.py
│ ├── f5trafficshield.py
│ ├── fastly.py
│ ├── fortigate.py
│ ├── fortiguard.py
│ ├── fortiweb.py
│ ├── frontdoor.py
│ ├── gcparmor.py
│ ├── godaddy.py
│ ├── greywizard.py
│ ├── huaweicloud.py
│ ├── hyperguard.py
│ ├── ibmdatapower.py
│ ├── imunify360.py
│ ├── incapsula.py
│ ├── indusguard.py
│ ├── instartdx.py
│ ├── isaserver.py
│ ├── janusec.py
│ ├── jiasule.py
│ ├── kemp.py
│ ├── keycdn.py
│ ├── knownsec.py
│ ├── kona.py
│ ├── limelight.py
│ ├── link11.py
│ ├── litespeed.py
│ ├── malcare.py
│ ├── maxcdn.py
│ ├── missioncontrol.py
│ ├── modsecurity.py
│ ├── naxsi.py
│ ├── nemesida.py
│ ├── netcontinuum.py
│ ├── netscaler.py
│ ├── nevisproxy.py
│ ├── newdefend.py
│ ├── nexusguard.py
│ ├── ninja.py
│ ├── nsfocus.py
│ ├── nullddos.py
│ ├── onmessage.py
│ ├── openresty.py
│ ├── oraclecloud.py
│ ├── paloalto.py
│ ├── panyun360.py
│ ├── pentawaf.py
│ ├── perimeterx.py
│ ├── pksec.py
│ ├── powercdn.py
│ ├── profense.py
│ ├── ptaf.py
│ ├── puhui.py
│ ├── qcloud.py
│ ├── qiniu.py
│ ├── qrator.py
│ ├── radware.py
│ ├── reblaze.py
│ ├── reflected.py
│ ├── rsfirewall.py
│ ├── rvmode.py
│ ├── sabre.py
│ ├── safe3.py
│ ├── safedog.py
│ ├── safeline.py
│ ├── scutum.py
│ ├── secking.py
│ ├── secupress.py
│ ├── secureentry.py
│ ├── secureiis.py
│ ├── securesphere.py
│ ├── senginx.py
│ ├── serverdefender.py
│ ├── shadowd.py
│ ├── shieldon.py
│ ├── shieldsecurity.py
│ ├── siteground.py
│ ├── siteguard.py
│ ├── sitelock.py
│ ├── sonicwall.py
│ ├── sophos.py
│ ├── squarespace.py
│ ├── squidproxy.py
│ ├── stackpath.py
│ ├── sucuri.py
│ ├── tencent.py
│ ├── teros.py
│ ├── threatx.py
│ ├── transip.py
│ ├── uewaf.py
│ ├── urlmaster.py
│ ├── urlscan.py
│ ├── variti.py
│ ├── varnish.py
│ ├── vercel.py
│ ├── viettel.py
│ ├── virusdie.py
│ ├── wallarm.py
│ ├── watchguard.py
│ ├── webarx.py
│ ├── webknight.py
│ ├── webland.py
│ ├── webray.py
│ ├── webseal.py
│ ├── webtotem.py
│ ├── west263cdn.py
│ ├── wordfence.py
│ ├── wpmudev.py
│ ├── wts.py
│ ├── wzb360.py
│ ├── xlabssecuritywaf.py
│ ├── xuanwudun.py
│ ├── yundun.py
│ ├── yunsuo.py
│ ├── yxlink.py
│ ├── zenedge.py
│ └── zscaler.py
└── wafprio.py
SYMBOL INDEX (318 symbols across 181 files)
FILE: tests/conftest.py
class MockResponse (line 7) | class MockResponse:
method __init__ (line 10) | def __init__(self, status_code=200, headers=None, text='', reason='OK'):
method content (line 18) | def content(self):
function mock_response (line 23) | def mock_response():
function wafw00f_instance (line 31) | def wafw00f_instance():
FILE: tests/test_detection.py
class TestCloudflareDetection (line 8) | class TestCloudflareDetection:
method test_detect_cloudflare_by_header (line 12) | def test_detect_cloudflare_by_header(self):
method test_detect_cloudflare_by_cf_ray (line 35) | def test_detect_cloudflare_by_cf_ray(self):
class TestFastlyDetection (line 58) | class TestFastlyDetection:
method test_detect_fastly_by_request_id (line 62) | def test_detect_fastly_by_request_id(self):
method test_detect_fastly_by_served_by (line 85) | def test_detect_fastly_by_served_by(self):
class TestAWSWAFDetection (line 108) | class TestAWSWAFDetection:
method test_detect_awswaf_by_header (line 112) | def test_detect_awswaf_by_header(self):
class TestNoWAFDetection (line 135) | class TestNoWAFDetection:
method test_no_waf_plain_response (line 139) | def test_no_waf_plain_response(self):
class TestAnubisDetection (line 167) | class TestAnubisDetection:
method test_detect_anubis_by_cookie (line 171) | def test_detect_anubis_by_cookie(self):
method test_detect_anubis_by_content (line 194) | def test_detect_anubis_by_content(self):
FILE: tests/test_evillib.py
class TestWafToolsEngine (line 8) | class TestWafToolsEngine:
method test_default_timeout (line 11) | def test_default_timeout(self):
method test_custom_timeout (line 16) | def test_custom_timeout(self):
method test_default_headers (line 21) | def test_default_headers(self):
method test_custom_headers (line 27) | def test_custom_headers(self):
method test_request_success (line 34) | def test_request_success(self):
method test_request_increments_counter (line 51) | def test_request_increments_counter(self):
method test_response_content_accessible (line 63) | def test_response_content_accessible(self):
class TestResponseSizeLimit (line 80) | class TestResponseSizeLimit:
method test_max_response_size_defined (line 83) | def test_max_response_size_defined(self):
method test_small_response_fully_read (line 89) | def test_small_response_fully_read(self):
method test_large_response_truncated (line 105) | def test_large_response_truncated(self):
class TestTimeoutEnforcement (line 123) | class TestTimeoutEnforcement:
method test_timeout_attribute_used (line 127) | def test_timeout_attribute_used(self):
class TestPathPreservation (line 151) | class TestPathPreservation:
method test_path_traversal_not_normalized (line 155) | def test_path_traversal_not_normalized(self):
method test_path_traversal_with_params (line 163) | def test_path_traversal_with_params(self):
FILE: tests/test_manager.py
class TestLoadPlugins (line 7) | class TestLoadPlugins:
method test_load_plugins_returns_dict (line 10) | def test_load_plugins_returns_dict(self):
method test_load_plugins_not_empty (line 15) | def test_load_plugins_not_empty(self):
method test_plugins_have_name_attribute (line 20) | def test_plugins_have_name_attribute(self):
method test_plugins_have_is_waf_function (line 26) | def test_plugins_have_is_waf_function(self):
method test_known_plugins_loaded (line 33) | def test_known_plugins_loaded(self):
method test_plugin_names_are_strings (line 40) | def test_plugin_names_are_strings(self):
FILE: tests/test_matching.py
class TestMatchHeader (line 7) | class TestMatchHeader:
method test_match_header_exact (line 10) | def test_match_header_exact(self, wafw00f_instance, mock_response):
method test_match_header_regex (line 15) | def test_match_header_regex(self, wafw00f_instance, mock_response):
method test_match_header_case_insensitive (line 20) | def test_match_header_case_insensitive(self, wafw00f_instance, mock_re...
method test_match_header_not_found (line 25) | def test_match_header_not_found(self, wafw00f_instance, mock_response):
method test_match_header_missing (line 30) | def test_match_header_missing(self, wafw00f_instance, mock_response):
method test_match_header_none_response (line 35) | def test_match_header_none_response(self, wafw00f_instance):
class TestMatchContent (line 42) | class TestMatchContent:
method test_match_content_exact (line 45) | def test_match_content_exact(self, wafw00f_instance, mock_response):
method test_match_content_regex (line 50) | def test_match_content_regex(self, wafw00f_instance, mock_response):
method test_match_content_case_insensitive (line 55) | def test_match_content_case_insensitive(self, wafw00f_instance, mock_r...
method test_match_content_not_found (line 60) | def test_match_content_not_found(self, wafw00f_instance, mock_response):
method test_match_content_none_response (line 65) | def test_match_content_none_response(self, wafw00f_instance):
class TestMatchCookie (line 72) | class TestMatchCookie:
method test_match_cookie (line 75) | def test_match_cookie(self, wafw00f_instance, mock_response):
method test_match_cookie_regex (line 82) | def test_match_cookie_regex(self, wafw00f_instance, mock_response):
method test_match_cookie_not_found (line 89) | def test_match_cookie_not_found(self, wafw00f_instance, mock_response):
class TestMatchStatus (line 97) | class TestMatchStatus:
method test_match_status_200 (line 100) | def test_match_status_200(self, wafw00f_instance, mock_response):
method test_match_status_403 (line 105) | def test_match_status_403(self, wafw00f_instance, mock_response):
method test_match_status_mismatch (line 110) | def test_match_status_mismatch(self, wafw00f_instance, mock_response):
method test_match_status_none_response (line 115) | def test_match_status_none_response(self, wafw00f_instance):
class TestMatchReason (line 122) | class TestMatchReason:
method test_match_reason_ok (line 125) | def test_match_reason_ok(self, wafw00f_instance, mock_response):
method test_match_reason_forbidden (line 130) | def test_match_reason_forbidden(self, wafw00f_instance, mock_response):
method test_match_reason_mismatch (line 135) | def test_match_reason_mismatch(self, wafw00f_instance, mock_response):
FILE: wafw00f/lib/asciiarts.py
class Color (line 14) | class Color:
method disable (line 25) | def disable(cls):
method unpack (line 36) | def unpack(cls):
function randomArt (line 52) | def randomArt():
FILE: wafw00f/lib/evillib.py
class waftoolsengine (line 36) | class waftoolsengine:
method __init__ (line 37) | def __init__(
method Request (line 55) | def Request(self, headers=None, path=None, params={}, delay=0):
FILE: wafw00f/main.py
class WAFW00F (line 28) | class WAFW00F(waftoolsengine):
method __init__ (line 36) | def __init__(self, target='www.example.com', debuglevel=0, path='/',
method normalRequest (line 51) | def normalRequest(self):
method customRequest (line 54) | def customRequest(self, headers=None):
method nonExistent (line 59) | def nonExistent(self):
method xssAttack (line 64) | def xssAttack(self):
method xxeAttack (line 72) | def xxeAttack(self):
method lfiAttack (line 80) | def lfiAttack(self):
method centralAttack (line 85) | def centralAttack(self):
method sqliAttack (line 95) | def sqliAttack(self):
method osciAttack (line 103) | def osciAttack(self):
method performCheck (line 111) | def performCheck(self, request_method):
method genericdetect (line 121) | def genericdetect(self):
method matchHeader (line 207) | def matchHeader(self, headermatch, attack=False):
method matchStatus (line 229) | def matchStatus(self, statuscode, attack=True):
method matchCookie (line 240) | def matchCookie(self, match, attack=False):
method matchReason (line 243) | def matchReason(self, reasoncode, attack=True):
method matchContent (line 255) | def matchContent(self, regex, attack=True):
method identwaf (line 277) | def identwaf(self, findall=False):
function calclogginglevel (line 292) | def calclogginglevel(verbosity):
function buildResultRecord (line 299) | def buildResultRecord(url, waf, evil_url=None):
function getTextResults (line 319) | def getTextResults(res=[]):
function create_random_param_name (line 341) | def create_random_param_name(size=8, chars=string.ascii_lowercase):
function disableStdOut (line 344) | def disableStdOut():
function enableStdOut (line 347) | def enableStdOut():
function getheaders (line 350) | def getheaders(fn):
class RequestBlocked (line 363) | class RequestBlocked(Exception):
function main (line 366) | def main():
FILE: wafw00f/manager.py
function load_plugins (line 10) | def load_plugins():
FILE: wafw00f/plugins/aesecure.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/airee.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/airlock.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/alertlogic.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/aliyundun.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/anquanbao.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/anubis.py
function is_waf (line 10) | def is_waf(self) -> bool:
FILE: wafw00f/plugins/anyu.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/applicationgateway.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/approach.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/armor.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/arvancloud.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/aspa.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/aspnetgen.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/astra.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/awswaf.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/azion.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/baffinbay.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/baidu.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/barikode.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/barracuda.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/bekchy.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/beluga.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/binarysec.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/bitninja.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/blockdos.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/bluedon.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/bulletproof.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/cachefly.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/cachewall.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/cdnns.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/cerber.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/chinacache.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/chuangyu.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/ciscoacexml.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/cloudbric.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/cloudflare.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/cloudfloordns.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/cloudfront.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/cloudprotector.py
function is_waf (line 9) | def is_waf(self):
FILE: wafw00f/plugins/comodo.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/crawlprotect.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/ddosguard.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/denyall.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/distil.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/dosarrest.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/dotdefender.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/dynamicweb.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/edgecast.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/eisoo.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/envoy.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/expressionengine.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/f5bigipapm.py
function is_waf (line 10) | def is_waf(self):
function check_schema_01 (line 23) | def check_schema_01(self):
function check_schema_02 (line 33) | def check_schema_02(self):
function check_schema_03 (line 43) | def check_schema_03(self):
FILE: wafw00f/plugins/f5bigipasm.py
function is_waf (line 10) | def is_waf(self):
function check_schema_01 (line 25) | def check_schema_01(self):
FILE: wafw00f/plugins/f5bigipltm.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/f5firepass.py
function is_waf (line 10) | def is_waf(self):
function check_schema_01 (line 20) | def check_schema_01(self):
function check_schema_02 (line 30) | def check_schema_02(self):
FILE: wafw00f/plugins/f5trafficshield.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/fastly.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/fortigate.py
function is_waf (line 9) | def is_waf(self):
function check_schema_01 (line 18) | def check_schema_01(self):
function check_schema_02 (line 27) | def check_schema_02(self):
FILE: wafw00f/plugins/fortiguard.py
function is_waf (line 9) | def is_waf(self):
function check_schema (line 15) | def check_schema(self):
FILE: wafw00f/plugins/fortiweb.py
function is_waf (line 10) | def is_waf(self):
function check_schema_01 (line 20) | def check_schema_01(self):
function check_schema_02 (line 30) | def check_schema_02(self):
FILE: wafw00f/plugins/frontdoor.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/gcparmor.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/godaddy.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/greywizard.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/huaweicloud.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/hyperguard.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/ibmdatapower.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/imunify360.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/incapsula.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/indusguard.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/instartdx.py
function is_waf (line 10) | def is_waf(self):
function check_schema_01 (line 20) | def check_schema_01(self):
function check_schema_02 (line 33) | def check_schema_02(self):
FILE: wafw00f/plugins/isaserver.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/janusec.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/jiasule.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/kemp.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/keycdn.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/knownsec.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/kona.py
function is_waf (line 13) | def is_waf(self):
FILE: wafw00f/plugins/limelight.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/link11.py
function is_waf (line 9) | def is_waf(self):
function check_schema_01 (line 16) | def check_schema_01(self):
FILE: wafw00f/plugins/litespeed.py
function is_waf (line 10) | def is_waf(self):
function check_schema_01 (line 20) | def check_schema_01(self):
function check_schema_02 (line 30) | def check_schema_02(self):
FILE: wafw00f/plugins/malcare.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/maxcdn.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/missioncontrol.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/modsecurity.py
function is_waf (line 10) | def is_waf(self):
function check_schema_01 (line 23) | def check_schema_01(self):
function check_schema_02 (line 48) | def check_schema_02(self):
function check_schema_03 (line 58) | def check_schema_03(self):
FILE: wafw00f/plugins/naxsi.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/nemesida.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/netcontinuum.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/netscaler.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/nevisproxy.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/newdefend.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/nexusguard.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/ninja.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/nsfocus.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/nullddos.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/onmessage.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/openresty.py
function is_waf (line 10) | def is_waf(self):
function check_schema_01 (line 20) | def check_schema_01(self):
function check_schema_02 (line 30) | def check_schema_02(self):
FILE: wafw00f/plugins/oraclecloud.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/paloalto.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/panyun360.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/pentawaf.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/perimeterx.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/pksec.py
function is_waf (line 10) | def is_waf(self):
function check_schema_01 (line 20) | def check_schema_01(self):
function check_schema_02 (line 30) | def check_schema_02(self):
FILE: wafw00f/plugins/powercdn.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/profense.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/ptaf.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/puhui.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/qcloud.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/qiniu.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/qrator.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/radware.py
function is_waf (line 10) | def is_waf(self):
function check_schema_01 (line 20) | def check_schema_01(self):
function check_schema_02 (line 30) | def check_schema_02(self):
FILE: wafw00f/plugins/reblaze.py
function is_waf (line 10) | def is_waf(self):
function check_schema_01 (line 20) | def check_schema_01(self):
function check_schema_02 (line 30) | def check_schema_02(self):
FILE: wafw00f/plugins/reflected.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/rsfirewall.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/rvmode.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/sabre.py
function is_waf (line 10) | def is_waf(self):
function check_schema_01 (line 20) | def check_schema_01(self):
FILE: wafw00f/plugins/safe3.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/safedog.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/safeline.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/scutum.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/secking.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/secupress.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/secureentry.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/secureiis.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/securesphere.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/senginx.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/serverdefender.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/shadowd.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/shieldon.py
function is_waf (line 10) | def is_waf(self):
function check_schema_01 (line 26) | def check_schema_01(self):
function check_schema_02 (line 42) | def check_schema_02(self):
function check_schema_03 (line 55) | def check_schema_03(self):
FILE: wafw00f/plugins/shieldsecurity.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/siteground.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/siteguard.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/sitelock.py
function is_waf (line 12) | def is_waf(self):
FILE: wafw00f/plugins/sonicwall.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/sophos.py
function is_waf (line 10) | def is_waf(self):
function check_schema_01 (line 20) | def check_schema_01(self):
function check_schema_02 (line 30) | def check_schema_02(self):
FILE: wafw00f/plugins/squarespace.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/squidproxy.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/stackpath.py
function is_waf (line 10) | def is_waf(self):
function check_schema_01 (line 20) | def check_schema_01(self):
function check_schema_02 (line 30) | def check_schema_02(self):
FILE: wafw00f/plugins/sucuri.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/tencent.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/teros.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/threatx.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/transip.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/uewaf.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/urlmaster.py
function is_waf (line 10) | def is_waf(self):
function check_schema_01 (line 20) | def check_schema_01(self):
function check_schema_02 (line 30) | def check_schema_02(self):
FILE: wafw00f/plugins/urlscan.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/variti.py
function is_waf (line 9) | def is_waf(self):
FILE: wafw00f/plugins/varnish.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/vercel.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/viettel.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/virusdie.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/wallarm.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/watchguard.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/webarx.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/webknight.py
function is_waf (line 10) | def is_waf(self):
function check_schema_01 (line 23) | def check_schema_01(self):
function check_schema_02 (line 33) | def check_schema_02(self):
function check_schema_03 (line 43) | def check_schema_03(self):
FILE: wafw00f/plugins/webland.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/webray.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/webseal.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/webtotem.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/west263cdn.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/wordfence.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/wpmudev.py
function is_waf (line 10) | def is_waf(self):
function check_schema_01 (line 20) | def check_schema_01(self):
function check_schema_02 (line 36) | def check_schema_02(self):
FILE: wafw00f/plugins/wts.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/wzb360.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/xlabssecuritywaf.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/xuanwudun.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/yundun.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/yunsuo.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/yxlink.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/zenedge.py
function is_waf (line 10) | def is_waf(self):
FILE: wafw00f/plugins/zscaler.py
function is_waf (line 10) | def is_waf(self):
Condensed preview — 202 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (210K chars).
[
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 3470,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: sandrogauci, 0xInfection\n"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 846,
"preview": "#### Which category is this pull request?\n<!-- Check the boxes with 'x' like '[x]' -->\n- [ ] A new feature/enhancement.\n"
},
{
"path": ".gitignore",
"chars": 611,
"preview": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*.swp\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.P"
},
{
"path": "CLAUDE.md",
"chars": 6648,
"preview": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## "
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 3355,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
},
{
"path": "CREDITS.txt",
"chars": 1204,
"preview": "===================\nTHE WAFW00F PROJECT\n===================\n\n$ AUTHORS\n =======\n\n* Current Maintainers :-\n - Sandro "
},
{
"path": "Dockerfile",
"chars": 99,
"preview": "FROM python:3.11.9-alpine\nWORKDIR /usr/src/app\nCOPY . .\nRUN pip install .\nENTRYPOINT [ \"wafw00f\" ]\n"
},
{
"path": "LICENSE",
"chars": 1499,
"preview": "Copyright (c) 2009-2026, WAFW00F Developers\nAll rights reserved.\n\nRedistribution and use in source and binary forms, wit"
},
{
"path": "MANIFEST.in",
"chars": 130,
"preview": "include CREDITS.txt\ninclude LICENSE\ninclude MANIFEST.in\ninclude README.md\ninclude wafw00f/__init__.py\ninclude wafw00f/bi"
},
{
"path": "Makefile",
"chars": 458,
"preview": "SRC_DIR = wafw00f\nDOC_DIR = docs\nMAKE = make\n\nall:\n\tmake install\n\tmake test\n\tmake html\n\tmake clean\n\ninstall:\n\tpip instal"
},
{
"path": "README.md",
"chars": 12609,
"preview": "<h1 align=\"center\">\n <a href=\"https://github.com/enablesecurity/wafw00f\"><img src=\"https://i.imgur.com/uAgp49o.png\" alt"
},
{
"path": "docs/Makefile",
"chars": 6766,
"preview": "# Makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS =\nSPHINXBUILD "
},
{
"path": "docs/conf.py",
"chars": 8287,
"preview": "# -*- coding: utf-8 -*-\n#\n# wafw00f documentation build configuration file, created by\n# sphinx-quickstart on Thu May 15"
},
{
"path": "docs/index.rst",
"chars": 426,
"preview": ".. wafw00f documentation master file, created by\n sphinx-quickstart on Thu May 15 20:04:22 2014.\n You can adapt this"
},
{
"path": "docs/wafw00f.8",
"chars": 2732,
"preview": ".TH WAFW00F \"8\" \"October 2020\" \"wafw00f \" \"User Commands\"\n.SH NAME\nWAFW00F \\- Identify and fingerprint Web Application F"
},
{
"path": "pyproject.toml",
"chars": 1516,
"preview": "[build-system]\nrequires = [\"setuptools>=64\", \"wheel\"]\nbuild-backend = \"setuptools.build_meta\"\n\n[project]\nname = \"wafw00f"
},
{
"path": "tests/__init__.py",
"chars": 16,
"preview": "# wafw00f tests\n"
},
{
"path": "tests/conftest.py",
"chars": 953,
"preview": "\"\"\"Shared pytest fixtures for wafw00f tests.\"\"\"\n\nimport pytest\nfrom unittest.mock import MagicMock\n\n\nclass MockResponse:"
},
{
"path": "tests/test_detection.py",
"chars": 6310,
"preview": "\"\"\"Integration tests for WAF detection.\"\"\"\n\nimport pytest\nimport responses\nfrom wafw00f.main import WAFW00F\n\n\nclass Test"
},
{
"path": "tests/test_evillib.py",
"chars": 5811,
"preview": "\"\"\"Tests for the evillib module.\"\"\"\n\nimport pytest\nimport responses\nfrom wafw00f.lib.evillib import waftoolsengine, MAX_"
},
{
"path": "tests/test_manager.py",
"chars": 1639,
"preview": "\"\"\"Tests for the plugin manager.\"\"\"\n\nimport pytest\nfrom wafw00f.manager import load_plugins\n\n\nclass TestLoadPlugins:\n "
},
{
"path": "tests/test_matching.py",
"chars": 6056,
"preview": "\"\"\"Tests for the WAFW00F matching functions.\"\"\"\n\nimport pytest\nfrom wafw00f.main import WAFW00F\n\n\nclass TestMatchHeader:"
},
{
"path": "wafw00f/__init__.py",
"chars": 75,
"preview": "#!/usr/bin/env python3\n\n__version__ = '2.4.2'\n__license__ = 'BSD 3-Clause'\n"
},
{
"path": "wafw00f/lib/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "wafw00f/lib/asciiarts.py",
"chars": 3590,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nfro"
},
{
"path": "wafw00f/lib/evillib.py",
"chars": 3988,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nimp"
},
{
"path": "wafw00f/main.py",
"chars": 24299,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copy"
},
{
"path": "wafw00f/manager.py",
"chars": 960,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nimp"
},
{
"path": "wafw00f/plugins/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "wafw00f/plugins/aesecure.py",
"chars": 327,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/airee.py",
"chars": 395,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/airlock.py",
"chars": 435,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/alertlogic.py",
"chars": 638,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/aliyundun.py",
"chars": 630,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/anquanbao.py",
"chars": 331,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/anubis.py",
"chars": 663,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/anyu.py",
"chars": 361,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/applicationgateway.py",
"chars": 366,
"preview": "#!/usr/bin/env python\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAME"
},
{
"path": "wafw00f/plugins/approach.py",
"chars": 457,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/armor.py",
"chars": 366,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/arvancloud.py",
"chars": 259,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/aspa.py",
"chars": 353,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/aspnetgen.py",
"chars": 577,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/astra.py",
"chars": 427,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/awswaf.py",
"chars": 605,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/azion.py",
"chars": 345,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/baffinbay.py",
"chars": 265,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/baidu.py",
"chars": 434,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/barikode.py",
"chars": 263,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/barracuda.py",
"chars": 547,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/bekchy.py",
"chars": 632,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/beluga.py",
"chars": 325,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/binarysec.py",
"chars": 406,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/bitninja.py",
"chars": 342,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/blockdos.py",
"chars": 259,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/bluedon.py",
"chars": 394,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/bulletproof.py",
"chars": 448,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/cachefly.py",
"chars": 322,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/cachewall.py",
"chars": 698,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/cdnns.py",
"chars": 286,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/cerber.py",
"chars": 686,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/chinacache.py",
"chars": 280,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/chuangyu.py",
"chars": 357,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/ciscoacexml.py",
"chars": 264,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/cloudbric.py",
"chars": 741,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/cloudflare.py",
"chars": 470,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/cloudfloordns.py",
"chars": 475,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/cloudfront.py",
"chars": 1049,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/cloudprotector.py",
"chars": 446,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/comodo.py",
"chars": 291,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/crawlprotect.py",
"chars": 423,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/ddosguard.py",
"chars": 517,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/denyall.py",
"chars": 334,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/distil.py",
"chars": 434,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/dosarrest.py",
"chars": 421,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/dotdefender.py",
"chars": 490,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/dynamicweb.py",
"chars": 385,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/edgecast.py",
"chars": 340,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/eisoo.py",
"chars": 516,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/envoy.py",
"chars": 993,
"preview": "#!/usr/bin/env python\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAME"
},
{
"path": "wafw00f/plugins/expressionengine.py",
"chars": 840,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/f5bigipapm.py",
"chars": 965,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/f5bigipasm.py",
"chars": 808,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/f5bigipltm.py",
"chars": 352,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/f5firepass.py",
"chars": 667,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/f5trafficshield.py",
"chars": 327,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/fastly.py",
"chars": 357,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/fortigate.py",
"chars": 762,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/fortiguard.py",
"chars": 596,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/fortiweb.py",
"chars": 891,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/frontdoor.py",
"chars": 262,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/gcparmor.py",
"chars": 270,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/godaddy.py",
"chars": 287,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/greywizard.py",
"chars": 559,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/huaweicloud.py",
"chars": 464,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/hyperguard.py",
"chars": 253,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/ibmdatapower.py",
"chars": 265,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/imunify360.py",
"chars": 502,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/incapsula.py",
"chars": 537,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/indusguard.py",
"chars": 379,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/instartdx.py",
"chars": 861,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/isaserver.py",
"chars": 323,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/janusec.py",
"chars": 280,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/jiasule.py",
"chars": 525,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/kemp.py",
"chars": 373,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/keycdn.py",
"chars": 247,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/knownsec.py",
"chars": 257,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/kona.py",
"chars": 382,
"preview": "#!/usr/bin/env python3\n\n\n#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for cop"
},
{
"path": "wafw00f/plugins/limelight.py",
"chars": 315,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/link11.py",
"chars": 362,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/litespeed.py",
"chars": 701,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/malcare.py",
"chars": 375,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/maxcdn.py",
"chars": 247,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/missioncontrol.py",
"chars": 300,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/modsecurity.py",
"chars": 1311,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/naxsi.py",
"chars": 482,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/nemesida.py",
"chars": 489,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/netcontinuum.py",
"chars": 264,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/netscaler.py",
"chars": 1010,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/nevisproxy.py",
"chars": 299,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/newdefend.py",
"chars": 522,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/nexusguard.py",
"chars": 354,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/ninja.py",
"chars": 400,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/nsfocus.py",
"chars": 262,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/nullddos.py",
"chars": 275,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/onmessage.py",
"chars": 589,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/openresty.py",
"chars": 660,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/oraclecloud.py",
"chars": 437,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/paloalto.py",
"chars": 392,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/panyun360.py",
"chars": 535,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/pentawaf.py",
"chars": 360,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/perimeterx.py",
"chars": 469,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/pksec.py",
"chars": 855,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/powercdn.py",
"chars": 421,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/profense.py",
"chars": 314,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/ptaf.py",
"chars": 395,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/puhui.py",
"chars": 254,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/qcloud.py",
"chars": 309,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/qiniu.py",
"chars": 253,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/qrator.py",
"chars": 248,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/radware.py",
"chars": 921,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/reblaze.py",
"chars": 777,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/reflected.py",
"chars": 602,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/rsfirewall.py",
"chars": 277,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/rvmode.py",
"chars": 492,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/sabre.py",
"chars": 563,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/safe3.py",
"chars": 427,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/safedog.py",
"chars": 586,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/safeline.py",
"chars": 266,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/scutum.py",
"chars": 267,
"preview": "#!/usr/bin/env python3\n\"\"\"\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n\"\"\"\n\nNAM"
},
{
"path": "wafw00f/plugins/secking.py",
"chars": 259,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/secupress.py",
"chars": 274,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/secureentry.py",
"chars": 285,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/secureiis.py",
"chars": 464,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/securesphere.py",
"chars": 523,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/senginx.py",
"chars": 259,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/serverdefender.py",
"chars": 273,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/shadowd.py",
"chars": 372,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/shieldon.py",
"chars": 1316,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/shieldsecurity.py",
"chars": 497,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/siteground.py",
"chars": 373,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/siteguard.py",
"chars": 360,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/sitelock.py",
"chars": 686,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/sonicwall.py",
"chars": 400,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/sophos.py",
"chars": 1024,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/squarespace.py",
"chars": 648,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/squidproxy.py",
"chars": 382,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/stackpath.py",
"chars": 825,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/sucuri.py",
"chars": 982,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/tencent.py",
"chars": 426,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/teros.py",
"chars": 244,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/threatx.py",
"chars": 363,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/transip.py",
"chars": 345,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/uewaf.py",
"chars": 402,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/urlmaster.py",
"chars": 694,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/urlscan.py",
"chars": 359,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/variti.py",
"chars": 267,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/varnish.py",
"chars": 264,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/vercel.py",
"chars": 344,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/viettel.py",
"chars": 411,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/virusdie.py",
"chars": 357,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/wallarm.py",
"chars": 266,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/watchguard.py",
"chars": 442,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/webarx.py",
"chars": 453,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/webknight.py",
"chars": 1215,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/webland.py",
"chars": 264,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/webray.py",
"chars": 351,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/webseal.py",
"chars": 442,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/webtotem.py",
"chars": 282,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/west263cdn.py",
"chars": 262,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/wordfence.py",
"chars": 509,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/wpmudev.py",
"chars": 1052,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/wts.py",
"chars": 329,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/wzb360.py",
"chars": 606,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/xlabssecuritywaf.py",
"chars": 439,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/xuanwudun.py",
"chars": 383,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/yundun.py",
"chars": 644,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/yunsuo.py",
"chars": 318,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/yxlink.py",
"chars": 403,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
},
{
"path": "wafw00f/plugins/zenedge.py",
"chars": 383,
"preview": "#!/usr/bin/env python3\n'''\nCopyright (C) 2026, WAFW00F Developers.\nSee the LICENSE file for copying permission.\n'''\n\nNAM"
}
]
// ... and 2 more files (download for full content)
About this extraction
This page contains the full source code of the sandrogauci/wafw00f GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 202 files (186.3 KB), approximately 53.9k tokens, and a symbol index with 318 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.