feat(core): make emit-time redaction programmable - #466
Conversation
Redaction is the only stage that runs before the console write — enrich and drains both run after it, so they can never scrub what already reached stdout. That made it the one place a consumer can act in time, and its policy language was entirely declarative: `replacement` could only be a constant. So any policy needing logic had nowhere to run. The escape hatch was `silent: true` plus a custom drain, which throws away the console sink to gain a transform — not a trade you can make on a platform that ingests stdout. `replacement` now also accepts a function, called with the matched value and its path, so a replacement can be derived from what it replaces — a stable fingerprint keeps requests correlatable without exposing the credential. `transform` covers what per-value replacement cannot: policies conditional on a sibling field, tenant-scoped, or allowlist-shaped. Both run where redaction already ran, so ordering and the documented contract are unchanged. Two deliberate choices: - `transform` runs before the declarative stages, not after, so it sees raw values and `paths` / `builtins` / `patterns` still apply to what it leaves behind. A hook that misses a field is not the last line of defence. - A replacement that throws falls back to `[REDACTED]` rather than emitting the raw value. Degrading to over-redaction is the only safe direction for a stage whose job is to not leak. Function policy cannot cross the build-time config bridges, which serialize to JSON. Rather than drop it silently — the failure mode of #408 and #441 — the Nitro modules warn, and the docs point to declaring it at runtime. Closes #463 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uyng7c1TAGK6eFSBMBE33e
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Thank you for following the naming conventions! 🙏 |
|
Warning Review limit reached
Next review available in: 30 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughChangesProgrammable emit-time redaction now supports computed replacement callbacks and synchronous transforms. The pipeline passes match context, handles callback failures, preserves functions during normalization, warns about JSON configuration loss, and documents the new behavior. The lab also receives minor syntax and promise-return cleanup. Programmable redaction
Lab cleanup
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant EventEmitter
participant redactEvent
participant ConsoleSink
EventEmitter->>redactEvent: finalize event and apply transform
redactEvent->>redactEvent: apply path and pattern replacements
redactEvent-->>EventEmitter: return redacted event
EventEmitter->>ConsoleSink: serialize and write event
Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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.
Inline comments:
In @.changeset/programmable-redaction.md:
- Line 20: Update the wording in programmable-redaction.md to distinguish
replacement-function failures from transform failures: state that a throwing
replacement function falls back to “[REDACTED]”, while a throwing transform is
caught and skipped without replacing the event.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 9bc7de58-803a-49d6-b4b9-05e57ba79691
📒 Files selected for processing (10)
.changeset/programmable-redaction.mdapps/docs/content/2.learn/6.redaction.mdapps/docs/content/7.reference/1.configuration.mdpackages/evlog/src/index.tspackages/evlog/src/nitro-v3/module.tspackages/evlog/src/nitro/module.tspackages/evlog/src/redact.tspackages/evlog/src/types.tspackages/evlog/test/core/redact-integration.test.tspackages/evlog/test/core/redact.test.ts
Not related to the redaction change in this branch, but they fail `lint` and `autofix` on every PR until main is clean, so they are fixed here rather than left to mask a real regression on the next push. - index.vue: `:suggested-name` same-name shorthand, matching the sibling props on the same element. Compiles to the identical `_ctx.suggestedName` binding. - assets.ts, db.ts: both functions were `async` with a single `return <promise>` body. The declared `Promise<T>` return type is unchanged and neither callee can throw synchronously — `request` is itself async — so dropping `async` preserves behaviour. Also narrow the changeset wording: it read as though any throwing function fell back to `[REDACTED]`, which is true of `replacement` but not of `transform`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uyng7c1TAGK6eFSBMBE33e
commit: |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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.
Inline comments:
In @.changeset/programmable-redaction.md:
- Line 20: Update the changeset description to document that resolveReplacement
falls back to [REDACTED] when a replacement callback returns a non-string value,
alongside the existing throwing-callback fallback behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: e07b95ac-2a49-4900-a158-22d50582f2ae
📒 Files selected for processing (4)
.changeset/programmable-redaction.mdapps/lab/app/pages/index.vueapps/lab/app/utils/lab/assets.tsapps/lab/app/utils/lab/db.ts
`resolveReplacement` treats a callback returning a non-string the same as one that throws — both fall back to `[REDACTED]`. The prose docs already said so; the changeset mentioned only the throwing case. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uyng7c1TAGK6eFSBMBE33e
…action # Conflicts: # apps/lab/app/utils/lab/assets.ts # apps/lab/app/utils/lab/db.ts
🔗 Linked issue
Closes #463
📚 Description
Make redaction policies programmable by allowing function-valued
replacementand introducing a newtransformhook:replacementas a function:RedactConfig.replacementnow accepts a function(matched, ctx) => stringin addition to stringspath,key,groupsfor pattern captures)[REDACTED]if the function throws or returns non-string — a broken policy degrades to over-redaction, never leaks the raw valueNew
transformhook:RedactConfig.transformruns before declarative stages (paths,builtins,patterns)Build-time config safety:
hasFunctionRedactPolicy()to detect function-valued policy before JSON serializationRedactReplacementandRedactReplacementContexttypes for public APIDocumentation:
📝 Checklist
https://claude.ai/code/session_01Uyng7c1TAGK6eFSBMBE33e
Summary by CodeRabbit
New Features
Bug Fixes
Documentation