Skip to content

v3.62.1 - Supervisor Injection Reliability and Review-Gate Fixes

Latest

Choose a tag to compare

@edmondscommerce edmondscommerce released this 07 Sep 12:09
· 163 commits to main since this release

Release v3.62.1 - Supervisor Injection Reliability and Upgrade-Path Fixes

Release Date: 2026-09-07
Type: Patch Release

Summary

This release fixes a field-urgent bug where the ccy PTY supervisor's
/compact injection could land unsubmitted in Claude Code's input box, and
closes out the residue of the v3.62.0 upgrade-path work: six field-reported
upgrade defects plus the design-level findings that surfaced while fixing
them, including a config-preservation baseline bug that could silently keep
a changed default from ever reaching a user who had accepted the old one.
It also closes further gaps in guard behaviour introduced in v3.62.0
(curl_pipe_shell's quoted-heredoc exemption, and project_containment,
which is itself new in v3.62.0) found by continued probing of the shipped
code. No new features. No breaking changes — but several commands that were
previously ALLOWED are now DENIED; see Upgrade Instructions.

Highlights

  • Supervisor /compact could sit unsubmitted in the input box (Plan
    00339).
    Measured against a real Claude Code v2.1.263 over a PTY: a
    large injected burst triggers Claude Code's paste detection, and a
    carriage return inside pasted text is read as a literal newline rather
    than a submit. The payload is now framed in explicit bracketed-paste
    markers so the trailing Enter is unambiguously a keypress, plus a second
    stall remedy (an alternating bare Enter) for a line left sitting
    unsubmitted in the input box — the opposite cause to the one [esc]
    already handled, which is a command Claude Code QUEUED behind an
    in-flight turn.
  • Six field-reported v3.61.0 -> v3.62.0 upgrade defects, fixed with a
    failing test written first
    (commits b1217789, a9866261; residue
    tracked in Plan 00336): a config-merge bug that
    silently dropped whole config sections, a crash on any config value
    containing a backslash, a version-parsing bug that skipped breaking-change
    warnings, a stale-code bug in the upgrade orchestrator, two documentation
    overstatements.
  • The config-preservation diff baseline was comparing against the wrong
    side of the version boundary
    on the documented (Layer 1) upgrade path,
    so a changed default could silently never reach a user who had simply
    accepted the previous one — not a one-release accident, an ongoing effect
    on every affected client.
  • Two more gaps closed in guard behaviour introduced in v3.62.0: curl_pipe_shell's
    quoted-heredoc exemption is now an allowlist of safe data-sink receivers
    (closing ssh host <<EOF and the whole word-expansion receiver family),
    and project_containment's relative-destination resolution now agrees
    with its own redirect route.

Changes

See CHANGELOG.md for the full,
categorised list of changes in this release.

Security

  • curl … | python3 was allowed by the priority-10 curl-pipe guard. The
    interpreter list holds bare names and the pattern ended in \b, which
    cannot follow a name ending in a digit — so the spelling real install
    instructions use, and often the only one present on a modern system, went
    straight through. /usr/bin/python3, python3.12, ruby3 and perl5
    likewise. Now matched with an optional numeric version suffix.
  • curl_pipe_shell: the quoted-heredoc exemption enumerated EXECUTING
    receivers, so any receiver nobody had thought to list defaulted to being
    granted the exemption.
    That enumeration had already failed three times;
    probing the shipped handler found a fourth (ssh host <<EOF, which
    executes the body on the remote host). Inverted to an allowlist of
    DATA-SINK receivers instead — an unrecognised receiver now withholds the
    exemption, closing ssh and the entire word-expansion family
    ($SHELL, b$ash, ...) in one change, by construction rather than by
    enumeration.
  • Three database clients had been listed as data sinks, and all three run
    shell commands from stdin
    (sqlite3 .shell, psql \!, mysql
    system), so a heredoc body naming one was blanked before the scan.
    Removed from the allowlist and recorded as executors.
  • A recognised sink whose OUTPUT is piped into an interpreter now withholds
    the exemption too
    (cat <<'X' … X) | bash fed a sink and then executed
    the body anyway.
  • project_containment: relative-destination resolution for curl -o,
    wget -O, mkdir -p, tar -cf and rsync disagreed with the redirect
    route on identical effect
    , and a trailing-slash copy destination
    (cp README.md /tmp/) was not recognised as a destination at all. Both
    now resolve against the same cwd.

See CHANGELOG.md for full detail.

Breaking Changes

None. No handlers, configuration keys, or CLI subcommands were removed or
renamed in this release, and no default behaviour changed in a way that
would fail previously-valid configuration.

Why this is a PATCH despite a list of new denials. This project's semver
table keys on features versus fixes: MINOR is "new handlers/features, config
options", PATCH is "bug fixes, security patches, docs". This bundle adds no
handler, feature or config option — every change is a bug or security fix, and
a security fix necessarily denies something that was previously allowed. The
release's own code review argued the bundle reads as MINOR under general
semver; that dissent was considered and recorded rather than dropped. If you
gate upgrades on the version component, read "Behaviour changes to expect"
below rather than the bump.

Upgrade Instructions

No special migration steps are required, and no configuration change is
needed: nothing was removed or renamed, and no previously-valid config fails.

Behaviour changes to expect. This is a bug-fix release, but several of
those fixes are security fixes, and a security fix necessarily denies
something it used to allow. Three RULES changed. They are stated as rules
because each covers an open-ended set — listing four examples of an unbounded
set is exactly the mistake the headline security fix in this release exists to
correct, so the examples below are illustrations, not the boundary.

1. The quoted-heredoc exemption became an allowlist. Previously any
receiver not on a list of known executors was granted the exemption; now only a
receiver on a list of 27 recognised DATA SINKS is (cat, tee, git, jq,
grep, checksum tools, …). Every other receiver withholds it — including
ssh, sed, awk, crontab, sqlite3, psql, mysql, env cat,
xargs echo, an assignment-prefixed command, and any receiver built by word
expansion ($SHELL).

Withholding does NOT deny the command — it scans the body that would otherwise
have been skipped. The denial only follows if that body also contains a
curl … | bash string.
So an ordinary heredoc through an unlisted receiver is
unaffected; only one carrying the anti-pattern in its body is newly denied.

2. curl/wget piped to a VERSIONED interpreter is now caught.
curl … | python3 was allowed, along with /usr/bin/python3, python3.12,
ruby3 and perl5. Any interpreter name followed by a numeric version now
matches. Separately, a recognised sink whose OUTPUT is piped into an
interpreter — (cat <<'X' … X) | bash — no longer keeps the exemption.

3. Containment now resolves destinations it previously left unresolved.
Two shapes escaped the repository check and no longer do:

  • A relative destination, for any destination-bearing construct
    (curl -o, wget -O, mkdir -p, tar -cf, rsync) — e.g.
    curl … -o ../../tmp/x.sh, mkdir -p ../shared/cache,
    rsync -a src/ ../backup/. A relative path was treated as never-outside,
    while the equivalent shell redirect was already denied. This is the one
    most likely to appear in an ordinary workflow.
  • A directory destination, however spelled — a trailing slash
    (cp README.md /tmp/, mv README.md /tmp/), the -t/--target-directory
    form which has no trailing slash at all (cp -t /tmp/ x,
    install -t /usr/local/bin/ x), and bare /.

Each denies a shape that could execute or exfiltrate, and each is covered by
a test. If one of these was load-bearing in your workflow, the handler's own
guidance names the supported alternative.

ccy supervisor users: restart the ccy session to get the paste framing.
The two halves of the /compact fix ship on different schedules, because the
supervisor is split into a long-lived PTY host and a hot-reloadable --worker
subprocess:

  • The alternating Enter remedy is decided in the worker, so it goes live on
    the next worker reload (~5s after the upgrade refreshes the file) with no
    session restart.
  • The bracketed-paste framing is applied in _perform_injection, which
    runs in the host, and the host never hot-reloads. It takes effect only
    when the ccy session itself is restarted.

Both behaviours are safe on their own, so an un-restarted session is not
broken — it simply keeps the pre-fix injection path plus the new Enter safety
net until you restart it.

Installation

New Installations

mkdir -p .claude
git clone -b v3.62.1 https://github.com/Edmonds-Commerce-Limited/claude-code-hooks-daemon.git .claude/hooks-daemon

bash .claude/hooks-daemon/scripts/install_version.sh "$PWD" "$PWD/.claude/hooks-daemon"

Upgrading Existing Installations

cp .claude/hooks-daemon.yaml .claude/hooks-daemon.yaml.backup
git -C .claude/hooks-daemon fetch --tags

bash .claude/hooks-daemon/scripts/upgrade.sh --project-root "$PWD" "v3.62.1"

.claude/hooks-daemon/bin/hooks-daemon restart

Use upgrade.sh (Layer 1), not upgrade_version.sh (Layer 2). Invoking
Layer 2 directly makes it check itself out half way through its own run, so
every step after that still comes from the release being replaced — a step the
new version ADDED does not run at all. That is the defect fixed in this
release, and it is what the v3.62.0 notes' own upgrade command triggered.

Testing

  • Tests: 18,229 passing, 0 failed, 7 skipped, 95.2% coverage
  • Type Safety: MyPy strict mode compliant
  • Security: Bandit scan clean
  • QA gate: 25/25 checks green

Release Review

This release's blocking code-review gate found six confirmed defects in the
bundle: one regression it introduced (a brace-group documentation heredoc was
newly denied), two ways the curl_pipe_shell exemption could still be granted
to a receiver that executes the body (three shell-escaping database clients on
the sink allowlist, and a sink whose output is piped into an interpreter), two
config-merge silent-loss defects, and one fabricated path in a containment
message. All six were fixed before tagging, with a failing test written first
for each. Fixing them surfaced a seventh, older bypass — curl … | python3
which is also fixed here.

A second review of the release documentation then found this file teaching the
Layer 2 upgrade command that the CHANGELOG entry says every document had been
fixed to stop teaching, plus several count and wording contradictions. Those
are corrected here too.

Contributors

  • joseph
  • Claude Opus 5 (Co-Authored-By across this release's commits)

Full Changelog

Compare: v3.62.0...v3.62.1