Merge remote-tracking branch 'gitea-ssh/main' into codex/p0-product-manifest-standard-20260629

This commit is contained in:
Your Name
2026-06-29 14:07:34 +08:00
5 changed files with 126 additions and 18 deletions

View File

@@ -49378,3 +49378,18 @@ production browser smoke:
**未做**
- 沒有讀 secret、token、`.env`、raw sessions / SQLite / auth沒有寫 credential marker沒有 host / Docker / Nginx / firewall / K3s / DB 操作;沒有使用 GitHub。
## 2026-06-29 — 14:04 P0-005 placeholder preflight guard hardening
**完成內容**
- 修正 `scripts/reboot-recovery/dr-escrow-evidence-checklist.py`owner response skeleton 改用既有 preflight 可辨識的 fail-closed placeholder不再輸出可能被誤當真實 ref 的 `<NON_SECRET_REF_FOR_...>`
- 強化 `scripts/reboot-recovery/post-reboot-owner-response-preflight.py`:拒收 `<...>``YYYY-MM-DD` 與 fake example ref prefix避免 placeholder 被改成 `accepted` 後誤過。
- 補測 `scripts/reboot-recovery/tests/test_dr_escrow_evidence_checklist.py`,覆蓋未填 skeleton fail-closed 與 angle-bracket placeholder ref 不可 accepted。
**本地驗證結果**
- `python3.11 -m py_compile scripts/reboot-recovery/dr-escrow-evidence-checklist.py scripts/reboot-recovery/post-reboot-owner-response-preflight.py`:通過。
- `python3.11 -m pytest scripts/reboot-recovery/tests/test_dr_escrow_evidence_checklist.py scripts/reboot-recovery/tests/test_post_reboot_owner_response_template.py -q``7 passed`
- `python3 scripts/reboot-recovery/dr-escrow-evidence-checklist.py --output /tmp/awoooi-dr-escrow-evidence-checklist-20260629-fixed.json` + `jq empty`:通過。
**仍維持**
- 未寫 credential marker、未讀 secret / token / `.env` / raw sessions / SQLite / auth未做 host / Docker / Nginx / firewall / K3s / DB 操作,未使用 GitHub。

View File

@@ -1,6 +1,6 @@
{
"schema_version": "awoooi_priority_work_order_readback_v1",
"generated_at": "2026-06-29T14:00:19+08:00",
"generated_at": "2026-06-29T14:05:53+08:00",
"status": "p0_005_dr_escrow_checklist_ready_waiting_redacted_refs",
"source_refs": {
"global_scorecard": "~/.codex/product-runtime-governance-completion-scorecard.snapshot.json",
@@ -13,7 +13,7 @@
"dr_escrow_evidence_checklist_generator": "scripts/reboot-recovery/dr-escrow-evidence-checklist.py"
},
"current_head": {
"gitea_main_sha": "bd55386e6edc46ce0b188011b171191b7773c5ba",
"gitea_main_sha": "c98e6d5e5309dad9f0be4399a3d2e1fe2ec669ca",
"latest_successful_deploy_marker": "9362588ce chore(cd): deploy a423301 [skip ci]",
"latest_successful_deployed_source_sha": "a4233017ad5fd03977233f3db6a4bb45d71507ed",
"latest_source_readiness_commit_sha": "0c8d4e88c39157b92322fa41a92e6b15c317ac49",
@@ -118,7 +118,8 @@
"credential_marker_write_authorized_count": 0,
"secret_value_collection_allowed": false,
"checklist_generator_present": true,
"checklist_schema_version": "awoooi_dr_escrow_evidence_checklist_v1"
"checklist_schema_version": "awoooi_dr_escrow_evidence_checklist_v1",
"placeholder_preflight_guard_present": true
},
"missing_items": [
"restic_repository_password",

View File

@@ -41,11 +41,11 @@ def evidence_item(item_id: str) -> dict[str, Any]:
"last_reviewed_at",
"contains_secret_value=false",
],
"accepted_ref_examples": [
f"vault-item-id-for-{item_id}",
f"sealed-envelope-id-for-{item_id}",
f"recovery-checklist-id-for-{item_id}",
f"ticket-id-for-{item_id}",
"accepted_ref_classes": [
"password_manager_item_id",
"sealed_envelope_id",
"recovery_checklist_id",
"review_ticket_id",
],
"rejected_values": [
"passwords",
@@ -84,13 +84,13 @@ def owner_response_skeleton() -> dict[str, Any]:
"responses": [
{
"gate_id": GATE_ID,
"owner_role": "backup_dr_owner",
"owner_team": "platform_security",
"owner_role": "owner_role_here",
"owner_team": "owner_team_here",
"decision": "pending",
"decision_reason": "fill_only_after_all_redacted_refs_are_present",
"decision_reason": "decision_reason_here",
"affected_scope": "P0-005 DR credential escrow evidence",
"redacted_evidence_refs": ["<ONE_PARENT_TICKET_OR_REVIEW_REF>"],
"followup_owner": "backup_dr_owner",
"redacted_evidence_refs": ["redacted_evidence_ref_here"],
"followup_owner": "followup_owner_here",
"runtime_action_requested": False,
"runtime_action_authorized": False,
"host_write_requested": False,
@@ -102,10 +102,10 @@ def owner_response_skeleton() -> dict[str, Any]:
"escrow_items": [
{
"item_id": item_id,
"non_secret_evidence_ref": f"<NON_SECRET_REF_FOR_{item_id}>",
"recovery_owner": "backup_dr_owner",
"reviewer": "security_reviewer",
"last_reviewed_at": "YYYY-MM-DD",
"non_secret_evidence_ref": "non_secret_evidence_ref_here",
"recovery_owner": "owner_role_here",
"reviewer": "reviewer_here",
"last_reviewed_at": "pending",
"contains_secret_value": False,
}
for item_id in ITEMS

View File

@@ -172,7 +172,22 @@ def normalized(value: Any) -> str:
def is_placeholder(value: Any) -> bool:
return normalized(value).lower() in PLACEHOLDER_VALUES
text = normalized(value)
lower = text.lower()
if lower in PLACEHOLDER_VALUES:
return True
if re.fullmatch(r"<[^<>]+>", text):
return True
if lower in {"yyyy-mm-dd", "yyyy/mm/dd"}:
return True
return lower.startswith(
(
"vault-item-id-for-",
"sealed-envelope-id-for-",
"recovery-checklist-id-for-",
"ticket-id-for-",
)
)
def collect_strings(value: Any, path: str = "$") -> list[tuple[str, str]]:

View File

@@ -8,6 +8,7 @@ from pathlib import Path
ROOT = Path(__file__).resolve().parents[3]
SCRIPT = ROOT / "scripts" / "reboot-recovery" / "dr-escrow-evidence-checklist.py"
PREFLIGHT_SCRIPT = ROOT / "scripts" / "reboot-recovery" / "post-reboot-owner-response-preflight.py"
ITEMS = {
"restic_repository_password",
@@ -28,6 +29,28 @@ def load_checklist() -> dict:
return json.loads(result.stdout)
def run_preflight(tmp_path: Path, owner_packet: dict, response: dict) -> dict:
packet_path = tmp_path / "owner-packet.json"
response_path = tmp_path / "owner-response.json"
packet_path.write_text(json.dumps(owner_packet, indent=2) + "\n", encoding="utf-8")
response_path.write_text(json.dumps(response, indent=2) + "\n", encoding="utf-8")
result = subprocess.run(
[
sys.executable,
str(PREFLIGHT_SCRIPT),
"--owner-packet-file",
str(packet_path),
"--response-file",
str(response_path),
"--json",
],
text=True,
capture_output=True,
check=True,
)
return json.loads(result.stdout)
def test_checklist_is_single_p0_005_intake_packet() -> None:
payload = load_checklist()
@@ -73,3 +96,57 @@ def test_checklist_outputs_marker_dry_run_commands_only() -> None:
assert " --evidence-id <NON_SECRET_REF_FOR_" in command
assert "--token" not in command
assert "password=" not in command.lower()
def test_unfilled_skeleton_fails_closed_against_preflight(tmp_path: Path) -> None:
payload = load_checklist()
preflight = run_preflight(
tmp_path,
payload["owner_packet"],
payload["owner_response_skeleton"],
)
assert preflight["status"] == "blocked_waiting_owner_response_content"
assert preflight["owner_response_received_count"] == 0
assert preflight["owner_response_accepted_count"] == 0
assert preflight["runtime_action_authorized"] is False
assert preflight["secret_value_collection_allowed"] is False
blockers = "\n".join(preflight["blockers"])
assert "credential_escrow_evidence.owner_role_missing" in blockers
assert "credential_escrow_evidence.restic_repository_password.non_secret_evidence_ref_missing" in blockers
def test_angle_bracket_placeholder_refs_cannot_be_accepted(tmp_path: Path) -> None:
payload = load_checklist()
response = payload["owner_response_skeleton"]
item = response["responses"][0]
item.update(
{
"owner_role": "backup_dr_owner",
"owner_team": "platform_security",
"decision": "accepted",
"decision_reason": "reviewed redacted evidence refs only",
"redacted_evidence_refs": ["review-ticket-20260629"],
"followup_owner": "backup_dr_owner",
}
)
for escrow_item in item["escrow_items"]:
item_id = escrow_item["item_id"]
escrow_item.update(
{
"non_secret_evidence_ref": f"<NON_SECRET_REF_FOR_{item_id}>",
"recovery_owner": "backup_dr_owner",
"reviewer": "security_reviewer",
"last_reviewed_at": "2026-06-29",
}
)
preflight = run_preflight(tmp_path, payload["owner_packet"], response)
assert preflight["status"] == "blocked_waiting_owner_response_content"
assert preflight["owner_response_received_count"] == 0
assert preflight["owner_response_accepted_count"] == 0
blockers = "\n".join(preflight["blockers"])
for item_id in ITEMS:
assert f"credential_escrow_evidence.{item_id}.non_secret_evidence_ref_missing" in blockers