Skip to content

Add support for deploying xsshunter-express behind a reverse proxy - #45

Open
dguerri wants to merge 9 commits into
mandatoryprogrammer:mainfrom
dguerri:main
Open

dguerri wants to merge 9 commits into
mandatoryprogrammer:mainfrom
dguerri:main

Conversation

@dguerri

@dguerri dguerri commented Feb 14, 2026

Copy link
Copy Markdown

No description provided.

Add several new functions and improvements to the XSS probe script:

- Add `strip_event_handlers_from_dom()` to remove inline event handlers,
  `<script>` elements, srcdoc iframes, and javascript: URLs before
  html2canvas clones the DOM, preventing infinite loop re-triggering

- Add `get_storage_data()` to collect localStorage, sessionStorage, and
  window.name data alongside cookies, capturing auth tokens and JWTs
  that HTTP-only cookies would otherwise miss

- Add `look_for_secrets()` with regex patterns to detect high-value
  secrets (AWS access keys, Slack webhooks, GCP service accounts) in
  page DOM content

- Add `check_cors()` to detect misconfigured CORS headers that may
  allow attacker-controlled origins to read sensitive responses

- Add `check_git()` to detect publicly exposed `.git/config` files
  indicating source code disclosure on misconfigured servers

- Merge storage data into the cookies field for backward compatibility
  with existing report consumers
dguerri and others added 5 commits March 21, 2026 18:39
Add AbortController-based 3-second timeouts to `check_cors()` and
`check_git()` to prevent hanging fetches from blocking the core
exfiltration flow. Run both checks concurrently using
`Promise.allSettled()` instead of awaiting them sequentially, reducing
total latency and ensuring one slow/failing check does not delay the
other.
…rt URL

- Add strip_event_handlers_from_dom() called before html2canvas:
  removes <script> tags, on* attributes, srcdoc iframes, javascript:
  URLs, broken images, inline <svg>, <img src="*.svg">, and CSS
  background-images that reference SVGs (the last two prevent
  canvas.toDataURL() SecurityError from tainted canvas)
- Add window.__xssh_fired global guard to prevent double-fire when
  onerror/onload re-triggers the probe
- Wrap html2canvas in 10s safety timer so data is always exfiltrated
  even if rendering hangs
- Skip screenshot field in FormData when empty so server never
  receives a broken upload
- Replace document.domain with location.host so collected URLs
  include the port number
- Add get_storage_data() to capture localStorage / sessionStorage /
  window.name alongside cookies
- Add look_for_secrets(), check_cors(), check_git() intel checks
  (parallel, 3s AbortController timeout each); findings prepended
  as <!-- PROBE-INTEL: {...} --> HTML comment in stored DOM

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
app.js: guard the screenshot processing block with `if (req.file)` so
that payload fires without a screenshot (html2canvas failure) still
save all other collected data instead of crashing with TypeError.

api.js: filter null screenshot_id records before building unlink paths
to prevent ENOENT on 'null.png.gz'; silence remaining unlink errors
with .catch(() => {}); wrap entire delete handler in try/catch to
return a 500 response instead of an unhandled promise rejection.
Add parse_intel() and has_intel() methods that extract the
<!-- PROBE-INTEL: {...} --> comment stored in report.dom.

Report card (list view): show badges when findings exist —
  red  "N Secret(s)"  for detected secrets
  yellow "CORS"       when Access-Control-Allow-Origin header found
  yellow ".git exposed" when /.git/config is publicly accessible

Expanded report view: add "Intel" section (same level as IP Address,
Browser Time) listing each secret with its type badge, the CORS
header value, and the git config content when present.

No server or DB schema changes required.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
BTREE indexes on the unbounded free-text columns of payload_fire_results
(url, referer, user_agent, cookies, title, origin) could exceed Postgres'
~8191-byte btree tuple limit, causing the entire INSERT to be rejected and
the payload fire to be lost (e.g. a large cookie/referer failing with
"index row requires 36896 bytes, maximum size is 8191").

- Drop the indexes on those unbounded columns; none were used by any query.
- Keep/add indexes only on bounded columns: ip_address (socket IP, useful
  for source correlation), was_iframe, browser_timestamp, and createdAt
  (the column the payload-fire list actually orders by).
- Wrap the fire-and-forget DB write + notification in try/catch. The HTTP
  response is sent before the insert, so a rejection there could not reach
  the client and instead surfaced as an UnhandledPromiseRejection.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant