{"record":{"id":"0b9db43d1e3840f6","repo":"Hmbown/CodeWhale","slug":"codewhale-owned-credential-file-dacl-must-grant-on","errorCode":null,"errorMessage":"Codewhale-owned credential file DACL must grant only one user","messagePattern":"Codewhale-owned credential file DACL must grant only one user","errorType":"validation","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/external_credentials.rs","lineNumber":490,"sourceCode":"        ));\n    }\n    if dacl.is_null() {\n        return Err(io::Error::new(\n            io::ErrorKind::PermissionDenied,\n            \"Codewhale-owned credential file must have an owner-only DACL\",\n        ));\n    }\n    let mut count = 0;\n    let mut entries: *mut EXPLICIT_ACCESS_W = std::ptr::null_mut();\n    // SAFETY: `dacl` is owned by the live security descriptor; Windows\n    // allocates the returned entries, released below.\n    let result = unsafe { GetExplicitEntriesFromAclW(dacl, &mut count, &mut entries) };\n    if result != ERROR_SUCCESS {\n        return Err(io::Error::from_raw_os_error(result as i32));\n    }\n    let _entries = WindowsLocalAllocation(entries.cast());\n    if count != 1 || entries.is_null() {\n        return Err(io::Error::new(\n            io::ErrorKind::PermissionDenied,\n            \"Codewhale-owned credential file DACL must grant only one user\",\n        ));\n    }\n    // SAFETY: `count == 1` proves the first returned entry is initialized.\n    let entry = unsafe { &*entries };\n    let trustee_sid: PSID = entry.Trustee.ptstrName.cast();\n    // SAFETY: form and null checked in this expression; sid owned by `user`.\n    let current_user_only = entry.Trustee.TrusteeForm == TRUSTEE_IS_SID\n        && !trustee_sid.is_null()\n        && unsafe { EqualSid(trustee_sid, user.sid()) } != 0\n        && matches!(entry.grfAccessMode, SET_ACCESS | GRANT_ACCESS)\n        && entry.grfAccessPermissions == FILE_ALL_ACCESS;\n    if !current_user_only {\n        return Err(io::Error::new(\n            io::ErrorKind::PermissionDenied,\n            \"Codewhale-owned credential file DACL is not current-user-only\",\n        ));","sourceCodeStart":472,"sourceCodeEnd":508,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/external_credentials.rs#L472-L508","documentation":"The library enumerates the DACL with GetExplicitEntriesFromAclW and requires exactly one explicit access entry; anything else (zero entries, or more than one grantee) means some principal other than the single intended user may hold access, so it throws PermissionDenied. This completes the owner-only policy: one owner, one ACE, no inheritance residue.","triggerScenarios":"read_codewhale_owned_to_string opens a file whose DACL has zero explicit entries or multiple ACEs — e.g. grants to both the user and Administrators/Users, or leftover inherited-style entries flattened onto the file.","commonSituations":"`icacls /grant` was run twice for different accounts; a sharing wizard granted Users or Authenticated Users read access; the file kept ACEs after a user/profile rename; group policies or folder inheritance added extra grants.","solutions":["Reset to a single user ACE: `icacls <file> /inheritance:r /grant:r \"%USERNAME%:F\"` (this drops all other ACEs).","Remove individual extra grantees: `icacls <file> /remove:g Users \"Authenticated Users\"` until only your account remains.","Inspect with `icacls <file>` or `Get-Acl <file> | Format-List` to see exactly who is granted access.","Avoid GUI sharing dialogs on the credential file; they add extra ACEs."],"exampleFix":":: before\nicacls C:\\Users\\me\\.codewhale\\token.json   :: grants me AND BUILTIN\\Users\n:: after\nicacls C:\\Users\\me\\.codewhale\\token.json /inheritance:r /grant:r \"%USERNAME%:F\"","handlingStrategy":"validation","validationCode":"// PowerShell pre-check: exactly one user should appear in the ACL\n// (Get-Acl $path).Access | Select-Object IdentityReference, AccessControlType","typeGuard":null,"tryCatchPattern":"match read_codewhale_owned_to_string(&path) {\n    Ok(creds) => use(creds),\n    Err(e) if e.to_string().contains(\"grant only one user\") => {\n        eprintln!(\"reset ACL: icacls {p} /inheritance:r /grant:r %USERNAME%:F\", p = path.display());\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Grant access to exactly one account; avoid GUI sharing dialogs on credential files.","Use /inheritance:r to drop inherited group grants when creating the file.","After any icacls change, re-list with `icacls <file>` to confirm a single ACE.","Audit ACLs periodically in environments with group policy folder redirection."],"tags":["windows","security","acl","dacl","credentials"],"backgroundTag":"permission-denied","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}