{"record":{"id":"c86e205e5a97d5df","repo":"Hmbown/CodeWhale","slug":"codewhale-issue-report-dacl-must-grant-only-one-user","errorCode":null,"errorMessage":"Codewhale issue-report DACL must grant only one user","messagePattern":"Codewhale issue-report DACL must grant only one user","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/github/report.rs","lineNumber":1091,"sourceCode":"        anyhow::ensure!(\n            !owner.is_null() && unsafe { EqualSid(owner, user.sid()) } != 0,\n            \"Codewhale issue-report storage owner is not the current user\"\n        );\n        anyhow::ensure!(\n            !dacl.is_null(),\n            \"Codewhale issue-report storage must have an owner-only DACL\"\n        );\n        let mut count = 0;\n        let mut entries: *mut EXPLICIT_ACCESS_W = std::ptr::null_mut();\n        // SAFETY: `dacl` belongs to the live descriptor; Windows allocates the\n        // returned entry array, released by the guard below.\n        let result = unsafe { GetExplicitEntriesFromAclW(dacl, &mut count, &mut entries) };\n        if result != ERROR_SUCCESS {\n            return Err(std::io::Error::from_raw_os_error(result as i32))\n                .context(\"reading Codewhale issue-report DACL entries\");\n        }\n        let _entries = WindowsLocalAllocation(entries.cast());\n        anyhow::ensure!(\n            count == 1 && !entries.is_null(),\n            \"Codewhale issue-report DACL must grant only one user\"\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        anyhow::ensure!(\n            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            \"Codewhale issue-report DACL is not current-user-only\"\n        );\n        Ok(())\n    }\n\n    #[cfg(windows)]","sourceCodeStart":1073,"sourceCodeEnd":1109,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tools/github/report.rs#L1073-L1109","documentation":"verify_windows_owner_only_handle validates that the DACL of a Codewhale issue-report handle grants exactly one access entry. GetExplicitEntriesFromAclW returned a count other than 1 (or a null entry pointer), so the security descriptor cannot be proven owner-only and the tool refuses to use the handle.","triggerScenarios":"The issue-report file/pipe handle was created with a DACL modified by another process, an ACL editor, or a CreateFile with security attributes that added extra ACEs (e.g. inherited group or Everyone entries). Also thrown if GetExplicitEntriesFromAclW succeeded but returned zero entries.","commonSituations":"Files created under directories whose inherited ACLs add ACEs; security-hardening tools or GPOs rewriting ACLs; manually running icacls edits on the report path; running the report tool against a handle created by an older Codewhale version with different security attributes.","solutions":["Delete the issue-report file/handle and let Codewhale recreate it with its owner-only DACL","Remove extra ACEs so exactly one grant remains, e.g. `icacls <path> /inheritance:r /grant:r \"$env:USERNAME:F\"`","Verify no group policy or antivirus is re-adding inherited ACEs on the parent directory","Check the handle-creation code path in report.rs for the security descriptor construction"],"exampleFix":"// before\nicacls report.txt /grant Users:F /grant Administrators:F\n// after\nicacls report.txt /inheritance:r /grant:r \"%USERNAME%\":F","handlingStrategy":"validation","validationCode":"// PowerShell: confirm the DACL grants exactly one ACE before handing the path to the tool\n(Get-Acl $path).Access.Count -eq 1","typeGuard":null,"tryCatchPattern":"match verify result { Err(e) if e.to_string().contains(\"only one user\") => recreate_handle(), ... }","preventionTips":["Create the report file with Codewhale's own security attributes; never pre-create it externally","Disable ACL inheritance on the report directory","Don't run icacls/GPO rewrites against temp/report paths"],"tags":["windows","acl","security"],"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"}