Skip to content

fix(Router): fully decode URI before checking disallowed characters - #10645

Closed
gr8man wants to merge 4 commits into
codeigniter4:developfrom
gr8man:fix/router-double-encoded-uri
Closed

gr8man wants to merge 4 commits into
codeigniter4:developfrom
gr8man:fix/router-double-encoded-uri

Conversation

@gr8man

@gr8man gr8man commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

Description

Router::handle() decoded the URI only once, so a multiply-encoded URI could still contain %XX sequences and bypass checkDisallowedChars() when decoded again. The check now decodes the URI until it stops changing before validating it; routing keeps using the single-decoded path so URI filter matching is unchanged.

Checklist:

  • Securely signed commits
  • Component(s) with PHPDoc blocks, only if necessary or adds value (without duplication)
  • Unit testing, with >80% coverage
  • User guide updated
  • Conforms to style guide

A double-encoded URI could pass checkDisallowedChars() because the Router decoded it only once. Any component decoding it again could then introduce disallowed characters.

Decode the URI until it stops changing before validation. The single-decoded path is still used for routing so that URI filter matching stays consistent.
@carson-codeigniter4 carson-codeigniter4 Bot added the bug Verified issues on the current code behavior or pull requests that will fix them label Oct 21, 2026
@gr8man

gr8man commented Oct 21, 2026

Copy link
Copy Markdown
Contributor Author

The random test failure in this PR (Autoloader / FileLocatorCachedTest::testDeleteCache) is unrelated to the changes here.

The random test execution runs the components in parallel, and the Commands component (OptimizeTest, ClearCacheTest) deletes the shared writable/cache/FileLocatorCache file that the Autoloader component asserts on.

Fixed in #10647 by giving FileVarExportHandler the directory it stores its files in, so the tests use their own directory.

@michalsn michalsn left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you provide a concrete example showing which component decodes the routed value again? %253C becomes literal %3C after one decode, which the whitelist permits. Laravel and Symfony also decode route paths once. Recursive validation would reject previously accepted input, so I'd like to understand the scenario that makes this necessary.

@gr8man gr8man closed this Oct 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Verified issues on the current code behavior or pull requests that will fix them

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants