Generate Policy Skill
Help users validate container images with Conforma policies. The user doesn't need to know anything about Rego, Conforma, SBOMs, or policy configuration - this skill handles everything.
What the User Provides
| Input | Description | Example |
|---|---|---|
| Policy set name | Descriptive name for the policy set | release_policies |
| Image reference | Container image to validate (optional) | quay.io/org/app@sha256:abc123... |
| Public key | Cosign public key file (optional) | cosign.pub |
| Validation requirements | What they want to check | "Ensure npm packages come from registry.npmjs.org" |
See Policy Requirements Template for a complete requirements checklist.
What to Generate
When a user requests policy validation, generate these artifacts:
| Artifact | Description |
|---|---|
Policy rule (.rego) | Standalone Rego v1 rule |
Test file (_test.rego) | Comprehensive tests for the rule |
Policy config (policy.yaml) | Conforma configuration referencing data files |
Data file (data/*.yaml) | Rule data configuration (e.g., allowed registries) |
| Conforma command | Ready-to-run ec validate image command |
| Instructions | Clear next steps for the user |
Workflow
- •Gather requirements - Ask what the user wants to validate
- •Prompt for required inputs - See domain references for what to ask (e.g.,
allowed_package_sourcesfor SBOM rules) - •Generate artifacts - Create files with directory matching package name (for Rego lint compliance)
- •VERIFY: Run OPA tests - Run
ec opa test ./policy -vand ensure ALL tests pass - •VERIFY: Run EC validation - If image provided, run
ec validate imageand verify expected results - •Report results - Only after verification passes, provide the Conforma command and next steps
⚠️ MANDATORY VERIFICATION STEPS ⚠️
YOU ARE NOT DONE UNTIL THESE STEPS PASS. DO NOT REPORT SUCCESS TO THE USER UNTIL VERIFICATION IS COMPLETE.
Step 1: Run OPA Tests (REQUIRED)
After creating all Rego files, you MUST run the tests:
cd <policy_set_directory> ec opa test ./policy -v --ignore '*.rego' --ignore 'lib/*'
Note: We exclude lib/* because sbom.rego uses ec.oci.blob() which OPA can't parse.
Tests must mock data.lib.sbom.spdx_sboms directly.
Requirements:
- •ALL tests must pass (exit code 0)
- •If tests fail, FIX the code and re-run until they pass
- •DO NOT skip this step or report success if tests fail
Step 2: Run EC Validation (REQUIRED if image provided)
If the user provided an image reference, you MUST validate the policy against it:
cd <policy_set_directory> ec validate image \ --image <IMAGE_REFERENCE> \ --policy policy.yaml \ --public-key cosign.pub \ --ignore-rekor \ --output text \ --info
Requirements:
- •Verify the output matches expectations:
- •If the policy should FAIL (e.g., image from wrong registry), confirm violations appear
- •If the policy should PASS, confirm no violations
- •If results don't match expectations, FIX the policy and re-run
- •DO NOT skip this step or report success if results are unexpected
Step 3: Debug Unexpected Results
If validation doesn't produce expected results:
- •Extract and examine the actual SBOM data from the image
- •Compare SBOM structure to what the policy expects
- •Adjust the policy to match the actual data structure
- •Re-run validation to confirm
- •Repeat until expected results are achieved
Directory Structure
For Rego lint compliance, directory path must match package name. Use a descriptive top-level directory name, with rules organized under a policy/ subdirectory:
<descriptive_name>/ # e.g., release_policies, sbom_validation
├── policy.yaml # Conforma configuration
├── cosign.pub # Public key (if provided)
├── data/ # Rule data files (referenced from policy.yaml)
│ └── <rule_data>.yaml # YAML file with rule_data configuration
└── policy/ # Contains all rule directories
├── lib/ # Shared library (required for SBOM rules)
│ └── sbom.rego # SBOM access library
└── <rule_name>/ # Directory matches package name
├── <rule_name>.rego # package <rule_name>
└── <rule_name>_test.rego # package <rule_name>_test
Example - For release policies with a package_sources rule:
release_policies/
├── policy.yaml
├── cosign.pub
├── data/
│ └── allowed_sources.yaml # rule_data for allowed package sources
└── policy/
├── lib/
│ └── sbom.rego # package lib.sbom
└── package_sources/
├── package_sources.rego # package package_sources
└── package_sources_test.rego # package package_sources_test
SBOM Access Library (Required for SBOM Rules)
IMPORTANT: SBOMs are NOT directly embedded in attestations. In Konflux/RHTAP builds, the SBOM is stored as an OCI blob and referenced from the SLSA provenance attestation. The library fetches these using ec.oci.blob().
For any rule that validates SBOM data, you MUST:
- •
Create the SBOM library at
policy/lib/sbom.regousing the SBOM Library Template - •
Include the lib and data in policy.yaml:
yamlsources: - name: my-rules data: - ./data # Rule data files policy: - ./policy/lib # SBOM access library - ./policy/my_rule - •
Import and use in your rules:
regoimport data.lib.sbom deny contains result if { some s in sbom.spdx_sboms # Fetches from OCI blobs via ec.oci.blob() some pkg in s.packages # validation logic } - •
Mock the SBOM data in tests (CRITICAL):
regotest_my_rule if { sboms := [_mock_spdx_sbom(packages)] count(my_rule.deny) == 1 with data.lib.sbom.spdx_sboms as sboms # MUST mock - bypasses ec.oci.blob() }
How it works:
- •The library uses
ec.oci.blob()to fetch SBOMs from OCI blobs referenced in SLSA provenance - •
ec.oci.blob()is an EC built-in function that OPA doesn't understand - •Tests MUST mock
data.lib.sbom.spdx_sbomsdirectly to bypass the library implementation - •At EC runtime, the library fetches real SBOMs from OCI registries
Testing Note: OPA tests will fail if they try to evaluate sbom.rego directly because OPA can't parse ec.oci.blob(). Always mock data.lib.sbom.spdx_sboms in tests.
DO NOT iterate directly over input.attestations for SBOM data - always use the library.
Conforma Command Template
Run from within the policy set directory (e.g., cd release_policies):
ec validate image \ --image <IMAGE_REFERENCE> \ --policy policy.yaml \ --public-key cosign.pub \ --ignore-rekor \ --output text \ --info
Important: The policy.yaml references rules with relative paths (./policy/<rule_name>), so the command must be run from the policy set directory where policy.yaml is located.
Reference Documents
Core References
- •Rego Patterns Reference - Rego v1 syntax, metadata format, result construction
- •Test Patterns Reference - OPA testing, mock helpers, required test cases
- •Policy Configuration Reference - Policy config structure, ruleData, collections
Domain-Specific References
SBOM Validation
- •SBOM Structure Reference - SPDX/CycloneDX paths, required inputs
- •SPDX 2.3 Schema, CycloneDX 1.5 Schema
Templates
- •Rule Template - Generic Rego v1 rule
- •Test Template - Test file with mock helpers
- •SBOM Rule Template - Package source validation example
- •SBOM Library Template - Standalone SBOM access library (REQUIRED for SBOM rules)
- •Policy Config Template - Conforma policy configuration
- •Rule Data Template - Data file for rule configuration
Quick References
Rego v1 Essentials (SBOM Rule)
package <rule_name>
import rego.v1
import data.lib.sbom # Use the SBOM library
# METADATA
# title: Rule Title
# description: >-
# What the rule checks.
# custom:
# short_name: rule_name
# failure_msg: "Error: %s"
# solution: How to fix.
deny contains result if {
# Use sbom.spdx_sboms or sbom.cyclonedx_sboms (NOT input.attestations)
some s in sbom.spdx_sboms
some pkg in s.packages
# validation logic
result := {
"code": "<rule_name>.rule_name",
"msg": sprintf("Error: %s", [value]),
"severity": "failure",
}
}
Policy Config Essentials
name: <policy_name>
description: <description>
sources:
- name: <source_name>
data:
- ./data # Directory containing rule_data YAML files
policy:
- ./policy/lib # SBOM library (required for SBOM rules)
- ./policy/<rule_name> # Reference each rule directory under policy/
config:
include:
- "<rule_name>.short_name"
Data File Format
Rule data is stored in separate YAML files under the data/ directory:
# data/allowed_sources.yaml
rule_data:
allowed_package_sources:
- "^https://registry\\.npmjs\\.org/"
- "^https://proxy\\.golang\\.org/"
The data is accessed in Rego rules via data.rule_data:
allowed := object.get(data.rule_data, "allowed_package_sources", [])
SBOM Quick Reference
Access SBOMs (use lib.sbom - handles OCI blob fetching):
import data.lib.sbom # SPDX SBOMs some s in sbom.spdx_sboms some pkg in s.packages # CycloneDX SBOMs some s in sbom.cyclonedx_sboms some component in s.components
SPDX Structure:
- •Packages:
s.packages - •PURL:
pkg.externalRefs[].referenceLocatorwherereferenceType == "purl" - •License:
pkg.licenseDeclared
CycloneDX Structure:
- •Components:
s.components - •PURL:
component.purl - •Distribution URL:
component.externalReferences[].urlwheretype == "distribution"