Skip to content

Conversation

kmruiz
Copy link
Collaborator

@kmruiz kmruiz commented Sep 2, 2025

Description

With this feature we support redacting known secrets from a dictionary. This is valuable for environments like the MCP Server where we create new users (user/password) or when we can infer secrets from CLI arguments (--user, --pasword).

Open Questions

Checklist

With this feature we support redacting known secrets from a
dictionary. This is value for environments like the MCP Server where
we create new users (user/password) or when we can infer secrets from
CLI arguments (--user, --pasword).
@kmruiz kmruiz self-assigned this Sep 2, 2025
Copy link
Contributor

@Copilot Copilot AI left a comment

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 adds support for redacting known secrets from a dictionary in the mongodb-redact package. The feature allows environments like MCP Server to redact specific user credentials, passwords, and other sensitive data that can be inferred from CLI arguments or user creation flows.

Key changes:

  • Add a new secrets.ts module with redactSecrets function and secret type definitions
  • Extract utility functions to utils.ts for code reusability
  • Update the main redact function to accept optional secrets dictionary parameter

Reviewed Changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/mongodb-redact/tsconfig.json Adds esModuleInterop compiler option for better module compatibility
packages/mongodb-redact/src/utils.ts Extracts isPlainObject utility function for code reuse
packages/mongodb-redact/src/secrets.ts Implements core secret redaction functionality with type definitions
packages/mongodb-redact/src/secrets.spec.ts Comprehensive test suite for secret redaction functionality
packages/mongodb-redact/src/index.ts Updates main redact function to support optional secrets parameter
packages/mongodb-redact/src/index.spec.ts Adds integration test for secret redaction feature
packages/mongodb-redact/package.json Adds regexp.escape dependency and type definitions
Comments suppressed due to low confidence (1)

packages/mongodb-redact/src/index.ts:1

  • The recursive call to redact(value) doesn't pass the secrets parameter, so secret redaction won't work for nested objects and arrays. It should be redact(value, secrets).
import { regexes } from './regexes';

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

if (proto === null) return true;
if (!Object.prototype.hasOwnProperty.call(proto, 'constructor')) return false;
const ctor = proto.constructor;
if (typeof ctor !== 'function') return ctor;
Copy link
Preview

Copilot AI Sep 2, 2025

Choose a reason for hiding this comment

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

This line returns ctor when it should return a boolean. The function signature indicates it should return val is object, but here it's returning the constructor itself when it's not a function.

Suggested change
if (typeof ctor !== 'function') return ctor;
if (typeof ctor !== 'function') return false;

Copilot uses AI. Check for mistakes.

Copy link
Collaborator

Choose a reason for hiding this comment

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

^ Copilot seems right about this one?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

If the constructor is not a function, it doesn't have a constructor, so returning a "truthy" value is correct here because it's a plain object. Right?

This code was as is, so after taking a look it seemed to be right, but maybe I misunderstood it.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, I wrote that previous code, so this is just Copilot pointing out that I'm wrong, not you 😅

If the constructor is not a function, it doesn't have a constructor

Not really, right? Does Object.create({ constructor: 42 }) "have" a constructor? Certainly not in the sense of a constructor function, but we shouldn't count this as a "plain object" in the sense of "objects whose prototype is null or Object.prototype"

so returning a "truthy" value is correct here because it's a plain object. Right?

Right now, this would say that Object.create({ constructor: true }) is a plain object and Object.create({ constructor: false }) isn't, which is wrong – these two are definitely the same degree of "not-plain-object-y" 🙂

Copy link
Collaborator Author

@kmruiz kmruiz Sep 3, 2025

Choose a reason for hiding this comment

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

Then, the condition would have to be:

if (ctor) return false;

right? We don't mind about the type of the constructor, we only care if it exists or not. Isn't it?

Edit:

Btw, both your cases with create would return true, because neither true and false are functions, so the constructor is considered just a plain property with a primitive.

Copy link
Collaborator

Choose a reason for hiding this comment

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

We don't mind about the type of the constructor, we only care if it exists or not. Isn't it?

Once we've established that val has a prototype proto, we want to return true if proto.constructor is the JS Object builtin, and false otherwise – that's the goal here

Comment on lines 37 to 38
const regex = new RegExp(`\\b${escape(value)}\\b`);
result = result.replace(regex, `<${kind}>`) as T;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
const regex = new RegExp(`\\b${escape(value)}\\b`);
result = result.replace(regex, `<${kind}>`) as T;
const regex = new RegExp(`\\b${escape(value)}\\b`, 'g');
result = result.replace(regex, `<${kind}>`) as T;

Otherwise this only replaces the first instance of the secret (this should probably also be tested 🙂)

if (proto === null) return true;
if (!Object.prototype.hasOwnProperty.call(proto, 'constructor')) return false;
const ctor = proto.constructor;
if (typeof ctor !== 'function') return ctor;
Copy link
Collaborator

Choose a reason for hiding this comment

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

^ Copilot seems right about this one?

Comment on lines 46 to 51
return Object.fromEntries(
Object.entries(message).map(([key, value]) => [
key,
redactSecrets(value, secrets),
]),
) as T;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
return Object.fromEntries(
Object.entries(message).map(([key, value]) => [
key,
redactSecrets(value, secrets),
]),
) as T;
return Object.setPrototypeOf(Object.fromEntries(
Object.entries(message).map(([key, value]) => [
key,
redactSecrets(value, secrets),
]),
), Object.getPrototypeOf(message)) as T;

since isPlainObject() also (correctly imo) returns true for objects with a null prototype?

secrets: Secret[] | undefined = undefined,
): T {
if (secrets) {
message = redactSecrets(message, secrets);
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not a blocker for sure, but I'd suggest merging the object walking here with the object walking in redactSecrets(), they do very similar transformations in a very similar fashion

We don't want to return a truthy value if the ctor is a non function
value (like an object). Plain objects either have a plain object as
a prototype or a null prototype
@kmruiz kmruiz merged commit 3b1d6e3 into main Sep 3, 2025
31 checks passed
@kmruiz kmruiz deleted the feat/mcp-29 branch September 3, 2025 16:10
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