🧯 fix: Bound Permission Superset Cache Inputs (#13065)

This commit is contained in:
Danny Avila 2026-05-11 08:39:37 -04:00 committed by GitHub
parent 52ccb1379b
commit 822ad6c36a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 13 additions and 0 deletions

View file

@ -1626,6 +1626,7 @@ describe('AclEntry Model Tests', () => {
describe('rejection of out-of-range inputs (cache-growth safety)', () => {
const MAX =
PermissionBits.VIEW | PermissionBits.EDIT | PermissionBits.DELETE | PermissionBits.SHARE;
const FIRST_TRUNCATED_32_BIT_VALUE = 2 ** 32;
const SHARED_EMPTY = permissionBitSupersets(MAX + 1);
test('returns a frozen empty array for requiredBits above MAX_PERM_BITS', () => {
@ -1641,11 +1642,21 @@ describe('AclEntry Model Tests', () => {
expect(permissionBitSupersets(MAX + 1)).toBe(SHARED_EMPTY);
expect(permissionBitSupersets(MAX + 100)).toBe(SHARED_EMPTY);
expect(permissionBitSupersets(-1)).toBe(SHARED_EMPTY);
expect(permissionBitSupersets(FIRST_TRUNCATED_32_BIT_VALUE)).toBe(SHARED_EMPTY);
expect(permissionBitSupersets(FIRST_TRUNCATED_32_BIT_VALUE * 2)).toBe(SHARED_EMPTY);
expect(permissionBitSupersets(Number.MAX_SAFE_INTEGER)).toBe(SHARED_EMPTY);
expect(permissionBitSupersets(NaN)).toBe(SHARED_EMPTY);
expect(permissionBitSupersets(1.5)).toBe(SHARED_EMPTY);
});
test('rejects large integers that truncate to in-range 32-bit values', () => {
expect(FIRST_TRUNCATED_32_BIT_VALUE & ~MAX).toBe(0);
expect(permissionBitSupersets(FIRST_TRUNCATED_32_BIT_VALUE)).toBe(SHARED_EMPTY);
expect(permissionBitSupersets(FIRST_TRUNCATED_32_BIT_VALUE + PermissionBits.VIEW)).toBe(
SHARED_EMPTY,
);
});
test('rejects inputs with bits above MAX_PERM_BITS even if some in-range bits are set', () => {
/**
* `permBits = 17 = 0b10001` has VIEW set AND bit 4 (out of range). A
@ -1692,6 +1703,7 @@ describe('AclEntry Model Tests', () => {
const before = setSpy.mock.calls.length;
for (let i = 0; i < 500; i++) {
permissionBitSupersets(MAX + 1 + i);
permissionBitSupersets(FIRST_TRUNCATED_32_BIT_VALUE + i);
permissionBitSupersets(-(i + 1));
permissionBitSupersets(i + 0.5);
}

View file

@ -51,6 +51,7 @@ export function permissionBitSupersets(requiredBits: number): readonly number[]
if (
!Number.isInteger(requiredBits) ||
requiredBits < 0 ||
requiredBits > MAX_PERM_BITS ||
(requiredBits & ~MAX_PERM_BITS) !== 0
) {
return EMPTY_SUPERSETS;