Skip to content

fix(safety): block direct launchctl/systemctl service registration - #1311

Merged
griffinwork40 merged 4 commits into
mainfrom
issue-1279-block-direct-service-cmds
Aug 28, 2026
Merged

fix(safety): block direct launchctl/systemctl service registration#1311
griffinwork40 merged 4 commits into
mainfrom
issue-1279-block-direct-service-cmds

Conversation

@griffinwork40

Copy link
Copy Markdown
Owner

Summary

Closes #1279 — blocks direct launchctl/systemctl service registration commands across all four safety guard layers so agents must go through afk service install (the /service-setup skill).

This was the primary enabler of the 2026-08-24 incident where an agent autonomously loaded a launchd plist that posted to a production Substack account without human approval.

Changes

Guard layer File What was added
BASH_HIGH risk classifier risk-classifier.ts launchctl load/bootstrap/submit/start, systemctl enable/start/daemon-reload
safe-destruct-patterns safe-destruct-patterns.ts Two BLOCK-tier patterns with redirect to /service-setup
Write denylist write-denylist.ts ~/Library/LaunchAgents, ~/Library/LaunchDaemons, /Library/Launch*, ~/.config/systemd
Bash sensitive roots bash-restriction-hook.ts Same paths added to builtinBashSensitiveRoots() + SENSITIVE_PATH_SIGNAL regex

Testing

  • All 253 existing tests pass (risk-classifier, safe-destruct-patterns, write-denylist, bash-restriction-hook)
  • The SENSITIVE_PATH_SIGNAL sync test now covers the new paths
  • pnpm lint clean

…1279)

Add launchctl load/bootstrap/submit/start and systemctl enable/start/daemon-reload
to all four safety guard layers:

- risk-classifier: BASH_HIGH tier (triggers AFK approval prompt)
- safe-destruct-patterns: BLOCK tier (hard block with redirect to /service-setup)
- write-denylist: deny writes to ~/Library/LaunchAgents, LaunchDaemons, ~/.config/systemd
- bash-restriction-hook: sensitive roots + SENSITIVE_PATH_SIGNAL regex

Closes #1279
@griffinwork40 griffinwork40 added bug Something isn't working priority: high P1 — high user-facing impact, address soon labels Aug 27, 2026
@vercel

vercel Bot commented Aug 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agent-afk-docs Ready Ready Preview Aug 28, 2026 12:48am

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9054ff9502

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/agent/safe-destruct-patterns.ts Outdated
},
{
id: 'systemctl-service-enable',
re: /\bsystemctl\s+(enable|start|daemon-reload)\b/i,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Handle systemctl global options before the subcommand

The command form used by this repository is systemctl --user daemon-reload / systemctl --user enable --now (src/service/systemd/install.ts), but this regex requires the subcommand to immediately follow systemctl. I checked systemctl --help, which documents --user as the global option that connects to the user service manager; consequently, the valid persistent registration command systemctl --user enable --now attacker.service produces no safe-destruct match, and the identical literals in BASH_HIGH classify it only as medium. Account for global options before matching the blocked subcommands.

Useful? React with 👍 / 👎.

@griffinwork40 griffinwork40 left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Automated review (scheduled sweep), generated by the /review tool — a maintainer will follow up.


Prior Automated Round — Finding Dispositions

No prior automated rounds on this PR — this is the first automated review. No findings to carry forward.


Decision

⚠️ DO NOT MERGE — 2 high (security, correctness)

Severity arithmetic: 2 high blocking (security/correctness: launchctl kickstart activation bypass; systemctl enable-linger double-block false-positive); 1 medium advisory (security: .config/systemd path mismatch in bash check-2); 2 medium advisory (test-coverage: no tests for 7 new BASH_HIGH entries; no tests for 2 new BLOCK patterns); 1 medium advisory (correctness: launchctl enable persistence bypass); 2 low advisory.

Per sweep policy: only critical and high block. medium and below are non-blocking follow-ups.


Review Summary

PR: fix(safety): block direct launchctl/systemctl service registration
Reviewed ref: 9054ff9502f256c3ab9e4a0e2bfa7c7be820c649
Regime: light (hotfix, ~50 lines added, 4 files)
CI: Lint & Build ✅, Test (ubuntu-latest) ✅, Test (macos-latest) ✅, all checks passing

The PR correctly adds launchd/systemd service-registration blocking across four guard layers. The architecture is sound and the stated mechanism is correctly implemented. Two high-severity defects remain that should be addressed before merge.


🔴 Blocking Findings (high)

H1 · high · blocking:true · security · src/agent/risk-classifier.ts · ref:9054ff9502 · diff-context

launchctl kickstart is absent from BASH_HIGH — service activation bypass.

afk service install (the sanctioned alternative the new blockReason text redirects agents to) internally calls launchctl kickstart -k gui/<uid>/<label> via Node execFileSync (file-state: src/service/launchd/manager.ts:96). Because Node execFileSync bypasses the bash tool hook, this is architecturally fine. However, launchctl kickstart is also the direct command an agent would use to immediately start a registered-but-not-yet-running service — and it is absent from BASH_HIGH and from the launchctl-service-register safe-destruct regex. An agent told "use afk service install" might instead discover launchctl kickstart and use it directly without triggering any guard.

# BASH_HIGH at reviewed ref — launchctl entries:
'launchctl load', 'launchctl bootstrap', 'launchctl submit', 'launchctl start'
# MISSING: 'launchctl kickstart'

# safe-destruct regex:
/\blaunchctl\s+(load|bootstrap|submit|start)\b/i
# MISSING: kickstart in alternation

Suggestion: Add 'launchctl kickstart' to BASH_HIGH and kickstart to the launchctl-service-register regex alternation: /\blaunchctl\s+(load|bootstrap|submit|start|kickstart)\b/i.


H2 · high · blocking:true · security/correctness · src/agent/safe-destruct-patterns.ts + src/agent/risk-classifier.ts · ref:9054ff9502 · diff-context

systemctl enable-linger false-positives on both the new safe-destruct BLOCK pattern and the BASH_HIGH substring entry.

Live verification:

  • 'systemctl enable'.includes('systemctl enable') when applied to 'systemctl enable-linger root'true (BASH_HIGH raw substring match, line 191 of risk-classifier.ts)
  • /\bsystemctl\s+(enable|start|daemon-reload)\b/i.test('systemctl enable-linger root')true (word boundary \b fires between enable and -)

The correct command for session-level login persistence is loginctl enable-linger (the sanctioned path documented at src/service/systemd/install.ts:211). A user who accidentally types systemctl enable-linger — a plausible typo — triggers a hard BLOCK with a misleading message ("enables/starts a systemd unit that persists across sessions") when the actual command was a user-level linger toggle, not a service installation. This is the same regex false-positive identified in prior reviews of the companion PR (#1298).

// Live regex test — CONFIRMED false-positive:
/\bsystemctl\s+(enable|start|daemon-reload)\b/i.test('systemctl enable-linger root')  // → true

// BASH_HIGH substring — CONFIRMED false-positive:
'systemctl enable-linger root'.includes('systemctl enable')  // → true

Suggestion (safe-destruct): Replace terminal \b with (?:\s|$) in the systemctl-service-enable regex:

re: /\bsystemctl\s+(enable|start|daemon-reload)(?:\s|$)/i,

Suggestion (risk-classifier): Append trailing space: 'systemctl enable ' (so enable-linger does not match).


🟡 Advisory Findings (non-blocking — follow-up issues recommended)

M1 · medium · advisory · security · src/agent/tools/hooks/bash-restriction-hook.ts · ref:9054ff9502 · diff-context

.config/systemd path mismatch: write-denylist blocks the full tree; bash check-2 only blocks user/ subdir.

The write-denylist adds ${homedir()}/.config/systemd (entire tree). The bash-restriction-hook adds path.join(home, '.config', 'systemd', 'user') (user subdir only). Check-2 (literal path substring scan in deriveRestrictedSubstrings) therefore does not block cat ~/.config/systemd/system/evil.service — the user suffix causes a non-match. The SENSITIVE_PATH_SIGNAL regex covers .config/systemd (no /user suffix) correctly, so check-1 (interpreter eval guard) does catch it. Live test: ~/.config/systemd/system/evil.service — check-2 miss, SENSITIVE_PATH_SIGNAL hit.

Suggestion: Add path.join(home, '.config', 'systemd') (without /user) to builtinBashSensitiveRoots() to align with the write-denylist entry. The more specific user/ subdir entry can remain alongside it.


M2 · medium · advisory · test-coverage · src/agent/risk-classifier.test.ts · ref:9054ff9502 · absence confirmed

0 tests for the 7 new BASH_HIGH entries in risk-classifier.test.ts.

Searched risk-classifier.test.ts at reviewed ref for launchctl and systemctl: 0 matches. Every prior BASH_HIGH addition has dedicated test coverage; the 7 new entries have none. A silent deletion of any of them would pass the full test suite.

Suggestion: Add a describe block with it() for each pattern: launchctl load, launchctl bootstrap, launchctl submit, launchctl start, systemctl enable, systemctl start, systemctl daemon-reload.


M3 · medium · advisory · test-coverage · src/agent/safe-destruct-detect.test.ts · ref:9054ff9502 · absence confirmed

0 tests for the 2 new BLOCK patterns in safe-destruct-detect.test.ts.

Searched safe-destruct-detect.test.ts at reviewed ref for launchctl, systemctl, launchctl-service-register, systemctl-service-enable: 0 matches. The BLOCK patterns (launchctl-service-register, systemctl-service-enable) — the core fix for the 2026-08-24 incident — have no regression guard. A future edit that misconfigures the pattern id, tier, or regex would go undetected.

Suggestion: Add to the BLOCK it.each table:

  • launchctl bootstrap gui/501 /path/to.plist'launchctl-service-register'
  • systemctl enable afk-telegram.service'systemctl-service-enable'
  • systemctl daemon-reload'systemctl-service-enable'

M4 · medium · advisory · correctness · src/agent/risk-classifier.ts · ref:9054ff9502 · diff-context

launchctl enable (persistence-without-load) is not blocked.

launchctl enable <service> marks a service as enabled in the launchd disabled-database — it persists across reboots and causes the service to start at next boot WITHOUT immediately loading it. It is absent from BASH_HIGH and from the launchctl-service-register safe-destruct regex. Less severe than bootstrap/load (no immediate execution), but it is a persistent system modification that survives reboots.

Suggestion: Add 'launchctl enable' to BASH_HIGH and enable to the launchctl-service-register regex alternation (if H1 is addressed: /\blaunchctl\s+(load|bootstrap|submit|start|kickstart|enable)\b/i).


L1 · low · advisory · spec-compliance · diff-context

PR title says "service registration" but start/daemon-reload are activation/housekeeping commands, not registration. No code change needed — cosmetic doc gap only. Update the commit body to say "registration and activation commands" to accurately describe the block scope.


L2 · low · advisory · test-coverage · src/agent/tools/handlers/write-denylist.test.ts · ref:9054ff9502 · file-state

Regression guard list (describe('regression guard — all original BUILTIN_WRITE_DENYLIST entries still block')) does not enumerate the 5 new entries. The meta-test at write-denylist.test.ts:97-103 iterates BUILTIN_WRITE_DENYLIST and provides automatic coverage for new entries. But the static regression guard at line 339 (which explicitly lists the original 8 entries) will not catch a future deletion of the new entries. Low severity — the meta-test covers the gap.


Spec-compliance

Stated intent: PR #1311 title+body — spec-compliance assessed.

Requirement Status
BASH_HIGH: launchctl load/bootstrap/submit/start ✅ All 4 added
BASH_HIGH: systemctl enable/start/daemon-reload ✅ All 3 added; ⚠️ systemctl enable has false-positive on enable-linger (H2)
safe-destruct: launchctl-service-register BLOCK pattern ✅ Implemented; ⚠️ kickstart missing (H1)
safe-destruct: systemctl-service-enable BLOCK pattern ✅ Implemented; ⚠️ enable-linger false-positive (H2)
write-denylist: LaunchAgents + LaunchDaemons (user + system) ✅ 4 entries added
write-denylist: .config/systemd ✅ Added
bash-restriction-hook: builtinBashSensitiveRoots() ✅ 5 entries added
bash-restriction-hook: SENSITIVE_PATH_SIGNAL ✅ 3 fragments added
All 253 existing tests pass ✅ Stated in PR body; CI green

Dimensions With No Issues

  • API-compat — no issues found. All changes are purely additive (new BASH_HIGH entries, new DESTRUCTIVE_PATTERNS entries, new BUILTIN_WRITE_DENYLIST entries, new builtinBashSensitiveRoots() entries, extended regex). No exported type signatures changed. No breaking changes.
  • Perf-observability — no issues found. 7 new entries in a small linear-scan array, 2 new compiled regex tests per bash call, 5 new path entries — all negligible additions to existing O(n) scans. No hot-path regressions.

What Was Not Checked

  • Citations verified inline against branch HEAD 9054ff9502f256c3ab9e4a0e2bfa7c7be820c649.
  • H1 (launchctl kickstart) shadow-verified: manager.ts:96 confirms kickstart is the restart path; risk-classifier.ts:95-104 confirms kickstart is absent.
  • H2 (enable-linger false-positive) shadow-verified: live regex test at reviewed ref confirms both the BLOCK pattern and the BASH_HIGH substring match systemctl enable-linger root.
  • Stated intent: PR #1311 title+body — spec-compliance assessed.
  • Not checked: runtime test execution — static review only. CI shows all checks passing.
  • Not checked: whether afk service install correctly bypasses all four layers at runtime (confirmed architecturally: it uses Node execFileSync, not the bash tool).
  • Not checked: Windows platform behavior (no Windows-specific changes in this PR).

@griffinwork40 griffinwork40 left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Automated review (scheduled sweep), generated by the /review tool — a maintainer will follow up.


Prior Automated Round — Finding Dispositions

1 prior automated review (on commit 9054ff9502, 2026-08-27). Current HEAD is the same commit — no new code since the prior round. All findings are re-confirmed at the same ref.

Prior finding Disposition
H1 (high): launchctl kickstart absent from BASH_HIGH and safe-destruct regex — service activation bypass STILL-PRESENT — no change at 9054ff9502; kickstart remains absent from both BASH_HIGH and the launchctl-service-register alternation.
H2 (high): systemctl enable-linger false-positives on both safe-destruct BLOCK pattern and BASH_HIGH substring match STILL-PRESENT — no change; \b terminal word boundary still fires between enable and - in enable-linger; 'systemctl enable'.includes() still matches systemctl enable-linger.
M1 (medium): .config/systemd path mismatch — write-denylist blocks full tree; bash check-2 only blocks user/ subdir STILL-PRESENT — advisory, non-blocking.
M2 (medium): 0 tests for the 7 new BASH_HIGH entries STILL-PRESENT — advisory, non-blocking.
M3 (medium): 0 tests for the 2 new BLOCK patterns STILL-PRESENT — advisory, non-blocking.
M4 (medium): launchctl enable (persistence without load) not blocked STILL-PRESENT — advisory, non-blocking.
L1 (low): PR title says "registration" but also blocks activation commands STILL-PRESENT — cosmetic, non-blocking.
L2 (low): Regression guard list in write-denylist.test.ts doesn't enumerate new entries STILL-PRESENT — non-blocking.

Decision

⚠️ DO NOT MERGE — 2 high blocking

Severity arithmetic: 2 high blocking (security: launchctl kickstart bypass, H1; correctness: systemctl enable-linger false-positive across two layers, H2); 4 medium advisory (non-blocking); 2 low advisory (non-blocking).

Per sweep policy: only critical and high findings block. The 4 medium and 2 low are non-blocking follow-ups.


Review Summary

PR: fix(safety): block direct launchctl/systemctl service registration
Reviewed ref: 9054ff9502f256c3ab9e4a0e2bfa7c7be820c649 (unchanged since prior round)
Regime: light (hotfix, ~50 lines added, 4 files)
CI: Lint & Build ✅, Test (ubuntu-latest, macos-latest) ✅, all checks passing

No new commits since the prior automated round. The two high-severity findings from that round remain unfixed. The architecture is sound and the stated mechanism is correctly implemented for the verbs it covers — but the two gaps (missing kickstart verb, enable-linger false-positive) remain open.


🔴 Blocking Findings (high) — carried forward, still present

H1 · high · blocking:true · security · src/agent/risk-classifier.ts + src/agent/safe-destruct-patterns.ts · ref:9054ff9502

launchctl kickstart absent from BASH_HIGH and safe-destruct regex — service activation bypass.

launchctl kickstart is the direct command to immediately start a registered-but-not-yet-running service. It is absent from both BASH_HIGH and the launchctl-service-register safe-destruct regex alternation. An agent told "use afk service install" might discover and use launchctl kickstart directly without triggering any guard.

Suggestion: Add 'launchctl kickstart' to BASH_HIGH and kickstart to the regex alternation: /\blaunchctl\s+(load|bootstrap|submit|start|kickstart)\b/i.

H2 · high · blocking:true · security/correctness · src/agent/safe-destruct-patterns.ts + src/agent/risk-classifier.ts · ref:9054ff9502

systemctl enable-linger false-positives across TWO independent security layers.

Layer 1 (safe-destruct): The regex /\bsystemctl\s+(enable|start|daemon-reload)\b/i false-positives on systemctl enable-linger because \b fires between enable and -.

Layer 2 (risk-classifier): The BASH_HIGH substring 'systemctl enable' matches via .includes(): 'systemctl enable-linger'.includes('systemctl enable') → true.

The correct command is loginctl enable-linger. A user who runs systemctl enable-linger hits a double-layer false block.

Suggestion (safe-destruct): Replace terminal \b with (?:\s|$):

re: /\bsystemctl\s+(enable|start|daemon-reload)(?:\s|$)/i,

Suggestion (risk-classifier): Append trailing space: 'systemctl enable '.


🟡 Advisory Findings (non-blocking — follow-up issues recommended)

  • M1 (medium): .config/systemd path mismatch between write-denylist (full tree) and bash-restriction-hook check-2 (user/ subdir only).
  • M2 (medium): 0 tests for the 7 new BASH_HIGH entries in risk-classifier.test.ts.
  • M3 (medium): 0 tests for the 2 new BLOCK patterns in safe-destruct-detect.test.ts.
  • M4 (medium): launchctl enable (persistence-without-load) not blocked.
  • L1 (low): PR title scope mismatch (cosmetic).
  • L2 (low): Regression guard list doesn't enumerate new write-denylist entries.

What Was Not Checked

  • Citations verified inline against branch HEAD 9054ff9502f256c3ab9e4a0e2bfa7c7be820c649.
  • Stated intent: PR #1311 title+body — spec-compliance assessed.
  • Not checked: runtime test execution — static review only. CI shows all checks passing.
  • Not checked: whether afk service install correctly bypasses all four layers at runtime.

…able-linger false-positive, systemctl flags, path alignment, tests
Adopt the same flag-prefix pattern used by git-reset-hard and
git-clean-force — (?:(?:-{2}[\w-]+(?:=\S+)?|-[A-Za-z]…)\s+)* — so
that systemctl --type=service enable and similar --key=val forms
are caught by the safe-destruct BLOCK regex.

Adds a test case for the --type=service form.
@griffinwork40

Copy link
Copy Markdown
Owner Author

Finding 3 re-evaluated: Library\/Application Support\b on main already lacks a left anchor. The new entries follow the same established pattern. This is a pre-existing style, not a regression introduced by this PR. Finding 3 downgraded to nit - the new entries are consistent with the existing convention; any anchoring improvement would be a separate cleanup.

Now Wave 2 - Synthesis:


PR #1311 Review — fix(safety): block direct launchctl/systemctl service registration

Ref: 77314aa322a0a658874bee8c7e997c27814758d2
Target: issue-1279-block-direct-service-cmdsmain
Change type: hotfix · 6 files · +87/-1
Stated intent: Closes #1279 - blocks direct launchctl/systemctl service registration commands across all four safety guard layers (risk classifier, safe-destruct patterns, write denylist, bash sensitive roots) so agents must use afk service install via /service-setup.


Findings

1. medium · blocking:true · high · security/test-coverage · safe-destruct-detect.test.ts:101-107 · ref:77314aa3 · diff-context

Safe-destruct Layer 2 tests cover only 2 of 6 launchctl verbs; the incident-vector launchctl load is untested.

The test table adds bootstrap and kickstart but omits load, start, submit, and enable for the launchctl-service-register pattern. The regex does cover all six via its alternation group, but zero tests exercise the three most common real-world invocations (load, start, submit). launchctl load is specifically cited in the PR body as the incident vector.

// Only these two are tested:
['launchctl bootstrap gui/501 /path/to/com.example.plist', 'launchctl-service-register'],
['launchctl kickstart -k gui/501/com.example.service', 'launchctl-service-register'],
// Missing: load, start, submit, enable

Suggestion: Add ['launchctl load ~/Library/LaunchAgents/com.example.plist', 'launchctl-service-register'], ['launchctl start com.example.service', 'launchctl-service-register'], and ['launchctl submit -l com.example.job -- /usr/bin/example', 'launchctl-service-register'] to the BLOCK test table. · waived: the regex alternation provably covers all verbs and Layer 1 (BASH_HIGH) independently catches all six; two untested verbs is a coverage gap, not a behavioral one.

2. medium · blocking:true · high · security/test-coverage · write-denylist.test.ts · ref:77314aa3 · file-state

Zero tests for any of the five new BUILTIN_WRITE_DENYLIST entries (LaunchAgents, LaunchDaemons, systemd).

Five paths were added to the write denylist (~/Library/LaunchAgents, ~/Library/LaunchDaemons, /Library/LaunchAgents, /Library/LaunchDaemons, ~/.config/systemd). None has a test in write-denylist.test.ts verifying that assertNotDenylisted throws for writes targeting those paths. A regression in path resolution or list construction would silently reopen these paths.

Suggestion: Add parameterized assertNotDenylisted tests for each new entry, mirroring the existing credential-path test shape.

3. nit · blocking:false · medium · security · bash-restriction-hook.ts:132 · ref:77314aa3 · file-state

New Library/LaunchAgents and Library/LaunchDaemons fragments in SENSITIVE_PATH_SIGNAL lack left-boundary anchoring — but this matches the pre-existing convention (Library/Application Support on main is anchored the same way).

Consistent with the existing pattern. A left / anchor would reduce false positives but that is a pre-existing style, not a regression. No action needed for this PR.

4. low · blocking:false · high · correctness · risk-classifier.ts:101 · ref:77314aa3 · file-state

'launchctl start' (no trailing space) in BASH_HIGH is inconsistent with 'launchctl enable ' (trailing space), making it a broader substring match.

classifyBash uses cmd.includes(p). 'launchctl start' matches any command containing that substring (e.g., a hypothetical launchctl start-something). The trailing space on 'launchctl enable ' was deliberately added to prevent enable-linger false positives (tested in safe-destruct-detect.test.ts). 'launchctl start' lacks this protection. The safe-side error is acceptable since Layer 2 (regex with word boundary) handles precise matching, but the inconsistency is worth noting.

Suggestion: Add trailing space: 'launchctl start ' for consistency with 'launchctl enable ' and 'systemctl start '.

5. low · blocking:false · high · spec-compliance · risk-classifier.test.ts · ref:77314aa3 · file-state

enable-linger false-positive test exists at Layer 2 (safe-destruct-detect.test.ts:113-116) but not at Layer 1 (risk-classifier.test.ts), despite the trailing-space fix being applied at both layers.

The PR adds a negative test proving systemctl enable-linger does not trip the safe-destruct regex. The risk-classifier's 'launchctl enable ' / 'systemctl enable ' entries use the same trailing-space trick but have no corresponding negative test.

Suggestion: Add it('launchctl enable-linger → not high') and it('systemctl enable-linger → not high') to risk-classifier.test.ts.


Spec-compliance assessment

Stated intent present (PR title + body) — spec-compliance assessed.

All four guard layers named in the intent are implemented:

  • ✓ Layer 1 (BASH_HIGH): 9 entries in risk-classifier.ts
  • ✓ Layer 2 (safe-destruct-patterns): 2 BLOCK-tier patterns
  • ✓ Layer 3 (write-denylist): 5 entries in BUILTIN_WRITE_DENYLIST
  • ✓ Layer 4 (bash-restriction-hook): entries in builtinBashSensitiveRoots() + SENSITIVE_PATH_SIGNAL

Unmet intent: none - all four layers are implemented as described.
Scope creep: none - every change directly implements the stated guard layers.


What was not checked

  • Citations verified inline against branch HEAD 77314aa3.
  • Stated intent: PR fix(safety): block direct launchctl/systemctl service registration #1311 title+body - spec-compliance assessed.
  • Did not run tests (pnpm test) - the PR body states all 253 tests pass.
  • Did not verify the SENSITIVE_PATH_SIGNAL sync test referenced in the PR body.
  • Did not audit the launchctl/systemctl subcommand surface beyond what the PR covers (e.g., launchctl unload is not blocked, which is correct since unloading is the inverse/remediation action).

Decision: DO NOT MERGE — 2 medium blocking (2 test-coverage/security); 1 nit, 2 low.

Both blocking findings are test-coverage gaps for security-sensitive guard layers. The implementation itself is correct and complete; the gaps are in regression protection. Inexpensive to close: add ~8 test cases across two files.


🤖 Posted by agent-afk /review --post github

…consistency

- Add 4 missing launchctl verb tests (load, start, submit, enable) to
  safe-destruct-detect BLOCK table; incident-vector `launchctl load`
  was untested at Layer 2
- Add trailing space to `'launchctl start '` in BASH_HIGH for
  consistency with `'launchctl enable '` and `'systemctl start '`
- Add false-positive negative tests for `enable-linger` at Layer 1
  (risk-classifier) matching the existing Layer 2 coverage
@griffinwork40
griffinwork40 merged commit 09de4da into main Aug 28, 2026
10 checks passed
@griffinwork40
griffinwork40 deleted the issue-1279-block-direct-service-cmds branch August 28, 2026 01:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working priority: high P1 — high user-facing impact, address soon

Projects

None yet

Development

Successfully merging this pull request may close these issues.

safe-destruct: block launchctl/systemctl service registration commands

1 participant