Files
policy-enforced/README.md
T
2026-09-28 14:49:02 -04:00

4.7 KiB

PolicyEnforced

Check that a stated policy is enforced in the code, not only written down.

PolicyEnforced is a small policy gate engine. A rule is data: which paths it covers, what content makes a file subject to it, what content satisfies it, and how an author documents an exemption. The engine is pure. You pass in the files (usually the ones a change touched) and it returns what was checked, what passed, what was exempted and what failed.

What it checks and why

Policy gates written as shell scripts tend to get copied from app to app and edited in place. Some copies drift for no reason. Others have to differ: an org-scoping check for an app that filters by an org_id column would flag every handler in an app that has no tenant columns and authorises through RBAC capability checks. One shared script cannot serve both, and a separate copy per app is the wrong fix. So the engine is shared and the patterns are parameters.

// An app that scopes rows by tenant column
orgScopingRule({
  guards: /requireOrg|resolveOrg/,
  inlineTenantFilter: /\.eq\((['"])(organization_id|org_id|owner_id)\1/,
})

// An app with no tenant columns that uses RBAC checks
orgScopingRule({
  serviceClients: /createServiceClient|createClient\(/,
  guards: /requireAuth|requireProjectCap|requireAdminCap/,
})

The engine is built against three failure modes.

  1. A vacuous run is not a pass. passed and vacuous are separate fields, and a rule that no file was subject to is reported as NOT ASSESSED. "No changed file was covered by this rule" is not evidence that the policy holds. A gate that prints a check mark for both is indistinguishable from one that does nothing, which is the same defect as a test that cannot fail.
  2. Regex state can skip files. A rule built with a /g pattern carries lastIndex from one file to the next, so later files can pass unexamined and the gate goes green while covering nothing. Every pattern is tested through a fresh non-global copy.
  3. A gate with no escape hatch gets switched off. The first legitimate cross-tenant cron job would force someone to disable it. Exemptions are a documented comment marker, and they are counted and cited in the report so they stay visible.

A pass names the files it checked, and a violation cites the file and line where the rule's trigger matched. A result of "3 files checked" that does not say which files is an asserted result, which is the kind of claim this library exists to catch.

One limit to keep in mind: the org-scoping rule confirms that a guard is present. It does not prove the guard filters to the caller's own rows. That still needs tests and review.

Rules included

  • orgScopingRule flags a handler that uses a service-role client (row-level security bypassed) with no authorization guard, tenant filter or exemption.
  • inOverflowRule flags a dynamic .in('col', ids) filter with no chunking helper. With supabase-js, a long id list goes into the query string, the request fails, and data || [] turns that into an empty result with no error. A literal array is bounded and is not flagged.
  • claimRule flags a public claim (for example "99.9% uptime SLA" on a pricing page) when nothing in the code enforces it.

You can also write a PolicyRule object directly.

Usage

import { evaluatePolicy, formatReport, orgScopingRule, inOverflowRule } from '@extant2000/policy-enforced'

const rules = [
  orgScopingRule({ guards: /requireOrg|resolveOrg/, inlineTenantFilter: /\.eq\((['"])(org_id|owner_id)\1/ }),
  inOverflowRule({ chunkHelpers: /selectInChunks/ }),
]

const files = [
  { path: 'server/api/invoices.get.ts', content: "const db = createServiceClient()\nreturn db.from('invoices').select()" },
  { path: 'server/api/contacts.get.ts', content: "await requireOrg(event)\nconst db = createServiceClient()" },
]

const report = evaluatePolicy(rules, files)
console.log({ passed: report.passed, vacuous: report.vacuous })
for (const v of report.violations) console.log(`${v.ruleId}  ${v.path}:${v.line}  ${v.excerpt}`)

// The full report for a CI log, with remedies under each violation
console.log(formatReport(report))

toReport(report) returns the same result as a CapabilityReport from @extant2000/evidence-record. evaluateRule(rule, files) runs a single rule.

Sample output

Produced by running the example above (first two lines):

{ passed: false, vacuous: false }
org-scoping  server/api/invoices.get.ts:1  const db = createServiceClient()

In the full report, in-overflow shows as NOT ASSESSED, since neither file used a dynamic .in() filter.

Install

npm install @extant2000/policy-enforced

License

MIT. Copyright (c) 2026 Extant 2000 LLC. See LICENSE.