Commit Graph
589 Commits
Author SHA1 Message Date
TigahandGitHub 03b023eba5 fix(httpx): stop leaking cookie/auth headers on cross-host redirects (#332)
roundTripper.RoundTrip injects the global cookie, custom headers and
user agent on every hop, including redirect requests. because that
injection sits below the client's redirect handling, it undoes
net/http's own stripping of Cookie/Authorization/Www-Authenticate
when a redirect crosses hosts, so a target that 302s to an attacker
host would receive the scan's secret cookie and bearer token.

req.Response is only set on requests net/http built to follow a
redirect, and its Request field is the prior hop, so comparing hosts
there is enough to detect a cross-host hop without any extra state.
skip the sensitive header classes on that hop and keep injecting
everything else as before.
2026-07-22 12:53:32 -07:00
TigahandGitHub 281e59cc9e feat(output): add routable Sink for per-scan output capture (#331)
* feat(output): add routable Sink for per-scan output capture

introduce a Sink (writer plus an interactive flag) that the package-level
chrome funcs delegate to via DefaultSink, and give ModuleLogger a sink it
writes through. a caller can now hand a scan its own Sink so that scan's
chrome lands on a dedicated writer instead of the process-wide default.
default routing is unchanged: Info/Warn/Module and friends still write to
the same stdout/stderr sink SetSilent configures.

* fix(output): trim redundant sink doc comments

drop comments on thin package-level wrappers that just forward to
the Sink methods, and tighten the Sink methods' own comments to
note the api-mode no-op instead of restating the func name
2026-07-22 12:53:23 -07:00
TigahandGitHub 1f6d0f7cf0 fix(scan): percent-encode dork queries before search (#327)
the google-search client only swaps spaces for '+' and drops the term
into the query string verbatim, so a dork carrying a raw '#' or '&' cut
the request url short at the fragment or split it into stray query
params. the search then ran against a fragment of the intended dork.
encode the term up front so the whole dork survives.
2026-07-22 12:53:04 -07:00
TigahandGitHub a35f3bc71f fix(scan): truncate shodan banners on rune boundaries (#326)
truncateBanner sliced by byte offset, so a multibyte utf-8 rune landing
across the limit was cut mid-sequence and emitted invalid utf-8. shodan
banners routinely carry non-ascii bytes, so this corrupted logged and
printed output. count runes instead so the cut lands on a boundary.
2026-07-22 12:52:55 -07:00
TigahandGitHub 41f35acd66 fix(scan): honour the caller context when downloading the ports list (#324)
* fix(scan): honour the caller context when downloading the ports list

the common-scope ports fetch built its request with context.TODO(),
so a cancelled run or -max-time deadline could not abort a hung list
download even though the dialer already used the passed context.

* test(scan): drop redundant comment restating release channel receive
2026-07-22 12:52:21 -07:00
TigahandGitHub b72e076fc7 fix(js): bound next.js manifest fetch with the scan timeout (#323)
GetPagesRouterScripts fetched _buildManifest.js with httpx.Client(0),
which sets no client timeout, so a slow or hostile manifest host could
hang the whole scan on the response read. thread the caller's scan
timeout through instead, matching every other fetch in the package.
2026-07-22 12:52:12 -07:00
TigahandGitHub 22935328b5 fix(scan): stop one over-long url from dropping the wayback feed (#321)
the wayback line scanner capped each line at 1mb, so a single archived
url with a huge query string made scanner.Err return ErrTooLong and the
whole feed was discarded. read lines without a per-line cap, still bounded
by the passiveMaxBytes read limit.
2026-07-22 12:51:57 -07:00
TigahandGitHub fa84b3f7a5 fix(frameworks): match cve entries against a bare-major version (#320)
drupal's generator meta only exposes a bare major, so the extracted
version is "10"/"9". versionAffected only matched when the affected
entry was a component-prefix of the version, never the reverse, so a
bare major never matched a dotted affected entry and every drupal cve
was silently missed. match on dotted boundaries in either direction.
2026-07-22 12:51:48 -07:00
TigahandGitHub 5ebeb89f93 feat(modules): add case-insensitive word matcher option (#319) 2026-07-22 12:51:39 -07:00
TigahandGitHub 356b16fc63 feat(modules): embed built-in modules in the binary (#316)
modules loaded only from an exe-adjacent modules/ dir or ~/.config, so a
bare `go install`ed binary shipped zero modules. embed the modules/ tree
and fall back to it when no filesystem builtin dir is found.

the on-disk builtin dir and the user-override dir still take precedence,
so a dev tree and a release that ships the folder alongside the binary
keep working unchanged. the embed directive lives at the repo root
because go:embed cannot reach a parent directory, and it registers the
filesystem with the loader through a package hook to avoid an import
cycle back into the root package.
2026-07-22 12:51:20 -07:00
TigahandGitHub f570dceadc feat(report): add json findings report with -json (#313)
sarif and markdown were the only structured exports and both drop the
per-item detail the normalized finding model already holds. add a -json
flag that writes the run's findings as a json array, one object per
finding with its target, module, severity, key, title and evidence.

carry detection confidence onto the finding model too so framework
detections report their score. it rides into the json report and is
omitted for scanners that carry none. the -silent line format and the
snapshot on-disk shape stay unchanged, since the report uses a dedicated
json view rather than marshalling the finding struct directly.
2026-07-22 12:50:08 -07:00
TigahandGitHub 5972cc9272 fix(report): map sarif level to finding severity (#312)
sarif results were hardcoded to level "warning", so a critical finding
and an informational one landed identically in code-scanning ingests.
carry the normalized severity onto report.Result and map it: critical
and high to error, medium to warning, low and info to note. the severity
is derived by flattening each module result and taking the worst rank,
and an absent severity keeps the old "warning" default.
2026-07-22 12:49:59 -07:00
TigahandGitHub a46aea12fc fix(scan): accept string-encoded jwt exp claim (#311)
numericClaim only accepted exp as a json number, but some emitters
serialize it as a quoted string. a token with a past string exp was
reported as missing exp instead of expired, an expiry false negative
and a missing-claim false positive on the same token. also parse a
string value via strconv.ParseFloat, falling back to absent when it
does not parse.
2026-07-22 12:49:50 -07:00
TigahandGitHub 5e1c029e09 fix(scan): grade hsts on the final response scheme after redirects (#310)
hsts grading was gated on the scheme of the originally requested url,
but the client follows redirects, so an http target that redirects to
https skipped the hsts check entirely and dropped a high-severity
finding. decide the scheme from the final response request url instead,
falling back to the requested url only when no response request is set.
2026-07-22 12:49:40 -07:00
TigahandGitHub e2da1bc5d8 fix(scan): tolerate ref path items when parsing openapi specs (#309)
paths was typed as a map of operation structs, so a spec with a $ref
string path item (valid in openapi 3.1) failed to decode and the whole
spec was dropped with no finding. decode path items into a generic tree
and extract per-operation security manually, treating a non-array
security value as absent so it inherits the global default rather than
being reported as an anonymous endpoint.
2026-07-22 12:49:31 -07:00
TigahandGitHub bf8c6a8643 feat(modules): add tcp module executor and recon modules (#308)
* feat(modules): tcp module executor

dial+probe+banner with word/regex/size matchers, ctx-honest watcher,
port required.

* feat(modules): add redis unauthenticated access tcp module

first shipped tcp module: sends INFO to redis (6379) and matches
redis_version in the reply, which an auth-required server never returns
(it answers -NOAUTH). extracts the leaked version.

* docs(modules): document the tcp module type

flip the http-only note and add a tcp config section (port, data,
word/regex/size matchers and regex extractors against the banner),
pointing at the redis demo module.

* feat(modules): support matchers-condition in tcp modules

tcp matchers were and-only; add the matchers-condition key (and default,
or) mirroring the http executor's checkMatchers, validated at load via the
shared validateMatchersCondition. documents the override in the combining
matchers section.

* feat(modules): decode escape sequences in tcp data payloads

decode C-style escapes (\\ \a \b \f \n \r \t \v \xHH) in a tcp module's
data value before it goes on the wire, so a single-quoted or plain yaml
scalar reaches the service with the same control bytes a double-quoted
one already would. an unrecognized escape is kept verbatim so no bytes
are silently lost.

* feat(modules): add tcp recon modules for common services

add five built-in tcp modules alongside the redis one so the executor
ships a real service set: memcached (unauth version probe), ssh and ftp
(passive banner version disclosure), mysql/mariadb (handshake exposure,
matched with matchers-condition: or across both auth plugin names), and
vnc (rfb handshake exposure). each fingerprint tracks the documented
on-wire protocol and pulls the version into an extractor.

* feat(modules): add mail and rsync tcp recon modules

add four more passive-banner tcp modules: smtp, pop3, and imap read the
greeting each mail service sends on connect (the smtp matcher requires
an esmtp/smtp marker so it does not fire on a bare 220 ftp greeting),
and rsync detects an exposed daemon by its @RSYNCD handshake. each pulls
the banner or protocol version into an extractor.
2026-07-22 12:49:22 -07:00
TigahandGitHub e8eee5c530 fix(scan): strip url whitespace before open-redirect host check (#306)
pointsAtSentinel parsed the reflected location without the whitespace
and control-byte removal a browser applies, so payloads like a
space-prefixed scheme-relative host or a tab embedded in the scheme
parsed to an empty host and a real off-site redirect was missed. strip
leading and trailing c0-or-space and all tab, cr and lf before parsing,
matching the navigation-time normalization, without removing mid-string
spaces so a same-site target is never reclassified as off-site.
2026-07-22 12:49:07 -07:00
TigahandGitHub 12067b4279 fix(scan): gate cors http-downgrade probe on https targets only (#305)
the http scheme downgrade origin was queued against every target, so on
a plain-http target the http://host probe is the target's own origin and
any correct same-origin reflection was reported as a high-severity
downgrade. mark that origin https-only and skip it when the target
scheme is not https, where a downgrade is meaningless, while still
firing it on https targets that genuinely trust the http origin.
2026-07-22 12:48:40 -07:00
TigahandGitHub 9fce39cd32 fix(scan): baseline-diff sql errors against shared templates (#304)
checkDatabaseErrors matched db error regexes against each response with
no baseline, so a site serving a shared error template, custom 500 or
waf block page containing a db error string reported an identical
high-severity sql injection finding on every probed path. capture the
main-url response as a baseline and skip, per pattern, any error string
already present in it while still checking other patterns, so only
differential probe-caused errors are reported.
2026-07-22 12:48:31 -07:00
TigahandGitHub 4f01345974 fix(scan): baseline-diff lfi evidence to stop static-content false positives (#303)
the lfi scan matched evidence regexes against each response with no
baseline, so a page whose static content matched a pattern (a blog
containing an /etc/passwd-shaped line, a code sample with <?php) was
reported as an lfi finding for every parameter and payload combination.
take one baseline get of the unmodified target and exclude, per
evidence class, any pattern already present in it, so only
payload-caused disclosures are counted.
2026-07-22 12:48:22 -07:00
TigahandGitHub 243e0aba16 feat(modules): add haproxy stats, nexus and pushgateway exposure modules (#301)
* feat(modules): add haproxy stats, nexus and pushgateway exposure modules

* chore(modules): trim redundant module header comments

drop the top-line comments on the new infra exposure modules that
just restated the id/name fields already in the info block
2026-07-22 12:48:13 -07:00
TigahandGitHub 5444090ea4 feat(modules): add chartmuseum and verdaccio registry exposure modules (#300)
* feat(modules): add chartmuseum and verdaccio registry exposure modules

* chore(modules): trim redundant module header comments

drop the top-line comments on the new package registry exposure
modules that just restated the id/name fields already in the info block
2026-07-22 12:48:03 -07:00
TigahandGitHub cb65f868c3 feat(modules): add terraform tfvars, ansible vault and ci config exposure modules (#299)
* feat(modules): add tfvars, ansible-vault and ci config exposure

* chore(modules): trim redundant module header comments

drop the top-line comments on the new iac config exposure modules that
just restated the id/name fields already in the info block
2026-07-22 12:47:54 -07:00
TigahandGitHub b4206ef269 feat(modules): add guacamole and webmin login exposure modules (#298)
* feat(modules): add guacamole and webmin login exposure modules

* chore(modules): trim redundant module header comments

drop the leading comment restating id/name on guacamole-login-exposure and webmin-login-exposure
2026-07-22 12:47:45 -07:00
TigahandGitHub 473c74da5e feat(modules): add nsq and pulsar exposure modules (#297)
* feat(modules): add nsq and pulsar exposure modules

* chore(modules): trim redundant module header comments

drop the leading comment restating id/name on nsq-stats-exposure and pulsar-metrics-exposure
2026-07-22 12:47:36 -07:00
TigahandGitHub f658fc2466 feat(modules): add hpe ilo, dell idrac and redfish exposure modules (#296) 2026-07-22 12:47:27 -07:00
TigahandGitHub 49e41e4d0f feat(modules): add vaultwarden, authentik and synapse version modules (#295) 2026-07-22 12:47:18 -07:00
TigahandGitHub 2a88881961 feat(modules): add grafana tempo and otel zpages exposure modules (#294) 2026-07-22 12:46:52 -07:00
TigahandGitHub 015f4583bb feat(modules): add node inspector, rails routes and openapi exposure modules (#293) 2026-07-22 12:46:43 -07:00
TigahandGitHub 80c2574800 feat(recon): add postfixadmin setup and panel fingerprints (#292)
detect a reachable postfixadmin setup.php (unhardened-install signal
and info disclosure, not an auth bypass since superadmin creation is
gated on a locally configured setup password) and the login panel.
markers and the setup-password form field and login field pair with
the brand string so prose mentions do not match.
2026-07-22 12:46:34 -07:00
TigahandGitHub a673eacdce feat(info): add roundcube and zimbra webmail fingerprints (#291)
fingerprint roundcube via its login-page body markers and zimbra via
its web client markers, both anded with status 200 and prose-trap
negatives to avoid matching pages that merely mention the products.
2026-07-22 12:46:24 -07:00
TigahandGitHub c24617d16c feat(recon): add kubeflow pipelines and metaflow exposure modules (#290)
detect an anonymously reachable kubeflow pipelines apiserver, whose
same api accepts pipeline run submission with an attacker-supplied
workflow manifest (arbitrary container execution), and a metaflow
metadata service leaking flow and owner enumeration. match distinctive
snake_case json keys with status 200, fail closed on empty instances
to avoid the bare-substring false-positive class.
2026-07-22 12:46:15 -07:00
TigahandGitHub b4fae85847 feat(recon): add localai and invokeai exposure modules (#289)
detect anonymously reachable localai /system (backend and loaded-model
disclosure) and invokeai /api/v1/app/runtime_config (host path and
config disclosure). both and json key markers with status 200 and
include secured-instance negatives so 401/403 boxes are not flagged.
2026-07-22 12:46:06 -07:00
TigahandGitHub 1ea8eff267 feat(modules): add s3, gcs and azure bucket listing modules (#288) 2026-07-22 12:45:57 -07:00
TigahandGitHub 9be3d4a51c fix(modules): guard ignition, web.config and appsettings against soft-404 shells (#286) 2026-07-22 12:45:48 -07:00
TigahandGitHub 251be8481e fix(frameworks): stop gatsby and svelte prose from faking detection (#285) 2026-07-22 12:45:38 -07:00
TigahandGitHub 39244b454e fix(recon): tighten directory-listing matchers to real listing furniture (#284)
require actual autoindex markup (parent-directory anchors, sort links,
bracketed parent link) instead of loose title text, so prose pages
titled "Index of /..." and iis tutorials no longer false-positive.
apache non-fancy parent-directory anchor tolerates surrounding
whitespace to avoid a false negative.
2026-07-22 12:45:29 -07:00
TigahandGitHub 64a22501c7 fix(scan): report the found url in dork results and dedupe by url (#283) 2026-07-22 12:36:38 -07:00
TigahandGitHub 6b7762fcdf fix(scan): require a real bucket listing body before flagging s3 exposure (#282) 2026-07-22 12:36:29 -07:00
TigahandGitHub 3ecc7e6f95 fix(scan): correlate cname before confirming subdomain takeover (#281)
the live-response path flagged any host whose body matched a provider
fingerprint without checking dns, so a page merely containing a
provider 404 string was reported as a takeover. resolve the cname and
only confirm when it maps to the same provider; a contradicting cname
kills the flag. providers with a body signature but no apex to
correlate, and unresolvable lookups, degrade to a medium potential
finding via a new confidence field rather than confirming or dropping.
2026-07-22 12:36:19 -07:00
TigahandGitHub 7742b8e731 feat(modules): add laravel telescope dashboard exposure module (#279)
Detect an exposed Laravel Telescope dashboard, which reveals requests, queries,
exceptions and dumped variables. Telescope aborts with 403 before render when
gated, so a 200 carrying both the Telescope title and the Vue mount id proves
the gate is open. The and-condition on the mount id keeps an unrelated page that
merely names telescope, or a soft-404, from matching.
2026-07-22 12:36:10 -07:00
TigahandGitHub 1f3708531d feat(modules): add mongo-express, redis-commander and pgweb exposure (#278)
Detect unauthenticated web database browsers, which ship with no default auth
and hand over a full data browser once reachable. Each keys on an app-specific
anchor (the ME_SETTINGS global, the redisCommander.js script, ace-pgsql.js)
under an and-condition so a branded soft-404 cannot trip it. mongo-express also
requires the database-list heading, since its login form renders the same
settings global, title and logo but serves no data.
2026-07-22 12:36:01 -07:00
TigahandGitHub 294bfb090b feat(modules): add host-header injection and xxe probe modules (#277)
* feat(modules): add host-header injection and xxe probe modules

host-header-injection sets an rfc 2606 .invalid marker in the forwarded-host
headers and fires only when it comes back inside an absolute-url construct
(href/src/action/content/url), so a benign echo into text or an attribute cannot
trigger it. xxe-error-based expands a SYSTEM entity pointing at a local file and
matches only that file's contents, so an echo of the payload (a path string)
cannot satisfy it.

* chore(modules): trim redundant module header comments

drop the leading comment restating id/name on host-header-injection and xxe-error-based
2026-07-22 12:35:51 -07:00
TigahandGitHub 2cfee4cc3f feat(modules): add disable-redirects http option (#273)
A module that fingerprints a redirect itself (open-redirect proofs, a Location
header, a 3xx status) could not see the redirect: the executor followed 3xx to
the final response before matchers ran, with no way to stop. Add an opt-in
disable-redirects field that scopes an ErrUseLastResponse policy to the module's
own client copy, so the shared httpx transport keeps following redirects for
every other module.
2026-07-22 12:35:36 -07:00
TigahandGitHub f8f3b8cca6 feat(modules): add active web-vuln probe modules (#271)
* feat(modules): add active web-vuln probe modules

Add five active-probe modules to modules/http: ssti-reflected (arithmetic
eval proof), command-injection (id output, not payload echo), and error-based
xpath, nosql, and ldap injection. Each keys on a transformed response the
payload cannot produce by reflection, so a benign echo cannot trigger it.

* chore(modules): trim redundant module header comments

drop top-line comments that just restate the id/name fields already
present in the same file
2026-07-22 12:35:27 -07:00
TigahandGitHub 0d96838ee4 fix(scan): bound crawl breadth with a page budget (#269)
Crawl only capped recursion depth, not breadth per level. A link-heavy page
(pagination, faceted search) drives fetch count toward branching^depth with no
ceiling: a depth-3 crawl of a page linking 40 fresh urls issued 1641 real
fetches.

Cap total fetches at a page budget via an atomic counter in an OnRequest hook
that aborts once exceeded, and add a Truncated flag on CrawlResult so callers
can tell a run was cut short.
2026-07-22 12:35:18 -07:00
celesteandGitHub ef4b690e77 ci: run go and functional-test on merge_group (#372)
* ci: trigger go workflow on merge_group

* ci: trigger functional-test workflow on merge_group
2026-07-22 11:51:05 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
296586911f chore(deps): bump reviewdog/action-markdownlint from 0.27.0 to 0.28.0 (#370)
Bumps [reviewdog/action-markdownlint](https://github.com/reviewdog/action-markdownlint) from 0.27.0 to 0.28.0.
- [Release notes](https://github.com/reviewdog/action-markdownlint/releases)
- [Commits](https://github.com/reviewdog/action-markdownlint/compare/v0.27.0...v0.28.0)

---
updated-dependencies:
- dependency-name: reviewdog/action-markdownlint
  dependency-version: 0.28.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-21 09:37:37 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
149fb6cb2d chore(deps): bump actions/setup-go from 5 to 7 (#371)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5 to 7.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v5...v7)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-21 09:37:24 -07:00
TigahandGitHub d13622dc1c feat(scan): add -concurrency flag to scan targets in parallel (#367)
* refactor(cli): extract per-target scan into scanTarget

pull the target loop body into scanTarget, which returns a targetScan
holding that target's findings, report rows, scan labels and log files
instead of appending onto run-wide slices. the run loop merges those in
target order and finishRun handles the post-loop notify/silent/report/
summary steps. behavior is identical for the sequential run today; the
isolation is the seam a bounded worker pool needs next.

* feat(cli): scan targets in parallel with -concurrency

add -concurrency N (default 1) to run the per-target scan pool over
several targets at once. scanTarget already returns isolated
accumulators, so workers share only the console: output.SetConcurrent
serializes sink writes and de-animates spinners/progress so parallel
lines stay whole. results merge in input order, and concurrency 1 keeps
the sequential path unchanged.
2026-07-12 14:23:35 -07:00