fix(windows): grant secret ACLs to effective token SID - #1180
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughChangesWindows ACL hardening now derives the effective principal from the Windows token SID. A new resolver runs trusted PowerShell synchronously or asynchronously, validates SID output, caches successful results, shares concurrent lookups, enforces timeouts, and reports Windows ACL identity
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant ACLHardening
participant PrincipalResolver
participant PowerShell
participant icacls
ACLHardening->>PrincipalResolver: Resolve effective Windows SID
PrincipalResolver->>PowerShell: Run WindowsIdentity.GetCurrent().User.Value
PowerShell-->>PrincipalResolver: Return SID output
PrincipalResolver-->>ACLHardening: Return normalized *S-1-... principal
ACLHardening->>icacls: Apply /grant:r using SID
icacls-->>ACLHardening: Return ACL result
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
🧪 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 |
Resolve the current token SID instead of trusting USERDOMAIN and USERNAME on workgroup hosts. Keep identity lookup failures separate from icacls timeouts.\n\nRefs lidge-jun#1149
|
⏳ DRAFT
What to do
Review readiness checklist
1/4 boxes ticked. This PR stays in draft until every box above is ticked. |
e8afb8f to
df6989c
Compare
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
Thank you — this is adopted, and the design decisions in it held up under review. Your commit is the first commit of #1216, cherry-picked unchanged with Three things I layered on top, all of which came out of an independent audit rather than a stylistic preference: The two most important tests never ran outside Windows.
I tried to add back a qualified name fallback for the optional path and was wrong. My reasoning was that Closing in favor of #1216. The credit for the fix is yours. |
…dable Builds on the contributor fix by luvs01 (lidge-jun#1180), which replaced the USERDOMAIN\USERNAME ACL principal with the effective token SID. Three things that fix left open: The synthetic principal POSIX CI needs lived in windows-secret-acl.ts and was chosen before the injected runner. That ordering made a lookup FAILURE unreachable outside Windows, so the two cases that defend the fail-closed boundary and the timedOutPaths isolation were guarded with `if (process.platform !== "win32") return;` and never ran on Linux or macOS. A test that silently returns on two of three CI platforms is not coverage of a security boundary. The synthetic value moves to the resolver as its own seam, runner selection becomes explicit > synthetic > default, and both guards are gone. sanitizedAclError re-attaches only allow-listed codes, and EACLIDENTITY was not among them. A required-mode harden therefore threw with the cause in the message but `error.code === undefined`, so no caller could branch on "the SID could not be resolved" versus "icacls stalled". The existing test matched the message and hid this. The absence of a name-shaped fallback is now stated as the fix rather than left as an omission. `DOMAIN\User` has a valid shape, but shape is not evidence of the token's subject, and both variables are writable by whatever launched us. runIcacls grants the principal Full Control and then removes inheritance, so a wrong principal either leaves another account holding the secret or strands the file with no usable ACE. An independent audit rejected an earlier draft of this change that restored that fallback for the optional read path. Coverage now runs the sync and async paths across required and optional on every platform, and asserts zero icacls invocations when the environment names a plausible-looking account. Ablation: reverting the runner ordering makes identityCalls 0 and the required harden succeed (2 red); dropping EACLIDENTITY from the allow-list makes both toMatchObject assertions fail (2 red). Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com> Closes lidge-jun#1149
Summary
USERDOMAIN\\USERNAME;icaclsas*S-1-...:(F);icaclstimeouts;Fixes #1149.
Why
On workgroup Windows hosts,
USERDOMAINmay be the literalWORKGROUPwhile the effective token belongs toCOMPUTER\\user. The previouscurrentWindowsUser()therefore askedicaclsto grant an unmappableWORKGROUP\\userprincipal and received exit code 52. Required ACL hardening then failed closed even though NTFS and the effective account were valid.The token SID is locale-independent and is already the authority used by the Codex coordinator identity work from #998. This PR extracts a low-level ACL-specific resolver rather than importing the higher-level coordinator module.
This is independent of #1130/#1135: those handle one retry after an ACL timeout; this fixes principal construction before
icaclscan succeed.Security boundary
GetSystemDirectoryW; PATH andSystemRootdo not select it.windowsHide: true,-WindowStyle Hidden,-NonInteractive, and-NoProfile.S-1-...output is accepted; raw output, paths, and account identifiers are not included in diagnostics.EACLIDENTITY, notETIMEDOUT, so it cannot poison the destination ACL timeout memo.Verification
Post-rebase at
6d04574d:b22e0e6d0): the same suites 166/166 passed.tests/server-management-auth.test.tsexceeded its fixed 5-second per-test cap on this host. The unchangeddevbaseline reproduced the same six timeouts plus one unrelated unhandled assertion, while this branch had no patch-specific correctness failure. This result is reported as baseline timing instability, not as a pass.bun x tsc --noEmit: passed on both runtimes.bun scripts/privacy-scan.ts: passed.git diff --check: passed.Checklist
Draft pending upstream CI and the repository-required human maintainer security review.
Review readiness checklist
This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:
All CI tests are green on my local testing.
I pushed my PR to the latest dev commit.
I resolved all correct Codex and CodeRabbit findings.
My PR is ready for review.
Summary by CodeRabbit
Bug Fixes
Tests