Skip to content

feat(e13-s4/s5): add --from-text (local) and --ai (opt-in, secret-free, interactive) parse - #62

Merged
imneov merged 4 commits into
mainfrom
feat/e13-ai
Jul 8, 2026
Merged

feat(e13-s4/s5): add --from-text (local) and --ai (opt-in, secret-free, interactive) parse#62
imneov merged 4 commits into
mainfrom
feat/e13-ai

Conversation

@imneov

@imneov imneov commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

What

Completes E13 — Smart add Onboarding with the paste-text paths.

  • E13-S4 add --from-text <text> / --from-text - — local heuristic parse via internal/blobparse. Zero network. The default paste path.
  • E13-S5 add --from-text ... --ai — opt-in escalation (internal/aiparse). Strips secrets locally, sends only the desensitized text to an Anthropic-compatible Messages endpoint using the active profile's credentials (--ai-profile <name> to override), receives strict core JSON, re-injects the secret locally. Off by default; explicit per run.

Security model (load-bearing — this is the only networked path in claudecm)

Defense in depth, because reliably redacting arbitrary prose is not a solved problem:

  1. Local redactionblobparse captures secrets by shape (sk-, JWT, gh*/xox*/pat/token, Google AIza, 40-hex, high-entropy ≥32) and by field name (secret/password/token/api_key/auth/authorization/credential/private_key/access_key/client_secret), taking the whole value to end-of-line.
  2. Outbound guardaiparse.EnsureSecretFree re-scans the request body (shape + name) and refuses to send if any secret-shaped/secret-named token survives.
  3. Mandatory human review--ai requires an interactive TTY; it prints the credential-lending profile and the exact desensitized payload and asks for confirmation before sending. Non-interactive / piped --ai refuses before any network call.
  4. Credential hygiene — borrowed api_key only ever goes in the x-api-key header; base_url userinfo is stripped from the endpoint; no credential or response body is echoed in errors/logs/dry-run.
  5. No guessing — LLM output must be strict core JSON (DisallowUnknownFields); a returned api_key must match a locally captured placeholder or it is rejected. Non-anthropic lender / missing creds / malformed response all refuse.

Review trail (two adversarial passes, both found real leaks)

  • Pass 1 → 2 fixes (6c74b86): non-shape secret-named fields (CLIENT_SECRET=…) were reaching the LLM; borrowed base_url userinfo leaked into the request URL.
  • Pass 2 → 3 fixes (bfdd9aa): secret-named unquoted values with spaces (AUTH=Bearer <token>) only redacted the first token; Authorization field name wasn't recognized; added the mandatory-interactive gate so the heuristic tail is backstopped by human review.

Independently verified (oracle not derived from production regexes; real binary): CLIENT_SECRET/PASSWORD/DATABASE_TOKEN/private_key/Bearer/Authorization values all redact to placeholders; endpoint strips userinfo; non-interactive --ai refuses (exit 1, no network); --from-text local path is zero-network. go test ./... + go vet ./... clean.

⚠️ For LF

ADR-0003 narrowly amends ADR-0001 Decision 8's no-network stance only for this opt-in, interactive-only, secret-free --ai parse. Status marked Accepted (CEO-delegated); the network direction is flagged for your ratification. Every other E13 path is zero-network.

🤖 Generated with Claude Code

@imneov
imneov merged commit 692bed7 into main Jul 8, 2026
8 of 9 checks passed
@imneov
imneov deleted the feat/e13-ai branch July 8, 2026 03:54
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