Skip to content

dbviewer: validate aggregation stages at every depth#7685

Closed
ar2rsawseen wants to merge 5 commits into
masterfrom
fix/dbviewer-recursive-agg-guard
Closed

dbviewer: validate aggregation stages at every depth#7685
ar2rsawseen wants to merge 5 commits into
masterfrom
fix/dbviewer-recursive-agg-guard

Conversation

@ar2rsawseen

@ar2rsawseen ar2rsawseen commented Jun 8, 2026

Copy link
Copy Markdown
Member

Summary

Hardening of the DB Viewer aggregation guard.

Changes

  1. Validate aggregation stages at every depth. The stage allow-list was only applied to the top level of the pipeline. $facet is allowed but carries sub-pipelines that were not inspected, so the allow-list was not enforced consistently inside them. The sanitizer now applies recursively, descending into $facet sub-pipelines (and, structurally, any kept stage exposing a .pipeline) at every depth, dropping sub-pipelines/stages emptied by sanitization.

  2. Block joins into redacted collections. The members / auth_tokens field redaction is only applied to the top-level source collection, so a join ($lookup/$unionWith/$graphLookup) into them would return un-redacted documents. Aggregations that join/union one of these collections at any depth are now rejected on both the admin and non-admin paths.

  3. Extracted the allow-list + guards into plugins/dbviewer/api/parts/aggregation_guard.js so they can be unit-tested in isolation.

Top-level behaviour for ordinary pipelines is unchanged.

Tests

test/unit-tests/plugins.dbviewer.aggregation-guard.js — 15 cases covering recursive stage sanitization (top-level, nested $facet, $facet-in-$facet, generic .pipeline, empty-cleanup) and protected-collection join detection ($lookup/$unionWith/$graphLookup, nested forms). All passing.

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings June 8, 2026 19:25
The aggregation stage allow-list was only applied to the top level of the
pipeline. $facet is allowed but carries sub-pipelines that were not inspected,
so the allow-list was not enforced consistently inside them.

Extract the allow-list and sanitizer into plugins/dbviewer/api/parts/
aggregation_guard.js and apply it recursively, descending into $facet
sub-pipelines at every depth (dropping sub-pipelines/stages emptied by
sanitization so no empty $facet pipeline is produced). Top-level behaviour is
unchanged. Adds unit tests for the recursive handling.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ar2rsawseen ar2rsawseen force-pushed the fix/dbviewer-recursive-agg-guard branch from f97b608 to 2ebbac8 Compare June 8, 2026 19:28

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

This PR hardens the DB Viewer plugin’s aggregation-stage guard by moving the allow-list/sanitizer into a dedicated module and ensuring sanitization recurses into $facet sub-pipelines at any nesting depth, closing a bypass where blocked stages (e.g. $lookup, $unionWith) could be smuggled via $facet.

Changes:

  • Extracted the aggregation stage allow-list and sanitization logic into plugins/dbviewer/api/parts/aggregation_guard.js.
  • Updated the sanitizer to recursively inspect and clean $facet sub-pipelines, removing emptied sub-pipelines/stages to avoid invalid empty $facet pipelines.
  • Added unit tests covering top-level behavior, nested $facet bypass cases (including $facet-within-$facet), and empty-sub-pipeline cleanup.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
plugins/dbviewer/api/parts/aggregation_guard.js New dedicated module implementing recursive $facet sanitization for aggregation pipelines.
plugins/dbviewer/api/api.js Switched to importing the sanitizer from the new module (removing inline implementation).
test/unit-tests/plugins.dbviewer.aggregation-guard.js Added unit tests validating top-level and deeply nested $facet sanitization behavior.

Comment thread plugins/dbviewer/api/parts/aggregation_guard.js Outdated
@ar2rsawseen ar2rsawseen changed the title dbviewer: sanitize aggregation stages recursively dbviewer: validate aggregation stages at every depth Jun 8, 2026
ar2rsawseen and others added 2 commits June 8, 2026 22:34
…ecific

Recurse into any kept stage's sub-pipelines by structure ($facet's
sub-pipelines today, plus any stage exposing a .pipeline array) so the guard
keeps holding if the allow-list ever gains another pipeline-bearing stage.
Adds a test simulating a future allow-listed pipeline-bearing stage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…r admins)

The members / auth_tokens redaction is only applied to the top-level source
collection, so a join into them (//) returns raw,
un-redacted documents (api_key, password, token values). Global admins skip
the stage sanitizer, so they could read these via a join even though the
top-level redaction intentionally denies them.

Detect joins/unions into the redacted collections (members, auth_tokens) at
any depth — including  sub-pipelines and nested .pipeline arrays — and
reject such aggregations on both the admin and non-admin paths. Adds unit
tests for top-level, $facet-nested, .pipeline-nested, $unionWith (object and
string forms) and $graphLookup.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ar2rsawseen and others added 2 commits June 8, 2026 23:14
Use === true (allow-list) and === true (protected-collection set) so inherited
Object.prototype keys (constructor, __proto__, …) on a user-controlled stage
object are never mistaken for allow-listed/known entries. Adds a regression
test for prototype-key stage names.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
findProtectedCollectionJoin now walks every nested object/array rather than
only $facet sub-pipelines and .pipeline arrays, so a join into a redacted
collection smuggled inside any (incl. future) stage shape is still detected.
Detection-only, so a blanket deep walk is safe. Adds a test for an arbitrary
nested stage shape.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ar2rsawseen

Copy link
Copy Markdown
Member Author

Consolidated into #7690 (single reviewable PR combining this and #7686). Closing in favor of #7690.

@ar2rsawseen ar2rsawseen closed this Jun 9, 2026
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.

2 participants