Skip to content

OPA/WASM Rules

OPA (Open Policy Agent) rules use the Rego language compiled to WebAssembly for complex policy logic that goes beyond simple pattern matching.

Structure

OPA rules require two files:

  1. Rego policy (.rego) - Rule logic
  2. Metadata (.wasm.json) - Rule information

Real example

Rego policy (docker-security.rego)

rego
package audit.docker

# Detect containers running as root
deny[msg] if {
    input.file_type == "dockerfile"
    node := input.nodes[_]
    node.path == "USER"
    contains(node.value, "root")
    msg := "Container runs as root user - use a non-root user for security"
}

# Detect use of 'latest' tag in FROM
deny[msg] if {
    input.file_type == "dockerfile"
    node := input.nodes[_]
    node.path == "FROM"
    contains(node.value, ":latest")
    msg := "Using 'latest' tag is not recommended - specify exact version"
}

# Detect use of ADD instead of COPY
deny[msg] if {
    input.file_type == "dockerfile"
    node := input.nodes[_]
    node.path == "ADD"
    msg := "Use COPY instead of ADD for local files"
}

# Detect privileged containers
deny[msg] if {
    input.file_type == "dockerfile"
    node := input.nodes[_]
    node.path == "RUN"
    contains(node.value, "sudo")
    msg := "Avoid using sudo in containers - not needed and security risk"
}

Metadata (docker-security.wasm.json)

json
{
  "id": "opa.docker.security",
  "severity": "HIGH",
  "category": "docker",
  "message": "Docker security violations detected",
  "entrypoint": "data.audit.docker.deny",
  "remediation": "Review Docker security best practices",
  "description": "Detects common Docker security issues: root user, latest tags, ADD usage, and sudo in containers"
}

Creation process

1. Write the Rego policy

rego
package audit.security

deny[msg] if {
    input.file_type == "dockerfile"
    node := input.nodes[_]
    node.path == "USER"
    contains(node.value, "root")
    msg := "Container runs as root user"
}

2. Compile to WebAssembly

bash
opa build -t wasm -e data.audit.security.deny policy.rego

3. Create metadata

json
{
  "id": "opa.docker.no-root",
  "severity": "HIGH",
  "category": "docker",
  "message": "Container runs as root user",
  "entrypoint": "data.audit.security.deny"
}

Rego syntax

Basic structure

rego
package audit.category

deny[msg] if {
    # Conditions
    condition1
    condition2
    msg := "Error message"
}

Data access

rego
# Access input properties
input.file_type == "dockerfile"
input.nodes[_].path == "USER"
input.nodes[_].value

# Iterate arrays
node := input.nodes[_]

Useful functions

rego
# Contains text
contains(node.value, "root")

# Starts with
startswith(node.value, "FROM")

# Ends with
endswith(node.value, ":latest")

Metadata fields

FieldDescription
idUnique identifier of the rule
severityLevel: CRITICAL, HIGH, MEDIUM, LOW
categoryLanguage/technology category
messageMain rule message
entrypointPolicy entry point (data.package.rule)
remediationSuggested remediation
descriptionDetailed description

Use cases

Docker validation

rego
package audit.docker

deny[msg] if {
    input.file_type == "dockerfile"
    node := input.nodes[_]
    node.path == "FROM"
    contains(node.value, ":latest")
    msg := "Using 'latest' tag is not recommended"
}

Configuration validation

rego
package audit.config

deny[msg] if {
    input.file_type == "yaml"
    node := input.nodes[_]
    node.path == "database.password"
    node.value == "admin"
    msg := "Default password detected"
}

Complex validation

rego
package audit.complex

deny[msg] if {
    input.file_type == "dockerfile"
    
    # Ensure it does not use root
    user_node := input.nodes[_]
    user_node.path == "USER"
    contains(user_node.value, "root")
    
    # And that it does not use latest
    from_node := input.nodes[_]
    from_node.path == "FROM"
    contains(from_node.value, ":latest")
    
    msg := "Container has multiple security issues"
}

Advantages

Perfect for:

  • Complex policy logic
  • Validation of multiple conditions
  • Relationship analysis
  • Reusable policies
  • Advanced conditional logic

Limitations:

  • Steep learning curve
  • Slower performance
  • Requires compilation
  • More complex to maintain

Differences from other types

AspectYAMLSemgrepOPA
ComplexityLowMediumHigh
LogicNoLimitedFull
PerformanceHighMediumLow
FlexibilityLowHighVery High
MaintenanceEasyMediumHard

When to use OPA

✅ Use OPA when:

  • You need complex logic
  • There are multiple conditions
  • You want reusable policies
  • You need relationship analysis
  • You have specific business rules

❌ Do not use OPA when:

  • You only need simple matching
  • You want maximum performance
  • You have basic patterns
  • You are not familiar with Rego

Next steps

RootCause - Modular Static Analysis Engine