Validating and testing policies

Validating policies

You can use the Cerbos compiler to make sure that your policies are valid before pushing them to a production Cerbos instance. We recommend setting up a git hook or a CI step to run the Cerbos compiler before you push any policy changes to production.

docker run -i -t -v /path/to/policy/dir:/policies ghcr.io/cerbos/cerbos:0.24.0-prerelease compile /policies

Testing policies

You can write optional tests for policies and run them as part of the compilation stage to make sure that the policies do exactly what you expect.

Tests are defined using the familiar YAML format as well. Make sure that your tests are in a separate directory from the policies to avoid confusion. We recommend storing them in a top-level directory named tests. A test file must have _test suffix in the name and one of the following file extensions: 'yaml', 'yml', or 'json'. For example, album_test.yml, album_test.yaml or album_test.json.

Test suite definition
---
name: AlbumObjectTestSuite (1)
description: Tests for verifying the album:object resource policy (2)
options:
  now: "2022-08-02T15:00:00Z" (3)
principals: (4)
  alicia:
    id: aliciaID
    roles:
      - user
  bradley:
    id: bradleyID
    roles:
      - user
resources: (5)
  alicia_album:
    id: XX125
    kind: album:object
    attr:
      owner: aliciaID
      public: false
      flagged: false
  bradley_album:
    id: XX250
    kind: album:object
    attr:
      owner: bradleyID
      public: false
      flagged: false
auxData: (6)
  validJWT:
    jwt:
      iss: my.domain
      aud: ["x", "y"]
      myField: value
tests: (7)
  - name: Accessing an album (8)
    options:
      now: "2022-08-03T15:00:00Z" (9)
    input: (10)
      principals: (11)
        - alicia
        - bradley
      resources: (12)
        - alicia_album
        - bradley_album
      actions: (13)
        - view
        - delete
      auxData: validJWT (14)
    expected: (15)
      - principal: alicia (16)
        resource: alicia_album (17)
        actions: (18)
          view: EFFECT_ALLOW
          delete: EFFECT_ALLOW
      - principal: bradley
        resource: bradley_album
        actions:
          view: EFFECT_ALLOW
          delete: EFFECT_ALLOW
1 Name of the test suite
2 Description of the test suite
3 Optional RFC3339 timestamp to be used as the return value of the now function. Applies to all tests in the suite unless overridden locally.
4 Map of principal fixtures. The key is a string that can be used to refer to the associated principal.
5 Map of resource fixtures. The key is a string that can be used to refer to the associated resource.
6 Map of (optional) auxiliary data fixtures required to evaluate some requests. The key is a string that can be used to refer to the associated auxData.
7 List of tests in this suite
8 Name of the test
9 Optional RFC3339 timestamp to be used as the return value of the now function. Applies to this test only and takes precedence over the global suite option.
10 Input to the policy engine
11 List of keys of principal fixtures to test
12 List of keys of resource fixtures to test
13 List of actions to test
14 Key of auxiliary data fixture to test (optional)
15 List of outcomes expected for each principal and resource. If a principal+resource pair specified in input is not listed in expected, then EFFECT_DENY is expected for all actions for that pair.
16 Key of the principal fixture under test
17 Key of the resource fixture under test
18 Expected outcomes for each action for the principal+resource pair

Sharing test fixtures

It is possible to share principals, resources and auxData blocks between test suites stored in the same directory. Create a testdata directory in the directory containing your test suite files, then define shared resources, principals and auxData in testdata/resources.yml, testdata/principals.yml, testdata/auxdata.yml respectively (yaml and json extensions are also supported).

tests
├── album_object_test.yaml
├── gallery_object_test.yaml
├── slideshow_object_test.yaml
└── testdata
   ├── auxdata.yaml
   ├── principals.yaml
   └── resources.yaml
An example of testdata/principals.yml
---
principals:
  john:
    id: johnID
    roles:
      - user
      - moderator
An example of testdata/resources.yml
---
resources:
  alicia_album:
    id: XX125
    kind: "album:object"
    attr:
      owner: aliciaID
      public: false
      flagged: false
An example of testdata/auxdata.yml
---
auxData:
  validJWT:
    jwt:
      iss: my.domain
      aud: ["x", "y"]
      myField: value

YAML anchors and overrides are a great way to reduce repetition and reuse definitions in test cases.

For example, the following definitions are equivalent:

Without anchors and overrides With anchors and overrides
resources:
  alicias_album1:
    id: "XX125"
    kind: "album:object"
    attr:
      owner: "alicia"
      public: false
      flagged: false

  alicias_album2:
    id: "XX525"
    kind: "album:object"
    attr:
      owner: "alicia"
      public: false
      flagged: false

  alicias_album3:
    id: "XX925"
    kind: "album:object"
    attr:
      owner: "alicia"
      public: false
      flagged: false
resources:
  alicias_album1:
    id: "XX125"
    kind: "album:object"
    attr: &alicia_album_attr
      owner: "alicia"
      public: false
      flagged: false

  alicias_album2:
    id: "XX525"
    kind: "album:object"
    attr:
      <<: *alicia_album_attr

  alicias_album3:
    id: "XX925"
    kind: "album:object"
    attr:
      <<: *alicia_album_attr

Running tests

To run the tests, provide the path to the tests directory using the --tests flag.

docker run -i -t \
    -v /path/to/policy/dir:/policies \
    -v /path/to/test/dir:/tests \
    ghcr.io/cerbos/cerbos:0.24.0-prerelease compile --tests=/tests /policies

Machine readable output can be produced by passing --format=json flag to the command.

By default, all discovered tests are run. To run just some of the tests, provide a regular expression that matches the test using the --run flag.

Example: Running only tests that contain 'Delete' in the name
docker run -i -t \
    -v /path/to/policy/dir:/policies \
    -v /path/to/test/dir:/tests \
    ghcr.io/cerbos/cerbos:0.24.0-prerelease compile --tests=/tests --run=Delete /policies

You can also skip entire suites or individual tests in a suite by adding skip: true to the test definition.

Example: Skipping a test
---
name: AlbumObjectTestSuite
description: Tests for verifying the album:object resource policy
tests:
  - name: View private album
    skip: true
    skipReason: "Policy under review"
    input:
      principals: ["alicia"]
      resources: ["alicia_private_album"]
      actions: ["view"]
    expected:
      - principal: alicia
        resource: alicia_private_album
        actions:
          view: EFFECT_ALLOW

Validating and testing policies in CI environments

Because Cerbos artefacts are distributed as self-contained containers and binaries, you should be able to easily integrate Cerbos into any CI environment. Simply configure your workflow to execute the commands described in the sections above using either the Cerbos container (you may need to configure mount points to suit your repo structure) or the binary.

GitHub Actions

Example workflow
---
name: PR Check
on:
  pull_request:
    branches:
      - main
jobs:
  cerbosCheck:
    name: Check Cerbos policies
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Setup Cerbos
        uses: cerbos/cerbos-setup-action@v1
        with:
          version: latest

      - name: Compile and test policies
        uses: cerbos/cerbos-compile-action@v1
        with:
          policyDir: policies
          testDir: tests

See https://github.com/cerbos/photo-share-tutorial for an example of Cerbos GitHub Actions being used in a workflow.

GitLab CI

Example pipeline
---
stages:
  - prepare
  - compile

download-cerbos:
  stage: prepare
  script:
    - curl https://github.com/cerbos/cerbos/releases/download/v0.24.0-prerelease/cerbos_0.24.0-prerelease_Linux_x86_64.tar.gz -L --output /tmp/cerbos.tar.gz
    - tar -xf /tmp/cerbos.tar.gz -C ./
    - chmod +x ./cerbos
  artifacts:
    paths:
      - cerbos

compile-job:
  stage: compile
  dependencies: ["download-cerbos"]
  script:
    - ./cerbos compile ./policies --tests ./tests