set_procedure_policy accepts a policy with immediate_threshold = 0 and delayed_threshold > 0 (a delay-only policy) without rejecting it. enforce_procedure_policy always evaluates policies in immediate mode, and compute_proc_policy_contribution panics whenever a procedure carries a policy configured only for the opposite mode. As a result, any procedure assigned a delay-only policy becomes permanently uncallable starting with the next transaction. Because set_procedure_policy is itself subject to policy evaluation and is the only procedure that writes the policy map, targeting set_procedure_policy with a delay-only policy leaves no way to correct it afterward. The same combination can also be baked into an account's initial storage through the public with_proc_policies and with_delay_threshold constructors, since deployment-time validation there checks only for duplicate roots and threshold bounds.
The delayed execution path is not implemented, so a delay-only policy has no working use today. Reaching this state requires the signing quorum to configure an unsupported combination, and that same quorum already has simpler ways to render the account unusable, such as rotating its own auth keys to something nobody controls. This is a self-inflicted misconfiguration of an incomplete feature rather than an exploit with an adversary on the other side.
Consider rejecting delay-only policies until delayed execution is implemented, requiring delayed_threshold == 0 whenever immediate_threshold == 0 in both the on-chain setter and the Rust constructors, and adding a regression test that exercises a policy update targeting set_procedure_policy itself.
set_procedure_policyaccepts a policy withimmediate_threshold = 0anddelayed_threshold > 0(a delay-only policy) without rejecting it.enforce_procedure_policyalways evaluates policies in immediate mode, andcompute_proc_policy_contributionpanics whenever a procedure carries a policy configured only for the opposite mode. As a result, any procedure assigned a delay-only policy becomes permanently uncallable starting with the next transaction. Becauseset_procedure_policyis itself subject to policy evaluation and is the only procedure that writes the policy map, targetingset_procedure_policywith a delay-only policy leaves no way to correct it afterward. The same combination can also be baked into an account's initial storage through the publicwith_proc_policiesandwith_delay_thresholdconstructors, since deployment-time validation there checks only for duplicate roots and threshold bounds.The delayed execution path is not implemented, so a delay-only policy has no working use today. Reaching this state requires the signing quorum to configure an unsupported combination, and that same quorum already has simpler ways to render the account unusable, such as rotating its own auth keys to something nobody controls. This is a self-inflicted misconfiguration of an incomplete feature rather than an exploit with an adversary on the other side.
Consider rejecting delay-only policies until delayed execution is implemented, requiring
delayed_threshold == 0wheneverimmediate_threshold == 0in both the on-chain setter and the Rust constructors, and adding a regression test that exercises a policy update targetingset_procedure_policyitself.