{"id":"CVE-2026-54442","kind":"vulnerability","aliases":["CVE-2026-54442","GHSA-c6q7-wqw2-h65w"],"related":[],"title":"NationalSecurityAgency/emissary has an incomplete fix for CVE-2026-35571","description":"### Summary\n\nThe URL validation regex `^(https?:/)?/.*` added to emissary's `NavAction.java` allows protocol-relative URLs (`//evil.com/...`) and single-slash scheme variants (`http:/evil.com`) to bypass validation, enabling SSRF and open redirect attacks.\n\n### Affected Package\n\n- **Ecosystem:** Maven\n- **Package:** NationalSecurityAgency/emissary\n- **Affected versions:** < e2078417464b\n- **Patched versions:** >= e2078417464b\n\n### Severity\n\nMedium\n\n### CWE\n\nCWE-918 — Server-Side Request Forgery (SSRF)\n\n### Details\n\nThe fix in `NavAction.java` (line 47) introduces a `VALID_LINK` pattern:\n\n```java\nPattern.compile(\"^(https?:/)?/.*\")\n```\n\nThis regex is intended to restrict navigation links to relative paths and HTTP/HTTPS URLs. However, it has multiple bypass vectors.\n\nProtocol-relative URLs (`//evil.com/...`): The group `(https?:/)?` is optional, so the pattern reduces to `//.*` when the group is absent. This matches `//evil.com/steal?c=SESSION`, which browsers resolve to the current page's protocol plus the attacker's host.\n\nSingle-slash scheme (`http:/evil.com`): The regex group captures `http:/` as a valid match, and browsers normalize `http:/evil.com` to `http://evil.com`, creating an open redirect to the attacker's server.\n\nProtocol-relative to cloud metadata (`//169.254.169.254/...`): This enables SSRF to cloud instance metadata endpoints, which is particularly dangerous in AWS/GCP environments where IAM credentials can be retrieved.\n\nThe pre-fix code had no validation at all, accepting any URL. The fix blocks `javascript:`, `ftp:`, and `data:` URIs, but the protocol-relative and single-slash bypasses remain exploitable.\n\n### PoC\n\n```python\n\"\"\"\nCVE-2026-35571 - NationalSecurityAgency/emissary SSRF via NavAction\n\nThe fix in commit e2078417464b adds URL scheme validation using:\n    Pattern.compile(\"^(https?:/)?/.*\")\n\nThis allows protocol-relative URLs (//evil.com) and single-slash variants\n(http:/evil.com) to bypass the validation.\n\nTested against: emissary@0d4dc56 (pre-fix) and patch e207841\nExtracted from: src/main/java/emissary/server/mvc/NavAction.java:47\n\"\"\"\n\nimport re\nimport sys\n\nFIXED_PATTERN = re.compile(r\"^(https?:/)?/.*\")\n\n\ndef vulnerable_accepts(link: str) -> bool:\n    \"\"\"Pre-fix behaviour: no validation, everything passes.\"\"\"\n    return True\n\n\ndef fixed_accepts(link: str) -> bool:\n    \"\"\"Post-fix behaviour: must match VALID_LINK pattern.\"\"\"\n    return bool(FIXED_PATTERN.match(link))\n\n\nTEST_VECTORS = [\n    (\"javascript: XSS payload\",\n     \"javascript:alert(document.cookie)\", True),\n    (\"ftp: scheme\",\n     \"ftp://evil.com/exfil\", True),\n    (\"data: URI\",\n     \"data:text/html,<script>alert(1)</script>\", True),\n\n    (\"Relative path\",\n     \"/path/to/resource\", False),\n    (\"HTTP URL\",\n     \"http://trusted.internal:8080/api\", False),\n    (\"HTTPS URL\",\n     \"https://trusted.internal/api\", False),\n\n    (\"Protocol-relative URL to attacker host (//evil.com)\",\n     \"//evil.com/steal?c=SESSION\", True),\n    (\"Protocol-relative with embedded credentials\",\n     \"//user:pass@evil.com/path\", True),\n    (\"Protocol-relative to cloud metadata (//169.254.169.254)\",\n     \"//169.254.169.254/latest/meta-data/\", True),\n    (\"Backslash trick (\\\\\\\\evil.com)\",\n     \"\\\\\\\\evil.com/path\", True),\n    (\"Tab in scheme (java\\\\tscript:...)\",\n     \"java\\tscript:alert(1)\", True),\n    (\"Path starting with attacker-like host (/evil.com)\",\n     \"/evil.com\", False),\n    (\"Triple slash (///evil.com)\",\n     \"///evil.com/etc/passwd\", True),\n    (\"http:/ single-slash to attacker host\",\n     \"http:/evil.com/steal\", True),\n    (\"https:/ single-slash to attacker host\",\n     \"https:/evil.com/steal\", True),\n    (\"URL-encoded javascript scheme (%6a...)\",\n     \"%6aavascript:alert(1)\", True),\n    (\"CRLF injection in path\",\n     \"/legit\\r\\nLocation: http://evil.com\", True),\n]\n\n\ndef main():\n    print(\"=\" * 72)\n    print(\"CVE-2026-35571 -- Emissary NavAction SSRF / Open-Redirect PoC\")\n    print(\"Pattern under test: ^(https?:/)?/.*\")\n    print(\"=\" * 72)\n    print()\n\n    bypasses_found = []\n    results_table = []\n\n    for desc, url, should_block in TEST_VECTORS:\n        vuln_result = vulnerable_accepts(url)\n        fixed_result = fixed_accepts(url)\n\n        if should_block and fixed_result:\n            status = \"BYPASS\"\n            bypasses_found.append((desc, url))\n        elif should_block and not fixed_result:\n            status = \"BLOCKED (good)\"\n        elif not should_block and fixed_result:\n            status = \"ALLOWED (good)\"\n        elif not should_block and not fixed_result:\n            status = \"FALSE-POS\"\n        else:\n            status = \"UNKNOWN\"\n\n        results_table.append((desc, url, vuln_result, fixed_result, status))\n\n    for desc, url, vuln, fixed, status in results_table:\n        display_url = repr(url) if len(url) > 50 else url\n        print(f\"[{status:>15}]  {desc}\")\n        print(f\"{'':>17}  URL: {display_url}\")\n        print(f\"{'':>17}  pre-fix={vuln}  post-fix={fixed}\")\n        print()\n\n    print(\"=\" * 72)\n    print(f\"Total vectors tested : {len(TEST_VECTORS)}\")\n    print(f\"Bypasses found       : {len(bypasses_found)}\")\n    print()\n\n    if bypasses_found:\n        print(\"VULNERABILITY CONFIRMED -- the following vectors bypass the fix:\")\n        print()\n        for desc, url in bypasses_found:\n            print(f\"  - {desc}\")\n            print(f\"    URL: {repr(url)}\")\n        print()\n        print(\"Key finding: The regex ^(https?:/)?/.* allows protocol-relative\")\n        print(\"URLs (//evil.com/...) because the optional group (https?:/) can be\")\n        print(\"absent, leaving //.* which matches //attacker.com. This enables\")\n        print(\"SSRF to arbitrary external hosts via navigation links.\")\n        print()\n        print(\"Additionally, http:/evil.com and https:/evil.com match the regex\")\n        print(\"(group 1 captures 'http:/' or 'https:/') and browsers normalise\")\n        print(\"these to http://evil.com, enabling open redirect / SSRF.\")\n        sys.exit(0)\n    else:\n        print(\"No bypasses found -- fix appears complete.\")\n        sys.exit(1)\n\n\nif __name__ == \"__main__\":\n    main()\n\n```\n\n**Steps to reproduce:**\n1. Run `python3 poc.py`\n2. Observe that protocol-relative and single-slash URLs bypass the regex\n\n**Expected output:**\n```\nVULNERABILITY CONFIRMED\nProtocol-relative URLs (//evil.com/) and single-slash variants (http:/evil.com) bypass the regex validation.\n```\n\n### Impact\n\nAn attacker can craft navigation links that bypass URL validation to redirect users to attacker-controlled servers (open redirect) or make the emissary server issue requests to internal services (SSRF). The `//169.254.169.254/latest/meta-data/` bypass is particularly dangerous in cloud environments, enabling retrieval of IAM credentials.\n\n### Suggested Remediation\n\nReplace the regex with one that explicitly rejects protocol-relative URLs. Alternatively, parse the URL with `java.net.URI` and validate the scheme and host against an allowlist rather than relying on regex.\n\n### References\n\n- Incomplete fix commit: https://github.com/NationalSecurityAgency/emissary/commit/e2078417464b9004620dde28dcbca2f73ea06c13\n- Original CVE: CVE-2026-35571","summary":"The URL validation regex added to emissary's NavAction.java accepts protocol-relative and single-slash URLs, so the fix for CVE-2026-35571 is incomplete. An attacker can send users to attacker-controlled hosts or make the server request internal services, including cloud metadata endpoints.","severity":"medium","cvss":4.8,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"gov.nsa.emissary:emissary","fixed":["8.51.0"],"affected":"< 8.51.0","ecosystem":"maven"}],"ecosystems":["maven"],"tags":["cwe-918","emissary","java","maven","package","self-hosted"],"not_affected_if":["no-navigation-links","version-8.51.0-or-later"],"how_to_check":"- Check the `gov.nsa.emissary:emissary` version in your build file; versions below `8.51.0` are affected.\n- Look for the `VALID_LINK` pattern `^(https?:/)?/.*` in `NavAction.java` (around line 47).","fix":"- `gov.nsa.emissary:emissary` `8.51.0` is the fixed version per the product range, but the advisory says the fix is incomplete, so confirm the regex in that release.","status":"open","published_at":"2026-10-30T22:51:19+00:00","first_seen_at":"2026-10-30T22:51:45+00:00","triaged_at":"2026-10-30T22:52:36+00:00","updated_at":"2026-10-30T22:52:36+00:00","category":"package","confidence":0.7,"triage":{"at":"2026-10-30T22:52:36Z","model":"claude-haiku-5-5","output":{"fix":"- `gov.nsa.emissary:emissary` `8.51.0` is the fixed version per the product range, but the advisory says the fix is incomplete, so confirm the regex in that release.","reach":"internet","title":"Emissary: navigation link validation bypass allows open redirect and SSRF","impact":"medium","summary":"The URL validation regex added to emissary's NavAction.java accepts protocol-relative and single-slash URLs, so the fix for CVE-2026-35571 is incomplete. An attacker can send users to attacker-controlled hosts or make the server request internal services, including cloud metadata endpoints.","audience":"developers","category":"package","nicknames":[],"who_cares":["java","maven","emissary","self-hosted"],"confidence":0.7,"fix_status":"available","needs_auth":"unknown","worst_case":"Server fetches cloud metadata credentials; users redirected to attacker sites.","analyst_note":"Confirm whether emissary 8.51.0 contains the corrected regex, since the advisory says the fix is incomplete: https://github.com/advisories/GHSA-c6q7-wqw2-h65w","how_to_check":"- Check the `gov.nsa.emissary:emissary` version in your build file; versions below `8.51.0` are affected.\n- Look for the `VALID_LINK` pattern `^(https?:/)?/.*` in `NavAction.java` (around line 47).","plain_action":"","needs_analyst":true,"not_affected_if":["no-navigation-links","version-8.51.0-or-later"],"who_is_affected":"Teams that run emissary and let user-supplied values reach the NavAction navigation path can be exposed to redirects and server-side requests. Applications that do not use NavAction or that run a version outside the affected range can skip this.","user_interaction":"yes","needs_special_setup":"unknown","severity_if_unknown":"medium"},"cost_usd":0.001975,"input_tokens":7115,"output_tokens":2526,"severity_before":"medium"},"analyst_status":null,"analyst_note":null,"search":"'-2026':8A,11A,19A,55B,306C,544C '-35571':9A,56B,307C,545C '-54442':12A,20A '-918':24B,130C '/169.254.169.254':248C,464C '/169.254.169.254/latest/meta-data':465C '/api':428C,434C '/evil.com':96C,104C,176C,216C,230C,330C,337C,443C,491C,492C,496C '/evil.com/etc/passwd':497C '/evil.com/steal':194C,444C,507C,517C '/exfil':413C '/legit':533C '/path':456C,472C '/path/to/resource':422C '/user':454C '0d4dc56':345C '1':482C,527C '47':143C,355C '6a':524C '6aavascript':525C '72':541C,559C 'absent':191C 'accept':41B,280C,367C,383C 'add':36B,86C,317C 'affect':114C,120C 'alert':406C,481C,526C 'allow':91C,325C 'attack':60B,66B,113C,208C,239C,441C,488C,504C,514C 'attacker-control':65B 'attacker-lik':487C 'aws/gcp':262C 'backslash':467C 'behaviour':374C,390C 'block':285C 'bool':370C,386C,397C 'browser':198C,227C 'bypass':106C,170C,300C,339C 'c':195C,445C 'c6q7':15A 'captur':220C 'cloud':77B,246C,253C,462C 'code':274C 'commit':315C 'control':67B 'creat':233C 'credenti':266C,453C 'crlf':529C 'current':202C 'cve':7A,10A,18A,54B,305C,543C 'cwe':23B,128C,129C 'danger':260C 'data':289C,415C,417C 'def':365C,381C,538C 'detail':137C 'document.cookie':407C 'e207841':351C 'e2078417464b':122C,125C,316C 'ecosystem':116C 'embed':452C 'emissari':25B,38B,88C,344C,546C 'emissary':22A 'enabl':108C,250C 'encod':521C 'endpoint':79B,256C 'environ':263C 'everyth':377C 'evil.com':232C,412C,469C,471C,536C 'evil.com/exfil':411C 'evil.com/path':470C 'exploit':302C 'extract':352C 'fals':423C,429C,435C,493C 'fix':5A,52B,139C,273C,284C,313C,348C,360C,373C,382C,389C 'fixed_pattern.match':398C 'forgeri':135C 'ftp':287C,409C 'ghsa':14A 'ghsa-c6q7-wqw2-h65w':13A 'gov.nsa.emissary':21A 'group':178C,189C,219C 'h65w':17A 'host':31B,68B,210C,442C,490C,505C,515C 'howev':166C 'http':103C,215C,221C,229C,336C,424C,499C,506C 'http/https':164C 'https':85C,151C,179C,323C,364C,430C,509C,516C,557C 'iam':265C 'import':356C,358C 'includ':76B 'incomplet':4A,58B 'inject':530C 'instanc':254C 'intend':155C 'intern':74B 'introduc':144C 'java':26B,149C,477C,479C 'javascript':286C,402C,405C,522C 'like':489C 'line':142C 'link':147C,159C,368C,384C,394C,399C 'main':539C 'make':70B 'match':193C,225C,392C 'maven':27B,117C 'medium':127C 'metadata':78B,247C,255C,463C 'multipl':169C 'must':391C 'nationalsecurityagency/emissary':1A,119C,308C 'navact':311C,547C 'navaction.java':40B,90C,141C 'navig':158C 'nlocat':535C 'normal':228C 'open':111C,235C,550C 'open-redirect':549C 'option':181C 'packag':28B,115C,118C 'page':203C 'particular':259C 'pass':378C 'pass@evil.com':455C 'patch':123C,350C 'path':162C,421C,484C,532C 'pattern':148C,184C,361C,395C,554C 'pattern.compile':150C,322C 'payload':404C 'plus':206C 'poc':303C,552C 'post':388C 'post-fix':387C 'pre':272C,347C,372C 'pre-fix':271C,346C,371C 'print':540C,542C,553C,558C,560C 'protocol':43B,93C,173C,205C,243C,294C,327C,437C,449C,459C 'protocol-rel':42B,92C,172C,242C,293C,326C,436C,448C,458C 'python':304C 'r':363C,534C 're':357C 're.compile':362C 'redirect':112C,236C,551C 'reduc':185C 'regex':35B,84C,153C,218C 'relat':44B,94C,161C,174C,244C,295C,328C,420C,438C,450C,460C 'remain':301C 'request':73B,134C 'resolv':199C 'restrict':157C 'retriev':269C 'return':379C,396C 'scheme':101C,214C,319C,410C,476C,523C 'self':30B 'self-host':29B 'send':62B 'server':72B,132C,241C 'server-sid':131C 'servic':75B 'session':196C,446C 'sever':126C 'side':133C 'singl':47B,99C,212C,298C,333C,501C,511C 'single-slash':46B,98C,211C,297C,332C,500C,510C 'slash':48B,100C,213C,299C,334C,495C,502C,512C 'src/main/java/emissary/server/mvc/navaction.java':354C 'ssrf':109C,136C,251C,309C,548C 'start':485C 'str':369C,385C 'summari':80C 'sys':359C 'tab':474C 'test':342C,400C,556C 'text/html':418C 'trick':468C 'tripl':494C 'true':380C,408C,414C,419C,447C,457C,466C,473C,483C,498C,508C,518C,528C,537C 'trusted.internal':433C 'trusted.internal/api':432C 'trusted.internal:8080':427C 'trusted.internal:8080/api':426C 'tscript':478C,480C 'uri':290C,416C 'url':33B,49B,82C,95C,165C,175C,282C,318C,329C,425C,431C,439C,520C 'url-encod':519C 'use':321C 'user':63B 'valid':34B,83C,107C,146C,224C,277C,320C,341C,376C,393C 'variant':102C,335C 'vector':171C,401C 'version':121C,124C 'via':310C 'vulner':366C 'wqw2':16A 'xss':403C","nicknames":[],"mitigations":[],"who_affected":"Teams that run emissary and let user-supplied values reach the NavAction navigation path can be exposed to redirects and server-side requests. Applications that do not use NavAction or that run a version outside the affected range can skip this.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"first_public_at":"2026-10-30T22:51:19+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:N","cvss_by":"GitHub","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-c6q7-wqw2-h65w","url":"https://github.com/advisories/GHSA-c6q7-wqw2-h65w","severity":"medium","published_at":"2026-10-30T22:51:19+00:00","modified_at":"2026-10-30T22:51:20+00:00","first_seen_at":"2026-10-30T22:51:45+00:00","fetched_at":"2026-10-30T22:51:45+00:00","note":null}],"documents":[{"id":26399,"url":"https://api.github.com/repos/NationalSecurityAgency/emissary/security-advisories/GHSA-c6q7-wqw2-h65w","title":"Maintainer's advisory: api.github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: api.github.com"},{"id":26400,"url":"https://github.com/NationalSecurityAgency/emissary/security/advisories/GHSA-c6q7-wqw2-h65w","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"},{"id":26401,"url":"https://github.com/NationalSecurityAgency/emissary/security/advisories/GHSA-cpm7-cfpx-3hvp","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"},{"id":26402,"url":"https://github.com/NationalSecurityAgency/emissary/commit/e2078417464b9004620dde28dcbca2f73ea06c13","title":"Patch: github.com","kind":"other","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":26403,"url":"https://github.com/NationalSecurityAgency/emissary/releases/tag/8.39.0","title":"Release: github.com","kind":"release","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Release: github.com"}],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}