# task-2612+1 — auto_remediation_planner _read_json/expected_files traversal hardening

> **분류**: AUTO_REMEDIATION_HOLD 자동 수렴 (non-Critical HIGH×2 · CRITICAL 0 ·
> critical7 false · 회장 확인 대기 0 · 분류 후 멈춤 금지).
> **HOLD 정본**: `memory/events/task-2612.independent-collector-adjudication.json`
> (Codex CRITICAL 0 · HIGH 2 · critical7 false).
> **spec sha256**: `826605c85d8644c7ea5fc1624cb8ff147a094a310b75e44f7abb489f3a26d241` (검증 일치).

## 1. 용어 — 산출 성격 불변 (무모순)

planner 의 **산출 성격**(plan 골격 생성만 · 실 dispatch 0 · 실 코드수정 0)은
변경하지 않았다. 본 remediation 은 planner **모듈 자체의 입력 read-path /
경로 정규화 보안 코드를 hardening** 한 것 — "plan-only" invariant 와 무모순
(입력 검증 강화이지 산출 성격 변경이 아님). `assert_plan_only()` AST 정적
가드는 변경 전후 PASS, `PLAN_ONLY/DISPATCH_PERFORMED` 불변.

## 2. 잔여 HIGH×2 교정 (task-2612 Codex 재audit 정본 verbatim)

### H1 — `_read_json()` read-path containment

- **gap**: 절대경로 무검 허용 · 상대경로 `resolve()`/containment 미검 →
  `../` traversal 또는 임의 절대 JSON 경로 read 가능.
- **fix**: `CANONICAL_WS_ROOT = Path("/home/jay/workspace").resolve()` 도입.
  신규 `_contained_resolved(path)` — 상대경로는 WS 루트 기준 결합, 절대경로는
  그대로 받되 **`Path.resolve()` 로 `..` 정규화 + symlink 실체화** 후
  `CANONICAL_WS_ROOT` 의 **strict 하위**(root 자기 자신 제외)인지 검증.
  이탈 시 `PathContainmentError`(fail-closed). `_read_json` 은 이 helper 만
  경유해 read.

### H2 — `expected_files` module_rel/test_rel 정규화

- **gap**: `_planner_detail` 의 `module_rel`/`test_rel` 이 정규화 없이
  `expected_files` 로 유입 → downstream executor out-of-scope 경로 유도 가능.
- **fix**: 신규 `_normalize_expected_rel(rel)` — 골격 placeholder(`<...>`)는
  실경로가 아니므로 그대로 통과(2604/2605/2609 plan 변환 byte-0 보존),
  실경로는 `_contained_resolved` 로 검증 후 **WS-relative POSIX** 로 정규화,
  이탈 시 `PathContainmentError`(fail-closed). `_expected_files` 의
  module_rel/test_rel 두 항목에만 적용(나머지 3개 경로 불변).

## 3. 변경 범위 (자기 deliverable allowlist 한정)

- `anu_v3/auto_remediation_planner.py` — H1/H2 입력검증 한정
  (`CANONICAL_WS_ROOT` · `PathContainmentError` · `_contained_resolved` ·
  `_read_json` · `_normalize_expected_rel` · `_expected_files` · `__all__`).
  그 외 본문 byte-0. sha256
  `74d03b0cb3822ba03591bbe41edb819a7b3f66c84441629dfb953ce687a6cd7d`.
- `tests/regression/test_auto_remediation_planner.py` — additive 23 케이스.
  sha256 `df49fedae5c70161646a0289b445223c7803375e93e048802716cd9bad905d2e`.
- `memory/events/task-2612+1.{decision,result}.json` · 본 report.

## 4. 검증 결과

- regression: `pytest tests/regression/test_auto_remediation_planner.py`
  → **23 passed in 0.22s**.
- self-check (실 entrypoint · mock 0): `all_passed=true`, 3 cases
  (`task-2604-AR1`/`task-2605-AR1`/`task-2609-AR1`), `disposition=AUTO_REMEDIATION_HOLD`,
  `plan_only=true` · `dispatch_performed=false`.
- H1 fail-closed: `../../etc/passwd`, `../outside.json`,
  `memory/../../../etc/passwd`, `/etc/passwd`, `/home/jay/.bashrc`, `..`,
  WS 밖 symlink → `PathContainmentError`. WS 하위 상대/절대 read 무회귀.
  WS 루트 자기 자신 거부(strict). `resolve()` 실적용(symlink→WS내부 허용).
- H2 fail-closed: placeholder byte-0 pass-through · 실경로 WS-relative
  정규화 · `../`·절대 이탈 거부.
- schema byte-0: `schemas/auto_remediation_plan.schema.json` sha256
  `272a6f415b0ddf00bf0c677c3e97691ce548bd8f5e12458053d58e249bc6f430`
  (task-2612 baseline 일치 — 무변).
- no-git (ANU Layer-A): git HEAD `20456b5f83fc039f2fd6f50f4b94095c29b41bfb`
  전후 EQUAL · branch 불변 · 커밋 0.

## 5. write-back gate / 다음 단계

premature write-back 금지 — durable-success(+53) write-back 은 **독립 ANU
collector(key `c119085addb0f8b7`)** 의 Codex 재audit HIGH/CRITICAL 0 확정
후에만. executor self key `7943afbe12c12f7d` 의 callback/collector/
adjudication/dispatch 절대 금지(+49 코드 정본). 잔여 non-Critical HIGH 발생
시 회장 보고 없이 task-2612+2 자동 수렴 계속. **authoritative = 독립 ANU only.**

## 6. HOLD_FOR_CHAIR 판정

신규 Critical7 0 · shared invariant 파손 0 → CHAIR_HOLD 아님.
non-Critical AUTO_REMEDIATION 자동 수렴 계속(회장 §6).
