Reduce CoordinatedTextWriter secret-scan contention - #3856
Conversation
Code reviewReviewed Verified correct (worth calling out since this touches secret redaction):
Architectural suggestion (not a bug, but worth considering as a follow-up):
Since this PR is already reworking No CLAUDE.md compliance issues — the changes stay within the core library ( |
|
CI diagnosis: the sole Ubuntu failure was an unrelated timing timeout in |
|
Refreshed onto current main at 1127d84. Current-head validation: SecretMaskingPatternTests 29/29; OutputCoordinatorTests 19/19; core Release build 0 warnings / 0 errors. Please re-review current head. |
There was a problem hiding this comment.
Code review
Re-reviewed current head (1127d849f, after the rebase onto main) — thanks for the ping to re-review. Confirmed the concurrency rework itself (per-buffer SyncRoot, _outputLock serializing real-console writes, lock ordering SyncRoot → _lineBufferLock/_outputLock never reversed) is sound and deadlock-free, and the new SearchValues<string>-based single-pass matching preserves the original leftmost-longest-match semantics.
Found one high-severity issue (posted inline): removing the _secretObfuscator.Obfuscate(...) calls from FlushDirectPrefix/WriteCompletedLine/FlushPartialPrefix drops a safety net that mattered. ObfuscateCompletePatterns's early break (when the longest match at the leftmost position overlaps the retained-prefix boundary) can flush a buffer region verbatim even though it contains a different, shorter, fully-contained registered secret earlier in that same region. I verified this with a concrete repro and a standalone reimplementation of the exact algorithm — confirmed the leak occurs post-PR and that the removed second Obfuscate() pass would have caught it pre-PR. Details and a suggested fix direction are in the inline comment.
My previous review on this PR (before the rebase) concluded the removed calls were "genuinely redundant" — that conclusion was wrong; this scenario slipped through because it depends on interaction between two separately-registered secrets near the retained-prefix boundary, which the earlier pass didn't construct a counterexample for.
Everything else from the previous review still stands: the architectural suggestion to extract a shared SecretPatternCache-style type (mirroring SecretObfuscator.GetRegisteredSecretCache) remains a reasonable non-blocking follow-up, not required for this PR.
|
@claude review |
|
@codex review |
Code reviewRe-reviewed at current head ( Verified correct — the nested-secret fix ( The old behavior: once a match overlapping the retained-prefix boundary was found, the loop appended everything from I also verified the invariant the fix depends on: when Also checked (unchanged from previous review, still holds at this head):
Still-open, non-blocking suggestion from the previous review: No CLAUDE.md compliance issues — both changed files stay within the core library ( |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ef575ed76f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| { | ||
| output.Append(pending, searchIndex, pending.Length - searchIndex); | ||
| break; | ||
| searchIndex = match.Index + 1; |
There was a problem hiding this comment.
Mask shorter same-start secrets before retaining longer ones
When direct output ends with a retained prefix for another secret and two registered secrets start at the same index, this branch skips all matches at that index. For example, with secrets abc, abcdef, and efxyZ, writing abcdefxy retains efxy; FindFirstPattern returns the longer abcdef, this code advances to index 1, and the shorter abc match is never obfuscated. Since the safe prefix is now written without a final _secretObfuscator.Obfuscate pass, abcd reaches the real console and leaks the registered abc secret; consider checking shorter same-start matches before skipping the overlapping longer one.
AGENTS.md reference: AGENTS.md:L158-L162
Useful? React with 👍 / 👎.
Summary
SearchValues<string>with each secret-pattern snapshot and use it for one-pass match discoveryValidation
SecretMaskingPatternTests: 25 passedOutputCoordinatorTests: 19 passedModularPipelines.slnxRelease build: 0 warnings, 0 errorsCloses #3755