Files
Gitea-Tools/tests/test_issue_709_decision_lock_cross_profile.py
T
sysadminandGrok 4.5 2b359e0c26 fix(workflow): durable HMAC, dedicated mint capability, head-exact approve match (#709)
Address formal review 435 REQUEST_CHANGES on PR #710:
- F4: require durable GITEA_IRRECOVERABLE_AUTH_HMAC_KEY (fail closed; no ephemeral
  per-process secret); bind key_version into HMAC; cross-process verify works
- F5: dedicated gitea.decision_lock.irrecoverable_recovery only; reject reconciler
  equivalence; authoritative incident body + author + content_digest; reject
  self-authored incident evidence
- F3 residual: lock_targets_merged_pr_approval requires recorded-head match when
  expected_head_sha is provided (legacy no-head approve no longer primary-clears)

Co-Authored-By: Grok 4.5 (xAI) <[email protected]>
2026-07-14 02:13:47 -04:00

1451 lines
50 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""#709: cross-profile decision-lock cleanup, overwrite protection, recovery.
Covers AC1AC8 plus review-434 F1/F2/F3 and review-435 F3-residual/F4/F5
remediations without fabricating historical PR provenance or special-casing
live PR numbers in production code.
"""
from __future__ import annotations
import os
import subprocess
import sys
import tempfile
import unittest
from unittest.mock import patch
import irrecoverable_provenance as irp
import mcp_session_state as ss
import stale_review_decision_lock as srdl
def _lock(
mutations=None,
*,
profile="prgs-reviewer",
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
head=None,
):
muts = []
for m in mutations or []:
row = dict(m)
if head and "head_sha" not in row:
row["head_sha"] = head
muts.append(row)
return {
"task": "review_pr",
"remote": remote,
"org": org,
"repo": repo,
"session_pid": os.getpid(),
"session_profile": profile,
"session_profile_lock": profile,
"profile_identity": profile,
"final_review_decision_ready": False,
"ready_pr_number": None,
"ready_action": None,
"ready_expected_head_sha": None,
"live_mutations": muts,
"correction_authorized": False,
"correction_reason": None,
"kind": ss.KIND_DECISION_LOCK,
}
APPROVE = {"pr_number": 100, "action": "approve", "review_id": 9}
APPROVE_OTHER = {"pr_number": 200, "action": "approve", "review_id": 10}
HEAD_A = "a" * 40
HEAD_B = "b" * 40
RECONCILER_OPS = [
"gitea.read",
"gitea.pr.close",
"gitea.pr.comment",
"gitea.issue.comment",
]
DEDICATED_RECOVERY_OPS = RECONCILER_OPS + [
irp.CAPABILITY_IRRECOVERABLE_RECOVERY,
]
DURABLE_TEST_HMAC_KEY = "0" * 64 # 32-byte hex durable key for F4 tests
def _canonical_incident_body(
*,
pr_number=42,
head=HEAD_A,
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
incident_issue=700,
narrative="forensic diagnosis",
):
return irp.build_canonical_incident_body(
remote=remote,
org=org,
repo=repo,
pr_number=pr_number,
expected_head_sha=head,
incident_issue=incident_issue,
narrative=narrative,
)
def _incident_comment_payload(
*,
comment_id=11489,
author="controller-ops",
pr_number=42,
head=HEAD_A,
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
incident_issue=700,
):
return {
"id": comment_id,
"body": _canonical_incident_body(
pr_number=pr_number,
head=head,
remote=remote,
org=org,
repo=repo,
incident_issue=incident_issue,
),
"user": {"login": author},
"created_at": "2026-07-13T00:00:00Z",
"html_url": f"https://gitea.example/{org}/{repo}/issues/{incident_issue}#issuecomment-{comment_id}",
}
def _mint_auth(
*,
pr_number=42,
head=HEAD_A,
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
incident_issue=700,
incident_comment_id=11489,
issuer="sysadmin",
profile="prgs-reconciler",
destroyed_subject=None,
):
return irp.build_authorization_artifact(
remote=remote,
org=org,
repo=repo,
pr_number=pr_number,
expected_head_sha=head,
incident_issue=incident_issue,
incident_comment_id=incident_comment_id,
destroyed_subject=destroyed_subject,
issuer_username=issuer,
issuer_profile=profile,
native_provenance={
"native_mcp_transport": True,
"production_native_mcp_transport": False,
"pytest": True,
"token_fingerprint": "testfp",
"entrypoint": "pytest",
"pid": os.getpid(),
},
)
class TestAC2InitOverwrite(unittest.TestCase):
def test_empty_lock_allows_reinit(self):
a = srdl.assess_init_overwrite(_lock([]), force=True)
self.assertTrue(a["overwrite_allowed"])
def test_terminal_lock_blocks_force_reinit(self):
a = srdl.assess_init_overwrite(_lock([APPROVE]), force=True)
self.assertFalse(a["overwrite_allowed"])
self.assertTrue(a["has_unresolved_terminal"])
self.assertEqual(a["last_terminal_pr"], 100)
def test_none_lock_allows_init(self):
a = srdl.assess_init_overwrite(None)
self.assertTrue(a["overwrite_allowed"])
class TestAC1TargetApproval(unittest.TestCase):
def test_targets_matching_approve(self):
self.assertTrue(
srdl.lock_targets_merged_pr_approval(
_lock([APPROVE], head=HEAD_A),
pr_number=100,
expected_head_sha=HEAD_A,
)
)
def test_rejects_other_pr(self):
self.assertFalse(
srdl.lock_targets_merged_pr_approval(
_lock([APPROVE_OTHER]), pr_number=100
)
)
def test_rejects_head_mismatch(self):
self.assertFalse(
srdl.lock_targets_merged_pr_approval(
_lock([APPROVE], head=HEAD_A),
pr_number=100,
expected_head_sha=HEAD_B,
)
)
def test_f3_residual_rejects_legacy_no_head_when_expected_head_given(self):
"""Primary approve-match requires recorded-head (#709 F3 residual / 435)."""
# APPROVE without head fields — legacy ledger.
legacy = _lock([APPROVE]) # no head=
self.assertIsNone(srdl.mutation_head_sha(APPROVE, legacy))
self.assertFalse(
srdl.lock_targets_merged_pr_approval(
legacy,
pr_number=100,
expected_head_sha=HEAD_A,
)
)
# Without expected_head_sha, PR-number-only match still works for
# non-destructive callers that do not pass a head pin.
self.assertTrue(
srdl.lock_targets_merged_pr_approval(legacy, pr_number=100)
)
class TestF1AuthorizationNotSelfAssertable(unittest.TestCase):
def test_operator_authorized_true_cannot_authorize_via_build(self):
rec = srdl.build_irrecoverable_provenance_record(
pr_number=42,
head_sha=HEAD_A,
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
actor_username="sysadmin",
profile_name="prgs-reconciler",
reason="evidence destroyed",
incident_ref="anything",
operator_authorized=True,
)
self.assertFalse(rec["applied"])
self.assertFalse(rec["historical_cleanup_proven"])
self.assertFalse(rec["merger_may_accept"])
def test_confirmation_string_not_authorization(self):
# Confirmation is only intent text; capability assess ignores it.
conf = irp.expected_confirmation(99)
self.assertEqual(conf, "IRRECOVERABLE DECISION PROVENANCE PR 99")
# Without auth artifact, merger cannot accept.
rec = srdl.build_irrecoverable_provenance_record(
pr_number=99,
head_sha=HEAD_A,
remote="prgs",
org="o",
repo="r",
actor_username="x",
profile_name="y",
reason="r",
operator_authorized=False,
)
self.assertFalse(rec["merger_may_accept"])
def test_gitea_read_alone_insufficient(self):
a = irp.assess_capability_for_irrecoverable_recovery(
allowed_operations=["gitea.read"],
forbidden_operations=[],
role_kind="author",
profile_name="prgs-author",
)
self.assertFalse(a["allowed"])
def test_expected_head_missing_fails(self):
g = irp.assess_live_head_binding(
expected_head_sha=None,
live_head_sha=HEAD_A,
)
self.assertFalse(g["valid"])
def test_expected_head_differs_fails(self):
g = irp.assess_live_head_binding(
expected_head_sha=HEAD_A,
live_head_sha=HEAD_B,
)
self.assertFalse(g["valid"])
def test_missing_incident_fails(self):
g = irp.assess_incident_evidence(
incident_issue=None,
incident_comment_id=None,
comment_payload=None,
)
self.assertFalse(g["valid"])
def test_nonexistent_incident_fails(self):
g = irp.assess_incident_evidence(
incident_issue=1,
incident_comment_id=2,
comment_payload=None,
comment_lookup_error="404",
)
self.assertFalse(g["valid"])
def test_valid_auth_succeeds_exact_scope(self):
auth = _mint_auth()
v = irp.verify_authorization_artifact(
auth,
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
pr_number=42,
expected_head_sha=HEAD_A,
incident_issue=700,
incident_comment_id=11489,
)
self.assertTrue(v["valid"], v)
rec = irp.build_irrecoverable_provenance_record(
pr_number=42,
head_sha=HEAD_A,
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
actor_username="sysadmin",
profile_name="prgs-reconciler",
reason="evidence destroyed",
incident_issue=700,
incident_comment_id=11489,
authorization=auth,
)
self.assertTrue(rec["merger_may_accept"])
self.assertFalse(rec["applied"])
body = irp.format_irrecoverable_audit_comment(rec)
self.assertIn("applied: `False`", body)
def test_wrong_repo_auth_fails(self):
auth = _mint_auth(repo="Other-Repo")
v = irp.verify_authorization_artifact(
auth,
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
pr_number=42,
expected_head_sha=HEAD_A,
incident_issue=700,
incident_comment_id=11489,
)
self.assertFalse(v["valid"])
def test_wrong_pr_auth_fails(self):
auth = _mint_auth(pr_number=1)
v = irp.verify_authorization_artifact(
auth,
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
pr_number=42,
expected_head_sha=HEAD_A,
)
self.assertFalse(v["valid"])
def test_wrong_head_auth_fails(self):
auth = _mint_auth(head=HEAD_B)
v = irp.verify_authorization_artifact(
auth,
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
pr_number=42,
expected_head_sha=HEAD_A,
)
self.assertFalse(v["valid"])
def test_altered_signature_fails(self):
auth = _mint_auth()
auth["server_signature"] = "0" * 64
v = irp.verify_authorization_artifact(
auth,
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
pr_number=42,
expected_head_sha=HEAD_A,
incident_issue=700,
incident_comment_id=11489,
)
self.assertFalse(v["valid"])
def test_fresh_non_pytest_process_cannot_mint_accepted_record(self):
"""Ordinary Python process: merger_may_accept stays False without server auth."""
script = (
"import stale_review_decision_lock as s\n"
"r=s.build_irrecoverable_provenance_record(\n"
" pr_number=1, head_sha=None, remote='prgs', org=None, repo=None,\n"
" actor_username='x', profile_name='y', reason='r',\n"
" incident_ref=None, operator_authorized=True)\n"
"print(r.get('merger_may_accept'), r.get('head_sha'))\n"
)
env = {k: v for k, v in os.environ.items() if not k.startswith("PYTEST")}
env.pop("PYTEST_CURRENT_TEST", None)
proc = subprocess.run(
[sys.executable, "-c", script],
cwd=os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
capture_output=True,
text=True,
env=env,
timeout=30,
)
self.assertEqual(proc.returncode, 0, proc.stderr)
out = (proc.stdout or "").strip()
self.assertTrue(out.startswith("False"), msg=out)
def test_unauthorized_profile_capability(self):
a = irp.assess_capability_for_irrecoverable_recovery(
allowed_operations=["gitea.read", "gitea.pr.comment"],
forbidden_operations=["gitea.pr.close"],
role_kind="author",
profile_name="prgs-author",
)
self.assertFalse(a["allowed"])
def test_f5_reconciler_equivalence_rejected(self):
"""Reconciler profile without dedicated capability cannot mint (#709 F5)."""
a = irp.assess_capability_for_irrecoverable_recovery(
allowed_operations=RECONCILER_OPS,
forbidden_operations=[
"gitea.pr.approve",
"gitea.pr.merge",
"gitea.pr.review",
],
role_kind="reconciler",
profile_name="prgs-reconciler",
)
self.assertFalse(a["allowed"], a)
self.assertTrue(
any("dedicated" in r for r in a["reasons"]),
msg=a["reasons"],
)
def test_f5_dedicated_capability_allowed(self):
a = irp.assess_capability_for_irrecoverable_recovery(
allowed_operations=DEDICATED_RECOVERY_OPS,
forbidden_operations=[
"gitea.pr.approve",
"gitea.pr.merge",
"gitea.pr.review",
],
role_kind="reconciler",
profile_name="prgs-reconciler",
)
self.assertTrue(a["allowed"], a)
self.assertEqual(a["via"], "dedicated_capability")
class TestF5AuthoritativeIncidentEvidence(unittest.TestCase):
def test_any_nonempty_body_rejected(self):
g = irp.assess_incident_evidence(
incident_issue=700,
incident_comment_id=11489,
comment_payload={
"id": 11489,
"body": "random forensic note without canonical fields",
"user": {"login": "controller-ops"},
},
expected_remote="prgs",
expected_org="Scaled-Tech-Consulting",
expected_repo="Gitea-Tools",
expected_pr_number=42,
expected_head_sha=HEAD_A,
)
self.assertFalse(g["valid"], g)
def test_missing_author_rejected(self):
body = _canonical_incident_body()
g = irp.assess_incident_evidence(
incident_issue=700,
incident_comment_id=11489,
comment_payload={"id": 11489, "body": body, "user": {}},
expected_remote="prgs",
expected_org="Scaled-Tech-Consulting",
expected_repo="Gitea-Tools",
expected_pr_number=42,
expected_head_sha=HEAD_A,
)
self.assertFalse(g["valid"], g)
self.assertTrue(any("author" in r for r in g["reasons"]), g["reasons"])
def test_self_authored_rejected(self):
g = irp.assess_incident_evidence(
incident_issue=700,
incident_comment_id=11489,
comment_payload=_incident_comment_payload(author="sysadmin"),
expected_remote="prgs",
expected_org="Scaled-Tech-Consulting",
expected_repo="Gitea-Tools",
expected_pr_number=42,
expected_head_sha=HEAD_A,
mint_actor_username="sysadmin",
reject_self_authored=True,
)
self.assertFalse(g["valid"], g)
self.assertTrue(
any("self-authored" in r for r in g["reasons"]), g["reasons"]
)
def test_canonical_body_with_independent_author_accepted(self):
g = irp.assess_incident_evidence(
incident_issue=700,
incident_comment_id=11489,
comment_payload=_incident_comment_payload(author="controller-ops"),
expected_remote="prgs",
expected_org="Scaled-Tech-Consulting",
expected_repo="Gitea-Tools",
expected_pr_number=42,
expected_head_sha=HEAD_A,
mint_actor_username="sysadmin",
reject_self_authored=True,
)
self.assertTrue(g["valid"], g)
def test_tampered_content_digest_rejected(self):
payload = _incident_comment_payload()
payload["body"] = payload["body"].replace(
"content_digest: ", "content_digest: " + "f" * 64 + "x"
)
# Force bad digest line
lines = []
for line in payload["body"].splitlines():
if line.startswith("content_digest:"):
lines.append("content_digest: " + "0" * 64)
else:
lines.append(line)
payload["body"] = "\n".join(lines)
g = irp.assess_incident_evidence(
incident_issue=700,
incident_comment_id=11489,
comment_payload=payload,
expected_remote="prgs",
expected_org="Scaled-Tech-Consulting",
expected_repo="Gitea-Tools",
expected_pr_number=42,
expected_head_sha=HEAD_A,
mint_actor_username="sysadmin",
)
self.assertFalse(g["valid"], g)
self.assertTrue(
any("content_digest" in r for r in g["reasons"]), g["reasons"]
)
class TestF4DurableHmacKey(unittest.TestCase):
def tearDown(self):
os.environ.pop(irp.ENV_AUTH_HMAC_KEY, None)
os.environ.pop(irp.ENV_AUTH_HMAC_KEY_VERSION, None)
irp.reset_process_auth_secret_for_tests()
def test_key_version_bound_into_artifact(self):
irp.reset_process_auth_secret_for_tests()
auth = _mint_auth()
self.assertIn("key_version", auth)
self.assertTrue(auth["key_version"])
self.assertNotIn("server_secret", auth)
self.assertNotIn("hmac_key", auth)
def test_production_fails_closed_without_durable_key(self):
"""Non-pytest process without env key must not generate ephemeral secret."""
script = (
"import os, sys\n"
"os.environ.pop('PYTEST_CURRENT_TEST', None)\n"
"os.environ.pop('GITEA_IRRECOVERABLE_AUTH_HMAC_KEY', None)\n"
# Force non-pytest path by patching guard after import.
"import mcp_daemon_guard as g\n"
"g.is_pytest_runtime = lambda: False\n"
"import irrecoverable_provenance as irp\n"
"irp.reset_process_auth_secret_for_tests()\n"
"try:\n"
" irp._process_secret()\n"
" print('UNEXPECTED_OK')\n"
"except irp.AuthSecretError as e:\n"
" print('FAIL_CLOSED', 'ephemeral' in str(e).lower() or 'required' in str(e).lower())\n"
)
env = {k: v for k, v in os.environ.items() if not k.startswith("PYTEST")}
env.pop("PYTEST_CURRENT_TEST", None)
env.pop(irp.ENV_AUTH_HMAC_KEY, None)
proc = subprocess.run(
[sys.executable, "-c", script],
cwd=os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
capture_output=True,
text=True,
env=env,
timeout=30,
)
self.assertEqual(proc.returncode, 0, proc.stderr)
out = (proc.stdout or "").strip()
self.assertTrue(out.startswith("FAIL_CLOSED"), msg=out)
def test_cross_process_verify_with_durable_key(self):
"""Mint in process A, verify in process B with same durable key (#709 F4)."""
import json as _json
mint_script = (
"import json, os, irrecoverable_provenance as irp\n"
"irp.reset_process_auth_secret_for_tests()\n"
"auth = irp.build_authorization_artifact(\n"
" remote='prgs', org='o', repo='r', pr_number=10,\n"
f" expected_head_sha={HEAD_A!r}, incident_issue=1, incident_comment_id=2,\n"
" destroyed_subject=None, issuer_username='sysadmin',\n"
" issuer_profile='prgs-reconciler',\n"
" native_provenance={'native_mcp_transport': True, 'pytest': True,\n"
" 'token_fingerprint': 'fp', 'entrypoint': 'pytest', 'pid': 1})\n"
"print(json.dumps(auth))\n"
)
env = dict(os.environ)
env[irp.ENV_AUTH_HMAC_KEY] = DURABLE_TEST_HMAC_KEY
env[irp.ENV_AUTH_HMAC_KEY_VERSION] = "test-v1"
mint = subprocess.run(
[sys.executable, "-c", mint_script],
cwd=os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
capture_output=True,
text=True,
env=env,
timeout=30,
)
self.assertEqual(mint.returncode, 0, mint.stderr)
auth = _json.loads(mint.stdout.strip())
self.assertEqual(auth.get("key_version"), "test-v1")
verify_script = (
"import json, sys, irrecoverable_provenance as irp\n"
"irp.reset_process_auth_secret_for_tests()\n"
"auth = json.loads(sys.stdin.read())\n"
"v = irp.verify_authorization_artifact(\n"
" auth, remote='prgs', org='o', repo='r', pr_number=10,\n"
f" expected_head_sha={HEAD_A!r}, incident_issue=1, incident_comment_id=2)\n"
"print(v.get('valid'), v.get('reasons'))\n"
)
verify = subprocess.run(
[sys.executable, "-c", verify_script],
cwd=os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
input=_json.dumps(auth),
capture_output=True,
text=True,
env=env,
timeout=30,
)
self.assertEqual(verify.returncode, 0, verify.stderr)
self.assertTrue(
(verify.stdout or "").strip().startswith("True"),
msg=verify.stdout + verify.stderr,
)
# Different durable key must fail verification (cross-process mismatch).
env_bad = dict(env)
env_bad[irp.ENV_AUTH_HMAC_KEY] = "1" * 64
verify_bad = subprocess.run(
[sys.executable, "-c", verify_script],
cwd=os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
input=_json.dumps(auth),
capture_output=True,
text=True,
env=env_bad,
timeout=30,
)
self.assertEqual(verify_bad.returncode, 0, verify_bad.stderr)
self.assertTrue(
(verify_bad.stdout or "").strip().startswith("False"),
msg=verify_bad.stdout,
)
class TestF2MergerConsumer(unittest.TestCase):
def test_merger_rejects_without_valid_auth(self):
rec = srdl.build_irrecoverable_provenance_record(
pr_number=10,
head_sha=HEAD_A,
remote="prgs",
org="o",
repo="r",
actor_username="a",
profile_name="p",
reason="x",
operator_authorized=True,
)
a = irp.assess_merger_consumption(
rec,
None,
remote="prgs",
org="o",
repo="r",
pr_number=10,
live_head_sha=HEAD_A,
approval_at_current_head=True,
has_blocking_change_requests=False,
)
self.assertFalse(a["allowed"])
def test_merger_rejects_wrong_head(self):
auth = _mint_auth(pr_number=10, head=HEAD_A, org="o", repo="r")
rec = irp.build_irrecoverable_provenance_record(
pr_number=10,
head_sha=HEAD_A,
remote="prgs",
org="o",
repo="r",
actor_username="a",
profile_name="p",
reason="x",
incident_issue=700,
incident_comment_id=1,
authorization=auth,
)
a = irp.assess_merger_consumption(
rec,
auth,
remote="prgs",
org="o",
repo="r",
pr_number=10,
live_head_sha=HEAD_B,
approval_at_current_head=True,
has_blocking_change_requests=False,
)
self.assertFalse(a["allowed"])
def test_merger_rejects_replayed_auth(self):
auth = _mint_auth(pr_number=10, head=HEAD_A, org="o", repo="r", incident_comment_id=1)
consumed = irp.mark_consumed(auth, consumer_username="m", consumer_profile="merger")
rec = irp.build_irrecoverable_provenance_record(
pr_number=10,
head_sha=HEAD_A,
remote="prgs",
org="o",
repo="r",
actor_username="a",
profile_name="p",
reason="x",
incident_issue=700,
incident_comment_id=1,
authorization=auth, # original unconsumed for record build
)
a = irp.assess_merger_consumption(
rec,
consumed,
remote="prgs",
org="o",
repo="r",
pr_number=10,
live_head_sha=HEAD_A,
approval_at_current_head=True,
has_blocking_change_requests=False,
)
self.assertFalse(a["allowed"])
def test_recovery_cannot_bypass_missing_approval(self):
auth = _mint_auth(pr_number=10, head=HEAD_A, org="o", repo="r", incident_comment_id=1)
rec = irp.build_irrecoverable_provenance_record(
pr_number=10,
head_sha=HEAD_A,
remote="prgs",
org="o",
repo="r",
actor_username="a",
profile_name="p",
reason="x",
incident_issue=700,
incident_comment_id=1,
authorization=auth,
)
a = irp.assess_merger_consumption(
rec,
auth,
remote="prgs",
org="o",
repo="r",
pr_number=10,
live_head_sha=HEAD_A,
approval_at_current_head=False,
has_blocking_change_requests=False,
)
self.assertFalse(a["allowed"])
self.assertTrue(any("approval" in r for r in a["reasons"]))
def test_recovery_cannot_bypass_blocking_crs(self):
auth = _mint_auth(pr_number=10, head=HEAD_A, org="o", repo="r", incident_comment_id=1)
rec = irp.build_irrecoverable_provenance_record(
pr_number=10,
head_sha=HEAD_A,
remote="prgs",
org="o",
repo="r",
actor_username="a",
profile_name="p",
reason="x",
incident_issue=700,
incident_comment_id=1,
authorization=auth,
)
a = irp.assess_merger_consumption(
rec,
auth,
remote="prgs",
org="o",
repo="r",
pr_number=10,
live_head_sha=HEAD_A,
approval_at_current_head=True,
has_blocking_change_requests=True,
)
self.assertFalse(a["allowed"])
def test_valid_recovery_resolves_only_prior_provenance(self):
auth = _mint_auth(pr_number=10, head=HEAD_A, org="o", repo="r", incident_comment_id=1)
rec = irp.build_irrecoverable_provenance_record(
pr_number=10,
head_sha=HEAD_A,
remote="prgs",
org="o",
repo="r",
actor_username="a",
profile_name="p",
reason="x",
incident_issue=700,
incident_comment_id=1,
authorization=auth,
)
a = irp.assess_merger_consumption(
rec,
auth,
remote="prgs",
org="o",
repo="r",
pr_number=10,
live_head_sha=HEAD_A,
approval_at_current_head=True,
has_blocking_change_requests=False,
mergeable=True,
lease_ok=True,
runtime_ok=True,
workspace_ok=True,
anti_stomp_ok=True,
)
self.assertTrue(a["allowed"], a)
self.assertTrue(a["resolves_prior_provenance_blocker"])
self.assertFalse(a["historical_cleanup_proven"])
def test_duplicate_consume_idempotent(self):
auth = _mint_auth(pr_number=10, head=HEAD_A, org="o", repo="r", incident_comment_id=1)
rec = irp.build_irrecoverable_provenance_record(
pr_number=10,
head_sha=HEAD_A,
remote="prgs",
org="o",
repo="r",
actor_username="a",
profile_name="p",
reason="x",
incident_issue=700,
incident_comment_id=1,
authorization=auth,
)
consumed_auth = irp.mark_consumed(auth, consumer_username="m", consumer_profile="mer")
consumed_rec = irp.mark_consumed(rec, consumer_username="m", consumer_profile="mer")
a = irp.assess_merger_consumption(
consumed_rec,
consumed_auth,
remote="prgs",
org="o",
repo="r",
pr_number=10,
live_head_sha=HEAD_A,
approval_at_current_head=True,
has_blocking_change_requests=False,
)
self.assertTrue(a["allowed"], a)
self.assertTrue(a["recovery_record_consumed"])
class TestF3ExactScopeEnforcement(unittest.TestCase):
def setUp(self):
self._tmp = tempfile.TemporaryDirectory()
self.state_dir = self._tmp.name
os.chmod(self.state_dir, 0o700)
def tearDown(self):
self._tmp.cleanup()
def test_same_pr_other_remote_not_loaded(self):
ss.save_state(
kind=ss.KIND_DECISION_LOCK,
payload=_lock([APPROVE], profile="prgs-reviewer", remote="other"),
remote="other",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
profile_identity="prgs-reviewer",
state_dir=self.state_dir,
)
loaded = ss.load_state_for_profile(
kind=ss.KIND_DECISION_LOCK,
profile_identity="prgs-reviewer",
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
state_dir=self.state_dir,
skip_identity_match=True,
)
self.assertIsNone(loaded)
def test_same_pr_other_org_not_loaded(self):
ss.save_state(
kind=ss.KIND_DECISION_LOCK,
payload=_lock(
[APPROVE],
profile="prgs-reviewer",
org="Other-Org",
),
remote="prgs",
org="Other-Org",
repo="Gitea-Tools",
profile_identity="prgs-reviewer",
state_dir=self.state_dir,
)
loaded = ss.load_state_for_profile(
kind=ss.KIND_DECISION_LOCK,
profile_identity="prgs-reviewer",
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
state_dir=self.state_dir,
skip_identity_match=True,
)
self.assertIsNone(loaded)
def test_same_pr_other_repo_not_loaded(self):
ss.save_state(
kind=ss.KIND_DECISION_LOCK,
payload=_lock(
[APPROVE],
profile="prgs-reviewer",
repo="Other-Repo",
),
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Other-Repo",
profile_identity="prgs-reviewer",
state_dir=self.state_dir,
)
loaded = ss.load_state_for_profile(
kind=ss.KIND_DECISION_LOCK,
profile_identity="prgs-reviewer",
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
state_dir=self.state_dir,
skip_identity_match=True,
)
self.assertIsNone(loaded)
def test_path_traversal_profile_fails(self):
g = irp.assess_profile_path_identity("../evil")
self.assertFalse(g["valid"])
loaded = ss.load_state_for_profile(
kind=ss.KIND_DECISION_LOCK,
profile_identity="../evil",
remote="prgs",
org="o",
repo="r",
state_dir=self.state_dir,
skip_identity_match=True,
)
self.assertIsNone(loaded)
def test_malformed_legacy_missing_repo_fails_closed(self):
# Record without repo identity when caller requires repo.
payload = _lock([APPROVE], profile="prgs-reviewer")
del payload["repo"]
ss.save_state(
kind=ss.KIND_DECISION_LOCK,
payload=payload,
remote="prgs",
org="Scaled-Tech-Consulting",
repo=None,
profile_identity="prgs-reviewer",
state_dir=self.state_dir,
)
loaded = ss.load_state_for_profile(
kind=ss.KIND_DECISION_LOCK,
profile_identity="prgs-reviewer",
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
state_dir=self.state_dir,
skip_identity_match=True,
)
self.assertIsNone(loaded)
def test_list_and_load_foreign_profile_lock(self):
ss.save_state(
kind=ss.KIND_DECISION_LOCK,
payload=_lock([APPROVE], profile="prgs-reviewer"),
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
profile_identity="prgs-reviewer",
state_dir=self.state_dir,
)
ss.save_state(
kind=ss.KIND_DECISION_LOCK,
payload=_lock([], profile="prgs-merger"),
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
profile_identity="prgs-merger",
state_dir=self.state_dir,
)
foreign = ss.load_state_for_profile(
kind=ss.KIND_DECISION_LOCK,
profile_identity="prgs-reviewer",
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
state_dir=self.state_dir,
skip_identity_match=True,
)
self.assertIsNotNone(foreign)
self.assertTrue(
srdl.lock_targets_merged_pr_approval(foreign, pr_number=100)
)
class TestAC3PostMergeRecoveryRecord(unittest.TestCase):
def test_recovery_record_is_not_applied_cleanup(self):
rec = srdl.build_post_merge_recovery_record(
pr_number=10,
head_sha=HEAD_A,
merge_commit_sha="m" * 40,
target_profile_identity="prgs-reviewer",
failed_step="audit_comment_publish",
error="timeout",
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
actor_username="sysadmin",
profile_name="prgs-merger",
)
self.assertEqual(rec["status"], "recovery_required")
self.assertFalse(rec["applied"])
self.assertTrue(rec["recovery_critical"])
class TestSessionStateTTL(unittest.TestCase):
def test_recovery_critical_kinds_ttl_exempt(self):
auth = _mint_auth(pr_number=1)
rec = irp.build_irrecoverable_provenance_record(
pr_number=1,
head_sha=HEAD_A,
remote="prgs",
org="o",
repo="r",
actor_username="a",
profile_name="p",
reason="gone",
incident_issue=700,
incident_comment_id=1,
authorization=auth,
)
rec["kind"] = ss.KIND_IRRECOVERABLE_DECISION_PROVENANCE
rec["recorded_at"] = "2000-01-01T00:00:00Z"
rec["updated_at"] = rec["recorded_at"]
rec["profile_identity"] = "prgs-reconciler"
rec["session_profile_lock"] = "prgs-reconciler"
reasons = ss.identity_match_reasons(
rec, profile_identity="prgs-reconciler"
)
self.assertFalse(any("expired" in r for r in reasons), msg=reasons)
class TestInitReviewDecisionLockIntegration(unittest.TestCase):
def setUp(self):
self._tmp = tempfile.TemporaryDirectory()
self.env = patch.dict(
os.environ,
{
"GITEA_MCP_SESSION_STATE_DIR": self._tmp.name,
"GITEA_SESSION_PROFILE_LOCK": "prgs-reviewer",
"GITEA_PROFILE_NAME": "prgs-reviewer",
},
clear=False,
)
self.env.start()
import mcp_server
self.mcp = mcp_server
self.mcp._REVIEW_DECISION_LOCK = None
def tearDown(self):
self.mcp._REVIEW_DECISION_LOCK = None
self.env.stop()
self._tmp.cleanup()
def test_init_does_not_wipe_terminal_ledger(self):
self.mcp._save_review_decision_lock(
_lock([APPROVE], profile="prgs-reviewer")
)
self.mcp.init_review_decision_lock("prgs", "review_pr", force=True)
loaded = self.mcp._load_review_decision_lock()
self.assertIsNotNone(loaded)
last = srdl.last_terminal_mutation(loaded)
self.assertIsNotNone(last)
self.assertEqual(last.get("pr_number"), 100)
def test_init_creates_empty_when_no_terminal(self):
self.mcp._save_review_decision_lock(None)
self.mcp.init_review_decision_lock("prgs", "review_pr", force=True)
loaded = self.mcp._load_review_decision_lock()
self.assertIsNotNone(loaded)
self.assertEqual(loaded.get("live_mutations"), [])
class TestIrrecoverableToolF1(unittest.TestCase):
def setUp(self):
self._tmp = tempfile.TemporaryDirectory()
self.env = patch.dict(
os.environ,
{
"GITEA_MCP_SESSION_STATE_DIR": self._tmp.name,
"GITEA_SESSION_PROFILE_LOCK": "prgs-reconciler",
"GITEA_PROFILE_NAME": "prgs-reconciler",
"GITEA_ALLOWED_OPERATIONS": ",".join(DEDICATED_RECOVERY_OPS),
},
clear=False,
)
self.env.start()
import mcp_server
self.mcp = mcp_server
def tearDown(self):
self.env.stop()
self._tmp.cleanup()
def _profile(self):
return {
"profile_name": "prgs-reconciler",
"role": "reconciler",
"allowed_operations": DEDICATED_RECOVERY_OPS,
"forbidden_operations": [
"gitea.pr.approve",
"gitea.pr.merge",
"gitea.pr.review",
],
}
def test_operator_authorized_true_rejected(self):
with patch.object(self.mcp, "get_profile", return_value=self._profile()):
r = self.mcp.gitea_record_irrecoverable_decision_lock_provenance(
pr_number=50,
reason="lost",
confirmation=irp.expected_confirmation(50),
operator_authorized=True,
expected_head_sha=HEAD_A,
incident_issue=700,
incident_comment_id=11489,
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
post_audit_comment=False,
)
self.assertFalse(r["success"])
self.assertTrue(any("operator_authorized" in x for x in r["reasons"]))
def test_missing_expected_head_fails(self):
with patch.object(self.mcp, "get_profile", return_value=self._profile()), patch.object(
self.mcp, "_authenticated_username", return_value="sysadmin"
), patch.object(
self.mcp, "_irrecoverable_capability_gate", return_value=None
), patch.object(
irp, "assess_transport_for_auth_mint", return_value={"allowed": True, "reasons": []}
), patch.object(
self.mcp, "_resolve", return_value=("h", "Scaled-Tech-Consulting", "Gitea-Tools")
):
r = self.mcp.gitea_record_irrecoverable_decision_lock_provenance(
pr_number=50,
reason="lost",
confirmation=irp.expected_confirmation(50),
expected_head_sha=None,
incident_issue=700,
incident_comment_id=11489,
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
post_audit_comment=False,
)
self.assertFalse(r["success"])
def test_wrong_confirmation_fails(self):
with patch.object(self.mcp, "get_profile", return_value=self._profile()), patch.object(
self.mcp, "_irrecoverable_capability_gate", return_value=None
), patch.object(
irp, "assess_transport_for_auth_mint", return_value={"allowed": True, "reasons": []}
), patch.object(
self.mcp, "_resolve", return_value=("h", "o", "r")
):
r = self.mcp.gitea_record_irrecoverable_decision_lock_provenance(
pr_number=50,
reason="x",
confirmation=irp.expected_confirmation(51),
expected_head_sha=HEAD_A,
incident_issue=1,
incident_comment_id=2,
remote="prgs",
post_audit_comment=False,
)
self.assertFalse(r["success"])
def test_records_with_server_auth(self):
auth = _mint_auth(
pr_number=50,
head=HEAD_A,
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
incident_issue=700,
incident_comment_id=11489,
)
auth_profile = irp.auth_state_profile_identity(
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
pr_number=50,
expected_head_sha=HEAD_A,
)
ss.save_state(
kind=ss.KIND_IRRECOVERABLE_PROVENANCE_AUTH,
payload=auth,
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
profile_identity=auth_profile,
state_dir=self._tmp.name,
)
def _api(method, url, auth=None, data=None, **kwargs):
if "/pulls/" in str(url):
return {"head": {"sha": HEAD_A}, "state": "open"}
if "/issues/comments/" in str(url):
return _incident_comment_payload(
comment_id=11489,
author="controller-ops",
pr_number=50,
head=HEAD_A,
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
incident_issue=700,
)
raise AssertionError(f"unexpected API {method} {url}")
with patch.object(self.mcp, "get_profile", return_value=self._profile()), patch.object(
self.mcp, "_authenticated_username", return_value="sysadmin"
), patch.object(
self.mcp, "_irrecoverable_capability_gate", return_value=None
), patch.object(
irp, "assess_transport_for_auth_mint", return_value={"allowed": True, "reasons": []}
), patch.object(
self.mcp, "_resolve", return_value=("h", "Scaled-Tech-Consulting", "Gitea-Tools")
), patch.object(
self.mcp, "_auth", return_value={"Authorization": "token test"}
), patch.object(
self.mcp, "api_request", side_effect=_api
), patch.object(
self.mcp, "repo_api_url", return_value="https://example.test/api/v1/repos/o/r"
):
r = self.mcp.gitea_record_irrecoverable_decision_lock_provenance(
pr_number=50,
reason="terminal evidence overwritten",
confirmation=irp.expected_confirmation(50),
expected_head_sha=HEAD_A,
incident_issue=700,
incident_comment_id=11489,
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
post_audit_comment=False,
)
self.assertTrue(r["success"], r)
self.assertFalse(r["applied"])
self.assertFalse(r["historical_cleanup_proven"])
self.assertTrue(r["merger_may_accept"])
self.assertEqual(r["record"]["status"], "provenance_irrecoverable")
# Idempotent
with patch.object(self.mcp, "get_profile", return_value=self._profile()), patch.object(
self.mcp, "_authenticated_username", return_value="sysadmin"
), patch.object(
self.mcp, "_irrecoverable_capability_gate", return_value=None
), patch.object(
irp, "assess_transport_for_auth_mint", return_value={"allowed": True, "reasons": []}
), patch.object(
self.mcp, "_resolve", return_value=("h", "Scaled-Tech-Consulting", "Gitea-Tools")
), patch.object(
self.mcp, "_auth", return_value={"Authorization": "token test"}
), patch.object(
self.mcp, "api_request", side_effect=_api
), patch.object(
self.mcp, "repo_api_url", return_value="https://example.test/api/v1/repos/o/r"
):
r2 = self.mcp.gitea_record_irrecoverable_decision_lock_provenance(
pr_number=50,
reason="terminal evidence overwritten",
confirmation=irp.expected_confirmation(50),
expected_head_sha=HEAD_A,
incident_issue=700,
incident_comment_id=11489,
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
post_audit_comment=False,
)
self.assertTrue(r2["success"], r2)
self.assertFalse(r2["performed"])
class TestClearProfileHelperF3(unittest.TestCase):
def setUp(self):
self._tmp = tempfile.TemporaryDirectory()
self.env = patch.dict(
os.environ,
{
"GITEA_MCP_SESSION_STATE_DIR": self._tmp.name,
"GITEA_SESSION_PROFILE_LOCK": "prgs-merger",
},
clear=False,
)
self.env.start()
import mcp_server
self.mcp = mcp_server
self.mcp._REVIEW_DECISION_LOCK = None
def tearDown(self):
self.mcp._REVIEW_DECISION_LOCK = None
self.env.stop()
self._tmp.cleanup()
def test_clear_only_matching_reviewer_approve(self):
ss.save_state(
kind=ss.KIND_DECISION_LOCK,
payload=_lock([APPROVE], profile="prgs-reviewer", head=HEAD_A),
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
profile_identity="prgs-reviewer",
state_dir=self._tmp.name,
)
ss.save_state(
kind=ss.KIND_DECISION_LOCK,
payload=_lock([], profile="prgs-merger"),
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
profile_identity="prgs-merger",
state_dir=self._tmp.name,
)
out = self.mcp._clear_decision_lock_for_profile(
profile_identity="prgs-reviewer",
pr_number=100,
expected_head_sha=HEAD_A,
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
)
self.assertTrue(out["cleared"], out)
skip = self.mcp._clear_decision_lock_for_profile(
profile_identity="prgs-merger",
pr_number=100,
expected_head_sha=HEAD_A,
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
)
self.assertFalse(skip["cleared"])
def test_pr_number_only_fallback_impossible(self):
ss.save_state(
kind=ss.KIND_DECISION_LOCK,
payload=_lock([APPROVE], profile="prgs-reviewer", head=HEAD_A),
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
profile_identity="prgs-reviewer",
state_dir=self._tmp.name,
)
# Missing expected_head_sha
out = self.mcp._clear_decision_lock_for_profile(
profile_identity="prgs-reviewer",
pr_number=100,
expected_head_sha=None,
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
)
self.assertFalse(out["cleared"])
self.assertIn("PR-number-only", out["reason"])
def test_wrong_head_not_cleared(self):
ss.save_state(
kind=ss.KIND_DECISION_LOCK,
payload=_lock([APPROVE], profile="prgs-reviewer", head=HEAD_A),
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
profile_identity="prgs-reviewer",
state_dir=self._tmp.name,
)
out = self.mcp._clear_decision_lock_for_profile(
profile_identity="prgs-reviewer",
pr_number=100,
expected_head_sha=HEAD_B,
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
)
self.assertFalse(out["cleared"])
def test_cross_repo_same_pr_number_not_cleared(self):
ss.save_state(
kind=ss.KIND_DECISION_LOCK,
payload=_lock(
[APPROVE],
profile="prgs-reviewer",
head=HEAD_A,
repo="Other-Repo",
),
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Other-Repo",
profile_identity="prgs-reviewer",
state_dir=self._tmp.name,
)
out = self.mcp._clear_decision_lock_for_profile(
profile_identity="prgs-reviewer",
pr_number=100,
expected_head_sha=HEAD_A,
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Gitea-Tools",
)
self.assertFalse(out["cleared"])
# Original lock still present under Other-Repo scope
still = ss.load_state_for_profile(
kind=ss.KIND_DECISION_LOCK,
profile_identity="prgs-reviewer",
remote="prgs",
org="Scaled-Tech-Consulting",
repo="Other-Repo",
state_dir=self._tmp.name,
skip_identity_match=True,
)
self.assertIsNotNone(still)
if __name__ == "__main__":
unittest.main()