Hmbown/CodeWhale · critical
Codewhale-owned xAI OAuth DACL must grant only one user
Error message
Codewhale-owned xAI OAuth DACL must grant only one user
What it means
Windows-only check in verify_windows_owner_only_handle. After confirming a DACL exists, the store enumerates its ACEs with GetExplicitEntriesFromAclW and requires exactly one entry. A DACL with zero entries (allow-nothing), multiple entries, or an unpopulated entries pointer does not match the owner-only contract, so the xAI OAuth storage is rejected. This prevents secret access being shared with groups or other principals.
Solutions
- Delete the storage directory and let Codewhale recreate it with its owner-only DACL, then re-authenticate the xAI OAuth flow.
- Fix the ACL manually to a single grant: icacls "<path>" /inheritance:r /grant:r "%USERNAME%":F (removing all other ACEs).
- Identify the tool that added the extra ACEs and exclude the credentials path from its management.
- If it fires on newly provisioned storage, this is a provisioning bug in the app — report it with icacls output for the path.
Example fix
// before icacls xai-store // BUILTIN\Administrators:(F) // Authenticated Users:(RX) // after icacls xai-store /inheritance:r /grant:r "%USERNAME%":F
Defensive patterns
Strategy: validation
Validate before calling
// Before opening the store, count ACEs and require exactly one grant
// icacls <path> /verify (or parse GetExplicitEntriesFromAclW in-process)
// Any output listing more than one principal means the store will refuse.
let output = Command::new("icacls").arg(&path).arg("/verify").output()?; Prevention
- Do not add group or service ACEs to the credentials directory; the contract is exactly one current-user grant.
- After restoring from backup, reset the ACL to a single grant before launching the app.
- Check the ACL after any third-party security tool runs on the profile directory.
When it happens
Trigger: Opening the xAI OAuth store (open_owned_credentials_directory / validate_owned_file_handle) or running windows_store_secures_every_new_owned_object_for_the_current_user, when the object's DACL contains any number of ACEs other than exactly one — e.g. ACLs set by icacls with extra group grants, inherited entries merged in, or an empty DACL.
Common situations: A user or admin tool widened the ACL ('Authenticated Users', 'Administrators' added); a sync client restored an ACL with multiple ACEs; an empty DACL was applied by a cleanup script (deny-all storage).
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- Codewhale-owned credential file DACL must grant only one…
- Codewhale-owned credential file must have an owner-only DACL
- Codewhale-owned xAI OAuth DACL is not current-user-only
- Codewhale-owned xAI OAuth storage must have an owner-only…
- Codewhale issue-report DACL is not current-user-only
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/b24a078e95765c0d.
Report an issue: GitHub.
Appendix: source
Thrown at crates/config/src/xai_credentials.rs:1520
anyhow::ensure!(
!owner.is_null() && unsafe { EqualSid(owner, user.sid()) } != 0,
"Codewhale-owned xAI OAuth storage owner is not the current user"
);
anyhow::ensure!(
!dacl.is_null(),
"Codewhale-owned xAI OAuth storage must have an owner-only DACL"
);
let mut count = 0;
let mut entries: *mut EXPLICIT_ACCESS_W = std::ptr::null_mut();
// SAFETY: `dacl` belongs to the live descriptor; Windows allocates the
// returned entry array, released by the guard below.
let result = unsafe { GetExplicitEntriesFromAclW(dacl, &mut count, &mut entries) };
if result != ERROR_SUCCESS {
return Err(std::io::Error::from_raw_os_error(result as i32))
.context("reading Codewhale-owned xAI OAuth DACL entries");
}
let _entries = WindowsLocalAllocation(entries.cast());
anyhow::ensure!(
count == 1 && !entries.is_null(),
"Codewhale-owned xAI OAuth DACL must grant only one user"
);
// SAFETY: `count == 1` proves the first returned entry is initialized.
let entry = unsafe { &*entries };
let trustee_sid: PSID = entry.Trustee.ptstrName.cast();
// SAFETY: form and null checked in this expression; sid owned by `user`.
anyhow::ensure!(
entry.Trustee.TrusteeForm == TRUSTEE_IS_SID
&& !trustee_sid.is_null()
&& unsafe { EqualSid(trustee_sid, user.sid()) } != 0
&& matches!(entry.grfAccessMode, SET_ACCESS | GRANT_ACCESS)
&& entry.grfAccessPermissions == FILE_ALL_ACCESS,
"Codewhale-owned xAI OAuth DACL is not current-user-only"
);
Ok(())
}
View on GitHub (pinned to 73e0f67d83)