> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chainloop.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# WASM Policy Examples & Patterns

> Common patterns and complete examples for writing WASM policies

<Warning>
  **Experimental Feature - WASM Policy Engine**

  These 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](/guides/custom-policies).
</Warning>

## Common Patterns

### Required Fields Validation

Ensure critical fields are present and non-empty.

<CodeGroup>
  ```go Go theme={"dark"}
  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
  }
  ```

  ```javascript JavaScript theme={"dark"}
  function validateRequiredFields(comp) {
    const result = success();

    if (!comp.name || comp.name === "") {
      result.addViolation("name is required");
    }
    if (!comp.version || comp.version === "") {
      result.addViolation("version is required");
    }

    return result;
  }
  ```
</CodeGroup>

### Allowlist/Blocklist Validation

Check values against approved or forbidden lists.

<CodeGroup>
  ```go Go theme={"dark"}
  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
  }
  ```

  ```javascript JavaScript theme={"dark"}
  function validateLicense(license, approved, forbidden) {
    const result = success();

    // Check blocklist
    if (forbidden.includes(license)) {
      result.addViolation(`${license} is forbidden`);
      return result;
    }

    // Check allowlist
    if (approved.length > 0 && !approved.includes(license)) {
      result.addViolation(`${license} not approved`);
    }

    return result;
  }
  ```
</CodeGroup>

### Nested Structure Validation

Validate arrays of objects (SBOM components, attestation subjects).

<CodeGroup>
  ```go Go theme={"dark"}
  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
  }
  ```

  ```javascript JavaScript theme={"dark"}
  function validateComponents(components) {
    const result = success();

    if (!components || components.length === 0) {
      result.addViolation("must contain at least one component");
    }

    components.forEach((comp, i) => {
      if (!comp.name) result.addViolation(`component ${i} missing name`);
      if (!comp.version) result.addViolation(`component ${i} missing version`);
    });

    return result;
  }
  ```
</CodeGroup>

### External API Validation

Verify data against external sources.

<CodeGroup>
  ```go Go theme={"dark"}
  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
  }
  ```

  ```javascript JavaScript theme={"dark"}
  function validateWithAPI(name, version) {
    try {
      const url = `https://registry.npmjs.org/${name}`;
      const data = httpGetJSON(url);

      const result = success();
      if (!data.versions[version]) {
        result.addViolation(`version ${version} not found`);
      }

      return result;
    } catch (e) {
      return skip(`API unavailable: ${e.message}`);
    }
  }
  ```
</CodeGroup>

<Warning>
  **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`
</Warning>

## Complete Examples

### SBOM License Validation

Validate component licenses in a CycloneDX SBOM.

<CodeGroup>
  ```go Go theme={"dark"}
  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() {}
  ```

  ```javascript JavaScript theme={"dark"}
  const {
    getMaterialJSON,
    success,
    skip,
    outputResult,
    run
  } = require('@chainloop-dev/policy-sdk');

  function Execute() {
    return run(() => {
      const sbom = getMaterialJSON();

      if (!sbom.components) {
        outputResult(skip("Not a valid CycloneDX SBOM"));
        return;
      }

      const approved = ["MIT", "Apache-2.0", "BSD-3-Clause"];
      const forbidden = ["GPL-3.0", "AGPL-3.0"];

      const result = success();

      sbom.components.forEach(comp => {
        (comp.licenses || []).forEach(lic => {
          // Check forbidden
          if (forbidden.includes(lic.id)) {
            result.addViolation(
              `component '${comp.name}' uses forbidden license: ${lic.id}`
            );
          }

          // Check approved
          if (!approved.includes(lic.id)) {
            result.addViolation(
              `component '${comp.name}' license not approved: ${lic.id}`
            );
          }
        });
      });

      outputResult(result);
    });
  }

  module.exports = { Execute };
  ```
</CodeGroup>

**Test data** (`sbom.json`):

```json theme={"dark"}
{
  "components": [
    {
      "name": "lodash",
      "version": "4.17.21",
      "licenses": [{ "id": "MIT" }]
    },
    {
      "name": "gpl-package",
      "version": "1.0.0",
      "licenses": [{ "id": "GPL-3.0" }]
    }
  ]
}
```

**Test:**

```bash theme={"dark"}
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.

<CodeGroup>
  ```go Go theme={"dark"}
  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() {}
  ```

  ```javascript JavaScript theme={"dark"}
  const {
    getMaterialJSON,
    success,
    skip,
    outputResult,
    run
  } = require('@chainloop-dev/policy-sdk');

  function Execute() {
    return run(() => {
      const att = getMaterialJSON();

      if (!att.subject) {
        outputResult(skip("Not a valid attestation"));
        return;
      }

      const result = success();

      if (att.subject.length === 0) {
        result.addViolation("attestation must have at least one subject");
      }

      att.subject.forEach(subj => {
        if (!subj.name || subj.name === "") {
          result.addViolation("subject missing name field");
        }
      });

      outputResult(result);
    });
  }

  module.exports = { Execute };
  ```
</CodeGroup>

### HTTP API Integration

Verify package version exists in npm registry.

<Warning>
  **Domain Allowlisting Required**

  By 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:

  ```bash theme={"dark"}
  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.
</Warning>

<CodeGroup>
  ```go Go theme={"dark"}
  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() {}
  ```

  ```javascript JavaScript theme={"dark"}
  const {
    getMaterialJSON,
    success,
    skip,
    outputResult,
    httpGetJSON,
    run
  } = require('@chainloop-dev/policy-sdk');

  function Execute() {
    return run(() => {
      const pkg = getMaterialJSON();

      try {
        const url = `https://registry.npmjs.org/${pkg.name}`;
        const registry = httpGetJSON(url);

        const result = success();
        if (!registry.versions[pkg.version]) {
          result.addViolation(`version ${pkg.version} not found in registry`);
        }

        outputResult(result);
      } catch (e) {
        outputResult(skip(`npm registry unavailable: ${e.message}`));
      }
    });
  }

  module.exports = { Execute };
  ```
</CodeGroup>

**Test:**

```bash theme={"dark"}
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.

<CodeGroup>
  ```go Go theme={"dark"}
  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() {}
  ```

  ```javascript JavaScript theme={"dark"}
  const {
    getMaterialJSON,
    success,
    skip,
    outputResult,
    discover,
    logInfo,
    run
  } = require('@chainloop-dev/policy-sdk');

  function Execute() {
    return run(() => {
      const img = getMaterialJSON();

      if (!img.chainloop_metadata?.digest?.sha256) {
        outputResult(skip("Not a container image"));
        return;
      }

      const digest = `sha256:${img.chainloop_metadata.digest.sha256}`;

      let discoverResult;
      try {
        discoverResult = discover(digest, "");
      } catch (e) {
        outputResult(skip(`Discovery unavailable: ${e.message}`));
        return;
      }

      const result = success();

      discoverResult.references.forEach(ref => {
        if (ref.kind === "ATTESTATION") {
          if (ref.metadata.hasPolicyViolations === "true") {
            result.addViolation(
              `attestation ${ref.digest} contains policy violations`
            );
          }
        }
      });

      outputResult(result);
    });
  }

  module.exports = { Execute };
  ```
</CodeGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Go SDK Guide" icon="golang" href="/guides/wasm-policies/go-sdk">
    Learn how to use the Go SDK
  </Card>

  <Card title="JavaScript SDK Guide" icon="js" href="/guides/wasm-policies/javascript-sdk">
    Learn how to use the JavaScript SDK
  </Card>
</CardGroup>
