Background
oauth_authorizations.policy_id has no foreign-key constraint to policies.id. As a result, an OAuth authorization can reference a non-existent policy_id (typo, stale, deleted policy). When the signer loads such an authorization, oauth_auth.permissions(...) returns an empty Vec<Permission>, and validate_permissions_for_sign / validate_permissions_for_encrypt / validate_permissions_for_decrypt fall through the permissions.is_empty() → Ok(()) backward-compat branch.
Net effect: a typo'd, stale, or deleted policy_id silently disables policy enforcement.
PR #131's test_18_no_permissions_allows_encrypt_and_decrypt pins the analogous behavior for "valid policy_id with zero linked permissions" (intended backward-compat). PR #132 (test_20_oauth_invalid_policy_id_allows_signing) pins the dangling-FK case as a regression test for current behavior — but current behavior may not be desired behavior.
Decision needed
Should an invalid / dangling policy_id fail closed (deny) or remain permissive (allow)? The current behavior is permissive; the question is whether that's intentional or a security gap.
Possible fixes if we want fail-closed
- Add a
FOREIGN KEY (policy_id) REFERENCES policies(id) ON DELETE RESTRICT constraint via migration. Prevents the dangling state at the DB level.
- Distinguish "dangling policy_id" from "valid policy with zero permissions" in the load path; deny on the former, allow on the latter (preserve backward-compat).
- Both.
Out of scope
References
Background
oauth_authorizations.policy_idhas no foreign-key constraint topolicies.id. As a result, an OAuth authorization can reference a non-existentpolicy_id(typo, stale, deleted policy). When the signer loads such an authorization,oauth_auth.permissions(...)returns an emptyVec<Permission>, andvalidate_permissions_for_sign/validate_permissions_for_encrypt/validate_permissions_for_decryptfall through thepermissions.is_empty() → Ok(())backward-compat branch.Net effect: a typo'd, stale, or deleted
policy_idsilently disables policy enforcement.PR #131's
test_18_no_permissions_allows_encrypt_and_decryptpins the analogous behavior for "valid policy_id with zero linked permissions" (intended backward-compat). PR #132 (test_20_oauth_invalid_policy_id_allows_signing) pins the dangling-FK case as a regression test for current behavior — but current behavior may not be desired behavior.Decision needed
Should an invalid / dangling
policy_idfail closed (deny) or remain permissive (allow)? The current behavior is permissive; the question is whether that's intentional or a security gap.Possible fixes if we want fail-closed
FOREIGN KEY (policy_id) REFERENCES policies(id) ON DELETE RESTRICTconstraint via migration. Prevents the dangling state at the DB level.Out of scope
test_18explicitly pins that).References
test_18no-permissions fallbackpolicy_idbehaviorsigner/src/signer_daemon.rs—validate_permissions_for_*methods