Skip to content

security(star-rating): forward only the widget's own parameters from /i/feedback/input (24.05) - #7953

Merged
shepardly[bot] merged 5 commits into
release.24.05from
backport/feedback-input-param-allowlist-2405
Aug 27, 2026
Merged

security(star-rating): forward only the widget's own parameters from /i/feedback/input (24.05)#7953
shepardly[bot] merged 5 commits into
release.24.05from
backport/feedback-input-param-allowlist-2405

Conversation

@ar2rsawseen

Copy link
Copy Markdown
Member

Backport of #7952 to release.24.05.

/i/feedback/input replays its request into the generic /i processor with no_checksum set, because the web feedback widget cannot compute a checksum (it does not hold the app's salt). It built that replayed request from the caller's entire query string, and the handler's only check is on events (a single [CLY]_star_rating event). Every other parameter was forwarded and then processed with checksum verification disabled.

On an app with a checksum salt configured, a caller holding only the public app key could therefore append unrelated write parameters and have them accepted unsigned: old_device_id reaches appUsers.merge(), token_session reaches the push token binding, and begin_session / user_details / consent / crash ride along the same way.

Fix (identical to #7952): rebuild the forwarded query from the parameters the widget actually sends — events, app_key, device_id, sdk_name, sdk_version, timestamp, hour, dow, app_version — and drop everything else. Values are URL encoded so a value cannot inject a second parameter, and non-scalars are dropped rather than stringified. The 24.05 widget sends the same nine parameters as master, so the allowlist is unchanged.

Tests: test/unit-tests/star-rating.input-utils.js, 8 cases, all passing on this branch; eslint clean.

🤖 Generated with Claude Code

ar2rsawseen and others added 3 commits August 18, 2026 14:35
…/i/feedback/input

Backport of #7952 to release.24.05.

/i/feedback/input accepts a feedback submission from the web widget, which cannot
compute a checksum because it does not hold the app's salt, so the handler replays
the request into the generic /i processor with no_checksum set. It built that
replayed request from the caller's entire query string:

    url: "/i?" + ob.params.href.split("/i/feedback/input?")[1]

The handler's only check is on the events parameter, which has to be a single
[CLY]_star_rating event. Every other parameter was forwarded untouched and then
processed with checksum verification disabled, so a caller holding just the public
app key could append unrelated write parameters and have them accepted unsigned on
an app that has a checksum salt configured. old_device_id reaches
appUsers.merge(), and token_session reaches the push token binding;
begin_session, user_details, consent and crash ride along the same way.

Rebuild the forwarded query from the parameters the widget actually sends
(events, app_key, device_id, sdk_name, sdk_version, timestamp, hour, dow,
app_version) and drop everything else, so the star rating submission keeps working
while any other operation has to go through /i and satisfy the checksum. Values are
URL encoded, so a parameter value cannot inject a second parameter, and non scalar
values are dropped rather than stringified, since a JSON body can put an object in
a query string parameter.

The helper lives in api/input-utils.js so it can be unit tested; the existing
plugin tests only vary device_id and never sent old_device_id here, so they are
unaffected.

Note this is the only unauthenticated no_checksum forwarder: the sole other
no_checksum caller is api/utils/taskmanager.js, which replays a stored task URL
created by an authenticated user.

Reported through the security bug bounty programme (received 2026-08-18).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The changelog is generated from PR and commit titles later, so an entry written by hand
here is duplicated work at best. It is also the single worst file in this wave for
conflicts: every merge to the base appends a line, which re-conflicts every open branch
that also appends one. Eight of the sixteen conflicts across these security PRs today
were this file and nothing else, and two of them came back within the hour.

Only the lines this branch added are removed - the file is otherwise the base's, and the
change here was purely additive, so nothing else moves.
@shepardly
shepardly Bot enabled auto-merge August 27, 2026 16:09
@shepardly
shepardly Bot merged commit 8f08402 into release.24.05 Aug 27, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants