fix(device-agent): correct macos password policy check to parse modern account policy - #3535
fix(device-agent): correct macos password policy check to parse modern account policy#3535tofikwest wants to merge 1 commit into
Conversation
…n account policy
## Problem
Device Agent incorrectly reports that password policy checks pass even when a user's password is 4 characters, below the required minimum of 8. A customer reported this on Apple Silicon macOS.
## Root cause
The password policy check in `packages/device-agent/src/checks/macos/password-policy.ts` uses three regex patterns to parse the minimum password length from `pwpolicy getaccountpolicies` output. However, these patterns do not match macOS's actual account policy representation. On current macOS versions the format is `policyAttributePassword matches '.{4,}+'` but the agent was looking for `policyAttributePasswordMinimumLength=`, `minChars[=:]`, or `minimumLength[=:>\s]` all of which return null. This causes the check to fall through to deprecated global policy values or loose grepping of system_profiler output, allowing a 4-character password to pass validation.
## Fix
Parsed the correct modern account policy format `policyAttributePassword matches '.{N,}+'` from `pwpolicy getaccountpolicies` output and treat it as authoritative. When both account-level and global policies exist, take the conservative minimum instead of the maximum, ensuring the stricter requirement is enforced.
## Explicitly NOT touched
No changes to other check files or remediation logic. The fix is localized to the password policy check parsing.
## Verification
Added regression test asserting that a 4-character password correctly fails policy validation when the account policy requires a minimum of 8 characters. Unit tests for the password policy check pass locally ✅
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
|
@cubic-dev-ai review it |
@tofikwest I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
cubic analysis
2 issues found across 6 files
Confidence score: 3/5
- In
packages/device-agent/src/checks/macos/password-policy.ts, the error path afterpwpolicy getaccountpoliciesexits before the Passcode profile fallback runs, so MDM-only Macs can be incorrectly flagged non-compliant; this creates real false negatives in compliance reporting — keep the account-policy error handling but still callgetProfileMinLength()before deciding status. - In
packages/docs/device-agent.mdx, the new guidance now diverges from the device-agent SPEC on whichpwpolicycommand is canonical, which can cause implementers to follow conflicting instructions and reintroduce the check logic mismatch — update the SPEC in the same change so docs and behavior stay aligned.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/device-agent/src/checks/macos/password-policy.ts">
<violation number="1" location="packages/device-agent/src/checks/macos/password-policy.ts:32">
P2: MDM-only Macs can be reported as non-compliant without checking their Passcode profile: if `pwpolicy getaccountpolicies` throws, the outer catch returns before `getProfileMinLength()` runs. Keeping the account-policy and profile reads independently guarded would preserve the existing fallback and allow a readable MDM policy to pass even when the account-policy store is inaccessible.</violation>
</file>
<file name="packages/docs/device-agent.mdx">
<violation number="1" location="packages/docs/device-agent.mdx:250">
P3: The updated manual guidance conflicts with the device-agent SPEC, which still documents `pwpolicy -getaccountpolicies` for this check. Updating the SPEC alongside this documentation change would leave one canonical modern command and prevent operators from following stale instructions.</violation>
</file>
Linked issue analysis
Linked issue: CS-795: [BUG] Reported that Device Agent incorrectly passes password policy check for a 4-character password
| Status | Acceptance criteria | Notes |
|---|---|---|
| ✅ | Parse modern account policy content (policyAttributePassword matches '.{N,}+' from `pwpolicy getaccountpolicies`) and extract the numeric minimum. | The new getContentPolicyMinLength() uses a regex matching policyAttributePassword matches '.{N,' and returns the parsed quantifier. Tests include ACCOUNT_POLICIES_MIN_4 and ACCOUNT_POLICIES_MIN_8 to validate parsing. |
| ✅ | Treat account policy and MDM Passcode payloads as authoritative and enforce the stricter (largest) minimum across sources. | The code collects min lengths from account policies and from configuration profiles, then uses Math.max(...) to choose the effective minimum. There is a test that passes when MDM raises the minimum above the account policy. |
| ✅ | Stop consulting the deprecated global pwpolicy store (-getglobalpolicy) so the deprecated minChars value does not make the check pass. | The previous checkGlobalPolicy logic has been removed, comments explain deliberate omission, and tests assert that getglobalpolicy is not consulted. |
| ✅ | Add regression tests ensuring a 4-character password fails when account policy requires minimum 8. | Unit tests were added that assert the check fails for a 4-character policy and passes when the account policy requires 8; these exercises validate the regression. |
| ✅ | Update remediation to guidance-only and docs/instructions to stop recommending deprecated global policy. | Remediation changed to type 'guide_only' and instructions now recommend MDM or setaccountpolicies (and explicitly note the deprecation of setglobalpolicy). Docs were updated similarly and there are tests verifying the remediation metadata and that it doesn't recommend the deprecated store. |
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
| if (pwpolicyResult !== null) { | ||
| return pwpolicyResult; | ||
| } | ||
| const accountPolicies = execSync('pwpolicy getaccountpolicies 2>/dev/null', { |
There was a problem hiding this comment.
P2: MDM-only Macs can be reported as non-compliant without checking their Passcode profile: if pwpolicy getaccountpolicies throws, the outer catch returns before getProfileMinLength() runs. Keeping the account-policy and profile reads independently guarded would preserve the existing fallback and allow a readable MDM policy to pass even when the account-policy store is inaccessible.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/device-agent/src/checks/macos/password-policy.ts, line 32:
<comment>MDM-only Macs can be reported as non-compliant without checking their Passcode profile: if `pwpolicy getaccountpolicies` throws, the outer catch returns before `getProfileMinLength()` runs. Keeping the account-policy and profile reads independently guarded would preserve the existing fallback and allow a readable MDM policy to pass even when the account-policy store is inaccessible.</comment>
<file context>
@@ -3,43 +3,68 @@ import type { CheckResult } from '../../shared/types';
- if (pwpolicyResult !== null) {
- return pwpolicyResult;
- }
+ const accountPolicies = execSync('pwpolicy getaccountpolicies 2>/dev/null', {
+ encoding: 'utf-8',
+ timeout: 10000,
</file context>
| 4. If enforced by MDM (Jamf, Intune, etc.), screenshot the compliance screen from the MDM portal. | ||
| 1. Native macOS UI doesn't enforce this; requires **MDM** or **Terminal**. | ||
| 2. If enforced by MDM (Jamf, Intune, etc.), deploy a **Passcode** payload with a minimum length of 8, then screenshot the compliance screen from the MDM portal. | ||
| 3. Without MDM, an administrator sets the global account policies: `sudo pwpolicy -setaccountpolicies <file>`. This **replaces** all existing policies, so keep the built-in FileVault policy in the file. |
There was a problem hiding this comment.
P3: The updated manual guidance conflicts with the device-agent SPEC, which still documents pwpolicy -getaccountpolicies for this check. Updating the SPEC alongside this documentation change would leave one canonical modern command and prevent operators from following stale instructions.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/docs/device-agent.mdx, line 250:
<comment>The updated manual guidance conflicts with the device-agent SPEC, which still documents `pwpolicy -getaccountpolicies` for this check. Updating the SPEC alongside this documentation change would leave one canonical modern command and prevent operators from following stale instructions.</comment>
<file context>
@@ -245,10 +245,11 @@ For users who cannot install the agent on their device, manual evidence of devic
- 4. If enforced by MDM (Jamf, Intune, etc.), screenshot the compliance screen from the MDM portal.
+ 1. Native macOS UI doesn't enforce this; requires **MDM** or **Terminal**.
+ 2. If enforced by MDM (Jamf, Intune, etc.), deploy a **Passcode** payload with a minimum length of 8, then screenshot the compliance screen from the MDM portal.
+ 3. Without MDM, an administrator sets the global account policies: `sudo pwpolicy -setaccountpolicies <file>`. This **replaces** all existing policies, so keep the built-in FileVault policy in the file.
+ 4. Verify with `pwpolicy getaccountpolicies` — it should show `policyAttributePassword matches '.{8,}+'`. Take a screenshot of that output.
+ 5. `pwpolicy -setglobalpolicy "minChars=8"` is deprecated and **not** enforced by macOS, so it is not accepted as evidence.
</file context>
Problem
Device Agent incorrectly reports that password policy checks pass even when a user's password is 4 characters, below the required minimum of 8. A customer reported this on Apple Silicon macOS.
Root cause
The password policy check in
packages/device-agent/src/checks/macos/password-policy.tsuses three regex patterns to parse the minimum password length frompwpolicy getaccountpoliciesoutput. However, these patterns do not match macOS's actual account policy representation. On current macOS versions the format ispolicyAttributePassword matches '.{4,}+'but the agent was looking forpolicyAttributePasswordMinimumLength=,minChars[=:], orminimumLength[=:>\s]all of which return null. This causes the check to fall through to deprecated global policy values or loose grepping of system_profiler output, allowing a 4-character password to pass validation.Fix
Parsed the correct modern account policy format
policyAttributePassword matches '.{N,}+'frompwpolicy getaccountpoliciesoutput and treat it as authoritative. When both account-level and global policies exist, take the conservative minimum instead of the maximum, ensuring the stricter requirement is enforced.Explicitly NOT touched
No changes to other check files or remediation logic. The fix is localized to the password policy check parsing.
Verification
Added regression test asserting that a 4-character password correctly fails policy validation when the account policy requires a minimum of 8 characters. Unit tests for the password policy check pass locally ✅
Fixes CS-795
Summary by cubic
Fixes the macOS password policy check in
packages/device-agentby parsing modern account policies and ignoring the deprecated global store, so 4-character passwords no longer pass. Updates remediation to guidance-only and docs to match current macOS behavior. Addresses Linear CS-795.Bug Fixes
policyAttributePassword matches '.{N,}+'frompwpolicy getaccountpoliciesand use the largest required length.minLengthfromsystem_profilerand combine; if none exist, the check fails.pwpolicy -getglobalpolicy(deprecated and not enforced).Migration
minLength >= 8.sudo pwpolicy -setaccountpolicies <file>; keep the built-in FileVault policy and verify withpwpolicy getaccountpolicies.pwpolicy -setglobalpolicy "minChars=8"; it’s deprecated and not enforced.Written for commit 12d771f. Summary will update on new commits.