Skip to content

[BUG] COUNTER_DIR 默认落 /tmp/claude-reviews,可预测路径存在软链接劫持面 #170

Open

Description

@WooDragon

问题

COUNTER_DIR 默认 /tmp/claude-reviews,目录名与文件名模式确定且可预测。在多用户系统上,攻击者可预先创建该目录并放入指向敏感文件的软链接;插件回写时会顺着软链接写穿目标。

由 #165 的 plan 审阅(plan-review Round 2)报为 Critical。核查后确认归属是既存债而非该 PR 引入,故独立成 issue。

证据

目录定义与其六个派生文件(plan-review.sh:245-266):

COUNTER_DIR="${REVIEW_COUNTER_DIR:-/tmp/claude-reviews}"
mkdir -p "$COUNTER_DIR"
COUNTER_FILE="$COUNTER_DIR/.review-count-${SESSION_ID}"
APPROVE_MARKER="$COUNTER_DIR/.review-approved-${SESSION_ID}"
DEGRADE_FILE="$COUNTER_DIR/.gemini-degraded"
CONV_FILE="$COUNTER_DIR/.conversation-${SESSION_ID}"
HISTORY_FILE="$COUNTER_DIR/.review-history-${SESSION_ID}"

三个脚本、五处引用共用该默认值(grep -rn '/tmp/claude-reviews' scripts/):

dispatch-check.sh:10   (注释)
dispatch-check.sh:29   DISPATCH_DIR=
precompact-review.sh:22 COUNTER_DIR=
plan-review.sh:246     COUNTER_DIR=
plan-review.sh:915     DISPATCH_DIR=

引入时间:32b5191(2026-05-18,v1.0.34)。

PR #168 之后新增第七类文件 .so-<hash>(second-opinion.sh 的 session key)。

修复要求

必须一次覆盖全部七类文件与三个脚本,不能只改一处:

  • .review-count-* / .review-approved-* / .gemini-degraded / .conversation-* / .review-history-* / .dispatch-*.json / .so-*
  • plan-review.sh / dispatch-check.sh / precompact-review.sh / second-opinion.sh

关键约束:DEGRADE_FILE 必须保持 hook 与 driver 同目录,否则降级状态共享失效(一侧探明容量耗尽、另一侧继续撞)。分批迁移会制造这个裂缝。

候选方案:

  1. ${XDG_STATE_HOME:-$HOME/.local/state}/claude-reviews(pr-review 的 session 落盘已用此惯例)或 ~/.cache/claude-reviews —— 用户私有空间,根治可预测性
  2. 保留 /tmp 但加权限校验:mkdir -p 后校验 owner 与 mode、拒绝软链接([ -L ])、chmod 700

倾向方案 1 —— 与仓库内既有惯例一致,且不需要每个写入点都做校验。

兼容性

REVIEW_COUNTER_DIR 是既有的测试隔离变量,迁移须保持它仍能覆盖默认值(237+ 个 bats 用例依赖它做隔离)。

迁移会让升级后的首次调用看不到旧计数器 —— 影响可接受(计数器是 session 级短期状态,丢失只是重新计数),但要在 README 写明。

验证

  • 七类文件全部落新目录,grep -rn '/tmp/claude-reviews' scripts/ 无残留
  • DEGRADE_FILE 在 hook 与 driver 间仍共享(一侧写、另一侧读到)
  • REVIEW_COUNTER_DIR 覆盖仍生效,全量 bats 全绿
  • 新目录权限 700,owner 正确

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions