Denied Docs

Testing Policies

Test and debug your authorization policies

Testing is essential for building reliable authorization policies. A policy that allows too much creates security vulnerabilities. A policy that denies too much breaks your application. This guide covers how to thoroughly test your policies using the Denied Platform Playground.

Policy Playground

The Policy Playground lets you test policies against your configured decision node without writing any application code. It's the fastest way to validate that your policies work as expected.

Accessing the Playground

  1. Go to GovernancePlayground
  2. Ensure you have the correct project selected
  3. Verify your decision node is configured and active

The Playground requires a running decision node. If you see connection errors, check that your decision node URL is configured in project settings and the service is accessible.

Playground Interface

The Playground has three main sections:

SectionPurpose
SubjectDefine who is making the request (user, agent, service)
ResourceDefine what they want to access
ActionSpecify the operation being attempted

Plus additional properties you can add to any section.

Constructing Test Requests

Subject Configuration

Enter details about the requesting entity:

Required fields:

  • Type: The subject's type (e.g. user, agent, service)
  • ID: The subject's unique identifier scoped to the type (e.g. john.doe, hr-assistant)

Adding properties: Click "Add Property" to include additional fields your policies use:

KeyValue
roleadmin
departmentengineering
verifiedtrue

Resource Configuration

Define the resource being accessed:

Optional fields:

  • Type: The resource's type (e.g. document, api, database)
  • ID: The resource's unique identifier scoped to the type (e.g. doc123)

Adding properties: Include fields your policies check:

KeyValue
owneruser://john.doe
typedocument
visibilitypublic
classificationinternal

Action

Specify what operation is being attempted:

read
write
delete
approve
export

Property Value Types

The Playground automatically parses property values:

InputParsed As
helloString: "hello"
123Number: 123
true / falseBoolean
{"key": "value"}JSON object
["a", "b"]JSON array

Running Tests

Basic Test Flow

  1. Fill in the subject, resource, and action
  2. Add any necessary properties
  3. Click Evaluate
  4. Review the result

Understanding Results

Allowed Result:

Result: ALLOWED
Matched Rules: allow-own-resources, admin-access

This means:

  • The request was allowed
  • Two policies contributed to the allow decision
  • Either rule matching would have allowed the request

Denied Result:

Result: DENIED
Matched Rules: (none)

This means:

  • No allow policies matched the request
  • Access was denied by default (secure by default)

Request History

The Playground maintains a history of your test requests. Use this to:

  • Compare results across different inputs
  • Quickly re-run previous tests
  • Track what you've already tested

Test Case Design

Positive Tests

Test cases where access should be allowed:

Test: Admin can delete any resource
Subject: { role: "admin" }
Action: delete
Resource: { owner: "someone-else" }
Expected: ALLOWED
Test: Owner can read their resource
Subject: { id: "user-123" }
Action: read
Resource: { owner: "user-123" }
Expected: ALLOWED

Negative Tests

Test cases where access should be denied:

Test: Viewer cannot delete
Subject: { role: "viewer" }
Action: delete
Resource: { type: "document" }
Expected: DENIED
Test: Non-owner cannot modify
Subject: { id: "user-456" }
Action: update
Resource: { owner: "user-123" }
Expected: DENIED

Edge Cases

Test boundary conditions and unusual inputs:

Test: Missing role field
Subject: { id: "user-123" }  // no role
Action: read
Resource: { visibility: "public" }
Expected: Check policy handles missing role
Test: Empty roles array
Subject: { id: "user-123", roles: [] }
Action: read
Resource: { type: "document" }
Expected: DENIED (no roles = no permissions)
Test: Null values
Subject: { id: "user-123", role: null }
Action: read
Resource: { owner: null }
Expected: Policy should handle gracefully

Security Tests

Attempt to bypass your policies:

Test: Cross-tenant access attempt
Subject: { id: "user-1", org_id: "org-A" }
Action: read
Resource: { id: "secret-doc", org_id: "org-B" }
Expected: DENIED (different organizations)
Test: Privilege escalation
Subject: { id: "user-1", role: "viewer", self_reported_role: "admin" }
Action: delete
Resource: { type: "user" }
Expected: DENIED (self-reported role shouldn't grant access)

Test Templates

Using Test Case Templates

If your policies have associated test templates:

  1. Click on the Test Templates section
  2. Select a template from your active policies
  3. The form auto-fills with the template values
  4. Click Evaluate

Templates help ensure consistent testing across your team.

Creating Test Templates

When creating policies, document expected test cases:

Policy: allow-team-resources

Test Cases:
1. Team member accessing team resource → Allow
   Subject: { team_id: "team-1" }
   Resource: { team_id: "team-1" }

2. Non-member accessing team resource → Deny
   Subject: { team_id: "team-2" }
   Resource: { team_id: "team-1" }

3. User with no team → Deny
   Subject: { id: "user-1" }  // no team_id
   Resource: { team_id: "team-1" }

Debugging Strategies

Policy Not Matching?

If your policy isn't allowing when expected:

  1. Check field names — Typos are common

    # Wrong
    input.subjct.properties.role
    
    # Right
    input.subject.properties.role
  2. Verify data types — String vs number matters

    # If input.subject.properties.age is "25" (string), this won't match
    input.subject.properties.age >= 18
    
    # Need to ensure it's a number
    to_number(input.subject.properties.age) >= 18
  3. Check for missing fields — Add logging

    print("subject:", input.subject)
    print("has role:", input.subject.properties.role)
    input.subject.properties.role == "admin"
  4. Simplify and isolate — Start with a condition that should always match

    # Temporary: Always match to test connectivity
    true

Unexpected Allows?

If access is granted when it shouldn't be:

  1. Check for overly broad conditions

    # Too broad - matches if subject has any user_id!
    input.subject.properties.user_id
  2. Review all allow policies — Any one can grant access

  3. Look for OR logic bugs — Multiple policies create OR logic

    # Policy 1: Allows ALL reads
    input.action.name == "read"
    
    # Policy 2: Admin access
    input.subject.properties.role == "admin"
    
    # Problem: Policy 1 allows ALL reads, regardless of role
    # Solution: Add more conditions to Policy 1
  4. Check deny policies — Make sure deny policies are enabled and have correct conditions

Common Mistakes

MistakeProblemFix
= vs ==Assignment vs comparisonUse == for comparisons
Wrong property pathField not foundUse input.subject.properties.role not input.subject.role
AND vs OR confusionLogic errorConditions in same policy = AND, multiple policies = OR
Type mismatchSilent failureEnsure consistent types
Missing .propertiesField not foundAlways access properties via .properties

Systematic Testing Approach

1. Identify Policy Rules

List all the rules in your policy:

  • Rule 1: Admins can do anything
  • Rule 2: Owners can access their resources
  • Rule 3: Public resources are readable

2. Create Test Matrix

For each rule, test:

RuleShould AllowShould Deny
Adminadmin + any actionnon-admin + admin-only action
Ownerowner + their resourcenon-owner + private resource
Publicanyone + read + publicanyone + write + public

3. Test Interactions

When rules combine:

  • Admin who is also owner
  • Owner of public resource
  • Non-admin, non-owner, public resource

4. Test Failure Modes

What happens when:

  • Decision node is unreachable
  • Policy has syntax errors
  • Input is malformed

Monitoring Production

After deploying policies, monitor real decisions:

Logs

Go to MonitoringLogs to:

  • View individual decisions
  • Filter by effect (allow/deny)
  • Search for specific subjects or resources
  • Investigate denied requests

If you see many denials for a legitimate use case, you may need to add a new allow rule. If you see unexpected allows, investigate for overly permissive policies.

Best Practices

Test Early and Often

  • Test while writing policies, not after
  • Run tests after every change
  • Keep a suite of regression tests

Test the Default

Always verify that your default deny works:

Test: Completely unknown request
Subject: { random: "data" }
Action: unknown
Resource: { something: "else" }
Expected: DENIED (default deny)

On this page