An authorization test matrix lists who (actor), what (resource) and how (action) for every route, with the expected allow or deny result. Store it as YAML or JSON, generate one automated test per cell, and run the suite in CI so a new feature cannot silently change who can do what.
Why a matrix beats scattered tests
OWASP's Authorization Testing Automation Cheat Sheet observes that most authorization problems appear because features are added or changed in later releases without anyone checking the effect on authorizations. A matrix is the fix: one place that says what should be true, and a harness that proves it each release.
It describes the matrix as two dimensions, the feature and the logical role, sometimes with a third for business data filtering. Testers call each role a point of view.
Step by step
- Name the actors. For example anonymous, basic user, tenant admin, another tenant's user, auditor.
- Name the resources and actions. Routes with methods are a good start: read, create, update, delete, export.
- Fill in the expected outcome for each cell, including the status code you want for denial.
- Store it in a pivot file, not a spreadsheet, so a program can read it and a person can still edit it.
- Write one test loop that reads the file, logs in as each actor, calls each route and compares the result.
- Fail clearly. Report the exact actor and route that broke the rule.
- Run it on every pull request.
A sample fixture
OWASP's regression testing sheet shows a YAML shape like this:
policies:
- resource: "/api/invoices/{id}"
method: "GET"
owner_role: "tenant_user"
allowed_roles: ["tenant_admin", "system_auditor"]
denied_roles: ["anonymous", "different_tenant_user"]
expected_denial_code: 403
The older sheet uses XML with roles, services and a section of test payloads, and a Java integration test that reads it. The format does not matter. What matters is that the matrix is the single input.
Patterns to prioritize
The regression sheet names three failure patterns that degrade over time.
| Pattern | What to test |
|---|---|
| Horizontal escalation | Same role, different account tries to read, update, delete |
| Vertical escalation | Every lower role tries admin routes |
| Tenant boundary | Tenant B calls broad reads and sees none of tenant A's data |
For the tenant test, even one leaked record identifier counts as a critical failure.
Tie it to the API contract
If you publish an OpenAPI file, use its security fields as a starting point and test them against your policy. The sheet adds a caution: a tool that probes missing or invalid credentials does not prove scope enforcement, so add explicit tests with otherwise valid tokens that lack the required scope, plus expired tokens.
Keep it fast and visible
- Swap tokens in the Authorization header instead of running a full login for every case.
- Tag the suite, for example a dedicated authz marker or npm script, so developers can run it locally.
- Make it a required check so a failing test blocks the merge.
- Watch for unusual counts of 401 and 403 in lower environments, which can point at a change colliding with existing rules.
Try it yourself
Click through it at your own pace, or open it full screen. Look at how a failing check holds a change, which is how a failing matrix test should behave.
Safeguard supports policies and gates on pull requests, along with SBOM and dependency scanning for the test and auth libraries you rely on. Try the free scan at gold.safeguard.sh/scan.
Quick check
Should the matrix include denied cases?
Yes. Denied cells are the point. A suite of only allowed cases proves the happy path and nothing about the boundary.
Who owns the matrix file?
Product and engineering together. Review changes to it like a policy change, since editing a cell edits who can access what.
Sources
- OWASP Authorization Testing Automation Cheat Sheet
- OWASP Authorization Regression Testing Cheat Sheet
Last reviewed: 19 October 2026.