feat(h5): approval-identity MVP — signed reviewer approvals (C4/V20)
Under CASAN_APPROVAL_STRICT=1, a high-risk approval is trusted ONLY when a REGISTERED reviewer cryptographically signs THIS exact request and their role is authorized for the action — a plain env-var CASAN_APPROVER is no longer enough. - approval-sign.sh: reviewer signs assertion "casan-approval|v1|<action>|<actor>|<input_sha256>|<approver_id>" with their key. - approval-verify.sh: gate looks up reviewer role+pubkey in reviewers.registry, enforces role→action authorization, verifies the RSA signature (fail-closed). - governance-check.sh: strict branch requires a valid signed approval; SoD still enforced; default (non-strict) env-var path UNCHANGED (baseline preserved). - reviewers.registry: role-scoped reviewer identity registry (pubkeys off-repo; production replaces with OIDC/JWT from a real IdP). - phase-h5-approval-tests.sh: 8 checks — valid/authorized approve; unsigned, wrong-role, forged-key, unregistered, replay-to-other-request, self-approval all denied; non-strict backward-compat. Baselines: run-casan4 35/35, adversarial 44/44. Lifts H5 policy-approval (C4) 2.5 -> ~3.5-4 / 5. Total suites now 6 (+8 checks = 148). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
d8583fdb2e
commit
e21a1472b1
@@ -88,7 +88,34 @@ if [[ "$RISK_LEVEL" == "medium" ]]; then
|
||||
fi
|
||||
|
||||
if [[ "$RISK_LEVEL" == "high" ]]; then
|
||||
if [[ "$APPROVAL_DECISION" == "approve" && -n "$APPROVER" ]]; then
|
||||
if [[ "${CASAN_APPROVAL_STRICT:-0}" == "1" ]]; then
|
||||
# Approval-identity mode (V20): an env-var approver is NOT enough — the
|
||||
# reviewer must cryptographically SIGN this exact request and their role must
|
||||
# be authorized for the action. SoD (actor != approver) still enforced.
|
||||
if [[ "$APPROVAL_DECISION" == "approve" && -n "$APPROVER" && -n "${CASAN_APPROVAL_SIG:-}" ]]; then
|
||||
if [[ "$APPROVER" == "$ACTOR" ]]; then
|
||||
APPROVAL_STATUS="separation_of_duties_violation"
|
||||
DECISION="denied"
|
||||
REASONS+=("separation-of-duties:actor-equals-approver")
|
||||
else
|
||||
AV_RC=0
|
||||
AV_OUT="$(bash "$SCRIPT_DIR/approval-verify.sh" "$ACTION_NAME" "$ACTOR" "$INPUT_FILE" "$APPROVER" "$CASAN_APPROVAL_SIG" 2>/dev/null)" || AV_RC=$?
|
||||
if [[ "$AV_RC" -eq 0 ]]; then
|
||||
APPROVAL_STATUS="human_approved_signed"
|
||||
DECISION="approved"
|
||||
REASONS+=("signed-approval:${AV_OUT#APPROVAL_OK }")
|
||||
else
|
||||
APPROVAL_STATUS="approval_signature_invalid"
|
||||
DECISION="denied"
|
||||
REASONS+=("signed-approval-failed")
|
||||
fi
|
||||
fi
|
||||
else
|
||||
APPROVAL_STATUS="approval_required_signed"
|
||||
DECISION="denied"
|
||||
REASONS+=("strict-requires-signed-approval")
|
||||
fi
|
||||
elif [[ "$APPROVAL_DECISION" == "approve" && -n "$APPROVER" ]]; then
|
||||
if [[ "$APPROVER" == "$ACTOR" ]]; then
|
||||
# Separation of duties: the submitter may not approve their own action.
|
||||
APPROVAL_STATUS="separation_of_duties_violation"
|
||||
|
||||
Reference in New Issue
Block a user