Skip to content

fix: redact filesystem watcher previews before transport#332

Open
honor2030 wants to merge 2 commits into
rohitg00:mainfrom
honor2030:fix/fs-watcher-redact-sensitive-previews
Open

fix: redact filesystem watcher previews before transport#332
honor2030 wants to merge 2 commits into
rohitg00:mainfrom
honor2030:fix/fs-watcher-redact-sensitive-previews

Conversation

@honor2030
Copy link
Copy Markdown

@honor2030 honor2030 commented May 13, 2026

Summary

  • Redact sensitive filesystem-watcher preview lines before the observation payload is serialized and sent to /agentmemory/observe
  • Treat dotenv filenames (.env, .env.*) as intentional text previews so they go through the same client-side redaction path instead of depending on extname() behavior
  • Add regression coverage proving fake dotenv values, bearer-token preview values, and quoted JSON-style sensitive keys do not appear in the captured fetch body

Why

mem::observe already has server-side redaction paths, but the fs-watcher sends preview content over the configured transport first. For remote/plain HTTP setups, preview redaction needs to happen in the watcher before fetch().

Conflict / duplicate check

Test Plan

  • RED check before implementation: PATH=/opt/homebrew/bin:$PATH npm test -- test/fs-watcher.test.ts --reporter=basic failed on the two new redaction tests
  • PATH=/opt/homebrew/bin:$PATH npm test -- test/fs-watcher.test.ts -t "redacts" --reporter=basic
  • PATH=/opt/homebrew/bin:$PATH npm test -- --reporter=basic81 passed, 880 passed
  • PATH=/opt/homebrew/bin:$PATH npm run build
  • PATH=/opt/homebrew/bin:$PATH node --check integrations/filesystem-watcher/watcher.mjs
  • git diff --check

Note: the initial build attempt exposed a local missing optional rolldown native binding; reinstalling node_modules with the same npm version restored the optional dependency, after which build passed.

Summary by CodeRabbit

  • New Features
    • File monitoring now redacts sensitive information in previews (secret keys, API tokens, bearer tokens) and recognizes environment-style files to ensure their contents are masked in emitted events.
  • Tests
    • Added tests validating redaction behavior across dotenv-style files, JSON-style keys, and inline bearer tokens to prevent accidental exposure.

Review Change Stack

@vercel
Copy link
Copy Markdown

vercel Bot commented May 13, 2026

@honor2030 is attempting to deploy a commit to the rohitg00's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 13, 2026

📝 Walkthrough

Walkthrough

The watcher now redacts sensitive information from file previews before emitting events. A redaction pipeline detects .env paths and masks environment variable assignment values and bearer tokens with [REDACTED]. .env and .env.* files are treated as text and redaction is applied during flush; tests validate three redaction scenarios.

Changes

Sensitive Data Redaction in Filesystem Watcher

Layer / File(s) Summary
Redaction pipeline implementation
integrations/filesystem-watcher/watcher.mjs
Defines the [REDACTED] placeholder, introduces dot-env path detection, classifies sensitive assignment keys, implements per-line redaction for sensitive key assignments and bearer tokens, and provides redactSensitivePreview to process preview text.
Text-file handling and flush integration
integrations/filesystem-watcher/watcher.mjs
Extends isTextFile to treat .env and .env.* as text and applies redactSensitivePreview to the preview immediately after reading it during flush before building the emitted payload.
Redaction validation tests
test/fs-watcher.test.ts
Adds three tests that write a .env file, a JSON-like file with sensitive keys, and a text file with an Authorization: Bearer token, then call flush and assert emitted observation data.content redacts secrets to [REDACTED] while preserving non-sensitive content and excluding original secret strings.

Sequence Diagram

sequenceDiagram
  participant Watcher as FilesystemWatcher
  participant File as FileReader
  participant Redactor as redactSensitivePreview
  participant Payload as WatcherPayload

  Watcher->>File: read preview text
  File-->>Watcher: preview content
  Watcher->>Redactor: pass preview content & filepath
  Redactor->>Redactor: detect .env paths
  Redactor->>Redactor: redact assignment keys and bearer tokens
  Redactor-->>Watcher: redacted preview
  Watcher->>Payload: include redacted preview in emitted event
Loading

Possibly Related PRs

  • rohitg00/agentmemory#163: Adds initial watcher and file-preview payload emission that this redaction change builds upon.

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 In burrows deep I sniff and peep,

Tokens tucked where shadows sleep.
I nibble secrets, stitch them neat,
Replace with "[REDACTED]" — tidy, sweet.
Hop safe, small files, beneath my feet.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: redact filesystem watcher previews before transport' directly and accurately summarizes the main change: adding client-side redaction of sensitive data in filesystem watcher preview payloads before transport.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
integrations/filesystem-watcher/watcher.mjs (1)

56-67: 💤 Low value

Optional: quoted-key assignments (JSON/YAML-quoted) bypass redaction.

The assignment regex requires the key to start with [A-Za-z_], so previews containing JSON- or YAML-quoted secrets such as "api_key": "sk-..." won't match the sensitive-key branch and will only be redacted if the value happens to trip the Bearer … fallback. That is outside the dotenv + bearer scope described in the PR, but worth a follow-up if watched roots may include JSON config files (*.json, *.yaml — both already in TEXT_EXTENSIONS).

♻️ Sketch: extend regex to allow optionally quoted keys
-  const assignment = line.match(/^(\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_.-]*)\s*([=:])\s*)(.*)$/);
+  const assignment = line.match(
+    /^(\s*(?:export\s+)?["']?([A-Za-z_][A-Za-z0-9_.-]*)["']?\s*([=:])\s*)(.*)$/,
+  );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@integrations/filesystem-watcher/watcher.mjs` around lines 56 - 67, The
assignment regex in redactSensitiveLine doesn't match quoted JSON/YAML keys
(e.g. "api_key": "sk-..."), so update the regex in redactSensitiveLine to accept
an optional single- or double-quoted key (capture the unquoted key in the same
group used for isSensitiveKey) and keep the existing capture groups for the
separator and value; ensure you strip quotes when calling
isSensitiveKey(assignment[2]) (or adjust the capture group to be the unquoted
name) so quoted-key assignments are treated the same as bare keys; leave the
Bearer fallback and redactSensitivePreview unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@integrations/filesystem-watcher/watcher.mjs`:
- Around line 56-67: The assignment regex in redactSensitiveLine doesn't match
quoted JSON/YAML keys (e.g. "api_key": "sk-..."), so update the regex in
redactSensitiveLine to accept an optional single- or double-quoted key (capture
the unquoted key in the same group used for isSensitiveKey) and keep the
existing capture groups for the separator and value; ensure you strip quotes
when calling isSensitiveKey(assignment[2]) (or adjust the capture group to be
the unquoted name) so quoted-key assignments are treated the same as bare keys;
leave the Bearer fallback and redactSensitivePreview unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 810f73f5-2b0a-4355-b21a-bf93cfee9a03

📥 Commits

Reviewing files that changed from the base of the PR and between cba234d and f558232.

📒 Files selected for processing (2)
  • integrations/filesystem-watcher/watcher.mjs
  • test/fs-watcher.test.ts

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
test/fs-watcher.test.ts (1)

148-213: ⚖️ Poor tradeoff

Consider aligning test patterns with existing async watcher tests.

The three new redaction tests (lines 148-213) call w.flush() directly rather than using the w.start() / writeFileSync() / await wait() / w.stop() pattern established by the first five tests (lines 43-146). While direct flush() calls allow focused unit testing of the redaction logic without debouncing complexity, mixing patterns within the same test suite reduces consistency and may not exercise the actual watcher code path (file detection → debouncing → flush).

If the goal is regression coverage of the full watcher pipeline, consider refactoring these tests to follow the existing async pattern:

Example refactor for dotenv test
  it("redacts sensitive dotenv preview values before sending observations", async () => {
-    writeFileSync(
-      join(root, ".env"),
-      [
-        "OPENAI_API_KEY=sk-test-secret-value",
-        "PUBLIC_FLAG=enabled",
-        "AUTHORIZATION=Bearer live-token-value",
-      ].join("\n"),
-    );
     const w = new FilesystemWatcher({
       roots: [root],
       baseUrl: "http://localhost:3111",
       logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() },
     });
+    w.start();
+    try {
+      writeFileSync(
+        join(root, ".env"),
+        [
+          "OPENAI_API_KEY=sk-test-secret-value",
+          "PUBLIC_FLAG=enabled",
+          "AUTHORIZATION=Bearer live-token-value",
+        ].join("\n"),
+      );
+      await wait(800);

-    await w.flush(root, ".env");
-
-    expect(captured).toHaveLength(1);
-    const content = (captured[0].body as { data: { content: string } }).data.content;
+      expect(captured.length).toBeGreaterThanOrEqual(1);
+      const content = (captured[captured.length - 1].body as { data: { content: string } }).data.content;
       expect(content).toContain("OPENAI_API_KEY=[REDACTED]");
       expect(content).toContain("PUBLIC_FLAG=enabled");
       expect(content).toContain("AUTHORIZATION=[REDACTED]");
       expect(content).not.toContain("sk-test-secret-value");
       expect(content).not.toContain("live-token-value");
+    } finally {
+      w.stop();
+    }
   });

(Apply similar changes to the other two redaction tests.)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/fs-watcher.test.ts` around lines 148 - 213, Tests for redaction call
FilesystemWatcher.flush() directly which bypasses the watcher pipeline; update
each redaction test (the dotenv, settings.json, and request.txt cases) to follow
the existing async watcher pattern by creating the FilesystemWatcher, calling
w.start(), writing the file with writeFileSync, awaiting the existing wait()
helper to allow the watcher debounce to detect the change, then calling w.stop()
before asserting captured results so the full file-detection → debouncing →
flush code path in FilesystemWatcher is exercised; ensure you reuse the same
logger/captured setup and apply this change consistently to the three tests
referencing FilesystemWatcher.start/stop/flush.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@test/fs-watcher.test.ts`:
- Around line 148-213: Tests for redaction call FilesystemWatcher.flush()
directly which bypasses the watcher pipeline; update each redaction test (the
dotenv, settings.json, and request.txt cases) to follow the existing async
watcher pattern by creating the FilesystemWatcher, calling w.start(), writing
the file with writeFileSync, awaiting the existing wait() helper to allow the
watcher debounce to detect the change, then calling w.stop() before asserting
captured results so the full file-detection → debouncing → flush code path in
FilesystemWatcher is exercised; ensure you reuse the same logger/captured setup
and apply this change consistently to the three tests referencing
FilesystemWatcher.start/stop/flush.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 70817c7c-2965-40a4-8ac6-acd36c2d34b2

📥 Commits

Reviewing files that changed from the base of the PR and between f558232 and 6524400.

📒 Files selected for processing (2)
  • integrations/filesystem-watcher/watcher.mjs
  • test/fs-watcher.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • integrations/filesystem-watcher/watcher.mjs

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.

1 participant