Commit Graph
597 Commits
Author SHA1 Message Date
TigahandGitHub d93fcbcd46 fix(scan): tune js secret rules for false positives and missed formats (#344)
* fix(scan): tune js secret rules for false positives and missed formats

drop stripe pk_ publishable keys (public by design) and require a digit in
the generic secret value so camelCase identifiers stop tripping the entropy
gate. add stripe rk_ restricted keys, github fine-grained pat (github_pat_),
encrypted pem headers and version-anchored slack xapp tokens; drop the
trailing word boundary on the aws-secret and google rules so keys ending in
/ + or - still match.

renames the "stripe live key" rule to "stripe secret key", which changes
the rule label in the json findings output.

* feat(scan): detect more provider keys in javascript

add unique-prefix credential rules to the js secret bank: gitlab pat
(glpat-), anthropic api/admin keys (sk-ant-), npm tokens (npm_), google
oauth client secrets (GOCSPX-), stripe webhook secrets (whsec_), shopify
app tokens (shp[at|ss|pa|ca]_), sendgrid keys (SG.) and slack incoming
webhook urls. all ride the no-entropy slot since the prefix alone is
proof, so they carry near-zero false-positive risk.
2026-07-22 12:55:17 -07:00
TigahandGitHub a44cfb230f fix(store,logger,scan): concurrency-adjacent races and correctness fixes (#343)
* fix(logger): create log dirs and flatten url log filenames

CreateFile/Write kept the '/' from a target's url path when building the
log filename, so a target like http://host:port/ tried to open
<dir>/host:port/.log whose parent directory never existed. os.OpenFile
failed and scanTarget aborted the whole target before any scanning ran.

fold every '/' and '\\' run in the sanitized url into a single '_' via a
shared logPath helper so CreateFile and Write always resolve to the same
flat file, and mkdir the parent defensively in getWriter as a second line
of defense.

* fix(scan): route module status lines through the locking sink

subdomaintakeover, cloudstorage, and the next.js framework detector
printed their status/error lines with a bare fmt.Println straight to
os.Stdout, bypassing the output package's sink. under -concurrency>1
only the sink is lock-wrapped, so these lines could interleave or tear
against lines other targets write concurrently.

swap them for output.ScanStart / output.Error, matching every other
scanner in the package.

* fix(store): make snapshot filenames injective and writes atomic

sanitize() folds every separator run (and a literal '_') to one '_', so
distinct targets like https://a.com/x and https://a.com//x, or
host:8443/path and host_8443_path, sanitized to the identical string and
shared one snapshot file - the second target's Save silently clobbered
the first's baseline. Save's os.WriteFile also wasn't atomic, so two
targets racing on a collided path (or -concurrency>1 in general) could
interleave partial writes.

pathFor now appends 16 hex chars of the target's sha256 to the sanitized
name, so distinct targets never collide, and Save writes through a temp
file in the same dir followed by os.Rename so a reader always sees a
complete snapshot or the previous one, never a partial write.

this changes the on-disk snapshot filename scheme: existing users' -diff
baselines under the old sanitize()-only names won't be found and the
next run re-baselines from scratch (every finding reports as newly
added once). noting this as a deliberate tradeoff for a local hardening
pass rather than silently orphaning them.

* fix(store): check the deferred temp-file cleanup error

golangci-lint (errcheck) flagged the bare defer os.Remove(tmpPath) in
writeFileAtomic; wrap it so the discard is explicit.
2026-07-22 12:55:08 -07:00
TigahandGitHub c1b7c7c027 fix(js): supabase jwt base64url decode and per-project error isolation (#342)
* fix(js): stop the scanning spinner on early script-parse errors

htmlquery.Parse and QueryAll failures returned before spin.Stop(), leaving
the "Scanning JavaScript files" spinner running forever on those paths while
every other error branch in the function stops it first.

* fix(js): stop reporting quoted regex-flag literals as endpoints

Quoted regex flag combos like "/gi" or "/su" in ordinary JS (e.g.
str.replace("/gi", x)) matched the root-relative path alternative in
endpointRegex and were reported as endpoints. Denylist the known flag
bigrams by exact match instead of raising minEndpointLen, so real short
endpoints like /v1, /me, /ws still extract normally.

* fix(js): keep supabase findings from other projects on a per-project error

ScanSupabase iterates every JWT found in a script and can discover several
distinct supabase projects. Four error paths inside that loop (reading the
signup response, parsing it, fetching the openapi spec, and a missing
Paths field) returned nil and the whole error, discarding any results
already collected for earlier, successfully scanned projects. Skip the
broken project with continue instead so the scan still returns what it
found.

* test(js): drop flaky wall-clock redos timing probe

go's regexp is re2 and linear by construction; the timing threshold added
only flake under -race load, not coverage. the regex-flag-literal false
positive it sat next to is guarded by TestRegexFlagEndpointsNotReported.
2026-07-22 12:54:58 -07:00
TigahandGitHub e04d4e1b59 fix(detectors): framework detector fp/fn corpus pass (#341)
* fix(detectors): let django csrf body field detect

csrfmiddlewaretoken is a hidden form body field django templates
render, never a header, so marking it HeaderOnly meant it could never
match and a real django form page (csrf field plus csrftoken cookie)
went undetected.

* fix(detectors): tighten aspnet, angular, astro and nextjs markers

* fix(frameworks): surface header version patterns to extraction

version extraction only ever searched the response body, so
header-shaped patterns like ASP.NET's "X-AspNet-Version: x.y.z" or
Flask's "Werkzeug/x.y.z" Server header could never match even when the
detector itself fired off that same header. add
ExtractVersionFromResponse, which also searches canonical header
lines, and point aspnet and flask at it. fix the aspnet header regexes
to match case-insensitively, since Go canonicalizes header names
("X-AspNet-Version" becomes "X-Aspnet-Version") and the old literal
pattern never matched the canonical form.

* test(detectors): pin fp/fn corpus for framework detection

promote the ad-hoc probe/sweep scratch tests used to find these
defects into a proper regression file: for each fix, assert both the
real-product positive still detects and the prose/other-product
negative does not, plus a sweep of unrelated pages against every
registered detector.
2026-07-22 12:54:49 -07:00
TigahandGitHub 598d85e501 fix(scan): validate git exposure responses are real git artifacts (#339)
check the 200 body looks like a git artifact (looksLikeGit per-path)
instead of trusting !text/html, killing catch-all-shell false positives.
2026-07-22 12:54:34 -07:00
TigahandGitHub 3e55f38c91 fix(modules): search system data dirs for built-in modules (#338)
packaged installs put the binary in /usr/bin and the modules under
/usr/share/sif/modules, so the loader found zero built-in modules with no
error. add the freedesktop data dirs ($XDG_DATA_DIRS, default
/usr/local/share and /usr/share) as fallback search paths after the existing
executable-relative and working-dir locations, first-existing wins. the new
IsDir check also makes resolution stricter: a plain file named modules no
longer counts as the directory.
2026-07-22 12:54:05 -07:00
TigahandGitHub d8ca2e96b1 fix(report): sort sarif driver rules and strip crlf from markdown headers (#337)
* fix(report): sort sarif driver rules for deterministic output

driver.rules was built by ranging a go map, so the emitted order
varied between runs over the same input, producing byte-unstable
sarif for identical scans. collect the rule ids into a slice and
sort them before building the rules list; ruleId still references
by id so no result is affected.

* fix(report): strip crlf from markdown target headers

target and module id are operator-supplied and were written verbatim
into "## " / "### " heading lines, so a target containing an
embedded newline followed by "## " text could inject a fake
standalone heading into the markdown report. sanitizeHeading
collapses cr/lf in both before they're written; the finding data
block itself was already unaffected.
2026-07-22 12:53:56 -07:00
TigahandGitHub 788d20c18e fix(crawl): stop redirects escaping crawl scope, cap fanout (#333)
* docs(scan): correct crawl's false robots.txt claim

the doc comment said robots.txt is respected because colly honors it
by default, but colly's default is IgnoreRobotsTxt=true and Crawl never
flips it, so Disallow rules were never enforced. state the actual,
intentional behavior instead: a recon crawler has no reason to treat
Disallow as a scope boundary.

pin the behavior with a regression test so it isn't "fixed" into a
partial robots.txt implementation by accident later.

* fix(scan): cap crawl request fanout

MaxDepth only bounds recursion depth; colly's own MaxRequests budget
was left at 0 (unlimited) and Crawl set no Limit rule, so a hostile
page fanning out to an attacker-controlled number of links got every
one of them fetched at that depth (b^d). cap the total number of
requests a single Crawl call will make with a fixed internal budget.

TestCrawl_BoundsRequestFanout proves a 2500-link fanout page no longer
drives the server past maxCrawlRequests+1 actual fetches.

* fix(scan): stop redirects from escaping crawl scope

Crawl called SetClient(httpx.Client(timeout)), which replaces colly's
whole *http.Client and discards the CheckRedirect colly installs to
re-check AllowedDomains on every redirect hop. with it gone, redirects
fell back to net/http's default: follow up to 10 hops to any host. an
in-scope page could 302 the crawler to an arbitrary off-scope host
(e.g. a cloud metadata endpoint) and it would be fetched.

colly's own AllowedDomains only matches on hostname anyway, which
would still let a same-host, different-port redirect through (an
internal service reachable on the same IP). build our own http.Client
on top of httpx's shared transport instead, with a CheckRedirect that
requires the redirect target to share the exact host:port of the
request that produced it, and caps the hop count ourselves since a
custom CheckRedirect drops net/http's own 10-redirect default.

TestCrawl_RedirectRejectsOffScopeHost proves the off-scope host is no
longer fetched; TestCrawl_RedirectFollowsSameHost proves ordinary
same-host redirects still are.
2026-07-22 12:53:41 -07:00
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