{
  "schema": "anu.task2616.detected_pattern_fixture.v1",
  "task_id": "task-2616",
  "ts_kst": "2026-05-19 KST",
  "mode": "READ_ONLY (scan 대상 byte-0·코드수정 0)",
  "pattern_class": "CLI_OUTPUT_ARG_UNVALIDATED_FS_WRITE",
  "pattern_class_alias": "forbidden-path / scope-expansion Critical7 (task-2613+1 proof packet 동일 클래스)",
  "canonical_signature": {
    "cli_framework": "argparse",
    "output_arg_decl": "p.add_argument('--out'|'--output', default='')",
    "write_sink": "Path(args.<out_dest>).write_text(text, encoding='utf-8')",
    "default_safe_branch": "if not args.<out>: print(text) | sys.stdout.write(...)  ← stdout-only 안전",
    "vulnerability": "args.<out> → Path(...).write_text() 무검증 직접 전달. 경로정규화·prefix검증·traversal차단·symlink거부·overwrite보호 일체 0.",
    "attack_surface": [
      "--out /absolute/arbitrary.json (절대경로 임의 write)",
      "--out ../../../etc/anything (../ traversal)",
      "--out /home/jay/workspace/memory/tasks/task-2553.md (frozen byte-0 anchor overwrite)",
      "--out <symlink> (symlink 경유 임의 write·TOCTOU)"
    ]
  },
  "positive_fixtures": [
    {
      "file": "anu_v3/batch_hold_adjudicator.py",
      "task_id": "task-2610",
      "arg_decl_line": 642,
      "arg": "--output",
      "write_sink_line": 656,
      "write_code": "Path(a.output).write_text(out + \"\\n\", encoding=\"utf-8\")",
      "safe_default_line": 658,
      "safe_default": "sys.stdout.write(out + \"\\n\")",
      "entrypoint": "def _main (line 636) · if __name__=='__main__' (line ~685)",
      "critical7": true,
      "status": "UNREMEDIATED (task-2610 Track A durable success 는 adjudication 로직 한정 — --output sink 미교정)"
    },
    {
      "file": "anu_v3/batch_dependency_classifier.py",
      "task_id": "task-2613",
      "arg_decl_line": 695,
      "arg": "--out",
      "write_sink_line": 725,
      "write_code": "Path(args.out).write_text(text, encoding=\"utf-8\")",
      "safe_default_line": 728,
      "safe_default": "print(text)",
      "entrypoint": "def main (line 679) · if __name__=='__main__' (line ~732)",
      "critical7": true,
      "status": "ESTABLISHED Critical7 (memory/events/task-2613+1.critical7-proof-packet_260519.json) — CHAIR_HOLD·auto-remediation 0·task-2613+1 remediation 은 dependency 로직 한정"
    },
    {
      "file": "anu_v3/pre_authorized_evidence_bundle_builder.py",
      "task_id": null,
      "arg_decl_line": 568,
      "arg": "--out",
      "write_sink_line": 581,
      "write_code": "Path(args.out).write_text(text, encoding=\"utf-8\")",
      "safe_default_line": 584,
      "safe_default": "print(text)",
      "entrypoint": "def main (line 562) · if __name__=='__main__' (line ~587)",
      "critical7": true,
      "status": "★NEW Critical7 — 기존 task/proof packet 없음 → §8 HOLD_FOR_CHAIR 트리거 (신규 Critical7)"
    }
  ],
  "negative_fixtures_stdout_only_safe": [
    {
      "file": "anu_v3/auto_remediation_planner.py",
      "task_id": "task-2612",
      "reason": "CLI(argparse·def main) 이나 출력 인자→write sink taint 0. print(json.dumps(...)) stdout-only. 파일 write sink 0.",
      "critical7": false
    },
    {
      "file": "anu_v3/codex_high_classifier.py",
      "task_id": null,
      "reason": "critical7_classifier 계열. CLI(argparse·def _main) 이나 --input/--rules 입력 전용. open() 은 read('r') 한정. stdout-only print. write sink 0.",
      "critical7": false,
      "critical7_classifier_family": true
    },
    {
      "file": "anu_v3/critical7_classifier.py",
      "task_id": null,
      "reason": "critical7_classifier 계열(spec §2 명시 확인 대상). CLI(argparse·def _main) 이나 --input/--rules 입력 전용. open() read('r') 한정. stdout-only print. write sink 0 → 공통 guard 대상 아님.",
      "critical7": false,
      "critical7_classifier_family": true
    }
  ],
  "detector_module": "scripts/scan_anu_v3_cli_output_sinks_2616.py",
  "detector_method": "ast.parse READ-ONLY + add_argument 추출 + write_text/write_bytes/open(w)/json.dump sink 검출 + dest→sink data-flow heuristic + stdout marker 구분"
}
