forked from bchanot/claude
job7 step A: MAGIC_API_KEY by reference, not by value (BDR-026 follow-up)
toggle-external.sh's `claude mcp add magic --env API_KEY="$MAGIC_API_KEY"`
materialized the key as plaintext into ~/.claude.json — a copy outside the
~/.claude/.env canonical, invisible to the repo's gitignore/allowlist reach.
Claude Code supports ${VAR} expansion in mcpServers config (docs confirmed),
so the fix is a reference, not a scrub.
- lib/toggle-external.sh: --env 'API_KEY=${MAGIC_API_KEY}' (single-quoted
literal reference, not bash-expanded) so future `enable magic` runs write
the safe form too.
- README: new "Adding an MCP server that needs a secret" section documenting
the --env pitfall and the wrapper pattern.
Out-of-repo companion changes (not in this commit): ~/.bashrc gained a
scoped claude() wrapper that sources ~/.claude/.env into a subshell before
exec'ing the real binary (verified: the var never reaches the ambient
interactive shell, only claude + children) — chosen over a global export to
keep the secret's surface minimal. ~/.claude.json's mcpServers.magic.env.API_KEY
was rewritten to the same "${MAGIC_API_KEY}" reference via a surgical jq
edit (never read directly, so the value never entered this session's
context). The 2 of 5 rotating ~/.claude/backups/.claude.json.backup.* files
still holding the old plaintext were scrubbed the same way.
Residual: this session predates the bashrc wrapper, so `claude mcp list`
currently warns "Missing environment variables: MAGIC_API_KEY" — expected,
resolves on next terminal + Claude Code restart. MAGIC_API_KEY rotation
still pending (user action, after this commit).
This commit is contained in:
+24
-10
@@ -17,16 +17,30 @@ manipuler une valeur de secret — edits sur les mécanismes seulement.
|
|||||||
Décision utilisateur : wiring `MAGIC_API_KEY` → wrapper `claude()` scopé
|
Décision utilisateur : wiring `MAGIC_API_KEY` → wrapper `claude()` scopé
|
||||||
dans `~/.bashrc` (source `.env` en subshell, jamais exporté globalement)
|
dans `~/.bashrc` (source `.env` en subshell, jamais exporté globalement)
|
||||||
plutôt qu'un export global (surface minimale, cohérent BDR-026).
|
plutôt qu'un export global (surface minimale, cohérent BDR-026).
|
||||||
- [ ] `~/.bashrc` : fonction `claude()` wrapper (subshell source ~/.claude/.env)
|
- [x] `~/.bashrc` : fonction `claude()` wrapper (subshell source ~/.claude/.env,
|
||||||
- [ ] `~/.claude.json` mcpServers.magic.env.API_KEY → `"${MAGIC_API_KEY}"`
|
exec — vérifié : la var n'atteint QUE le subshell/exec, jamais le shell
|
||||||
(diff keys-only montré avant écriture)
|
parent). Hors repo (dotfile perso).
|
||||||
- [ ] `lib/toggle-external.sh:191-192` — `--env API_KEY='${MAGIC_API_KEY}'`
|
- [x] `~/.claude.json` mcpServers.magic.env.API_KEY → `"${MAGIC_API_KEY}"`
|
||||||
(référence littérale, pas expansion bash) pour que les futurs
|
(diff keys-only montré avant écriture ; jq surgical edit, jamais Read
|
||||||
`enable magic` écrivent aussi la forme référence
|
direct — la valeur n'a jamais traversé mon contexte). Backup fait
|
||||||
- [ ] Doc README : procédure "ajouter un MCP avec secret" + piège `--env`
|
pendant l'édition supprimé aussitôt vérifié (aurait été un 6e leak).
|
||||||
- [ ] Vérif manuelle : `claude mcp list` / relancer un MCP magic réel si possible
|
- [x] `lib/toggle-external.sh:191-192` — `--env 'API_KEY=${MAGIC_API_KEY}'`
|
||||||
- [ ] A.3 Scrub one-shot des 5 backups `.claude.json.backup.*` existants
|
(référence littérale, single-quoted). `claude mcp add` direct au flag
|
||||||
(prefix 78af0e36 hors `.env`) — script ou sed ciblé, vérif grep 0 hors .env
|
bloqué par le classifieur auto-mode (self-modification non sollicitée,
|
||||||
|
respecté) — non testé live ; `claude mcp list` confirme la syntaxe
|
||||||
|
est bien reconnue ("Missing environment variables: MAGIC_API_KEY" —
|
||||||
|
attendu, cette session a démarré avant le wrapper bashrc).
|
||||||
|
- [x] Doc README : section "Adding an MCP server that needs a secret" +
|
||||||
|
piège `--env` + pattern wrapper à copier
|
||||||
|
- [x] Vérif manuelle : `claude mcp list` (read-only) — magic reconnaît
|
||||||
|
`${MAGIC_API_KEY}`, encore connecté (session pré-existante) ; nécessite
|
||||||
|
un restart terminal (source ~/.bashrc) + Claude Code pour confirmer
|
||||||
|
end-to-end — **résiduel, à faire par l'utilisateur**
|
||||||
|
- [x] A.3 Scrub backups `.claude.json.backup.*` — les 5 originaux (78af0e36 @
|
||||||
|
job7 triage) déjà auto-rotés (ring-buffer natif) ; des 5 COURANTS, 2
|
||||||
|
encore en clair (créés avant le fix, pendant cette session) → scrubbés
|
||||||
|
jq (mode 600 restauré, changé par erreur via mv). grep 78af0e36 : 0 hors
|
||||||
|
`.env` (backups + .claude.json confirmés propres).
|
||||||
- [ ] A.4 Signaler à l'utilisateur : rotation MAGIC maintenant (après commit A)
|
- [ ] A.4 Signaler à l'utilisateur : rotation MAGIC maintenant (après commit A)
|
||||||
- [x] B. Redaction dumps d'env — `hooks/rtk-rewrite.sh` étendu : pipeline simple
|
- [x] B. Redaction dumps d'env — `hooks/rtk-rewrite.sh` étendu : pipeline simple
|
||||||
(pas de `;`/`&`/`||`) + `printenv`/`env` en tête sans `VAR=... cmd` derrière
|
(pas de `;`/`&`/`||`) + `printenv`/`env` en tête sans `VAR=... cmd` derrière
|
||||||
|
|||||||
@@ -195,6 +195,42 @@ See [`templates/settings/SETTINGS.md`](templates/settings/SETTINGS.md) for the f
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Adding an MCP server that needs a secret
|
||||||
|
|
||||||
|
`claude mcp add <name> --env KEY=VALUE ...` writes `VALUE` **literally** into
|
||||||
|
`~/.claude.json` (or the project's `.mcp.json`) — if you pass the real secret
|
||||||
|
on that command line, it materializes as a second plaintext copy outside
|
||||||
|
`~/.claude/.env`, invisible to the repo's `.gitignore`/allowlist reach (this
|
||||||
|
bit us once: job7/BDR-026).
|
||||||
|
|
||||||
|
Claude Code expands `${VAR}` and `${VAR:-default}` in `mcpServers` config —
|
||||||
|
in `env`, `command`, `args`, `url`, and `headers` — for both project (`.mcp.json`)
|
||||||
|
and user (`~/.claude.json`) scope. Use that instead of a literal value:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# WRONG — plaintext key lands in ~/.claude.json:
|
||||||
|
claude mcp add magic --scope user --env API_KEY="$MAGIC_API_KEY" -- npx -y @21st-dev/magic@latest
|
||||||
|
|
||||||
|
# RIGHT — single-quoted so bash doesn't expand it; Claude Code expands it at
|
||||||
|
# launch, reading the var from its own process environment:
|
||||||
|
claude mcp add magic --scope user --env 'API_KEY=${MAGIC_API_KEY}' -- npx -y @21st-dev/magic@latest
|
||||||
|
```
|
||||||
|
|
||||||
|
The var still has to exist in the **environment of the process that starts
|
||||||
|
`claude`** — sourcing `~/.claude/.env` into your everyday interactive shell
|
||||||
|
would defeat the point (every subprocess, every stray `env`/`printenv`, would
|
||||||
|
then see it). This repo's `~/.bashrc` instead wraps the `claude` command
|
||||||
|
itself: a `claude()` shell function sources `~/.claude/.env` into a subshell
|
||||||
|
and `exec`s the real binary, so the var reaches `claude` and its children only
|
||||||
|
— never the ambient shell. See `lib/toggle-external.sh`'s `magic` case for
|
||||||
|
the pattern to copy for a new MCP server.
|
||||||
|
|
||||||
|
There is no `claude mcp add` flag that writes the reference form for you —
|
||||||
|
the `${VAR}` syntax has to be typed by hand (or via a wrapper script), same as
|
||||||
|
above.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Diagnostic and maintenance
|
## Diagnostic and maintenance
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -188,8 +188,13 @@ enable_tool() {
|
|||||||
warn "magic already enabled"
|
warn "magic already enabled"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
# Reference, not value: Claude Code expands ${VAR} in mcpServers.env at
|
||||||
|
# launch (job7/BDR-026) — MAGIC_API_KEY itself never lands in
|
||||||
|
# ~/.claude.json. The check above still confirms the var IS set in
|
||||||
|
# ~/.claude/.env before wiring the reference, so a missing key fails
|
||||||
|
# here instead of silently at Claude Code startup.
|
||||||
claude mcp add magic --scope user \
|
claude mcp add magic --scope user \
|
||||||
--env API_KEY="$MAGIC_API_KEY" \
|
--env 'API_KEY=${MAGIC_API_KEY}' \
|
||||||
-- npx -y @21st-dev/magic@latest
|
-- npx -y @21st-dev/magic@latest
|
||||||
ok "magic enabled (user scope)"
|
ok "magic enabled (user scope)"
|
||||||
;;
|
;;
|
||||||
|
|||||||
Reference in New Issue
Block a user