Skip to content

codeArbiter 2.6.1: P1 security hardening

Choose a tag to compare

@github-actions github-actions released this 01 Jul 10:12
4d12e87

[2.6.1] — 2026-07-01

A security-hardening release. Five P1 reports (thanks to @tg12) showed the enforcement layer being enforced against a forgeable or lexical surface (a writable activation file, writable gate markers, raw path strings, a literal command string, an env-controlled delete root) rather than the real underlying operation. Each is closed, and the rest of the repo was swept for the same class in every case. Also fixes a task-id parsing gap and finishes two stale AGPLv3 relicense surfaces.

Security

  • The activation switch can no longer be flipped off from inside the repo it governs (#159). Every enforcement hook stays dormant unless .codearbiter/CONTEXT.md frontmatter reads arbiter: enabled, yet nothing protected that file, so writing arbiter: disabled (or corrupting the frontmatter) silenced every gate. The Write/Edit guards (H-18) now block a CONTEXT.md change whose resulting content drops arbiter: enabled or breaks the frontmatter, and the Bash pre-hook blocks the shell flank. A legitimate stage: change that keeps the flag enabled still passes.
  • Gate-pass markers can no longer be forged by a direct write (#160). The .codearbiter/.markers/ tokens that turn a hard BLOCK into an allow were ordinary writable repo files, so a hand-written marker forged a security, migration, or ADR gate pass. The Write/Edit guards (H-19) now block tool writes to any marker, and the Bash pre-hook blocks shell redirects and file verbs naming security-gate-passed or migration-gate-passed. The sanctioned recorder scripts and the /ca:adr authoring marker still work.
  • Symlink aliases no longer bypass the audit-log and ADR guards (#162). The Write and Edit guards classified the raw file_path, so a symlink whose visible path lacked .codearbiter/ slipped a write onto an append-only audit log or an immutable ADR. Every protected-path check now resolves the real path (the approach the Read and post-write hooks already used) and matches both the raw and resolved forms. NotebookEdit, previously wired to no hook, is now covered too.
  • Git mutations are enforced below the command spelling (#161). The Bash pre-hook gates git commit, push, and add by matching the literal command string, so shell indirection such as g=git; c=commit; $g $c built a real commit the hook never saw, with no enforcement beneath it. codeArbiter now installs repo-level pre-commit and pre-push hooks that enforce the protected-branch, force-push, and crypto/secret/migration gates at the git operation itself, where spelling no longer matters. They reuse the same detection primitives as the Bash hook so the two cannot drift, never overwrite a pre-existing hook, and install idempotently at init and on session start.
  • Farm worktree deletes are contained to the repo-scoped farm root (#163). The dispatcher resolved a worktree path from the env-controlled FARM_WORKTREE_ROOT plus a plan-controlled task id and recursively force-deleted it before git validated it, so a broad or misconfigured root plus a plausible id could delete an unrelated directory. The resolved root must now live inside the repository unless FARM_ALLOW_EXTERNAL_WORKTREE_ROOT=1 is set, every worktree path is asserted strictly inside that root before deletion, and the reserved ids . and .. are rejected.

Fixed

  • A mistyped multi-part --id no longer strands a task (#158). /ca:task add --id accepted a value with more than two dot-separated parts and minted an un-targetable four-segment id that start/done could not reach and the board linter could not see. The id is now validated as a single GROUP.TYPE pair and rejected with a clear message otherwise, and the board reader recognizes an over-segmented id so it can be surfaced and repaired.
  • Release-skill changelog heading and license declarations aligned (#151). The /ca:release skill now documents the Keep-a-Changelog ## [X.Y.Z] heading the repo and its release guards use, replacing the last reference to the bare ## vX.Y.Z form. The plugin manifest now declares AGPL-3.0-only (correcting a stale MIT), and the README license notice no longer implies a commercial license is currently on offer, completing the AGPLv3 relicense across those surfaces.