Skip to Content
AuthorizationAttack classes and how each is handled

Unreleased changes

These pages document main, which has changes 0.1.2 does not. npm install @evanion/acl gives you 0.1.2, documented here. Values on these pages are produced by running the current source on every build.

Security Analysis and Attack Mitigations

Authorization is not just about defining who can do what; it is about ensuring that those definitions cannot be bypassed. @evanion/acl is designed to neutralize a wide array of common security vulnerabilities by building defenses directly into the engine’s core.

This page provides a detailed mapping of forty classes of attack—referenced by their Common Weakness Enumeration (CWE) and OWASP entries—and how the library handles them.

The Defense Model: Tiers of Mitigation

Not every attack is handled in the same way. We categorize our mitigations into three tiers, which determine whether the library prevents the attack automatically or provides the primitives for the consumer to handle it.

Tier 1: Prevented by the Engine

These attacks are neutralized by the library’s internal logic. The engine is designed so that these vulnerabilities are structurally impossible. Tests for Tier 1 mitigations assert either a construction error (the policy cannot be built) or an explicit refusal of the action.

Tier 2: Handled via Primitives

The library provides the necessary security primitives, but the consumer must call them correctly to achieve the mitigation. In these cases, the library ensures the primitive is correct and provides an idiomatic way to replace insecure patterns.

Tier 3: Out of Scope (Contractual)

Some vulnerabilities are external to the ACL engine’s responsibilities (e.g., session management). These are documented to ensure developers are aware of the gap and implement the necessary defenses in their own application layer.


Tier 1: Prevented Mitigations

These defenses are baked into the engine and verified in tier1-prevented.test.ts.

IDAttack ClassCWE / OWASPMitigation Mechanism
SEC-001Mass assignment via proposed keysCWE-915, API3:2023The write axis evaluates every key in the proposed object, regardless of whether the library expects it.
SEC-002Fail-open on absent/null conditionsCWE-1188, CWE-276The when property is required; an empty list is treated as an open grant, forcing explicit definition.
SEC-003Key/Object/Action mismatchCWE-566Validation ensures the key exactly matches object.action and contains no unexpected separators.
SEC-004Deny rule reading missing dataCWE-863, A01:2021If a deny rule references a path the object lacks, it is treated as unevaluable and outranks any allow rule.
SEC-005Prototype pollution in decisionsCWE-1321Every path read is guarded by hasOwnProperty; internal maps are created using Object.defineProperty.
SEC-006Prototype pollution in transitionsCWE-1321The transitions map is accessed using own-properties only, preventing inherited prototype members from altering logic.
SEC-007__proto__ injection in narrowed writesCWE-1321__proto__ is not a recognized field; pickAllowedFields drops it at every level the rules descend into. A subtree no rule reaches is copied by reference, own __proto__ key included.
SEC-008Syntax characters as literal keysCWE-915* and !name are treated as rule syntax, not as literal keys on the row or in the write request. This holds at every level the rules descend into; a subtree no rule reaches is copied by reference.
SEC-009Operator/Value confusion in conditionsCWE-20Any operand that the engine cannot unambiguously interpret is rejected during policy construction.
SEC-010Incorrect default permissionsCWE-276, A01:2021By default, an empty rule list or an unknown action results in an immediate refusal (fail-closed).
SEC-011Bypass via user-controlled keysCWE-566, API1:2023Untrusted paths fail closed on unknown keys; authored paths throw an error if they are invalid.
SEC-012Post-adoption matrix mutationCWE-913The policy matrix is deep-frozen upon adoption; any attempt to mutate the matrix will fail.
SEC-013Divergence between validation and evaluationCWE-367The envelope is rebuilt from a single read, then frozen and validated as a single atomic unit.
SEC-014Non-coercible object dataCWE-754, CWE-248Only primitives can name an edge; any object with a hostile toString method results in a denial.
SEC-015Exhaustion via matrix depthCWE-674, CWE-400The freeze walk is iterative; values nested beyond the maximum allowed depth are refused.
SEC-016Linear growth of decision costCWE-400Decisions only read their own specific permission; the test asserts that bound as a count of subject reads.
SEC-017Type confusion across JSON boundariesCWE-1287All comparisons use strict equality (e.g., '1' is not 1, and null is not the same as an absent field).
SEC-018Unparseable clock or boundaryCWE-754A clock that fails to parse is treated as a high-priority deny, ensuring timed windows fail closed.
SEC-019Write escaping the decisionCWE-915Property tests verify that every key in the generated write was explicitly marked as allowed by the decision.
SEC-020Malformed envelope documentCWE-20Documents missing permissions, having invalid versions, or being bare lists are all refused by the engine.
SEC-021Write into a frozen matrix via Date, Map, or SetCWE-915, CWE-471The hydration walk seals every mutator of Date, Map, and Set with an own property that throws a TypeError before freezing the object, to prevent moving window boundaries through the public matrix getter.
SEC-022Nested exclusion leaking through its parent or a raw dotted columnCWE-863A field rule names a path. The nested key is denied and pickAllowedFields rebuilds the parent without it. Construction refuses malformed paths and contradictory lists. An array a rule indexes into, such as items under !items.0.price, is denied with not-traversable
SEC-023Write replacing a subtree with a key the caller may not writeCWE-915, CWE-1321The parent is denied with not-traversable and nothing of it is picked. A raw write key with a constructor or prototype segment is refused the same way
SEC-024Value the engine cannot walk, returned wholeCWE-863A Date, class instance, accessor or primitive under a descending rule is denied with not-traversable and left out of the pick

Tier 2: Consumer-Driven Mitigations

These mitigations rely on the consumer using the provided ACL primitives correctly. They are verified in tier2-primitives.test.ts.

IDAttack ClassCWE / OWASPCorrect Primitive / Idiom
SEC-101Mass assignment during write applicationCWE-915, API3:2023Use pickAllowedFields to keep only what was decided as allowed; avoid simple !== 'denied' checks.
SEC-102Field config read as writable listCWE-915Distinguish between targets (a single field) and fields.fields (the set of writable fields).
SEC-103Object-level auth (IDOR / BOLA)CWE-639, API1:2023Use conditions over object.* combined with a projection that carries the specific field being accessed.
SEC-104Unevaluable decisions read as grantsCWE-863Use the allowed boolean as the primary gate; the missing state should trigger a data refetch.
SEC-105Unicode/Key-shape attacks on namesCWE-176Names are matched byte-for-byte; use an explicit allow-list of permitted names to close the question.
SEC-106in and eq disagreement on NaNCWE-1077Use equality checks to explicitly define “never” cases, as includes (SameValueZero) and === differ on NaN.
SEC-107Stale matrix granting revoked permissionsCWE-672Monitor access.version to detect mismatches; the consumer is responsible for refetching the matrix.
SEC-108Field maps treated as the primary gateCWE-863Field maps only refine the decision; the primary allowed boolean must still be checked.

Tier 3: Out-of-Scope (Contractual)

The following classes are not handled by @evanion/acl because they occur outside the library’s boundary. These are documented as part of the security contract to ensure they are handled by the application.

IDAttack ClassCWE / OWASPWhy it is Out of Scope
SEC-201Confused Deputy (Forged Subject)CWE-441, A01:2021can authorizes the subject it is handed. The application must resolve the subject from a verified, secure session.
SEC-202Lack of Complete MediationCWE-862, A01:2021While authorize(subject) simplifies checks, the library cannot physically block other code paths from accessing data.
SEC-203TOCTOU (Time-of-Check to Time-of-Use)CWE-367A decision is a snapshot. To prevent data from changing between the check and the write, re-check inside the database transaction.
SEC-204Self-Authorizing ConditionsCWE-639The engine cannot know if the subject is attempting to write to a field that would then grant them more permissions.
SEC-205Client-Side EnforcementCWE-602The library works identically in browser and server; the application must ensure the server is the final authority.
SEC-206Information Disclosure via MatrixCWE-200The matrix contains roles and keys. Name these elements carefully, as they are shipped to the client.
SEC-207Caller-Supplied ClockCWE-807, CWE-367If a provided clock parses correctly, the engine accepts it. The application must provide a trusted, server-side clock.
SEC-208Live-read Subject/Object DataCWE-367The matrix is frozen, but the app’s data is not. Pass resolved, static objects to the evaluator to ensure consistency.

Where to go next

Last updated on