Skip to content

[backport 24.05] dbviewer: harden aggregation and query handling#7691

Merged
ar2rsawseen merged 2 commits into
release.24.05from
backport/24.05/dbviewer-hardening
Jun 9, 2026
Merged

[backport 24.05] dbviewer: harden aggregation and query handling#7691
ar2rsawseen merged 2 commits into
release.24.05from
backport/24.05/dbviewer-hardening

Conversation

@ar2rsawseen

@ar2rsawseen ar2rsawseen commented Jun 9, 2026

Copy link
Copy Markdown
Member

Backport of #7686 to release.24.05, adapted to the older dbviewer code (which only blocked top-level $graphLookup and had no stage allow-list, so $lookup/$unionWith/$function/$out and nested $facet joins all passed through — a non-admin with access to any collection could $lookup into the global members collection).

What it adds

  • Role-parameterized aggregation guard (parts/aggregation_guard.js): validate the pipeline against an allow-list of stages — ALLOWED_STAGES_USER (no joins/writes) or ALLOWED_STAGES_GLOBAL_ADMIN (adds the join/union stages) — stripping anything not allowed at every depth, and rejecting server-side-JS operators ($function/$accumulator/$where) and joins/unions into members/auth_tokens at any depth (including for global admins). Replaces the $graphLookup-only check; both aggregation paths call the same routine with the role's list. The guard is collection-agnostic, so it works the same regardless of how events are stored.
  • find() projection restricted to strict include/exclude (drops expression/alias values); _id search escaped to a literal (ReDoS-safe).
  • members.two_factor_auth redacted alongside password/api_key; the redaction is placed as the first aggregation stage so no user stage can read raw fields first.
  • Result-size caps and generic 500s instead of raw MongoDB errors.

Notes vs master

  • The single-document app-scope from dbviewer: harden aggregation and query handling #7686 is omitted: it relied on getBaseAppFilter (which does not exist on 24.05). It is also not needed here — 24.05 stores events per app/event (no combined events_data/drill_events collection), and the document path has no cross-app bypass, so there is no combined collection to read across apps by _id.
  • The 24.05 aggregate() has no "removed stages" output, so stripped stages aren't reported back (they are still stripped).
  • The guard modules and unit tests are identical to master.

27 unit tests pass.

🤖 Generated with Claude Code

Backport of #7686 to release.24.05, adapted to the older dbviewer code (which
only blocked top-level $graphLookup and had no stage allow-list).

Adds the role-parameterized aggregation guard (parts/aggregation_guard.js):
validate the pipeline against an allow-list of stages — ALLOWED_STAGES_USER
(no joins/writes) or ALLOWED_STAGES_GLOBAL_ADMIN (adds $lookup/$graphLookup/
$unionWith) — stripping anything not allowed at every depth, and rejecting
server-side-JS operators ($function/$accumulator/$where) and joins/unions into
members/auth_tokens at any depth (incl. for global admins). Replaces the
$graphLookup-only check; both aggregation paths now call the same routine with
the role's list.

Also (parts/query_guard.js + api.js):
- restrict find() projections to strict include/exclude (drop expression/alias)
- treat the _id search term as a literal (ReDoS-safe)
- redact members.two_factor_auth alongside password/api_key; place the
  members/auth_tokens redaction as the first aggregation stage
- cap result size (limit/iDisplayLength) and return generic 500s instead of
  raw MongoDB errors

Adds unit tests for the guard modules.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 9, 2026 10:41

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.

Pull request overview

Backports DB Viewer security hardening to release.24.05 by introducing role-based aggregation pipeline sanitization, tightening find() query handling, and expanding credential redaction to reduce data exfiltration and server-side execution risks.

Changes:

  • Add a role-parameterized aggregation pipeline guard with an allow-list, deep stage stripping, and hard rejects for server-side-JS operators and joins into protected collections.
  • Harden find() handling by restricting projections to strict include/exclude and escaping _id search input for safe RegExp construction.
  • Expand redaction (members.two_factor_auth) and make server responses less error-revealing; add unit tests for the new guards.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
plugins/dbviewer/api/api.js Integrates aggregation/query guards, caps limits, improves redaction, and returns generic 500s for unexpected Mongo errors.
plugins/dbviewer/api/parts/aggregation_guard.js New deep-walking aggregation sanitizer with role allow-lists and hard-rule rejection for dangerous operators/joins.
plugins/dbviewer/api/parts/query_guard.js New helpers to sanitize find() projections and escape user-supplied regex input.
test/unit-tests/plugins.dbviewer.aggregation-guard.js Adds unit tests covering allow-list behavior, deep stripping, and hard-rule rejections.
test/unit-tests/plugins.dbviewer.query-guard.js Adds unit tests for projection sanitization and regex escaping.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread plugins/dbviewer/api/parts/aggregation_guard.js Outdated
Comment thread plugins/dbviewer/api/api.js Outdated
- joinTargetsOf now extracts the collection from the cross-database object
  form (from: {db, coll}) as well as the string form, so a join into
  members/auth_tokens can't bypass the protected-collection check via
  {db, coll}. Adds a test.
- aggregate() checks Array.isArray before pushing the iDisplayLength $limit,
  so a non-array aggregation returns a controlled error instead of throwing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ar2rsawseen ar2rsawseen merged commit 0c480e4 into release.24.05 Jun 9, 2026
5 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants