wscan: a web scanner that runs from a WebUI or a repeatable CLI command
Wscan is a web security scanner that focuses on web security, dedicated to making web security accessible to everyone.
At a glance
- What is it?
- wscan is a Go web security scanner for authorized testing that combines an active crawler, a headless browser crawler, a MITM passive listener and a plugin set in one binary. It suits a tester who wants both an interactive console and a scriptable command, and it does not suit anyone scanning hosts they are not authorized to test.
- Who is it for?
- wscan suits a tester who needs a single binary that covers active crawling, headless browser crawling and passive traffic inspection, and who wants the same scan expressed as a CLI command for CI or a repeatable engagement.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 32 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Two front ends over one scanner binary
wscan is described as a web security scanner for authorized security testing, and the design choice that defines it is that the same capabilities are reachable from two different places. The WebUI is positioned as the fastest start, and the CLI as the route for repeatable scans and automation. Both are served by the same executable, with the WebUI started as a subcommand and the scan engine invoked as another.
The WebUI is not a thin report viewer. The listed features are managing targets, scan tasks, vulnerabilities, pages and technology findings, creating scans of several kinds, exporting results, keeping scan history across restarts, running selected plugins or proof-of-concept templates on demand, and starting an AI-assisted pentest project for guided multi-step testing. The reverse platform for blind-vulnerability callbacks is built in as well, which is the mechanism by which a server-side payload can call back to the scanner to prove that a blind injection works.
The CLI form is the same engine in a different costume. The command is available as `ws` or `webscan`, and the documentation is explicit that it is the option for scripts, CI jobs and reproducible scans. That combination is the actual reason to look at this project: an assessment that starts interactively in a browser and then needs to be repeated exactly can move to a command without changing tools or re-implementing a workflow.
The repository layout backs this up. There is a cmd/ directory for entry points, a core/ directory for the engine, a config.yaml at the root for configuration, a doc/ directory, a CHANGELOG.md, and a vendor/ directory, which means the dependency tree is committed into the tree and a build does not have to reach the network.
Four ways to reach a target, and what each one costs you
An active scan can be driven by a static crawler, a headless-browser crawler, a single URL, a URL file, or a raw request. The distinction that matters in practice is the first two. The static crawler is described as a faster HTML crawl, and the browser crawler is the option for JavaScript-heavy applications, where the interesting endpoints and parameters only exist after client-side code has run.
The underlying technology is visible in the module file. github.com/chromedp/chromedp and github.com/chromedp/cdproto drive a real browser over the DevTools protocol, and PuerkitoBio/goquery handles HTML parsing. So the dynamic mode is not a JavaScript interpreter bolted onto a crawler, it is an actual browser that the scanner observes, which is why it can see what a static crawl never fetches.
The passive mode is a different shape entirely. Instead of crawling, the scanner listens on a local man-in-the-middle proxy and inspects traffic produced by a browser or client you point at it. Passive mode does not crawl the target itself, and the target you enter defines the allowed host and path scope, so traffic to unrelated hosts is rejected. The proxy address defaults to `127.0.0.1:7100`.
That last point is the design's safety mechanism and the reason to read the documentation carefully. A passive task requires a scan target, and the documentation says plainly that the passive proxy must not be used as a general-purpose browser proxy. The proxy is implemented with github.com/google/martian and its v3 line, and interception of HTTPS traffic is handled through a generated project CA, which is the subject of the next section.
A project CA you generate, install and then protect
Intercepting HTTPS traffic from a browser requires a certificate the browser trusts. wscan handles this by generating its own project CA rather than asking the operator to install a system-wide trust anchor:
./wscan gencaThe command produces a ca.crt, which is installed in the browser or client that will generate the traffic, and a ca.key, which is explicitly called out as sensitive. The documentation says not to upload it or share it.
The workflow the documentation describes for a passive scan runs in nine steps: open New Scan and select Passive listener, enter the scan target URL that defines the allowed scope, keep the default proxy address or pick another, select the plugins and scope, start the listener, configure the browser or client to use the displayed proxy, install the generated project CA certificate in that browser, browse the authorized target through the proxy, then review traffic and findings and stop the task when finished.
That ordering matters. Because the CA is per project and per browser, a passive scan does not require the elevated trust you would need for a system-wide interception certificate, and the blast radius of a mistake is bounded to the browser you configured rather than to the machine's certificate store.
The scope restriction is enforced in configuration as well. The documentation points at a `mitm.restriction` section in `config.yaml` for enforcing hostname, path, port and parameter restrictions for command-line passive scans, so the same limits that the WebUI expresses through the scan target are available to a scripted run.
The CLI reference, and the flags that make a scan reproducible
A static crawl with a scan, writing both machine-readable and human-readable output, looks like this:
# Static crawl + scan
./wscan ws --basic-crawler \
--url https://example.com/ \
--json-output=result.json --html-output=result.htmlThe headless browser variant swaps the crawler flag and is documented for JavaScript applications:
# Headless-browser crawl for JavaScript applications
./wscan ws --browser-crawler \
--url https://example.com/ \
--html-output=result.htmlThe rest of the reference covers the cases a scanner needs: a file of URLs with one per line, a single endpoint with a request body, a crawl with checks turned off, and a restricted plugin set. The last of these is the one to remember:
# Run only selected plugins
./wscan ws --basic-crawler --url https://example.com/ \
--plug=sqldet,xss --json-output=result.jsonRestricting the plugin set with `--plug` is both a speed control and a risk control, since it decides which checks are actually sent to the target. The passive equivalent listens on an address and writes JSON:
# Listen on the default-style local address
./wscan ws --listen=127.0.0.1:7100 --json-output=result.jsonOutput formats are consistent across the modes, and the WebUI export actions can download collected URLs as TXT, JSON or XML, with the same JSON and HTML writers used from the command line.
What the plugin set actually covers
The coverage list is broad, and it is organized by vulnerability family rather than by tool name. Injection covers SQL injection, command injection, expression and template injection, and CRLF and header injection. Cross-site checks cover reflected and stored XSS, JSONP and cross-origin data exposure. File and path issues cover path traversal, sensitive file and directory discovery, backup and configuration exposure, and file upload checks.
The server-side category is the long one, with SSRF, XXE, unsafe deserialization, and checks tied to specific frameworks and formats: Fastjson, Shiro, XStream, ThinkPHP and Struts. Authentication and session issues cover HTTP Basic and Digest weaknesses, simple form weak-password checks, and cookie and security flag problems. Configuration and information exposure covers missing or unsafe HTTP security headers, TLS and SSL configuration, debug and error information, sensitive comments, and credentials, API keys, emails and phone numbers found in responses or in JavaScript.
Discovery is treated as a first-class category, covering component fingerprinting, JavaScript endpoint discovery, Swagger and OpenAPI discovery, and redirects. Custom rules are YAML-based custom FUZZ rules, and the project also accepts Nuclei, Xray and Goby compatible proof-of-concept templates, which the module file confirms by depending on github.com/projectdiscovery/nuclei/v3.
Two qualifications in the documentation are worth repeating. Coverage depends on the selected plugins, the request data, the authentication state, the target behaviour and the scan mode, so no single run is exhaustive. And a finding is described as a lead for review, not automatically proof of impact, with the recommendation to confirm important findings manually inside the authorized scope, especially those involving active probes, authentication, file uploads, SSRF or blind callbacks.
Where wscan will not help you
The first limitation is scope, and it is a real boundary rather than a formality. Active mode sends probes to the target, and the documentation restricts that to systems you own or are explicitly authorized to test, with the passive mode carrying the additional condition that traffic through the proxy is captured. Running an active scan against a third party is both a legal problem and a practical one, since the scanner is designed to send the requests that trigger the behaviours it looks for.
The second limitation is that a scanner produces leads, not verdicts. The project says this plainly, and it has consequences for how you use the output. A finding has to be reproduced by hand before it goes into a report, and the checks that matter most for a business are the ones hardest to automate: authenticated flows, file upload handling, SSRF, and blind injection where the proof is a callback. For those, the reverse platform helps, but the confirmation step is still yours.
The third is the dependency on a browser for dynamic scanning. Headless crawling means a working Chrome or Chromium controlled over the DevTools protocol, which is a heavier environmental requirement than a static crawl and is the usual reason a scan behaves differently on a CI runner than on a laptop. A headless browser also sees what a real user sees, including third-party scripts, which is exactly the point and also the source of noise.
The fourth is the licensing and the third-party templates. The repository ships a LICENSE.md that GitHub cannot classify as a standard licence, and the project asks you to read it before use. Separately, the optional proof-of-concept templates come from other people's projects, so their behaviour and their licences are theirs, and a scan that mixes them in inherits that uncertainty. The project also depends on a model context protocol SDK and a JavaScript runtime engine in its module file, which suggests the AI pentest feature and script evaluation are part of the build rather than an optional extra.
Editorial conclusion
wscan suits a tester who needs a single binary that covers active crawling, headless browser crawling and passive traffic inspection, and who wants the same scan expressed as a CLI command for CI or a repeatable engagement. It is the wrong tool for anything outside an authorized scope, for an assessment that needs a signed report rather than raw findings, and for a team that wants a scanner whose every plugin is verifiable by reading its source, since the project also embeds a third-party template engine for proof-of-concept checks. Before using it, define the target scope first because a passive task refuses traffic outside its scan target, install the generated ca.crt only in the browser you control, keep ca.key off any shared machine, and confirm the release you run is current, since version 1.0.59 is the newest tag and the last push was on 2026-09-21.
Frequently asked questions
How do I start the wscan WebUI?
Run ./wscan webui --webui-host=127.0.0.1 --webui-port=7002 and open http://127.0.0.1:7002. The documentation says to use 0.0.0.0 only when the WebUI must be reachable from another machine.
What is the difference between the basic crawler and the browser crawler in wscan?
The static or basic crawler is a faster HTML crawl, while the browser crawler drives a headless browser and is the option for JavaScript-heavy applications. The CLI flags are --basic-crawler and --browser-crawler.
How does passive scanning work in wscan?
Passive mode listens on a local MITM proxy and inspects traffic from a browser or client you point at it, without crawling the target itself. You generate a project CA with ./wscan genca, install ca.crt in that browser, and set the scan target so traffic to unrelated hosts is rejected.
Can I run only specific checks with wscan?
Yes, the --plug flag selects the plugin set, for example --plug=sqldet,xss, and the same restriction is available in the WebUI when you select plugins for a task.
What licence does wscan use and what are the rules for scanning?
The repository ships a LICENSE.md that GitHub does not classify as a standard licence, and the project asks you to read it before use. Scanning is limited to systems you own or are explicitly authorized to test, and a passive task requires a scan target so unrelated traffic is rejected.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/chushuai-wscan)Was this useful?