Skip to content

Anchor is_valid_scheme regex so invalid schemes are rejected (#192)#193

Open
apoorvdarshan wants to merge 1 commit into
gruns:masterfrom
apoorvdarshan:fix-scheme-validation-anchor
Open

Anchor is_valid_scheme regex so invalid schemes are rejected (#192)#193
apoorvdarshan wants to merge 1 commit into
gruns:masterfrom
apoorvdarshan:fix-scheme-validation-anchor

Conversation

@apoorvdarshan

Copy link
Copy Markdown

Fixes #192.

Problem

is_valid_scheme matches with re.match against an unanchored pattern:

@static_vars(regex=re.compile(r'[a-zA-Z][a-zA-Z\-\.\+]*'))
def is_valid_scheme(scheme):
    return is_valid_scheme.regex.match(scheme) is not None

re.match anchors only the start, so any string whose prefix looks like a scheme is accepted. A URL like a b:c is therefore mis-parsed as scheme a b + path c:

>>> from furl import furl
>>> furl('a b:c').scheme
'a b'          # expected None ('a b' contains a space)

The module's other validators (is_valid_encoded_path_segment, is_valid_encoded_query_key, is_valid_encoded_query_value) all anchor with ^...$; this one didn't.

Fix

Anchor the scheme pattern at both ends, matching the sibling validators — and add 0-9 to the character class:

@static_vars(regex=re.compile(r'^[a-zA-Z][a-zA-Z0-9\-\.\+]*$'))

The 0-9 matters: RFC 3986 allows digits in schemes (scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )). Without it, adding the anchor alone would regress digit-bearing schemes like ws2 or h2c — the current unanchored regex accepts them today via prefix-match (furl('ws2://host').scheme == 'ws2'), so they must keep working.

After:

>>> furl('a b:c').scheme          # None (whole string becomes the path)
>>> furl('ws2://host').scheme     # 'ws2'

Testing

  • Extended test_scheme with the reported case (a b:c → no scheme, a%20b:c path), another spaced input, and two digit-scheme cases (ws2, a1b) to guard the RFC behavior. The spaced cases fail on master and pass with this change.
  • The rest of the suite is unchanged: 74 passed. Three pre-existing failures (test_hosts, test_netloc, test_odd_urls) are unrelated IPv6/urllib.parse issues on newer Python (see ipv6 addresses now need to be valid in 3.9+ #182, test_odd_urls fails with the recent fixes in urllib.parse #176); they fail identically with and without this change.
  • flake8 is clean on the changed lines.

Disclosure: this change was prepared with the assistance of an AI tool (Claude Code). I reproduced the issue, verified the fix avoids regressing valid digit schemes, added and ran the tests and linter, and take responsibility for the contribution and will respond to review feedback personally.

is_valid_scheme matched with re.match against an unanchored pattern, so
any string whose prefix looked like a scheme was accepted -- e.g.
furl('a b:c').scheme returned 'a b' instead of None, corrupting the parse.
Anchor the pattern with ^...$ like the module's other validators, and add
0-9 to the character class (valid in schemes per RFC 3986) so digit-bearing
schemes such as 'ws2' keep parsing. Fixes gruns#192.
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.

is_valid_scheme is missing an end anchor, so invalid schemes are accepted and URLs mis-parsed

1 participant