ShakerScan 2.11.1

The client for this release is shakerscan 0.11.1.

A patch release: security and correctness fixes, longer time budgets for thorough and deep Scans, roomier Hunt profiles, reliable Hunt recording, and dependency updates. No schema changes.

Security

  • Login attempts and state-changing GETs need the state-changing permission. Active Nuclei templates that sign in by GET ran in any Scan with active testing (tomcat-default-login sends its password list in an Authorization header), and a login attempt can lock the account. A template tagged default-login, or one that sends a username or password in a header or the body, or a password in the query string, now needs state-changing HTTP whatever its method. So do the GETs reviewed as changing state: one sets a device password, one restores a default admin password, one changes a plugin's settings, one registers a user, one adds an administrator. 56 bundled GET-only templates of the default selection move behind it. The Scan form and the terminal's --allow-state-changing-http help say so, and the step's receipt counts them (template_selection.default_login).
  • Exploit templates need the state-changing permission. The active template sweep ran exploit templates sent by GET in any Scan with active testing: 68 of the default selection, 13 of them tagged rce (one puts ;curl ...; in a parameter, another a Runtime.exec expression in a path). Only a verify Scan held them back. A template tagged rce, lfi, sqli, ssrf, ssti, xxe, deserialization, code-injection or cmdi, or one that runs a code, JavaScript or flow step, now needs state-changing HTTP in the sweep as well. With the permission the sweep runs the same templates in the same windows as before. The Scan form and the terminal's help say so, and the step's receipt counts them (template_selection.exploit_class).
  • Go-based scanner and Model Intake tools use patched compilers. Go 1.27.3 and 1.26.10 address CVE-2026-111943, which removes trust in affected intermediate certificates. Builder images remain pinned by digest; the image vulnerability gate checks the compiled tools.
  • shakerscan record never follows a redirect with the API token (client 0.11.1). The recorder's HTTP client followed 301, 302 and 303 answers and re-sent Authorization to the Location host, including another origin and from HTTPS to HTTP. It now refuses every redirect, like the other client commands, and names the refused origin.
  • Copyable commands pass every target-chosen value as one shell word. The reproduce, verify, replay and check commands on finding pages could carry an unquoted URL, parameter or payload chosen by the target, which a shell would expand when pasted. Replay commands stored before the upgrade are rebuilt from their stored inputs when read.
  • Capture replay withholds credentials carried in URL paths. Magic-login, share, invite and reset links, cookieless session ids, long random tokens and userinfo in a captured URL are no longer replayed, and copies of the page URL in Referer, Origin, HX-Current-URL, Next-Url, query values and bodies lose the credential. Captures recorded before the upgrade are treated the same way when replayed. The display mask used elsewhere is unchanged.
  • Session headers stay on the target's origin across redirects. A Scan authenticated with an API-key or other custom session header sent that header to whatever origin a redirect named: curl, httpx, aiohttp, sqlmap, dalfox and nuclei drop only Authorization and Cookie on a cross-host redirect, and an inline -b cookie string goes everywhere. A request that carries such material now follows a redirect only to the same scheme, host and port, or to the same host upgraded from http on port 80 to https on 443; on that origin it follows exactly as curl -L does (output, request bodies, cookie jar, --max-redirs). Authorization, Cookie, -u and --oauth2-bearer keep curl's own behaviour, since curl 7.83 and later strip them itself. Trade-off: sqlmap, dalfox and nuclei runs that carry the session do not follow redirects at all, and a curl request with a custom session header stops at a redirect to another origin, so a check whose proof sits behind such a redirect is not reached in a custom-header session.
  • Browser sessions send their headers to the target's origin only. Playwright's extra headers went with every request a page made, including images and scripts from other origins and redirect hops to another origin, which no route handler sees. A browser context that carries session headers (the XSS browser proof, the authenticated browser crawl, the interactive browser session once a header is set, the AI widget target) is now confined at the network layer: all of its traffic goes through a local proxy that admits only its bound origins (scheme, host and port, or the same host upgraded from http on port 80 to https on 443) and refuses every other connection before it is made. The headers stay the context's own, so Chromium keeps its streaming, CORS, cookie and size behaviour; an HTTPS connection elsewhere is refused before any byte of the request is sent, and a plain-HTTP one reaches only the local proxy. The cost: such a context loads nothing from another origin (a CDN, a third-party script, a redirect to another site). Each refusal is listed in the Scan's coverage, the browser crawl's and XSS proof's results, the interactive session's action result and the AI widget's evidence, without changing a family's status: session_cross_origin_refused, and session_navigation_redirect_refused for a navigation a bound origin redirected elsewhere. These confined contexts are granted Chromium's Local Network Access, so an internal application on private addresses loads; private_network_subresource_blocked is listed where Chromium still refuses one. A refused connection is closed unanswered, so the page sees a network error (never a response that would read as the other origin's), and WebRTC is disabled in these contexts, since it sends UDP outside any proxy. A context that carries only cookies is not confined. A custom session header makes a cross-origin request between two bound origins non-simple, so Chromium preflights it, as it did before. When an interactive (Hunt or AI verification) browser session is given a session header, it moves to a confined context that keeps its cookies, localStorage and current URL, but not its sessionStorage or any other page the user had open in that session.
  • Scan and Hunt browser actions keep trusted headers on the bound origin. The browser capability's pinned proxy admitted the target's own address written as a host, so a redirect to that address as another origin received the trusted headers Chromium carries into redirect hops. The pinned proxy of a hostname target now refuses that address as a destination and counts it on its receipt (address_literal_destinations_refused); every scanner on that path (sqlmap, katana, nuclei, dalfox, httpx, ffuf) addresses it by hostname. The browser's tunnel also admits only its bound origin's scheme (a TLS record for https, plaintext for http), so an https origin's redirect to http on the same port is refused (scheme_mismatch_refused), and WebRTC is disabled in that browser. The action's result and the Scan's coverage list session_cross_origin_refused and session_navigation_redirect_refused when the proxy refused a connection.
  • Dalfox does not use its headless browser with a session. Its headless verifier and --deep-domxss run a Chromium outside any proxy that adds the session headers to every request a page makes. A session-carrying run now passes --skip-headless and no --deep-domxss, and the Scan's coverage lists session_header_withheld_from_headless.
  • Known limit of browser sessions. A Scan or Hunt browser XSS proof that sends its payload in a request body follows a 307 or 308 redirect the way the browser does for the page's own navigation, so the redirected request is a GET without the body.
  • A refused-redirect hint names only a plain origin (client 0.11.1). The suggested shakerscan connect <origin> / --url <origin> used the Location host as sent, so a crafted Location produced a command that ran shell syntax when pasted. The origin is rebuilt from a validated host and port and quoted, or the hint says <origin>.
  • Path-segment SQL injection replay commands are quoted like the others. Replay commands for a marked path segment (/products/1*) passed the stored URL to the shell with control characters and a backslash-quote intact.
  • A token used as a bare query name is masked. A URL carrying a credential as a query key without a value (?<token>) showed it in capture request lists, replay plans and receipts.
  • Copy commands stored by earlier releases are rebuilt or shown as text only. A finding's stored manual verification commands are rebuilt from the evidence's URL and path when read; a stored command that cannot be rebuilt or checked is shown as recorded text without a copy button.
  • The client shows server text without terminal control characters (client 0.11.1). Public check results, instance health answers and error messages could carry escape sequences or a carriage return that rewrote the terminal; they are shown as visible text now.
  • aiohttp 3.14.5 in the scanner image, for CVE-2026-111064 (GHSA-2g87-pp6c-29x5, high), published after 2.11.0 (fixed in 3.14.4).
  • certifi 2026.10.19 in every Python image and tool lock, for GHSA-6q48-6c7w-9gw2 (critical): 2026.7.22 bundles emSign root certificates that were compromised. The image vulnerability gate refused the older version. pip 26.2.2 in the Model Intake image's pip-audit environment, whose vendored copy of certifi carries the same fix.
  • filelock 4.2.4 in every Python lock that carries it (the scanner image, the Model Intake image's pip-audit environment, the locally built Firecracker guest and the CI pip-audit tool), for CVE-2026-112940 (GHSA-v4x8-3wr5-3wq8, high) and the other filelock advisories fixed in 4.2.1, published after 2.11.0. The image vulnerability gate refused 3.32.2. tldextract, CacheControl, huggingface-hub and torch, which pull it in, all accept 4.x. The scanner and API images also remove the Playwright base image's virtualenv and its dependencies, which nothing in them uses: virtualenv requires filelock older than 4, and the base's filelock 3.32.5 stayed installed beside the locked version. Their builds now fail when a Python package is installed twice.
  • Next.js 16.4.1 in the UI image, for GHSA-2464-fhr4-w9xw and GHSA-673q-mjm6-r5xr (critical) and CVE-2026-94482 (high), published after 2.11.0. The image vulnerability gate refused 16.4.0.
  • Go scanner tools build with patched echo and compress libraries. echo 4.16.1 in dalfox, for GHSA-cxf3-9h37-7rr8 (critical) and GHSA-9969-6676-49h5, GHSA-f48p-c9v4-h7xv and GHSA-h7wr-284f-5qvc (high), published after 2.11.0; klauspost/compress 1.18.7 in gungnir, httpx, katana, naabu, subfinder and tlsx, for CVE-2026-63209 (high). The image vulnerability gate refused the older versions. The tools' own versions are unchanged, and the image build now stops if a library lift would move one.

Fixes

  • A pre-2.6 install is refused instead of starting on a new, empty database. An install that still holds PostgreSQL 16 data in postgres-data and has no migrated postgres-cluster no longer passes the start checks; the refusal names the backups to restore from. This corrects 2.11.0, whose notes said such a start refuses. redis reset refuses in that state before it changes anything.
  • A pre-upgrade backup is taken when the release record does not name the running API's image (running or stopped), or when that container cannot be inspected.
  • TLS legacy-protocol probes run only when the action's reservation pays for them.
  • A parallel Scan's summary covers every part when a finding has no CVSS score (a verified XSS) or gives it as text, or carries its OWASP or CWE labels as a list; the parent kept one part's summary. The merge no longer copies the summary grader's compliance block over the report's.
  • An open-redirect probe that got no answer stays inconclusive and is probed again on resume, instead of counting as examined.
  • Exposure service pages give a device and its host target, which share an id, their own inventories.
  • Opening a browser capture by an IPv6 target URL finds the target.
  • A credentials link with ?target=<url> reads search pages until it finds the origin; a failed read is reported as such, not as a missing target.
  • A browser XSS proof or retest finishes when the headless browser stops answering. Opening the proof page had no time limit, so a browser that stayed connected but stopped answering held the Scan's proof, or the worker's retest, for good. A browser that does not start within 60 s, or open the page within 30 s, now fails that proof and is replaced for the next one; the retest of a verified XSS finding is then inconclusive and says so. The browser kept between proofs now belongs to the event loop and thread that started it and is closed when that loop ends: a process that ran proofs in a second event loop was handed the first one's browser, on which every call waited forever.

Finding accuracy

  • A retest uses only the prover of the finding's own kind of check. Title words matched anywhere chose the prover ("source" contains "rce"), so exposure, header and template findings were retested with command-injection or JWT payloads. The finding's tool now decides; a title is read, by whole words, only for an earlier scanner's attack checks. A finding with no prover, or only an AI verifier, offers no retest and says why (retest_unsupported_reason); run a new Scan, or the Hunt, again. GET /findings/{id} adds retest_active.
  • A verified SQL injection, NoSQL injection or XSS finding is retested with the request its proof sent. The retest read neither the tested field the proof recorded nor the route's site, so it sent its payloads to the bare route with no parameter and could never conclude; a retest request was refused with "The target's authorization does not cover the host". The retest now rebuilds the proved request (the route on the target's site with the tested query parameter, or the page with the browser proof's own payload, checked against the digest the finding kept) and runs the check that proved the finding, a page judged by the browser proof's own marker: still vulnerable when it reproduces, likely fixed when it does not and every request was answered, inconclusive otherwise (a refusal, a service that is unavailable, rate-limiting or no longer serves the page). A finding whose request cannot be rebuilt (a path segment, a templated route such as /products/{int}, a request body, a recorded request whose values were not kept, a fragment) offers no retest and says what is missing (retest_unsupported_message); so does a potential SQL injection or XSS from a request differential, which keeps no route. New SQL and NoSQL proofs record the site they were proved on (origin).
  • An AI retest verdict is at most inconclusive. An AI "likely fixed" or "false positive" could refute a Hunt candidate or close a finding; it is now inconclusive, with the model's note. The finding page no longer offers an "AI only" retest (retest_modes lists tiered and deterministic); the API still takes mode=ai.
  • Reflected XSS is browser-proven again. The Scan's browser proof declared an adapter identity the capability registry did not accept, so every proof was rejected before it ran and no "Verified cross-site scripting" finding could be produced. A proof that breaks now leaves its family partial and the grade provisional, instead of complete.
  • "Nothing to test" needs a crawl that ran. When every crawl of a Scan failed or never started, its selected XSS, SQLi and NoSQLi checks found no candidates and read complete (no_candidates) with a reliable grade, although nothing was tested. They now read partial with the reason discovery_failed, so the Scan is not complete and its grade is provisional. When one crawl ran (the static crawl on a target without a browser runtime), an empty candidate list still means there was nothing to test.
  • A CORS policy that reflects any origin is detected. The receipt redactor rewrote the echoed Access-Control-Allow-Origin before the check compared it with the probe origin.
  • Reproduce and check commands work from masked evidence. Engine-authored probe values (the open-redirect target, the GraphQL introspection document) are kept; a stored URL whose query the scanner masked is not replayed as stored.
  • Active Nuclei templates run against the scanned URL, not a discovered endpoint's path, on every profile, so host-level templates such as exposed-service checks match. Each active-template attempt now targets the directory of the URL the Scan targets (/app/login.php runs at /app/), and then the root of every other origin the Scan discovered; plans, holds and attempts are unchanged, only the URL each attempt targets. A host with more endpoints than template windows no longer repeats a window at the same URL. A finding a 2.11.0 Scan matched at a discovered subpath with a template that asks only its base URL (108 of the permitted templates) may not be reproduced by 2.11.1 and then reads as fixed: those templates now run at the Scan's base and the other origins' roots, not at discovered subpaths.
  • Active-template coverage counts every origin. nuclei_active.template_windows counts (origin, template window) pairs and lists each origin's run and total, so a second origin that ran 11 of 39 windows no longer reads as 39 of 39. A thorough or deep Scan whose active templates did not all run at one of its allowed origins reads partial, with the reason template_windows_incomplete_on_origin. Each parallel scan part starts its template windows at its own offset, so alone it runs as many windows as before and the parts together run at least as many; the merged coverage counts each (origin, window) pair once and says how many pairs two parts both ran (repeated_pairs); an attempt resumed from before the upgrade is kept and never sent again, and a window it ran at a discovered endpoint's path is listed apart (legacy_windows_run), not counted at an origin.
  • Known limit: a thorough or deep host whose endpoints span more than one origin and whose plan has fewer than 39 positions per origin reads partial on active templates (template_windows_incomplete_on_origin): 2.11.1 does not change the plan, and a later release will plan the windows per origin.
  • Active Nuclei never runs a template that requests a heap dump, on any profile. A test checks the bundled template set, so a template update cannot bring one back.
  • A SQLi point sqlmap marked in a URL path segment (/products/1*, #1*): the brief and replay commands use the unmarked URL with the payload in that segment, the database name comes from sqlmap's summary line, and a retest declines (inconclusive, nothing sent) because the retest prover injects only query and form parameters.
  • Each variant of one form is tested as its own candidate. A form seen as declared, as crawled with its hidden fields and as a JSON variant has one candidate identity (its route, method and the field that anchors the body), and the three shared one attempt record: a variant could take another's finished SQLi techniques as its own, so the crawled form's extra fields went untested while the candidate read complete; a variant whose attempt ended differently from another's could fail the verifier, the SQLi proof, the XSS browser proof or the NoSQLi check; and one variant's verifier signal sent all three to the proof. Each variant now keeps its own attempt, techniques and count of what it sent in each of them, and a signal sends only the variant that produced it to the proof; the first keeps its earlier record, so a scan resumed across the upgrade keeps what it already ran (a signal recorded before the upgrade still sends every variant). The anonymous-versus-signed-in route comparison probes a route seen with several bodies once and counts each of its entries, so coverage no longer reads them as never attempted, and the exposure sweep likewise reads such a URL once instead of once per body.
  • A scan allowed state-changing requests no longer fails to plan its verifiers when two forms post to one action with the same rank (a login page's form and a sidebar sign-in widget, a search form seen with and without a filter): building the candidates raised TypeError at admission, in a continuation round and in the parallel fan-out. Both forms are now candidates.
  • TLS: a weak signature or key no longer also raises "chain is not trusted"; the certificates the server sent are read correctly on Python 3.12; weak-cipher evidence comes from the weak handshake; reproduce commands show the weakness.
  • Finding pages: a page titled "Directory listing" is no longer a verified listing without a real listing; GraphQL findings get GraphQL commands and advice; JDK 9+ stack frames are recognized; non-secret key names are not masked; weak-CSP advice never loosens framing; Tomcat's Apache-Coyote is not reported as Apache httpd; DNS posture findings use the target's origin; the coverage page and the stored grade agree.

Authenticated scans and Hunts

  • A credential profile left with no capability list now covers the crawl. On an HTTP identity it grants every read-only capability that uses the identity (probe, crawl, browser crawl, content discovery, spec ingest, passive templates, safe collection replay, browser navigation); sign-in kinds also keep their session capabilities. Active verifiers are never a default and still need an approved elevation. Profiles saved with a list keep it. New Scan says when the attached profile leaves the crawl or a selected verifier running anonymously.
  • A crawl or content discovery that carries a session leaves action links alone: sign-out, delete, disable, reset and similar paths and query actions are not requested with the session.
  • XSS and SQLi verifiers no longer fail with zero requests when a replayed collection request and a declared endpoint share a route.
  • A signed-in crawl starts where the signed-in area is. A crawl or browser crawl that carries the session also starts at the page the sign-in landed on and at the GET seed endpoints on its origin (pages before API routes, at most 20), so an area linked from nowhere the target's root reaches (a /hub/ behind /hub/login) is crawled with the session. The target keeps its whole time box: the static crawl runs its inputs side by side under its one rate limiter and request ceiling, so the seeds share the target's requests while they run (a seed that finishes returns its share), and the browser crawl adds only its first seed, so the target keeps half of that box. Action paths (also percent-encoded), route templates and the profile's own sign-in page are never a seed, and a seed the crawler cannot take is left out instead of failing the crawl. A static primary identity (a bearer token, a header or a cookie), which has no sign-in step, starts from the same seed endpoints, whether the Scan carries one profile or two.
  • The credential profile's own sign-in endpoint is tested at a lockout-safe pace. An authenticated balanced scan of a test application sent about 1,460 injection requests to the sign-in form it used. On that one endpoint (a request with a body to the sign-in path, its routing query included, or to the form's action) the SQLi and XSS verifiers keep their full payloads (sqlmap level 2 / risk 2, Dalfox's own payloads for the body), send at most 20 requests a minute, one attempt at a time, and share one probe set per verifier across every candidate and round of the scan, sized at twice what a verdict was measured to cost: 946 sqlmap requests per tested field (twice its level-2 ladder of 473) and 64 Dalfox requests per field, at most four fields, Dalfox stopping at its first verified finding. A check the probe set leaves without a verdict is inconclusive, never negative, and its family reads partial with the reason auth_endpoint_probe_limited. A GET to the sign-in page and every other endpoint keep their full holds, and plans are unchanged. sqlmap's stages on the endpoint are paced and recorded at its pace, so the latency they measure is that endpoint's and paces no other candidate. The checks that send from the worker itself (the SQLi and XSS request verifiers, the SQLi proof, the NoSQLi check and the XSS browser proof) keep the same pace there and each draw from a probe set of their own, twice what one attempt sends per field, and three times that again for the checks whose attempts test every field (the NoSQLi check and the request verifiers): 28 for the SQLi proof, 24 for NoSQLi, 12 for each request verifier and 2 for the XSS browser proof. Each set is the endpoint's four-field set whichever variant of the form draws from it, so the declared form, the crawled form and a JSON variant all fit, twice over, in any order (sized by each variant's own fields, the crawled form's NoSQLi attempt could spend a two-field variant's set, and a fresh scan left up to two variants untested). Such a check runs on the endpoint only when its slice can pace the whole attempt (a sign-in form's SQLi proof is 8 requests, so 32 seconds); otherwise it waits rather than running short, and a proof no slice could pace leaves the family partial (auth_endpoint_probe_limited).
  • How much the sign-in endpoint may receive. Up to 4,352 requests per scan (the four-field sets: 3,784 sqlmap, 256 Dalfox, 112 SQLi proof, 96 NoSQLi, 48 for each request verifier and 8 XSS browser proof), at most 20 a minute, so about 3 h 38 min if the scan's budget lets all of them run; clearing a two-field form with no SQL injection takes about 950 sqlmap requests (about 47 minutes). In a scan split across parallel parts each part paces the endpoint by the number of parts, so together they stay at 20 a minute, but each part holds its own probe sets: N parts may send N times that total. The bound covers the injection verifiers and the checks above; the active templates (run by default on thorough and deep) are neither paced nor counted there, so a template for a product whose sign-in path is the profile's own (the default-login ones, for example, when state-changing HTTP is allowed) may send its sign-in attempts to it as well.
  • Expect XSS to read partial on an authenticated scan that tests its sign-in form, and the grade provisional, unless an XSS is verified there. Dalfox sends its whole payload set to a request body whether or not a field reflects (1,347 requests for a two-field sign-in form, measured in the scanner image), ten times the 128 its probe set holds there, so its attempt on the sign-in endpoint cannot end negative: it is inconclusive (auth_endpoint_probe_limited). Every other endpoint is unaffected.
  • A credentialed scan admitted on the target's standing authorization records a credential-tier receipt, as New Scan does, whether or not the request named that authorization: the scan is bound to a receipt derived from it (same scope and approver, a seven-day bound, the scan and the source named in it), which covers the scan's credentials and the source's tier only and authorizes nothing else. It ends with its source: every revocation path revokes it (also one filed under an application service from its host's authorization), and the running-scan check and the worker's credential check, the only check a passive credentialed scan has, refuse it once the source no longer stands. Who may start the scan is unchanged.
  • A browser session the session store refuses ends the Hunt action with its reason.
  • A scan refused at admission leaves no target behind and keeps its blocked audit record.
  • ASM: a completed recon fills the target's endpoint inventory from what the scan observed, without probing the target again; a target without an inventory can be opened from the ASM page; the continuous-testing controls are hidden where the deployment does not offer them. Several web apps on one host (different ports) are each listed in their domain group, so every target with an inventory appears on the Coverage page. GET /targets/grouped therefore lists a domain's other root targets, such as other ports of one host, in subdomains and counts them in subdomain_count, total_count, total_findings and total_targets; best_score and latest_scan remain the group root's own values (the root is the last root target in URL order). Only an ASM batch or recon scan is labelled as hidden from the Scans list, in the view and in the 409 an ASM action gets while a scan runs.
  • Client 0.11.1: shakerscan scan help prints the options instead of submitting a scan, under its own name (usage: shakerscan scan); hunt call exits 2 when the action was refused; a certificate the client does not trust names SSL_CERT_FILE, once per line; a state-changing scan from the terminal relies on the target's standing authorization; a submitted scan prints the families it runs and, on fast or balanced without them, how to add active templates (--include-family nuclei_active); hunt start --credential-ref takes the short slot names (primary=, secondary=, ...). The Hunt page keeps decided permission requests visible.

Hunt recording and budgets

  • Only the duration ends a Hunt. A used-up safety ceiling (state-changing requests, TCP ports, hosts, UDP ports, out-of-band interactions, device fragility) or any other used-up limit refuses only the actions that need it, each with a budget.raise request a person can approve (a dimension whose permission is off is refused as before), and the Hunt keeps running: on 2.11.0 it stopped the whole Hunt, read-only work included. Only max_duration_seconds running out ends a Hunt as budget_exhausted. The ceilings keep their values. Hunts that stopped on another dimension under 2.11.0 resume as before.
  • Ports are counted once. A port a Hunt already attempted (listed in a sweep, or reported open by one) costs nothing more to fingerprint, check or inspect, and a tls.inspect is one port instead of its ten handshakes. Device fragility still counts every connection: on a device Hunt a re-sweep or a tls.inspect handshake costs a fragility point and a device request even when the port costs nothing more. A network action stopped part way is charged the new hosts and ports of the commands that started. A mutating candidate.verify is charged the requests and state-changing requests its workflow reports it sent, not its whole 24 / 12 hold.
  • SNMP inspection is charged what it uses. service.snmp.inspect is charged its UDP port and, on a device, its fragility estimate for each address it reached; 2.11.0 charged no UDP port and one fragility point.
  • Limits that outlast the duration. Every call that runs is charged at least one second of the duration, and call, verification, browser and HTTP limits now cover the whole duration at the most one call can be charged in that second: fast 1 h, 3,600 calls and verifications, 115,200 browser actions, 460,800 requests; balanced 4 h, 14,400, 460,800 and 1,843,200; thorough 24 h, 86,400, 2,764,800 and 11,059,200. Every call stays paced by its tool. A request refused before it sends is charged the wall it took (at least a second), not its 15-second hold, and a paced Dalfox or sqlmap run without its own request log is charged its rate over the seconds it ran, not its whole hold.
  • Approval-gated calls may use the whole call allowance (max_active_actions 3,600 / 14,400 / 86,400, was 4 / 20 / 80); every such call still checks its approval receipt. TCP ports, state-changing requests, hosts, UDP ports, out-of-band interactions and device fragility keep their values.
  • The default profile needs no raise for ordinary web work. Logging in, crawling, content discovery, verification and recording draw no start warning; a remaining warning names a safety ceiling that matters only when the plan needs that capability.
  • Useful verifier reservations on every profile: sqli.verify reserves 900 requests / 540 s and xss.verify 1,800 requests / 480 s. A Hunt that cannot fund one asks for a budget raise instead of starting a shortened verification.
  • Recording never stops a Hunt. Findings and candidates share max_candidates (one record per call: 3,600 / 14,400 / 86,400) and need no calls or traffic; a full allowance refuses only that write, a refused write spends nothing, and an unfinished budget-exhausted Hunt can still record. A Hunt's findings hold at most 64 MiB, and its candidate writes stop at 256 MiB of candidate records; a write past either is refused and the Hunt keeps running.
  • Findings stay on the service their evidence came from. A relative path keeps the target's own service when it is evidenced and otherwise follows the single evidenced service, including a nonstandard port; a finding created without a path is located on the evidenced service too. Device Hunts can save findings with a path: a device's address or name is a host, and a cited device HTTP probe names its service. A location is stored in one spelling, so HTTPS://EXAMPLE.TEST:443/x and https://example.test/x are the same finding. Malformed context origins and invalid ports are refused as input errors.
  • A repeated finding returns the existing record unchanged, spends nothing and says whether newly cited evidence was attached; findings.update attaches it.
  • Finishing proposes reusable steps; it never saves them. The Hunt's newest completed steps that can be replayed join one rolling proposal per target for shakerscan knowledge review, outside the Hunt's own proposal allowance, for a person to review before accepting. A step is left out when its inputs carry query values, headers, bodies, browser input, sessions, credentials or other hosts, or its path names an action, a credential, a code or a token, or carries a payload character or dot segment. Accepting the proposal names the digest of the text reviewed, so steps a later finish added are never accepted unseen. The debrief and results page report the outcome, and a failure does not lose the debrief.
  • Known limit: on a slow sign-in endpoint a Hunt sqli.verify can count about twice the requests that reach the target. With the longer 540 s window it can then reach its 900-request ceiling, end with "above the hard ceiling" and be charged its full hold; run it again on a narrower parameter set. Counting it once is planned for the next patch.

Time budgets on thorough and deep

fast and balanced Scans plan exactly as in 2.11.0; a test fixture generated on 2.11.0 pins them.

  • Active Nuclei runs every template window. Each endpoint attempt runs one window of 30 templates. 2.11.0 ran as many windows as a host had endpoints, so a small host ran 1-6 of the 39 windows of the active set. thorough and deep direct Scans that include active templates now run all 39. Coverage shows nuclei_active.template_windows (windows run of total) on every profile, and so does the scan page; a sweep whose every (origin, window) pair ran is complete in the report and on the page, not partial for endpoints the plan left without a position (the templates run at the origins, not at each endpoint).
  • The standard active set runs the active templates on thorough and deep. The standard_active preset (New Scan's "Standard active", and --active-testing in the terminal) now adds nuclei_active on those two profiles; fast and balanced resolve and plan exactly as before. A scan whose request ceiling was lowered below 8,000 (twice the 4,000 the sweep holds at admission), since that hold might not fit beside the scan's own admission traffic, or with state-changing HTTP whose mutation ceiling was lowered below 5,000 (the sweep is the only mutation held at admission, and thorough's own ceiling is 6,000), keeps the earlier set; --include-family nuclei_active still adds it, and --exclude-family nuclei_active removes it on any profile. The scan contract states the addition and its floors (family_preset_profile_additions, family_preset_addition_floors); New Scan's preview takes the ceilings typed under Advanced, and the terminal names the lowered ceiling when it leaves the active templates out.
  • SQLi extensions are sized to the techniques left. 2.11.0 gave an extension the lane's whole time share and paced its requests to fill it (6.25 s per request on thorough). Extensions now hold what the remaining techniques need, at about 0.5 s per request.
  • Later rounds step down to a slice the remaining budget affords instead of planning one the allocator refuses.
  • Request-body candidates get at least one attempt when the Scan may send state-changing requests, through a floor in the first slice or a floor extension.
  • A lowered max_duration_seconds also lowers the tool wall when no tool wall is given.

Expect thorough Scans of small hosts to take about 25 minutes instead of about 4.

Documentation

  • The OWASP coverage matrix counts only what a Scan runs. It marked SSRF, command injection, SSTI and XXE as covered, but no Scan family tests them: the ssrf and rce families are planned and a Scan refuses them, and SSTI and XXE have no family. A10 and API7 (SSRF), A04, A08 and API6 now read "not covered". A06, A07, A09, API2, API3 and API5 drop from covered to partial. The other rows name the family or baseline check behind them and say which are opt-in, need credentials or need state-changing HTTP. API3 names its one narrow check, the plaintext-password account-record exposure. The matrix also gives the number of active Nuclei templates for named products that touch other categories; no row is marked covered for them. It says how much of the active pack a Scan runs: a fast or balanced Scan runs one 30-template window per endpoint, so a small host runs only part of the pack. The functionality reference lists all eleven Scan families, and a test keeps both documents in step with them.
  • The Scan contract describes xss, sqli and bola as they run. GET /scan/contracts described xss as "Reflected, stored, and DOM XSS probes" and sqli as having extraction depth. No Scan action looks for stored XSS or extracts data, and the descriptions now say so. It said bola "Requires Lab/deep policy"; a Scan needs active testing and two distinct credential profiles that allow authz.verify, and only a focused Continuous ASM batch needs Lab/deep policy.

Dependencies

  • UI: lucide-react, postcss, Playwright 1.64.
  • CI: CodeQL actions 4.38.3, actions/download-artifact 8.0.2, actions/upload-artifact 7.0.2 (with the workflow changes it needs).
  • posture: @types/node 26.6.5.

Upgrade notes

  • No database schema changes and no new settings.
  • A Scan with active testing but without state-changing HTTP no longer sends default-login or password attempts, or exploit templates, so it reports no default-login findings and no findings from those templates; allow state-changing HTTP to test them. Authentication-bypass checks that name an account but send no password still run. Its active template sweep has 22 windows instead of 26 (636 templates instead of 760).
  • Template, exposure, posture and Hunt findings no longer offer a retest: run a new Scan, or the Hunt, to check them again. A Hunt's verified XSS was retested with a Scan prover's default payload and check; it is now checked again by running the Hunt. Neither do verified injection findings whose request cannot be rebuilt, potential injections from a request differential, nor a SQL injection sqlmap found in a URL path segment (its retest sent nothing).
  • thorough and deep Scans run longer and send more requests to each host than on 2.11.0 (for the active template windows, about 2,200-2,600 requests per small host at the unchanged 2 requests per second). Their standard active set now includes the active templates, so a thorough or deep scan with active testing runs them unless it excludes nuclei_active.

This page is rendered from docs/releases/2.11.1.md in the open-source repository at v2.11.1. When documentation and implementation disagree, the code, database schema, and tests at that tag are authoritative.