[backport 24.05] dbviewer: harden aggregation and query handling#7691
Merged
Conversation
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>
Cookiezaurs
approved these changes
Jun 9, 2026
Contributor
There was a problem hiding this comment.
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
_idsearch 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.
- 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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Backport of #7686 to
release.24.05, adapted to the older dbviewer code (which only blocked top-level$graphLookupand had no stage allow-list, so$lookup/$unionWith/$function/$outand nested$facetjoins all passed through — a non-admin with access to any collection could$lookupinto the globalmemberscollection).What it adds
parts/aggregation_guard.js): validate the pipeline against an allow-list of stages —ALLOWED_STAGES_USER(no joins/writes) orALLOWED_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 intomembers/auth_tokensat 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.members.two_factor_authredacted alongsidepassword/api_key; the redaction is placed as the first aggregation stage so no user stage can read raw fields first.Notes vs master
getBaseAppFilter(which does not exist on 24.05). It is also not needed here — 24.05 stores events per app/event (no combinedevents_data/drill_eventscollection), and the document path has no cross-app bypass, so there is no combined collection to read across apps by_id.aggregate()has no "removed stages" output, so stripped stages aren't reported back (they are still stripped).27 unit tests pass.
🤖 Generated with Claude Code