Skip to content

Instantly share code, notes, and snippets.

@migmartri
Created September 16, 2025 12:27
Show Gist options
  • Select an option

  • Save migmartri/7694ce5cfd979762654705fbe02e21b9 to your computer and use it in GitHub Desktop.

Select an option

Save migmartri/7694ce5cfd979762654705fbe02e21b9 to your computer and use it in GitHub Desktop.
Claude example for policy authoring

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Chainloop Policy Development

This repository contains built-in policies for Chainloop. Use the chainloop policy devel CLI tool for policy development workflows.

Creating a New Policy in This Repository

  1. Create policy directory: Create a new directory for your policy:

    mkdir policy-name
    cd policy-name
  2. Initialize policy: Run the init command inside the directory:

    chainloop policy develop init --name policy-name --description "Policy description"
  3. Rename YAML file: The init command creates a YAML file named after the policy. Rename it to policy.yaml:

    mv policy-name.yaml policy.yaml

Policy Development Commands

Lint Policy

chainloop policy develop lint --policy policy.yaml --format

Validates YAML structure, Rego code, enforces best practices, and formats the code.

Evaluate Policy

chainloop policy develop eval \
  --policy policy.yaml \
  --material sample.json \
  --kind MATERIAL_TYPE

Key flags:

  • --debug: Show detailed evaluation output with complete input data and execution path results
  • --material: Input file to test against
  • --kind: Specify material type (e.g., SBOM_CYCLONEDX_JSON, SARIF)

Debug Mode Deep Dive

The --debug flag provides comprehensive evaluation information including:

  1. Complete Input Structure: Shows the full input data passed to the policy evaluation:

    {
      "args": {
        "threshold": "80",
        "counter": "INSTRUCTION"
      },
      "chainloop_metadata": {
        "annotations": {...},
        "content": "base64-encoded-content",
        "digest": {...},
        "name": "material-filename"
      },
      // ... processed input data
    }
  2. Execution Path Results: Detailed results from each execution path that was evaluated:

    {
      "raw_results": [
        {
          "data.policy_package": {
            "result": {
              "violations": [...],
              "skip_reason": "...",
              "skipped": false
            },
            // ... internal policy variables and calculations
          }
        }
      ]
    }
  3. Skip Reasons: When execution paths are skipped, debug mode shows exactly why:

    • "SARIF CWE taxonomy only supported for SpotBugs, KICS and Zap"
    • "No matching execution path"

Usage Examples:

# Debug basic evaluation
chainloop policy develop eval --policy policy.yaml --material sample.json --kind SARIF --debug

# Debug with parameters
chainloop policy develop eval --policy policy.yaml --material jacoco.xml --kind JACOCO_XML threshold=80 --debug

# Debug to understand why a policy is being skipped
chainloop policy develop eval --policy policy.yaml --material unsupported.json --kind SARIF --debug

Policy Structure

Each policy lives in its own directory with this structure:

./policy-name/
├── policy.yaml          # Policy configuration (required)
└── policy-name.rego     # Rego implementation (optional)

Note: Unit tests (*_test.rego files) are not necessary and should not be created. All testing is done through integration tests.

Policy YAML Configuration

Basic structure:

apiVersion: workflowcontract.chainloop.dev/v1
kind: Policy
metadata:
  name: policy-name
  description: Policy description
  annotations:
    category: security,compliance
spec:
  policies:
    - kind: SBOM_CYCLONEDX_JSON
      path: policy-name.rego
    - kind: SARIF
      path: policy-name-sarif.rego

Adding Parameters

Define input parameters for flexible policies:

spec:
  inputs:
    - name: threshold
      description: Maximum allowed threshold
      required: true
    - name: severity
      description: Minimum severity level
      required: false
  policies:
    - kind: SBOM_CYCLONEDX_JSON
      path: policy-name.rego

Access parameters in Rego with input.args.threshold and input.args.severity.

Multiple Execution Paths

Support different material types with separate Rego files:

spec:
  policies:
    - kind: SBOM_CYCLONEDX_JSON
      path: cves-cyclonedx.rego
    - kind: CSAF_SECURITY_ADVISORY
      path: cves-csaf-sa.rego
    - kind: SARIF
      path: cves-sarif.rego

CRITICAL: When creating multiple Rego files for the same policy, each file must use a unique package name to avoid OPA conflicts:

// sast-polaris-sarif.rego
package sast

// sast-gitlab-sonarqube.rego
package sast_gitlab_sonarqube

Package naming conventions:

  • Use descriptive names like policy_format or policy_format_tool
  • Examples: vulns_bd, sarif_vulns, sast_gitlab_sonarqube
  • Never use the same package name across different Rego files in the same policy directory

Testing Policies

All testing is done through integration tests only. Unit tests are not required.

Integration Testing

Run the comprehensive integration test suite:

./integration-tests/run_tests.sh

Options:

  • --update-golden: Update golden files with current CLI output (use this when creating new tests)
  • --debug: Show the eval commands being executed during test runs

Adding Integration Tests

The integration test system uses three components:

  1. Test Materials (integration-tests/materials/): Sample input files to test against
  2. Expected Outputs (integration-tests/outputs/): Golden files with expected policy evaluation results
  3. Test Definitions: Array entries in run_tests.sh

Test Definition Format

Tests are defined as array entries in run_tests.sh:

HAPPY_TESTS=(
  policy-name MATERIAL_TYPE materials/input.json outputs/expected-output.json "parameters"
)

Elements:

  • policy-name: Name of the policy directory
  • MATERIAL_TYPE: Type of material (SBOM_CYCLONEDX_JSON, SARIF, etc.)
  • materials/input.json: Input file to evaluate
  • outputs/expected-output.json: Expected evaluation result
  • "parameters": Optional policy parameters (e.g., "threshold=80")

Creating New Tests

  1. Add or reuse test material: Place sample input file in integration-tests/materials/ or reuse existing materials
  2. Add test definition: Add entry to HAPPY_TESTS array in run_tests.sh
  3. Generate expected output: Run with --update-golden to create the golden file:
    ./integration-tests/run_tests.sh --update-golden
  4. Verify tests pass: CRITICAL - Always run tests without --update-golden to actually test them:
    ./integration-tests/run_tests.sh
    Note: --update-golden updates files instead of testing them. You must run without this flag to verify tests actually pass.

Note: Materials and outputs can be reused across multiple policies. The same input material can test different policies, and the same expected output can be shared when policies should produce identical results.

Test Types

  • HAPPY_TESTS: Expected to pass (exit code 0)
  • UNHAPPY_TESTS: Expected to fail or have no matching execution path

TDD Development Environment

For effective policy development, you can set up a Test-Driven Development environment:

Setting Up Development Tests

  1. Add example files: Place your sample input files in ../integration-tests/materials/

    # Add your test material
    cp my-sample.json ../integration-tests/materials/
  2. Add test definition: Edit ../integration-tests/run_tests.sh and add your test to the HAPPY_TESTS array:

    HAPPY_TESTS=(
      # ... existing tests ...
      policy-name MATERIAL_TYPE materials/my-sample.json outputs/expected-output.json "parameters"
    )
  3. Generate initial output: Use --update-golden to create the expected output file:

    cd ../integration-tests
    ./run_tests.sh --update-golden
  4. Run your specific test: Filter tests during development:

    ./run_tests.sh --debug | grep "policy-name"

TDD Development Loop

  1. Write/modify policy logic in your Rego file
  2. Lint and format: chainloop policy develop lint --policy policy.yaml --format
  3. Test individual samples: chainloop policy develop eval --kind TYPE --material ../integration-tests/materials/sample.json --policy policy.yaml --debug
  4. Run integration tests: ../integration-tests/run_tests.sh (without --update-golden to actually test!)
  5. Update expected outputs if needed: ../integration-tests/run_tests.sh --update-golden
  6. Verify tests pass again: ../integration-tests/run_tests.sh (critical step to ensure golden files are correct)
  7. Iterate until tests pass

This allows rapid iteration and ensures your policy works correctly with real data.

Development Workflow

  1. Create new policy directory: mkdir policy-name && cd policy-name
  2. Initialize policy with chainloop policy develop init --name policy-name --description "..."
  3. Rename YAML file: mv policy-name.yaml policy.yaml
  4. Set up development tests (see TDD section above)
  5. Implement Rego logic with TDD loop
  6. Final validation with chainloop policy develop lint --policy policy.yaml --format
  7. CRITICAL: Verify all tests pass with ../integration-tests/run_tests.sh (without --update-golden!)

Important Testing Notes:

  • Always run ./run_tests.sh (without flags) to actually test your implementation
  • Only use --update-golden when you need to create/update expected output files
  • After using --update-golden, always run tests again without it to verify correctness
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment