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 ...
Affected versions
| Package | Affected | Fixed in |
|---|---|---|
| github/gh-aw Product | < v0.86.0 | v0.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("")` 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.
More GitHub advisories
All GitHub| Date | Advisory | Severity | Fixed in |
|---|---|---|---|
| Aug 8 | gh-aw: safe-output validator forwards undeclared agent fields to the appliers (scope escape / mass assignment) | Critical9.1 | v0.86.1 |
| Aug 7 | gh-aw: unauthenticated prompt-injection to code execution in the shipped ai-moderator workflow | Medium6.5 | v0.86.1 |
| Aug 7 | command injection in compiled workflow via unsanitized `sandbox.mcp.env` exports | Critical9.6 | v0.86.0 |
| Aug 6 | Safe-outputs config emitter: JSON injection via templated values despite env-var indirection | High8.5 | v0.78.0 |
| Aug 5 | GitHub Enterprise Server: denial of service | Medium6.6 | No fix yet |
| Aug 5 | GitHub Enterprise Server: path traversal | High8.8 | No fix yet |