test: audit-driven coverage for security-relevant pure logic
+16 tests (432 -> 448): permissions-catalog least-privilege guard (non-board roles carry no high-risk capability; member/associate are read-only); new invite-logic suite (exact-match creator role, non-admin grants nothing, invalid/mis-cased targets); door-logic SSRF host-spoof edges (userinfo-spoofed host rejected, case-insensitive pin match, trailing-dot FQDN fails closed) + redactDoorSecrets regex-special password and end-of-string e= value; classes pickPromotion unlimited- capacity branch + equal-timestamp tie-break.
This commit is contained in:
@@ -37,4 +37,28 @@ describe('permissions catalog', () => {
|
||||
it('does not grant admin defaults (admin is implicit and cannot be locked out)', () => {
|
||||
expect(DEFAULT_ROLE_PERMISSIONS.admin).toBeUndefined()
|
||||
})
|
||||
|
||||
// Least-privilege guard: a future catalog edit must not silently hand
|
||||
// non-board roles a high-risk capability. (treasurer legitimately has
|
||||
// payments.manage; member/associate should be read-only.)
|
||||
it('keeps high-risk capabilities off non-board default roles', () => {
|
||||
const HIGH_RISK = [
|
||||
'door.manage', 'door.operate', 'members.manage', 'governance.manage',
|
||||
'settings.manage', 'customize.manage', 'forms.manage',
|
||||
'certifications.grant', 'ops.secrets.read', 'ops.secrets.write',
|
||||
'forum.moderate',
|
||||
]
|
||||
for (const role of ['treasurer', 'member', 'associate'] as const) {
|
||||
const perms = DEFAULT_ROLE_PERMISSIONS[role] ?? []
|
||||
expect(perms.filter(p => HIGH_RISK.includes(p))).toEqual([])
|
||||
}
|
||||
})
|
||||
|
||||
it('member and associate defaults are read-only', () => {
|
||||
for (const role of ['member', 'associate'] as const) {
|
||||
for (const p of DEFAULT_ROLE_PERMISSIONS[role] ?? []) {
|
||||
expect(p.endsWith('.read')).toBe(true)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user