anthropics / anthropics/claude-code

security-guidance: agentic commit review body is replaced by the inner CLI's stderr warning

未关闭 适合新手
#92,987 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看
area:auth area:hooks area:plugins bug has repro platform:macos
主要语言
Python
星标
145k
派生
23.1k
PR 合并指标
PR 指标待抓取

描述

## Summary

For a subscription (OAuth) user with no `ANTHROPIC_API_KEY`, the security-guidance
plugin's **agentic** commit/push review runs successfully and produces findings, but
the findings body never reaches the model. It is replaced by the inner `claude` CLI's
startup warning:

```
⚠ claude.ai connectors are disabled because ANTHROPIC_API_KEY or another auth source is set
and takes precedence over your claude.ai login · Unset it to load your organization's connectors
```

The `rewakeSummary` is still correct, so the notification looks like a real review —
it just carries no findings. Observed in a real session as four consecutive rewakes:

```
Commit security review found: control-regression in .claude/settings.json
---
Background security review of commit — address or acknowledge the findings below:
⚠ claude.ai connectors are disabled because ANTHROPIC_API_KEY or another auth source is set ...
⚠ (same line, x3 — one per SDK spawn in the agentic run)
```

## Environment

- Claude Code 2.1.265 (VS Code extension native binary) / 2.1.266 (PATH install)
- security-guidance plugin 2.0.7 (claude-plugins-official)
- claude-agent-sdk (Python) 0.2.137
- macOS 26.6.2, arm64
- Auth: claude.ai OAuth login (subscription). No `ANTHROPIC_API_KEY`, no `apiKeyHelper`,
no third-party provider env vars.

## Root cause

Three independent behaviours combine:

1. **The OAuth token is forwarded into the SDK grandchild as an env var.**
`hooks/llm.py:1091-1135` (`_agentic_spawn_env`) deliberately forwards
`ANTHROPIC_AUTH_TOKEN` when no `ANTHROPIC_API_KEY` exists — otherwise the grandchild
has no credentials at all. This is correct and intentional.

2. **The grandchild classifies an env-supplied bearer token as API-key precedence.**
Seeing `ANTHROPIC_AUTH_TOKEN` in its env, the inner CLI takes the
`api_key_precedence` path, disables claude.ai connectors, and prints the warning
above **to stderr** — even though the token is the user's own claude.ai OAuth token.

3. **The SDK does not pipe the child's stderr unless a callback is registered, and the
hook's stderr is the model-visible body channel.**
`claude_agent_sdk/_internal/transport/subprocess_cli.py:850-851`:

```python
# Pipe stderr only when the caller registered a callback.
stderr_dest = PIPE if self._options.stderr is not None else None
```

`_arun()` (`hooks/llm.py:1281-1330`) builds `ClaudeAgentOptions` **without** a
`stderr=` callback, so the grandchild's stderr is inherited straight from the hook
process. Per the plugin's own note at `hooks/security_reminder_hook.py:239-244`,
Claude Code uses the hook's `stderr || stdout` as the asyncRewake body. The
commit-review path emits its findings only via
`hookSpecificOutput.additionalContext` on **stdout**
(`hooks/security_reminder_hook.py:1478-1487`) and writes nothing to stderr.

Result: stderr is non-empty (the warning lines), stderr wins, the findings body is
discarded. The summary survives because it is parsed separately from the stdout JSON.

The sibling helper `_call_claude_via_sdk` **does** register a
stderr callback at `hooks/llm.py:404` — `_arun()` looks like a simple omission.

## Reproduction

Minimal — shows step 2 in isolation (no API call, read-only):

```console
$ env -u ANTHROPIC_AUTH_TOKEN claude mcp list 2>&1 >/dev/null
# (no warning; claude.ai connectors connect fine)

$ ANTHROPIC_AUTH_TOKEN=dummy-not-a-real-token claude mcp list 2>&1 >/dev/null
⚠ claude.ai connectors are disabled because ANTHROPIC_API_KEY or another auth source is set
and takes precedence over your claude.ai login · Unset it to load your organization's connectors
```

End to end:

1. Log in with a claude.ai subscription (`/login`); ensure `ANTHROPIC_API_KEY` is unset.
2. Enable the security-guidance plugin (agentic commit review is on by default).
3. Make a commit that the reviewer will flag.
4. The rewake notification arrives with a correct summary and a body consisting only of
the connector warning, repeated once per SDK spawn.

## Impact

The agentic commit/push review path is effectively unusable for OAuth subscription users
— which is the default configuration. The user sees a security notification with a real
finding category in the title and no detail at all, so there is nothing to act on. The
Stop-hook review is unaffected: `handle_stop_hook` calls `analyze_code_security` directly
(`hooks/security_reminder_hook.py:1988`) and never spawns a grandchild CLI.

Workaround: `SG_AGENTIC_COMMIT_REVIEW=0` falls back to the single-shot path
(`hooks/llm.py:1061-1073`), which makes no child process and delivers the body correctly,
at the cost of the agentic exploration and adjudication passes.

## Suggested fixes

**Primary (plugin, one line).** Register a `stderr` callback in `_arun()`'s
`ClaudeAgentOptions`, mirroring `hooks/llm.py:404`, so the grandchild's stderr goes to
`debug_log` instead of being inherited:

```python
stderr=lambda line: debug_log(f"agentic child stderr: {line}"),
```

**Secondary (Claude Code, defence in depth).** Session-level advisory warnings written to
stderr are unsafe under `--print` / stream-json, because a plugin hook's stderr is the
model-visible asyncRewake body channel. Consider suppressing them in that mode, or
routing them to the stream instead of stderr.

**Optional (classification).** The warning is arguably a misclassification: the credential
in `ANTHROPIC_AUTH_TOKEN` here *is* the user's claude.ai OAuth token, but supplying it via
env makes it count as an external auth source that overrides the claude.ai login. Not the
cause of the lost body, but it is what makes the message confusing to users.

## Related issues

- **#87145** — the same credential path on Windows, in the opposite direction: there
`ANTHROPIC_AUTH_TOKEN` is *not* injected into hook subprocesses at all, so
`HAS_API_CREDENTIALS` is false and the LLM review never runs. On macOS it *is*
injected, which is what makes this report possible. The two together suggest the
hook-subprocess credential contract is worth documenting explicitly.
- **#81057** — security-guidance fail-quiet behaviour (a failed LLM review reports "no
vulnerabilities found"). Same family of problem: a review that did not deliver its
result still looks like a successful one to the user.

贡献指南

这个仓库没有索引到贡献指南

调研方向

Start in hooks/llm.py at _arun() and compare its ClaudeAgentOptions with _call_claude_via_sdk around line 404. Reproduce the OAuth agentic review or the ANTHROPIC_AUTH_TOKEN warning, then verify that child stderr is logged without replacing the findings in the asyncRewake body.

由索引模型根据 Issue 内容生成。

评估

领域
cli, security
Issue 类型
缺陷
难度
2/5
预计耗时
1-3 小时
活跃度
活跃
描述清晰度
描述清楚
新手友好度
85/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。