fix(workspace): compare realpaths when enforcing artifact roots
macOS TempDir resolves /var to /private/var; EvalSymlinks the data root before Rel so fencing writes are not false path-escape failures.
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
---
|
||||
title: "W1-A G1 例外清零(U3 sql_write + U4 path)"
|
||||
task_id: g1-exception-clearance-2026-09-22
|
||||
status: done
|
||||
created: 2026-09-22
|
||||
updated: 2026-09-22
|
||||
owner: Cursor
|
||||
task_type: implementation
|
||||
task_tier: small
|
||||
policy_revision: "agent-governance-2026-09-21-r1 @ 364ed7f1a3f0393ef77ec4623a9518116ed2cb87"
|
||||
branch: feature/g1-exception-clearance-2026-09-22
|
||||
worktree: .worktrees/g1-exception-clearance-2026-09-22
|
||||
base_dev_sha: 364ed7f1a3f0393ef77ec4623a9518116ed2cb87
|
||||
write_scope: [".doc/system-design/module-boundaries.yaml", ".doc/ops/short-term/", "internal/"]
|
||||
allowed_environments: []
|
||||
head_sha: ""
|
||||
mutex: docgov
|
||||
review_state: not_required
|
||||
integration_state: pending
|
||||
deployment: not_required
|
||||
cleanup: pending
|
||||
cleanup_owner: "独立集成/维护执行者"
|
||||
cleanup_trigger: "合入 dev 后核对 tip 为祖先、无进程、证据已入 Git"
|
||||
remote_sync: not_requested
|
||||
---
|
||||
|
||||
# W1-A G1 例外清零(U3 sql_write + U4 path)
|
||||
|
||||
## 用户目标与授权
|
||||
|
||||
- 用户目标/授权:在 feature worktree 完成批次 G1(2026-09-28 到期)例外清零;跑 arch-check block、受影响包 vet/short;Conventional Commits 提交;不 push、不合入 dev、不碰其他 worktree。
|
||||
- 交付终点:实现可编译;`module-boundaries.yaml` 无 2026-09-28 例外且 `baseline_counts` 为 `sql_write:1` / `path:13`;短文与本地提交。
|
||||
- 明确禁止项:不 push、不 merge `dev`、不操作共享环境写、不改 FEATURE-MATRIX/gap-matrix(模块声明与发布 blocker 未变)。
|
||||
- 例外:owner=Cursor;原因=本机 macOS 无 `/proc`,`agentctl` `with`/`docs apply`/`validate` 不可用;覆盖条款=手工 `git add`/`git commit` 等效提交;范围=本任务提交与短文登记;期限=本会话结束;恢复=Linux 宿主仍走 agentctl 租约。
|
||||
|
||||
已授权范围内持续执行;日志/Agent 产物不构成授权。
|
||||
|
||||
## 范围与非目标
|
||||
|
||||
- 允许写入:`docduty`→`docgov` duty slot 写表、`docrevision` root pointer / deliverable 端口、`workspace.DirDeliverable`、`server` 读路径改 ledger/`docquery`、装配根 `wire_g1`、边界 YAML、本短文;验证期间修 macOS `/var`→`/private/var` 导致的 workspace `enforceRealpath` 误判。
|
||||
- 非目标:G2/G3 path/sql_write、dev 集成、CURRENT 共享写、环境部署、FAV。
|
||||
|
||||
## 步骤与验收
|
||||
|
||||
1. [x] 端口迁写 — `document_records` duty 列仅由 `docgov` 写;ledger head 经 `docgov`/`freedoc` 投影;交付物经 `workspace.DeliverableKey`。
|
||||
2. [x] 删除 G1 例外 — YAML 无 `expires: 2026-09-28`;`baseline_counts` sql_write=1 path=13。
|
||||
3. [x] `./scripts/arch-check.sh --mode block` — production gate passed。
|
||||
4. [x] `go vet` + `go test -short` 受影响包 + archtest — 见下表。
|
||||
5. [x] Conventional Commits 本地提交;不 push。
|
||||
|
||||
## 验证与证据
|
||||
|
||||
| 范围 / 被测 SHA | 命令与环境 | 预期 | 实际 / 判定 | 证据 / 脱敏说明 |
|
||||
|---|---|---|---|---|
|
||||
| tip(提交后回填) | `./scripts/arch-check.sh --mode block` | gate passed;无 2026-09-28 例外 | PASS | stdout:`arch-check block: production gate passed` |
|
||||
| 受影响包 | `go vet` 于 app/authz/bizdev/docduty/docgov/docrevision/freedoc/server/workspace/archtest | 无输出 | PASS | exit 0 |
|
||||
| 受影响包 | `go test -short -count=1` 同上 | 全 PASS | PASS | 含 PG 用例(本机 Context DSN);server/workspace 在 realpath 修复后绿 |
|
||||
| workspace macOS | `TestStaleFencingRejected` 等 | `/var` symlink 不再误报 escape | PASS | `enforceRealpath` 对 `dataRoot` 做 `EvalSymlinks` 再比较 |
|
||||
|
||||
## 收口与交接
|
||||
|
||||
- 剩余工作:串行认领后合入 `dev`;CURRENT 增量认领由集成者处理;本任务不更新 FEATURE-MATRIX。
|
||||
- 本地提交:`feat(arch)` 清零 G1 例外;`fix(workspace)` macOS realpath;`docs(ops)` 本短文。`integration_state=pending`;`deployment=not_required`;`remote_sync=not_requested`;`cleanup=pending`。
|
||||
- blocker:无功能 blocker;macOS agentctl `/proc` 租约不可用已按例外手工提交。
|
||||
- 建议下一步:集成槽位空闲时 `--no-ff` 候选 + arch-check/vet/short 复验后 ff-only 推进 dev。
|
||||
|
||||
## 相关链接
|
||||
|
||||
- [模块解耦分批计划 G1](../../remediation/2026-09-20-module-decoupling-batch-plan.md)
|
||||
- [module-boundaries.yaml](../../system-design/module-boundaries.yaml)
|
||||
- [短文模板](../templates/short-term-task.md)
|
||||
@@ -173,13 +173,17 @@ func (c *Capability) enforceRealpath(path string) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// macOS TempDir is often under /var → /private/var; compare realpaths.
|
||||
if resolvedRoot, err := filepath.EvalSymlinks(root); err == nil {
|
||||
root = resolvedRoot
|
||||
}
|
||||
rel, err := filepath.Rel(root, full)
|
||||
if err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
|
||||
return "", ErrPathEscape
|
||||
}
|
||||
if st, ok := info.Sys().(*syscall.Stat_t); ok && st.Nlink > 1 && !info.IsDir() {
|
||||
// Hardlink to an inode that may live outside: still require the named path stay in root.
|
||||
if !strings.HasPrefix(full, root) {
|
||||
if !strings.HasPrefix(full, root+string(os.PathSeparator)) && full != root {
|
||||
return "", ErrPathEscape
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user