v0.35.3: claude runner.start prompt leak (#478) + help-centre FAQ (#477) + local-context protection#479
Merged
Nathan Schram (nathanschram) merged 3 commits intoMay 5, 2026
Conversation
The Claude runner's run_impl override at src/untether/runners/claude.py had its own duplicate runner.start log call that was missed when the base runner was fixed for #205. Every Claude session emitted `prompt=prompt[:100] + "…"` at INFO level — leaking the first ~100 chars of the Untether preamble (boilerplate, but spec-violating). Discovered during the v0.35.3 follow-up E2E pass. Fix mirrors the base runner impl: - INFO `runner.start`: only `engine`, `resume`, `prompt_len`, `args` - DEBUG `runner.start_prompt`: preview of first 100 chars (opt-in) Argv redaction also tightened: - env -i KEY=VAL pairs redacted via redact_env_i_args (was already applied at subprocess.spawn but not at runner.start, so e.g. BWS_ACCESS_TOKEN, GEMINI_API_KEY values would land in INFO logs) - Legacy-mode (no permission_mode) `-- <prompt>` tail collapsed to `-- <prompt redacted>` so prompt content never reaches INFO under any code path 2 new regression tests cover both control-channel and legacy modes: - test_runner_start_does_not_log_prompt_at_info - test_runner_start_redacts_legacy_mode_prompt_in_args Closes #478. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Marketing-site infra (FAQPage extractor on `feature/help-seo-geo-items-1-4` in littlebearapps/littlebearapps.com) already extracts question-shaped H2s and emits Schema.org FAQPage JSON-LD on any help article with `category: faq` frontmatter or ≥3 question-shaped H2s. No tool currently has a dedicated FAQ scaffold; this commit closes the loop for Untether. The new file lives at docs/faq/index.md (Diátaxis-aligned scaffold — plain title + description frontmatter, marketing-site sync injects category/tool/dates). 12 question-shaped H2s exceed the 7-minimum acceptance criterion: 1. What is Untether? 2. How do I install Untether? 3. Which AI coding agents does Untether support? 4. Do I need an API key to use Untether? 5. Where does my code and data go? 6. How do I approve tool calls from my phone? 7. What happens if my agent crashes or my phone loses signal mid-run? 8. How do I keep agents from spending too much money? 9. Can I send voice notes instead of typing? 10. How do I update Untether? 11. How do I uninstall Untether? 12. Where can I get help or report a bug? Each answer is a complete paragraph (no TODO / placeholder), sourced from README + real common-channel topics. Cross-links to existing help-guide URLs preserve nav chains. Coordinated mapping in `littlebearapps/littlebearapps.com` (`scripts/docs-sync.config.ts` → add `untether` → `docs/faq` → `category: faq`) is a separate one-line PR per the issue's "Coordinated mapping" section. Once both land, the next nightly sync surfaces the FAQ at <https://untether.littlebearapps.com/help/untether/faq/> with a visible `<script type="application/ld+json">` FAQPage block, unlocking AI-citation surface (ChatGPT, Perplexity, Google AI Overviews) and SERP rich-snippet eligibility. Closes #477. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 5, 2026
…#477) The FAQ doc is part of the marketing-site FAQPage Schema.org pipeline (littlebearapps/littlebearapps.com:scripts/docs-sync.config.ts → untether → category: faq). Removing it silently breaks the docs-sync mapping and regresses AI-citation surface. This commit hardens local Claude Code context so the file: - cannot be silently deleted, moved, or truncated by accident - has explicit guidance on when/how to update it during releases - is registered in CLAUDE.md so future contributors know it exists Changes: * `.claude/hooks/help-faq-protect.sh` (new) — PreToolUse Bash hook blocking `rm`, `git rm`, `mv`-away, and shell `>` truncation targeting `docs/faq/index.md`. Edits via Edit/Write/append `>>` are intentionally allowed — the FAQ is meant to evolve. Smoke-tested with 7 synthetic inputs covering both deny and allow paths. * `.claude/hooks/release-guard-protect.sh` (updated) — also protects `help-faq-protect.sh` from being weakened or removed via Edit/Write. * `.claude/hooks.json` (updated) — - registers help-faq-protect.sh under PreToolUse Bash - extends the existing Edit/Write context-prompt with a docs/faq/* branch (HELP-FAQ CONTEXT) reminding contributors of question-shape rules and the maintain-as-features-land cadence - extends the version-bump-checklist (PostToolUse) with an FAQ touch-up step * `.claude/rules/help-faq.md` (new) — auto-loads when editing `docs/faq/**`. Documents the hard rules (NEVER delete; MUST update with feature changes), soft conventions (question-shaped H2, ≥7 Q/A, real behaviour not aspirational), and the release-cadence workflow. * `.claude/rules/release-discipline.md` (updated) — adds an FAQ touch-up step to the version-bump checklist. * `CLAUDE.md` (updated) — - new "Help-centre FAQ" section after "Documentation screenshots" explaining the file's role and the no-deletion rule - Hooks table registers `help-faq-protect` - Rules table registers `help-faq.md` Refs #477. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3 tasks
Nathan Schram (nathanschram)
added a commit
that referenced
this pull request
May 5, 2026
Bumps pre-release version so TestPyPI can publish a fresh wheel that includes the v0.35.3 follow-up bundle merged via PR #479: - fix(security): claude runner.start no longer leaks prompt at INFO (#478) - docs(faq): add docs/faq/index.md for help-centre FAQPage schema (#477) - ctx: protect docs/faq/index.md from deletion + register in local docs (#477) The rc6 wheel on TestPyPI predates this work — without the bump the publish step skips ("File already exists") and the staging upgrade path keeps installing the older wheel. Per release-discipline.md, pre-release versions don't require a CHANGELOG entry (validate_release.py skips them) and aren't tagged (auto-tag-on-master.yml skips pre-releases). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Combined v0.35.3 follow-up bundle (3 commits on the same fix branch):
Commits
1.
fix(security): claude runner.start no longer leaks prompt at INFO ([#478](https://github.com/littlebearapps/untether/issues/478))The Claude runner override at
runners/claude.py:run_implhad its own duplicaterunner.startlog call that was missed when the base runner was fixed for #205. Practical leak was Untether-preamble boilerplate; spec violation either way. Fix mirrors the base runner impl plus adds two redaction layers (redact_env_i_args+ legacy-mode--boundary).2.
docs(faq): add docs/faq/index.md for help-centre FAQPage schema ([#477](https://github.com/littlebearapps/untether/issues/477))12 question-shaped H2 FAQs covering install, supported engines, API keys, data flow, interactive approvals, crash recovery, cost budgets, voice notes, update, uninstall, and support channels. Sourced from README + real common-channel topics. Coordinated one-line mapping PR in
littlebearapps/littlebearapps.com(scripts/docs-sync.config.ts→untether→docs/faq→category: faq) is a separate follow-up.3.
ctx: protect docs/faq/index.md from deletion + register in local docs (#477)Hardens the local Claude Code context so the FAQ file:
help-faq-protect.shBash hook blocksrm,git rm,mv-away,>truncation).claude/rules/help-faq.md)CLAUDE.md(new "Help-centre FAQ" section + Hooks/Rules table entries)docs/faq/*(extended PreToolUse Edit/Write prompt)The hook is self-protected via
release-guard-protect.shso Claude Code can't trivially weaken it. Smoke-tested with 7 synthetic inputs covering both deny and allow paths.What changed
src/untether/runners/claude.pyprompt=...from INFOrunner.start; add DEBUGrunner.start_prompt; redact env -i pairs and legacy-mode-- <prompt>tail before loggingtests/test_claude_runner.pytest_runner_start_does_not_log_prompt_at_info,test_runner_start_redacts_legacy_mode_prompt_in_args)docs/faq/index.md.claude/hooks/help-faq-protect.sh.claude/hooks/release-guard-protect.shhelp-faq-protect.sh.claude/hooks.json.claude/rules/help-faq.md.claude/rules/release-discipline.mdCLAUDE.mdCHANGELOG.md### fixes(#478) and new### docssubsection (#477)Test plan
uv run pytest tests/test_claude_runner.py tests/test_claude_control.py tests/test_runner_utils.py tests/test_exec_runner.py tests/test_exec_bridge.py— 426 passed, 1 skippeduv run ruff format --check src/ tests/— cleanuv run ruff check src/untether/runners/claude.py tests/test_claude_runner.py— cleanjq . .claude/hooks.json— valid JSONrunner.start [untether.runners.claude]only carriesengine,resume,prompt_len,args(env redacted), noprompt='...'field?Closes #478, closes #477.
🤖 Generated with Claude Code