Skip to main content
Experimental Feature - WASM Policy EngineThese examples are specifically for writing WASM policies using Chainloop’s experimental WASM policy engine. The WASM policy engine is NOT the default policy engine in Chainloop.
  • Default engine: Rego-based (recommended for most users)
  • WASM engine: Experimental alternative for Go/JavaScript policies
  • Currently supported languages: Go and JavaScript only
  • Status: Experimental preview - APIs may change in future releases
For the default Rego-based policy engine, see Writing Custom Policies.

Common Patterns

Required Fields Validation

Ensure critical fields are present and non-empty.
func validateRequiredFields(comp Component) chainloop.Result {
    result := chainloop.Success()

    if comp.Name == "" {
        result.AddViolation("name is required")
    }
    if comp.Version == "" {
        result.AddViolation("version is required")
    }

    return result
}

Allowlist/Blocklist Validation

Check values against approved or forbidden lists.
func validateLicense(license string, approved, forbidden []string) chainloop.Result {
    result := chainloop.Success()

    // Check blocklist first
    for _, blocked := range forbidden {
        if license == blocked {
            result.AddViolationf("%s is forbidden", license)
            return result
        }
    }

    // Check allowlist
    if len(approved) > 0 {
        found := false
        for _, allowed := range approved {
            if license == allowed {
                found = true
                break
            }
        }
        if !found {
            result.AddViolationf("%s not approved", license)
        }
    }

    return result
}

Nested Structure Validation

Validate arrays of objects (SBOM components, attestation subjects).
func validateComponents(components []Component) chainloop.Result {
    result := chainloop.Success()

    if len(components) == 0 {
        result.AddViolation("must contain at least one component")
    }

    for i, comp := range components {
        if comp.Name == "" {
            result.AddViolationf("component %d missing name", i)
        }
        if comp.Version == "" {
            result.AddViolationf("component %d missing version", i)
        }
    }

    return result
}

External API Validation

Verify data against external sources.
func validateWithAPI(name, version string) chainloop.Result {
    url := fmt.Sprintf("https://registry.npmjs.org/%s", name)

    var data RegistryResponse
    err := chainloop.HTTPGetJSON(url, &data)
    if err != nil {
        return chainloop.Skipf("API unavailable: %v", err)
    }

    result := chainloop.Success()
    if _, exists := data.Versions[version]; !exists {
        result.AddViolationf("version %s not found", version)
    }

    return result
}
Important HTTP Request Notes:
  • Always use skip() when external APIs are unavailable - don’t fail policies due to network issues
  • All domains are blocked by default except www.chainloop.dev and www.cisa.gov
  • Use --allowed-hostnames flag to allowlist domains: --allowed-hostnames registry.npmjs.org

Complete Examples

SBOM License Validation

Validate component licenses in a CycloneDX SBOM.
package main

import (
    "fmt"
    chainloop "github.com/chainloop-dev/chainloop/labs/wasm-policy-sdk/go"
    "github.com/extism/go-pdk"
)

type SBOM struct {
    Components []Component `json:"components"`
}

type Component struct {
    Name     string    `json:"name"`
    Version  string    `json:"version"`
    Licenses []License `json:"licenses"`
}

type License struct {
    ID string `json:"id"`
}

//export Execute
func Execute() int32 {
    return chainloop.Run(validate)
}

func validate() error {
    var sbom SBOM
    if err := chainloop.GetMaterialJSON(&sbom); err != nil {
        return chainloop.Skip("Not a valid CycloneDX SBOM")
    }

    approved := []string{"MIT", "Apache-2.0", "BSD-3-Clause"}
    forbidden := []string{"GPL-3.0", "AGPL-3.0"}

    result := chainloop.Success()

    for _, comp := range sbom.Components {
        for _, lic := range comp.Licenses {
            // Check forbidden
            for _, f := range forbidden {
                if lic.ID == f {
                    result.AddViolationf("component '%s' uses forbidden license: %s",
                        comp.Name, lic.ID)
                }
            }

            // Check approved
            approved := false
            for _, a := range approved {
                if lic.ID == a {
                    approved = true
                }
            }
            if !approved {
                result.AddViolationf("component '%s' license not approved: %s",
                    comp.Name, lic.ID)
            }
        }
    }

    return chainloop.OutputResult(result)
}

func main() {}
Test data (sbom.json):
{
  "components": [
    {
      "name": "lodash",
      "version": "4.17.21",
      "licenses": [{ "id": "MIT" }]
    },
    {
      "name": "gpl-package",
      "version": "1.0.0",
      "licenses": [{ "id": "GPL-3.0" }]
    }
  ]
}
Test:
chainloop policy devel eval \
  --policy policy.yaml \
  --material sbom.json \
  --kind SBOM_CYCLONEDX_JSON

Attestation Signature Validation

Validate in-toto attestation has git commit subjects.
package main

import (
    chainloop "github.com/chainloop-dev/chainloop/labs/wasm-policy-sdk/go"
    "github.com/extism/go-pdk"
)

type Attestation struct {
    Subject []Subject `json:"subject"`
}

type Subject struct {
    Name string `json:"name"`
}

//export Execute
func Execute() int32 {
    return chainloop.Run(validate)
}

func validate() error {
    var att Attestation
    if err := chainloop.GetMaterialJSON(&att); err != nil {
        return chainloop.Skip("Not a valid attestation")
    }

    result := chainloop.Success()

    if len(att.Subject) == 0 {
        result.AddViolation("attestation must have at least one subject")
    }

    for _, subj := range att.Subject {
        if subj.Name == "" {
            result.AddViolation("subject missing name field")
        }
    }

    return chainloop.OutputResult(result)
}

func main() {}

HTTP API Integration

Verify package version exists in npm registry.
Domain Allowlisting RequiredBy default, all HTTP requests are blocked except for:
  • www.chainloop.dev
  • www.cisa.gov
You must explicitly allowlist any domains your policy needs to access using the --allowed-hostnames flag:
chainloop policy devel eval \
  --policy policy.yaml \
  --material data.json \
  --kind EVIDENCE \
  --allowed-hostnames registry.npmjs.org,api.example.com
In production, configure allowed hostnames in your policy engine settings.
package main

import (
    "fmt"
    chainloop "github.com/chainloop-dev/chainloop/labs/wasm-policy-sdk/go"
    "github.com/extism/go-pdk"
)

type PackageData struct {
    Name     string `json:"name"`
    Version  string `json:"version"`
}

type RegistryResponse struct {
    Versions map[string]interface{} `json:"versions"`
}

//export Execute
func Execute() int32 {
    return chainloop.Run(validate)
}

func validate() error {
    var pkg PackageData
    if err := chainloop.GetMaterialJSON(&pkg); err != nil {
        return chainloop.Skip("Invalid JSON")
    }

    url := fmt.Sprintf("https://registry.npmjs.org/%s", pkg.Name)
    var registry RegistryResponse

    if err := chainloop.HTTPGetJSON(url, &registry); err != nil {
        return chainloop.Skipf("npm registry unavailable: %v", err)
    }

    result := chainloop.Success()
    if _, exists := registry.Versions[pkg.Version]; !exists {
        result.AddViolationf("version %s not found in registry", pkg.Version)
    }

    return chainloop.OutputResult(result)
}

func main() {}
Test:
chainloop policy devel eval \
  --policy policy.yaml \
  --material package.json \
  --kind EVIDENCE \
  --allowed-hostnames registry.npmjs.org

Artifact Discovery

Check if related attestations have policy violations.
package main

import (
    "fmt"
    chainloop "github.com/chainloop-dev/chainloop/labs/wasm-policy-sdk/go"
    "github.com/extism/go-pdk"
)

type ContainerImage struct {
    ChainloopMetadata struct {
        Digest struct {
            SHA256 string `json:"sha256"`
        } `json:"digest"`
    } `json:"chainloop_metadata"`
}

//export Execute
func Execute() int32 {
    return chainloop.Run(validate)
}

func validate() error {
    var img ContainerImage
    if err := chainloop.GetMaterialJSON(&img); err != nil {
        return chainloop.Skip("Not a container image")
    }

    digest := fmt.Sprintf("sha256:%s", img.ChainloopMetadata.Digest.SHA256)

    discoverResult, err := chainloop.Discover(digest, "")
    if err != nil {
        return chainloop.Skipf("Discovery unavailable: %v", err)
    }

    result := chainloop.Success()

    for _, ref := range discoverResult.References {
        if ref.Kind == "ATTESTATION" {
            if ref.Metadata["hasPolicyViolations"] == "true" {
                result.AddViolationf(
                    "attestation %s contains policy violations", ref.Digest)
            }
        }
    }

    return chainloop.OutputResult(result)
}

func main() {}

Next Steps