Skip to content

give query param meta maps a null prototype#21506

Open
ubeddulla wants to merge 1 commit into
emberjs:mainfrom
ubeddulla:qp-map-null-prototype
Open

give query param meta maps a null prototype#21506
ubeddulla wants to merge 1 commit into
emberjs:mainfrom
ubeddulla:qp-map-null-prototype

Conversation

@ubeddulla

Copy link
Copy Markdown
Contributor

forEachQueryParam and Route.queryParamsDidChange look up query param names taken from the url against the plain objects built in _queryParamsFor and _qp, so /?toString=1 finds Object.prototype.toString, treats it as query param metadata, and the transition dies in qp.route.deserializeQueryParam. any app is affected, including one that declares no query params at all. forEachQueryParam already guards the queryParams side of that loop with hasOwnProperty and only misses the map side, so giving both maps a null prototype fixes it at the source and matches _qpCache and _engineInstances a few lines up. test covers /?toString=1 arriving alongside a real qp.

@NullVoxPopuli

Copy link
Copy Markdown
Contributor

Do you have a real app that's running in to this?

@ubeddulla

Copy link
Copy Markdown
Contributor Author

No real app report behind it, no. I hit it reading the lookup code in forEachQueryParam and Route.queryParamsDidChange, then confirmed it with the test that's in this PR.

For an app to hit it the URL just needs a param named after something on Object.prototype: ?toString=1, ?constructor=1, ?valueOf=1. On main that's enough to kill the transition, in two different ways depending on the app:

  • no query params declared anywhere, visit /?toString=1TypeError: Cannot read properties of undefined (reading 'deserializeQueryParam'), from _deserializeQueryParamsforEachQueryParam
  • a qp declared, visit /?toString=1&foo=bazAssertion Failed: The key provided to get must be a string or number, you passed undefined in _optionsForQueryParam, because qp.urlKey is undefined

Either way the transition aborts and the app doesn't render.

How a URL like that shows up in practice is the boring part: a crawler or scanner tacking params on, a marketing/analytics param getting passed through, someone hand editing a link. Nothing is attacker controlled beyond the param name itself, so it's not a pollution issue, it just means a link anyone can construct blanks the page for whoever follows it.

The test in the PR is the repro. It fails on main and passes with the two Object.create(null) calls. If an app level test feels heavy for this I can swap it for a smaller unit test against _queryParamsFor.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants