CVE-2026-79273
Overview
Background
- `WebAddressParser`
- the Android WebView helper that parses a user/loaded address string into scheme, host, port, and path via an anchored regex, used to “fix up” URLs before navigation.
- `MATCH_GROUP_PORT`
- the regex capture group for the port that follows a
':'; the':'itself is not returned in the captured text, so the group can be an empty string when a bare':'matched. - `URISyntaxException`
- the exception thrown to reject malformed addresses instead of silently emitting a fixed-up URL.
Root Cause Analysis
The parsing routine handled the port group with if (t != null && t.length() > 0), so when the PORT regex matched a bare ':' with zero following digits, the group was non-null but empty and the entire branch was skipped — silently dropping the ':' and keeping whatever had been captured as mHost. Because the ':' may actually have been a scheme delimiter (for an unrecognized scheme such as rtsp: in rtsp://www.example.com/media.mp4), dropping it changes how the remainder is interpreted and can yield a fixed-up URL whose host is not what the input denoted. The invariant that a parsed address either faithfully represents the input’s host or is rejected was violated, producing a wrong-host resolution (the advisory’s “incorrect reference resolution”).
The fix changes the guard to if (t != null) and, when t.length() == 0, throws URISyntaxException if mScheme.isEmpty() — i.e. it rejects a bare ':' whenever no scheme was recognized, rather than silently absorbing it; a legitimate empty port after a recognized scheme still passes. This tightens validation, and the test updates move several former “bad but accepted” inputs to the throwing set.
':' with no digits) as “nothing to do” silently dropped a colon that could be a scheme delimiter, letting the parser emit a URL with a mis-attributed host; the fix rejects that ambiguous ':' with URISyntaxException when no scheme was recognized.Attack Path
- Craft an ambiguous address
Supply WebView an address containing a
':'not followed by port digits and using an unrecognized scheme, e.g.rtsp://www.example.com/media.mp4,foo.example.com://bar.example.com/, or:www.example.com@@example.com:80. - Bypass port handling
The PORT regex matches the bare
':'with an empty capture, so the oldt.length() > 0guard skips port parsing and drops the':'without error. - Emit a wrong-host URL
WebAddressParserproduces a fixed-up URL whose host differs from the input’s apparent host (e.g.http://rtsp//www.example.com/media.mp4), i.e. incorrect reference resolution. - Consume the mis-resolved URL
Downstream WebView navigation/comparison acts on the wrong host, potentially routing or attributing the request to an unintended origin (specific downstream security effect is
(inferred)).
Impact Assessment
WebAddressParser can, per the advisory, cause incorrect reference resolution: the parser silently rewrites the host so a URL resolves to a different host than the input indicated. This runs in the WebView/browser-side URL-fixup logic; any concrete escalation such as origin confusion or navigation to an attacker-chosen host is (inferred) and not demonstrated by the patch. There is no indication in the advisory text of in-the-wild exploitation; the class is a logic error (incorrect reference resolution in WebView).Changed Functions
| Function | Change | Notes |
|---|---|---|
ifandroid_webview/java/src/org/chromium/android_webview/WebAddressParser.java |
modified |
Files Changed
android_webview/java/src/org/chromium/android_webview/WebAddressParser.javaandroid_webview/junit/src/org/chromium/android_webview/robolectric/WebAddressParserTest.java
Audit Directions
- Empty-but-non-null capture groupsAudit regex-driven parsers that gate on
length() > 0(or similar) for delimiter captures; an empty match often means the delimiter was present and is being silently discarded rather than validated. - Scheme/host ambiguity in URL fixupReview URL “fix-up”/canonicalization paths that proceed when no scheme is recognized, ensuring a stray
':'or delimiter cannot be reinterpreted so the resulting host diverges from the input. - Silent normalization vs. rejectionLook for parsers that repair malformed input by dropping characters instead of throwing; any drop that can alter host/origin interpretation is a reference-resolution bug candidate.
Patch
From 57f01d9195a83c45266cf9145f0facefeea9d56e Mon Sep 17 00:00:00 2001 From: Torne (Richard Coles) <[email protected]> Date: Fri, 17 Jul 2026 09:05:58 -0700 Subject: [PATCH] webview: Reject ambiguous ':' in WebAddressParser WebAddressParser's PORT regex accepted a ':' followed by zero digits, silently dropping it. If no scheme was recognised the colon may have been intended to be part of a scheme delimiter, and dropping it may change the interpretation of the URL's host. Instead, throw URISyntaxException when PORT matched a bare ':' and no scheme was recognised. Update WebAddressParserTest; several existing "bad input" test cases now throw, and add some new cases for unrecognized schemes. Fixed: 517167020 Change-Id: I090927d8dca336a915c6103ba258c7b65110fb01 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8118007 Reviewed-by: Peter Pakkenberg <[email protected]> Auto-Submit: Richard Coles <[email protected]> Commit-Queue: Peter Pakkenberg <[email protected]> Commit-Queue: Richard Coles <[email protected]> Cr-Commit-Position: refs/heads/main@{#1663937} --- diff --git a/android_webview/java/src/org/chromium/android_webview/WebAddressParser.java b/android_webview/java/src/org/chromium/android_webview/WebAddressParser.java index b53c87bb..5fab0f33 100644 --- a/android_webview/java/src/org/chromium/android_webview/WebAddressParser.java +++ b/android_webview/java/src/org/chromium/android_webview/WebAddressParser.java @@ -77,12 +77,22 @@ t = m.group(MATCH_GROUP_HOST); if (t != null) mHost = t; t = m.group(MATCH_GROUP_PORT); - if (t != null && t.length() > 0) { + if (t != null) { // The ':' character is not returned by the regex. - try { - mPort = Integer.parseInt(t); - } catch (NumberFormatException ex) { - throw new URISyntaxException(address, "Bad port"); + if (t.length() == 0) { + // PORT matched a bare ':' with no digits. If no scheme was + // recognised, the ':' may have been a scheme delimiter and + // mHost may not actually be the host, so reject the input + // rather than risk emitting a URL with the wrong host. + if (mScheme.isEmpty()) { + throw new URISyntaxException(address, "Bad port"); + } + } else { + try { + mPort = Integer.parseInt(t); + } catch (NumberFormatException ex) { + throw new URISyntaxException(address, "Bad port"); + } } } t = m.group(MATCH_GROUP_PATH); diff --git a/android_webview/junit/src/org/chromium/android_webview/robolectric/WebAddressParserTest.java b/android_webview/junit/src/org/chromium/android_webview/robolectric/WebAddressParserTest.java index 5e54c91..b8478f5 100644 --- a/android_webview/junit/src/org/chromium/android_webview/robolectric/WebAddressParserTest.java +++ b/android_webview/junit/src/org/chromium/android_webview/robolectric/WebAddressParserTest.java @@ -97,13 +97,6 @@ Assert.assertEquals("http:///.some.domain", fixupUrl(".some.domain")); Assert.assertEquals("http:///.some.domain", fixupUrl("http://.some.domain")); - Assert.assertEquals("http://www.example.com/-1", fixupUrl("www.example.com:-1")); - Assert.assertEquals( - "http:///www.example.com@@example.com:80", - fixupUrl(":www.example.com@@example.com:80")); - Assert.assertEquals( - "http://rtsp//www.example.com/media.mp4", - fixupUrl("rtsp://www.example.com/media.mp4")); Assert.assertEquals("http:///^", fixupUrl("^")); Assert.assertEquals("http:///.", fixupUrl(".")); Assert.assertEquals("http:///", fixupUrl("")); @@ -113,16 +106,26 @@ @SmallTest @Feature({"AndroidWebView", "Privacy"}) public void testInputWithURISyntaxException() { - try { - fixupUrl("www.example.com:1234567890123"); - Assert.fail("Bad port should throw an exception"); - } catch (URISyntaxException e) { - } + assertBadAddress("www.example.com:1234567890123"); + + // A ':' that isn't followed by a port number and isn't preceded by a recognised scheme + // is rejected, since it may have been intended as a scheme delimiter and the parser + // can't determine the host with confidence. + assertBadAddress("www.example.com:-1"); + assertBadAddress(":www.example.com@@example.com:80"); + assertBadAddress("rtsp://www.example.com/media.mp4"); + assertBadAddress("foo.example.com://bar.example.com/"); + assertBadAddress("foo.example.com://[email protected]/"); + assertBadAddress("a.example.com://b.example.com/path"); // Because the ANCHOR regex matches everything, WebAddressParser won't throw exception // because of no matching. } + private void assertBadAddress(String url) { + Assert.assertThrows(URISyntaxException.class, () -> fixupUrl(url)); + } + private String fixupUrl(String url) throws URISyntaxException { return new WebAddressParser(url).toString(); }