code-to10 v6 A3: enforce signer separation on audit-verdict submission
process_audit_verdict now calls validate_signer_separation before any state save or ledger write (previously the function had no production caller on this path: it only ran inside collect_failed_checks via decide_pushback, which itself has no production caller). Signer is bound to the authenticated caller -- a body signer differing from the caller is rejected -- and executor/coordinator_approver/qa_tester must be declared and distinct from the caller; missing field, self-audit, or forged signer raises ValueError, mapped by _loop_run to HTTP 400 with no side effects. No new route. Honest limits are in the helper docstring: declared-identity enforcement only (task rows store no authoritative executor / coordinator / QA identity; task executor is a backend, not a person), and audit-fix reaudit verdicts are out of scope (the fix poster is not necessarily the re-auditor, so binding signer to the fix poster would be a mis-binding). (cherry picked from commit 56dce5165f4d08f38fe1730b9eea3b78f90cb97d) Reconcile-port: handler_lifecycle.py docstring hunk redirected into cloud/server.py audit-verdict handler (target keeps 6-mixin split; enforcement itself is in audit_loop_wiring.py and applied cleanly).
This commit is contained in:
1 parent
72d16a95dc
commit
bb00ba57aa
4 files changed
+160
-3
No files matched your search
@@ -64,6 +64,11 @@ def fail_body(source="audit"):
|
||||
"failures": [{"check": "ci_green", "detail": "x"}],
|
||||
"passed_items": ["a", "b", "c"],
|
||||
"contract_version": VERSION,
|
||||
# A3: signer is the HTTP caller; the other three
|
||||
# roles must be declared and distinct from it.
|
||||
"executor": "executor-1",
|
||||
"coordinator_approver": "coordinator-1",
|
||||
"qa_tester": "qa-1",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -79,6 +84,10 @@ def pass_body():
|
||||
"failures": [],
|
||||
"passed_items": ["a", "b", "c"],
|
||||
"contract_version": VERSION,
|
||||
# A3: see fail_body.
|
||||
"executor": "executor-1",
|
||||
"coordinator_approver": "coordinator-1",
|
||||
"qa_tester": "qa-1",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -452,6 +461,23 @@ class AuditLoopServerTestCase(unittest.TestCase):
|
||||
fail_body(), self.contrib_key)
|
||||
self.assertEqual(status, 403)
|
||||
|
||||
def test_verdict_self_audit_rejected_http(self):
|
||||
# A3 HTTP 层:调用方(owner)自审(声明的 executor 即
|
||||
# 调用方本人)回 400,且循环状态未落盘(audit-loop 404)。
|
||||
task = self._completed_task()
|
||||
body = fail_body()
|
||||
body["verdict"]["executor"] = self.owner_id
|
||||
status, _ = self._req(
|
||||
"POST", "/tasks/%s/audit-verdict" % task["id"],
|
||||
body, self.owner_key)
|
||||
self.assertEqual(status, 400)
|
||||
status, _ = self._req(
|
||||
"GET", "/tasks/%s/audit-loop?module_id=%s"
|
||||
"&contract_version=%s"
|
||||
% (task["id"], MODULE, VERSION),
|
||||
key=self.owner_key)
|
||||
self.assertEqual(status, 404)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user