Skip to content

fix(auth-server): reject prototype polluting device command names - #21262

Open
vbudhram wants to merge 1 commit into
mainfrom
fxa-13270
Open

vbudhram wants to merge 1 commit into
mainfrom
fxa-13270

Conversation

@vbudhram

Copy link
Copy Markdown
Contributor

Because

  • A device could send a command name of __proto__, constructor or prototype. The availableCommands map used that name as a key.
  • The map came from an object literal, so those keys reached Object.prototype.

This pull request

  • Builds the aggregated map with Object.create(null) in transformers.ts, and skips rows with a blocked name.
  • Adds a negative lookahead to DEVICE_COMMAND_NAME in validators.js that rejects the three exact names. The character class and the 1-100 length stay the same. No URI prefix is necessary.
  • Uses Object.hasOwn in the invoke_command handler, because a null prototype map has no inherited hasOwnProperty.
  • Updates the documented regex in auth-server-api.ts to match.
  • Adds a mocha spec for the aggregation, and jest cases for the regex and the availableCommands schema. The invoke_command fixture now uses a null prototype map, so a return to .hasOwnProperty() fails the suite.

Issue that this pull request solves

Closes: https://mozilla-hub.atlassian.net/browse/FXA-13270

Checklist

Put an x in the boxes that apply

  • My commit is GPG signed.
  • If applicable, I have modified or added tests which pass locally.
  • I have added necessary documentation (if appropriate).
  • I have verified that my changes render correctly in RTL (if appropriate).
  • I have manually reviewed all AI generated code.

How to review (Optional)

  • Key files/areas to focus on: db/transformers.ts and lib/routes/validators.js.
  • Suggested review order: validators.js, transformers.ts, devices-and-sessions.js, then the tests.
  • Risky or complex parts: the lookahead blocks the three exact names only. constructor2 and a URI that ends in /prototype still pass, which the tests cover.

Screenshots (Optional)

Other information (Optional)

  • The server validates the payload with stripUnknown: true. A blocked command name drops out of the payload, and the request does not fail. Joi also drops an own __proto__ key when it clones the object, so that name never raises a Joi error.
  • There is no DB migration here. Rows that already hold a blocked name stay in the database, and the transformer skips them. Cleanup of production data is a separate human task.
  • Local results: jest --selectProjects unit in fxa-auth-server gives 3993 passed, 0 failed. The fxa-shared mocha unit run gives 570 passing, and the jest unit run gives 18 passed. eslint exits 0 for both packages, and tsc -p packages/fxa-shared/tsconfig.json --noEmit exits 0.
  • The nx lint target could not run in this sandbox. It starts with glean-lint, which needs a Python venv from PyPI, and the sandbox has no route to PyPI. CI covers it.
  • Functional tests were not run locally.

## Because

- A device could send a command name of `__proto__`, `constructor` or `prototype`. The `availableCommands` map used that name as a key.
- The map came from an object literal, so those keys reached `Object.prototype`.

## This pull request

- Builds the aggregated map with `Object.create(null)` in `transformers.ts`, and skips rows with a blocked name.
- Adds a negative lookahead to `DEVICE_COMMAND_NAME` in `validators.js` that rejects the three exact names. The character class and the 1-100 length stay the same. No URI prefix is necessary.
- Uses `Object.hasOwn` in the `invoke_command` handler, because a null prototype map has no inherited `hasOwnProperty`.
- Updates the documented regex in `auth-server-api.ts` to match.
- Adds a mocha spec for the aggregation, and jest cases for the regex and the `availableCommands` schema. The `invoke_command` fixture now uses a null prototype map, so a return to `.hasOwnProperty()` fails the suite.

## Issue that this pull request solves

Closes: https://mozilla-hub.atlassian.net/browse/FXA-13270
@vbudhram vbudhram added the auto label Sep 18, 2026
Copilot AI balanced review requested due to automatic review settings September 18, 2026 20:55
@vbudhram
vbudhram requested a review from a team as a code owner September 18, 2026 20:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

The implementation appears sound and tested, but this security-sensitive authentication and persistence path warrants final human review.

Pull request overview

Hardens device-command handling against prototype-polluting names.

Changes:

  • Validates and filters unsafe command names.
  • Uses null-prototype maps and Object.hasOwn.
  • Adds focused validation, aggregation, and route tests.
File summaries
File Description
packages/fxa-shared/test/db/transformers.ts Tests secure aggregation behavior.
packages/fxa-shared/db/transformers.ts Builds safe maps and filters blocked keys.
packages/fxa-auth-server/lib/routes/validators.spec.ts Tests command-name regex.
packages/fxa-auth-server/lib/routes/validators.js Rejects unsafe exact names.
packages/fxa-auth-server/lib/routes/devices-and-sessions.spec.ts Exercises null-prototype command maps.
packages/fxa-auth-server/lib/routes/devices-and-sessions.js Safely checks command ownership.
packages/fxa-auth-server/lib/devices.spec.ts Tests schema filtering behavior.
packages/fxa-auth-server/docs/swagger/auth-server-api.ts Documents the revised regex.
Review details
  • Files reviewed: 8/8 changed files
  • Comments generated: 0
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

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.

3 participants