Three CLI/installer findings from the 2026-05-31 audit (no server.mjs):
1. ocp-plugin `cmdRestart` hardcoded uid 501 + the legacy `ai.openclaw.proxy`
label, and fell through to a dangerous `pkill -f 'node.*server.mjs' && cd
~/.openclaw/projects/*/; node server.mjs &` that could kill an unrelated node
process and relaunch an env-less ghost proxy from a glob-ambiguous dir. Now uses
process.getuid() + the live labels (dev.ocp.proxy on macOS, ocp-proxy on Linux
systemd; the OpenClaw gateway label is unchanged) and drops the pkill fallback
entirely (returns a manual `ocp restart` message on failure).
2. ocp-connect wrote the quota key unquoted into rc files and a world-readable
environment.d/ocp.conf. Now single-quotes the value and chmod 600s the rc files
and ocp.conf (matching the existing auth-profiles.json 0o600 convention).
3. setup.mjs interpolated the injected service-unit secrets (CLAUDE_BIN,
OCP_ADMIN_KEY, PROXY_ANONYMOUS_KEY) raw into plist <string> and systemd
Environment= lines. Added xmlEscape() for all plist <string> values and
assertSafeInjectValue() which rejects control characters (\x00-\x1f — newline,
CR, tab) before any unit is written, blocking a newline-injected rogue
Environment= directive. Spaces are intentionally allowed (CLAUDE_BIN paths may
contain them). XML-escaping on write also resolves plist-merge.mjs's [^<]* regex
concern transitively (no raw < reaches it) — comment added, logic unchanged.
ALIGNMENT.md: CLI/installer scripts only, no Anthropic operation forwarded → cli.js
citation N/A. No blacklisted tokens or port literals introduced; alignment.yml passes.
Independent fresh-context reviewer (opus): APPROVE (Iron Rule 10) — verified the
control-char regex byte-exact via od (no space-rejection regression), the pkill
fallback fully removed, restart labels match setup.mjs ground truth, OCP keys
(base64url) cannot break the single-quoting, and the validator runs before any write.
Closes#113.
Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* fix(setup): merge plist/systemd env vars instead of overwriting
setup.mjs previously wrote the launchd plist and the Linux systemd unit
with writeFileSync(path, NEW_TEMPLATE_STRING), which silently dropped any
user-customised env vars (CLAUDE_HEARTBEAT_INTERVAL, CLAUDE_CACHE_TTL,
etc.) on every re-setup or upgrade. This change introduces
scripts/lib/plist-merge.mjs which preserves keys present only in the
existing file and lets the template's known keys win. Linux variant uses
the same logic against Environment=KEY=VALUE lines.
Tests added in test-features.mjs cover preserve / override / first-install
for both formats.
No cli.js citation needed: this is OCP-internal installer behaviour with
no corresponding cli.js operation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(setup): plist-merge code-quality follow-up
Three issues raised by the code-quality reviewer on 0fd1838:
1. mergeSystemdEnv now early-returns when the template has no
Environment= anchor, instead of silently dropping preserved lines
(defensive guard for a future template change).
2. Both parsers (parsePlistEnv, parseSystemdEnv) now Buffer-normalise
their input, removing the TypeError-vs-coerce asymmetry.
3. Two idempotency tests added (calling merge twice on the same
input/output pair must be stable).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>