Skip to content

Close cdidx shell-path loopholes and ship a non-mutating cloud bootstrap - #104

Merged
Widthdom merged 8 commits into
mainfrom
temp
Apr 13, 2026
Merged

Close cdidx shell-path loopholes and ship a non-mutating cloud bootstrap#104
Widthdom merged 8 commits into
mainfrom
temp

Conversation

@Widthdom

Copy link
Copy Markdown
Owner

Summary

  • Extend the repo-tracked .claude/settings.json tripwire with Bash(~/.local/bin/cdidx:*) and Bash($HOME/.local/bin/cdidx:*) so the common absolute-path spellings used by install.sh no longer bypass the bare Bash(cdidx:*) deny
  • Reframe the deny list as a best-effort tripwire (not a sandbox) in CLAUDE.md / README / CHANGELOG / CLOUD_BOOTSTRAP; fully expanded paths, command cdidx, env cdidx, etc. remain uncovered, and the authoritative rule lives in the prose (use Grep/Glob built-ins + the locally built dotnet ./src/CodeIndex/bin/Debug/net8.0/cdidx.dll)
  • Add a non-mutating Cloud Bootstrap Step 1.5 that resolves $HOME/.local/bin/cdidx to its fully expanded absolute path via readlink -f and invokes every smoke-test / reindex step as "$CDIDX" ... — editing the tracked .claude/settings.json is explicitly rejected because it would dirty the worktree and could leak a weakened deny list into a commit
  • Split the Cloud Bootstrap Step 3 reindex examples: --files / full "$CDIDX" . is the default after local edits; --commits HEAD is explicitly scoped to after a commit (it does not pick up uncommitted work-tree edits)
  • Resolve the .claude/settings.local.json override contradiction: observed behavior is that tracked deny is not overridden by local allow, so CLAUDE.md drops the prior "edit the workspace settings.json as an escape hatch" guidance and tells contributors to stop and ask the user when no non-mutating path exists
  • Update the README # Code Search Rules template: remove the rg/grep/find/cat fallback for Claude Code sessions, point the last-resort path at the built-in Grep / Glob tools, and add a signpost above both the English and Japanese templates so cdidx-repo contributors know to follow this repo's own CLAUDE.md instead of running bare cdidx from the downstream template

Test plan

  • Fresh clone + git switch temp → open in Claude Code locally and confirm Bash(cdidx:*), Bash(~/.local/bin/cdidx:*), and Bash($HOME/.local/bin/cdidx:*) are all denied
  • Walk a dry-run Cloud Bootstrap: resolve $CDIDX via readlink -f "$HOME/.local/bin/cdidx", then run every Step 2 smoke command as "$CDIDX" ... and confirm none of them hit the deny list
  • Edit a tracked file, run "$CDIDX" . --files <file>, and confirm the index reflects the uncommitted edit; confirm "$CDIDX" . --commits HEAD does not pick it up until the edit is committed
  • Confirm no remaining "edit the tracked settings.json as a session-local escape" language survives in README / CLAUDE.md / CLOUD_BOOTSTRAP_PROMPT.md / CHANGELOG (English and Japanese)
  • Render README on GitHub and confirm the new downstream-template signpost above # Code Search Rules reads correctly in both languages

🤖 Generated with Claude Code

Widthdom and others added 8 commits April 13, 2026 21:34
Add Bash(~/.local/bin/cdidx:*) to the tracked deny list so agents
cannot bypass the cdidx:* deny via the install.sh absolute path on
Linux / macOS. Because Claude Code treats deny as absolute, add a
Step 1.5 to CLOUD_BOOTSTRAP_PROMPT.md telling SDK-less cloud sessions
to strip the two cdidx deny entries from the workspace copy of
.claude/settings.json (session-local, uncommitted) so the installed
binary remains usable. Sync CLAUDE.md and the existing Unreleased
CHANGELOG entry in both English and Japanese.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CLAUDE.md and the CHANGELOG entry previously said contributors could
allow-list a denied command in .claude/settings.local.json, but the
Cloud Bootstrap workaround added in the prior commit states that
Claude Code treats deny as absolute and settings.local.json cannot
override it. Align all three docs on the single truthful recovery
path: edit the workspace copy of .claude/settings.json for the
session only and do not commit the change.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Address Codex review #2: the prior wording treated "tracked deny
cannot be overridden by settings.local.json allows" as a guaranteed
rule, but this was written from observation without a public spec
citation. Rewrite the claim across CLAUDE.md, CLOUD_BOOTSTRAP_PROMPT.md,
and the Unreleased CHANGELOG entry (English + Japanese) as observed
behavior with a verify-in-your-own-environment caveat, and offer
the settings.local.json allow path as an equivalent alternative for
builds where it does override deny.

Also broaden install-path coverage: add Bash($HOME/.local/bin/cdidx:*)
alongside the existing Bash(~/.local/bin/cdidx:*) to close both the
tilde- and $HOME-spelled forms used by install.sh, and state
plainly that Claude Code permission matching is textual — fully
expanded absolute paths (/Users/alice/.local/bin/cdidx) are not
covered and rely on contributor discipline.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Address Codex review #3.

#1 (absolute-path bypass, high): wildcard absolute-path denies
(/Users/*/.local/bin/cdidx:*) are not confirmed to be supported by
Claude Code permission matching, so instead of claiming to close
the loophole this commit reframes the deny list as a best-effort
tripwire. CLAUDE.md, CHANGELOG, and the Cloud Bootstrap wording
now state plainly that textual matching means fully expanded
absolute paths, `command cdidx`, `env cdidx`, and similar alternate
spellings remain uncovered, and that the authoritative rule is the
written guidance (use Grep/Glob built-ins and the locally built
dotnet ./src/CodeIndex/bin/Debug/net8.0/cdidx.dll) rather than the
settings file itself.

#2 (README template contradiction, medium): the # Code Search Rules
template in both English and Japanese previously told agents to fall
back to `rg`, `grep`, `find`, `cat` when neither cdidx nor sqlite3
was available, which directly contradicts the new policy in Claude
Code sessions. The last-resort fallback now points at the Claude
Code built-in Grep / Glob tools (or the host harness's equivalent)
and explicitly says not to reach for shell rg/grep/find or a global
cdidx in a Claude Code session.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Address Codex review #4: the previous Step 1.5 told cloud sessions
to remove cdidx deny entries from the tracked .claude/settings.json
for the duration of the session. That dirtied the worktree from the
first step — breaking git_is_dirty as a trust signal that this
repo's own README / MCP responses promote for deciding whether
indexed results can be trusted — and created an obvious accidental
commit path that would weaken the tripwire for every other
contributor.

Replace Step 1.5 with a non-mutating unblock that matches the
best-effort tripwire framing: resolve the installed binary to its
fully expanded absolute path once via `readlink -f "$HOME/.local/bin/cdidx"`
(falling back to `realpath`), stash it in $CDIDX, and invoke every
smoke-test command as "$CDIDX" .... Claude Code permission matching
is textual, so the three denied spellings (cdidx, ~/.local/bin/cdidx,
$HOME/.local/bin/cdidx) do not match the expanded form. Explicitly
flag editing the tracked settings file as a non-recommended path
and spell out the consequences. Mirror the same guidance in
CLAUDE.md and the Unreleased CHANGELOG entry in both English and
Japanese.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The previous commit routed Step 2 through the fully expanded absolute
path via \$CDIDX but left the Step 3 incremental-reindex examples as
raw 'cdidx . --files ...' / 'cdidx . --commits HEAD' in both the
English and Japanese sections. Those raw spellings are matched by the
tracked Bash(cdidx:*) deny, so a cloud session following the bootstrap
would pass Step 2 and then dead-end on the first reindex. Route the
Step 3 examples through \$CDIDX so the non-mutating unblock is
end-to-end usable.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tings escape hatch

Address Codex review #6.

#1 (stale reindex, high): Step 3 presented 'cdidx . --files' and
'cdidx . --commits HEAD' as interchangeable 'after edits' options,
but --commits HEAD only refreshes paths in the last committed diff
and silently skips uncommitted work-tree edits. For a cloud session
that just edited a file, following the previous instructions would
leave the index stale and re-introduce the exact false-negative
failure mode this prompt is supposed to prevent. Split the reindex
block in both English and Japanese sections: --files (or a full
'"$CDIDX" .') is the default after local edits; --commits HEAD is
explicitly scoped to AFTER a commit. Add a short note explaining
the distinction so the trap is visible on review.

#2 (settings.json policy contradiction, medium): the Code search
tools paragraph in CLAUDE.md said editing tracked .claude/settings.json
was 'explicitly not the recommended path' in one sentence and then
told contributors to do exactly that as a session-local escape
hatch in the next sentence, citing Cloud Bootstrap Step 1.5 — which
itself now forbids that edit. Resolve the contradiction in both
languages: remove the edit-settings escape hatch, keep the
non-mutating rule (absolute path / built-in Grep-Glob / locally
built binary), and require stopping to ask the user when no
non-mutating path exists.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Address Codex review #7 with the minimal fix discussed: leave the
# Code Search Rules template as-is (it is a template for downstream
projects that adopt cdidx, and downstream projects do not inherit
this repo's deny list), but add a one-line note above the template
in both the English and Japanese sections pointing cdidx-repo
contributors to CLAUDE.md, which routes execution through the
locally built dotnet .../cdidx.dll instead of a bare cdidx command.
This closes the only real papercut (a new contributor copying the
template verbatim in a Claude Code session on this repo) without
forcing a \$CDIDX_CMD indirection on every downstream consumer.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Widthdom
Widthdom merged commit a22d9c2 into main Apr 13, 2026
6 checks passed
@Widthdom
Widthdom deleted the temp branch April 13, 2026 12:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant