Commit: 2f26d91 · Finding: SEC-A-07
Problem
Unlike GrantPermission { permission: Permission }, SetPermission { role_id: String, permission: String, granted: bool } at crates/state/src/event.rs:115-119 uses an untyped string. Any value is accepted and stored in role.permissions during apply at crates/state/src/materialize.rs:367-379. But Role.permissions is BTreeSet<Permission> — so the string→enum conversion has to happen somewhere; if the parser is strict the role silently becomes a dead set, if permissive we risk injecting unknown permission names.
Coupled with SEC-A-01 (#224), this makes the role system both ignored by has_permission and weakly typed at the wire layer.
Fix
Change the field type to permission: willow_state::Permission. Provide a migration for any on-disk DAGs that serialized the String form.
Obvious fix — will be auto-PR'd (low-risk enum swap).
Commit:
2f26d91· Finding:SEC-A-07Problem
Unlike
GrantPermission { permission: Permission },SetPermission { role_id: String, permission: String, granted: bool }atcrates/state/src/event.rs:115-119uses an untyped string. Any value is accepted and stored inrole.permissionsduring apply atcrates/state/src/materialize.rs:367-379. ButRole.permissionsisBTreeSet<Permission>— so the string→enum conversion has to happen somewhere; if the parser is strict the role silently becomes a dead set, if permissive we risk injecting unknown permission names.Coupled with SEC-A-01 (#224), this makes the role system both ignored by
has_permissionand weakly typed at the wire layer.Fix
Change the field type to
permission: willow_state::Permission. Provide a migration for any on-disk DAGs that serialized the String form.Obvious fix — will be auto-PR'd (low-risk enum swap).