Policymaker is an embedded authorization decision library and monorepo. Security reports about the core, React, or Next.js package are welcome.
Do not open a public issue for a suspected vulnerability.
Use GitHub's private vulnerability reporting for
neplextech/policymaker when it is available. If private reporting is not
available, email info@neplextech.com with:
- The affected Policymaker version.
- The runtime and relevant toolchain versions.
- A description of the impact and the authorization boundary involved.
- Minimal reproduction steps or a private reproduction repository.
- Any known mitigations.
Remove access tokens, credentials, personal data, and proprietary policy input from the report. Maintainers will coordinate disclosure and remediation with the reporter. No fixed response or release timeline is promised because severity and maintainer availability vary.
For ordinary defects that do not create a security boundary bypass, use the bug report form.
Before the first stable release, security fixes are made on the current development line. After releases begin, the project will document supported release lines here. Consumers should use the newest available patch release and review the changelog before upgrading.
Policymaker answers whether an already identified subject may perform a typed action against application-provided data. The default engine is strictly synchronous. The explicit async engine awaits only application-provided rule predicates and performs no hidden I/O.
Policymaker does not:
- Authenticate users, services, API keys, or sessions.
- Validate identity-provider assertions or access tokens.
- Fetch roles, relationships, resources, or tenant membership.
- Filter database queries or enforce object access automatically.
- Persist, distribute, or sandbox policies.
- Store audit logs.
- Make a client-side decision authoritative.
- Prove that an application policy is correct or compliant.
The application owns these responsibilities and must pass trustworthy, current facts into the engine.
Build the subject from a trusted authentication and session-validation process. Do not copy role names, direct permissions, tenant IDs, or administrator flags directly from untrusted request fields.
Runtime role names that are not configured do not grant access, but that behavior does not make untrusted role assignment safe. Validate the authority that issued every role and permission.
Run authorization checks at the trusted boundary that performs the protected operation. Browser-side checks can hide or disable interface controls, but an attacker can alter browser code and requests. Repeat the authorization check on the server, worker, or trusted service before reading protected data or applying a mutation.
@policymaker/react is intentionally limited to user-interface checks. Its
provider and hooks do not authenticate the subject and never make a browser
decision authoritative. @policymaker/next helpers are server-side enforcement
tools, but applications still own identity, data loading, and the protected
operation.
Rule predicates, role and permission extractors, resource type extractors, helper selectors, and comparators are trusted application code. Policymaker does not sandbox them, deep-freeze inputs, or prevent nested input mutation.
createPolicy predicates must be synchronous and should be pure. Exceptions,
non-boolean returns, promises, and thenables become
PolicyEvaluationError. createPolicyAsync predicates may return
boolean | Promise<boolean>; thrown or rejected values and resolved
non-booleans reject with the same safe error type and rule ID metadata.
Role, permission, and resource-type extractors remain synchronous for both
engines. Extractor failures and asynchronous results become
PolicyEvaluationError.
Prefer resolving facts before evaluation when practical. When on-demand database or network work is required, put it only in an async-policy rule predicate. The application still controls authentication, timeouts, retries, connection limits, batching, caching, and failure handling. Async predicates run sequentially in policy order and may not all execute because deny and allow checks short-circuit.
Do not treat callback failures as ordinary denials without deliberate application-level error handling.
Unknown runtime actions, invalid runtime inputs, and requests without a matching allow are denied. Explicit policy denies, role denies, and direct permission denies override allows.
Deny-by-default limits some accidental grants, but it cannot identify missing checks, an overly broad allow rule, incorrect application attributes, or a policy attached to the wrong operation. Review policies and protected call sites together.
Tenant isolation is explicit. Use a cross-tenant deny rule where appropriate, and constrain application queries before returning or mutating data. A successful action check for one resource does not authorize other resources or remove the need for tenant-aware database filters.
sameScope compares application-selected values; it does not load or verify
tenant membership. A missing configured scope returns false. When used as
!sameScope(input) in a deny rule, that fail-closed behavior protects missing
scope data.
isOwner compares application-selected identities; it does not establish that
either object is authentic.
The global * pattern and hierarchical wildcards can authorize many known
actions. Review the action registry whenever it grows because a wildcard role
or rule automatically covers new matching known actions after recompilation.
A permission named Administrator has no implicit special behavior in
createPermissions; superuser behavior must be configured. Once configured,
the selected bit satisfies permission checks and should be assigned with the
same care as a wildcard administrator role.
Bitfields compactly represent permissions; they do not provide identity, tenant isolation, revocation, or integrity. Validate the source of a bigint mask and bind it to the correct authenticated subject and scope.
JavaScript bigints cannot be serialized directly as JSON. Use an explicit, validated encoding. Reject malformed, negative, out-of-range, or application-inappropriate values before using them.
The engine reads roles, permissions, resources, and context during each check and does not cache decisions. Application caches, sessions, tokens, snapshots, or bound data can become stale after suspension, role removal, tenant changes, or resource ownership changes.
Define invalidation and maximum-age behavior appropriate to the protected operation. High-risk operations may require fresh attributes even when read-only operations tolerate short-lived caches.
Policymaker decisions contain action, reason, matched rule summaries, and matched role names. They deliberately omit subject, resource, and context. Authorization errors include the action and reason but do not serialize application input.
Application logging can still leak data if it records complete inputs, callback causes, tokens, or request objects. Apply redaction and access controls to logs. Treat rule IDs and role names as potentially sensitive when they reveal internal security design.
Protect global boundaries with explicit denies, then add narrow allows:
const policy = createPolicy({
actions: ['invoice:read', 'invoice:refund'] as const,
rules: [
{
id: 'deny-suspended-subjects',
effect: 'deny',
actions: ['*'],
when: ({ subject }) => subject.suspended === true,
},
{
id: 'deny-cross-tenant-access',
effect: 'deny',
actions: ['*'],
when: (input) => !sameOrganization(input),
},
{
id: 'allow-finance-refunds-with-mfa',
effect: 'allow',
actions: ['invoice:refund'],
when: ({ subject, context }) =>
subject.department === 'finance' &&
context?.authenticationMethod === 'mfa',
},
],
});This is an example, not a complete policy for every application. Test allowed, denied, missing-data, stale-data, and cross-tenant cases for the actual domain.
Official npm releases for policymaker, @policymaker/react, and
@policymaker/next are intended to use npm trusted publishing from the
repository's release.yml workflow with GitHub Actions OIDC and provenance.
The release workflow must not use a long-lived npm publishing token. Consumers
should install the expected package name and version, retain a lockfile, and
review package provenance and release notes where their environment supports
those checks.
The presence of provenance does not establish that a policy or release is free of defects; it links an eligible published package to its build source.