Safeguard Docs
Integrations

WebAssembly (Wasm)

Connect a registry of WebAssembly components and modules to Safeguard, or scan .wasm files with the Safeguard CLI. The credentials each connection needs, which token types work, what a Wasm scan can and cannot know, and step-by-step setup.

Availability

This integration is switched on per workspace. If you do not see the card under Integrations, ask your Safeguard contact. The safeguard wasm CLI commands work in every workspace.

What it does

The WebAssembly (Wasm) card connects Safeguard to Wasm components and modules stored as OCI artifacts in any registry that implements the OCI distribution spec (GitHub Container Registry, Docker Hub, Harbor, zot and others). Safeguard lists every repository and tag, and says for each tag whether it is a Wasm artifact. Each tag you select becomes a project that Safeguard scans. Find the card under Integrations on the Project/Product tab.

A Wasm scan reads the binary itself:

  • whether it is a core module or a component, its size and sha256;
  • the toolchain that built it, from the producers custom section (for example rustc 1.90.0, wit-component 0.234.0), recorded as metadata, not as dependencies;
  • the WIT interfaces a component imports and exports, with versions (for example wasi:sockets/tcp@0.2.3), and the imports and exports of a core module (for example wasi_snapshot_preview1.path_open);
  • dependencies embedded at build time: Rust crates recorded by cargo-auditable in the .dep-v0 custom section, which are matched against vulnerability advisories;
  • other notable custom sections: name, sourceMappingURL, build_id, DWARF debug sections;
  • cosign signatures on the OCI artifact.

The result is a CycloneDX 1.6 SBOM with the artifact as the main component, plus capability findings.

What a Wasm scan cannot know

  • No OS packages. A Wasm artifact has no operating system layer.
  • Only embedded dependencies. Dependencies are listed only when the build embedded them. Today that is Rust built with cargo auditable build. Go, TinyGo, C and C++, AssemblyScript and JavaScript (for example ComponentizeJS) components usually carry only toolchain names, so their dependency list is empty. An empty list means unknown, not clean.
  • Build-only crates are not matched. Crates cargo-auditable marks as build dependencies are listed with scope excluded and are not matched against advisories, because they are not in the binary.
  • Nested components are read recursively. Nested core modules and nested components are parsed (up to 16 levels). Capabilities come from the top-level imports and exports only, because imports of nested parts are satisfied inside the component.
  • Imports are capabilities, not vulnerabilities. An import says what the code can ask the host for; the host decides what it grants. Language standard libraries import file system and environment interfaces even when the program never calls them.
  • Signatures are verified only with your key. See Signatures.

Capability findings

FindingSeverityShown when the artifact imports or exports
Network sockets (TCP/UDP)Lowwasi:sockets/* (except ip-name-lookup), or preview 1 sock_*
DNS name lookupLowwasi:sockets/ip-name-lookup
Outgoing HTTP requestsLowwasi:http/outgoing-handler
File system accessLowwasi:filesystem/*, or preview 1 path_*, fd_prestat_*
Environment variablesLowwasi:cli/environment, or preview 1 environ_get
Key-value, config, blob or messaging storesLowwasi:keyvalue/*, wasi:config/*, wasi:blobstore/*, wasi:messaging/*, wasi:sql/*
Standard input, output and terminalInfowasi:cli/stdin, stdout, stderr, terminal-*
Clocks and randomnessInfowasi:clocks/*, wasi:random/*, preview 1 clock_*, random_get
Process exit and signalsInfowasi:cli/exit, preview 1 proc_exit, proc_raise
Command-line argumentsInfopreview 1 args_get
Machine learning inferenceInfowasi:nn/*
Serves HTTP requestsInfoexports wasi:http/incoming-handler
Runs as a commandInfoexports wasi:cli/run
Host-defined importsInfoanything else (what it can do depends on the host)

Low means the capability reaches outside the sandbox if the host grants it. Info means ordinary plumbing. Nothing is rated higher, because an import alone never proves misuse.

Public vs private

PublicPrivate
What you enterOne or more repositories, as host/path (for example ghcr.io/webassembly/wasi/http)The registry host, a username and a password or token; optionally the repositories to connect
Credential neededNoneA registry login that can pull
What you can scanRepositories that allow anonymous pullsEvery repository the credential can pull. Without a repository list, Safeguard reads the registry's catalog (/v2/_catalog) to the last page.

Many registries (GitHub Container Registry and Docker Hub among them) do not offer a catalog to ordinary users. List the repositories by URL for those.

Supported auth methods

MethodUse it whenNotes
AnonymousThe repositories are publicSafeguard answers the registry's token challenge without a credential.
Username and password or tokenThe repositories are privateSent as HTTP Basic, or exchanged at the registry's https token service for a pull token (the distribution token flow).
Safeguard CLIYou scan a file, or a registry the internet cannot reachsafeguard wasm scan runs where you are, with the credential you give it.

Only https registries are connected. Plain http:// registries are refused.

Permissions required

Safeguard only reads: it lists repositories and tags, and pulls manifests and blobs. It never pushes, tags or deletes. The least-privilege credential is a read-only (pull) token that can see only the repositories you want scanned.

Username and password or token

Permission / scopeRequired?What Safeguard uses it for
Pull (read) on each repositoryRequiredReading tags (/v2/<name>/tags/list), manifests and Wasm layers (/v2/<name>/manifests/<ref>, /v2/<name>/blobs/<digest>), and cosign signatures (sha256-<digest>.sig tags and the referrers API).
Catalog listing (registry:catalog:*)OptionalListing every repository when you do not enter a list. Not needed when you list repositories.

Registry names for pull access:

RegistryPull permission
GitHub Container Registry (ghcr.io)Personal access token (classic) with the read:packages scope
Docker HubPersonal access token with Repo Read-only (or Repo Public Read-only for public images)
HarborRobot account with Pull Repository on the project
Other OCI registriesAny account or token that can docker pull or oras pull the repository

Least privilege: a dedicated read-only token or robot account limited to the repositories that hold Wasm artifacts.

Anonymous

Permission / scopeRequired?What Safeguard uses it for
Anonymous pull allowed by the registryRequiredThe same reads as above, without a credential.

Least privilege: nothing to configure.

Token types

Token typeWorks?Notes
Read-only personal access tokenYes (recommended)Enter your username and the token as the password.
Robot or service accountYesUse its name as the username and its secret as the password.
Account passwordYes, not recommendedUse a token instead where the registry offers one.
GitHub fine-grained personal access tokenNoGitHub Packages supports only classic tokens.
Tokens with push or delete rightsWork, not neededSafeguard never writes; use a read-only token.

Expiry: prefer tokens that expire, for example after 90 days. When a token expires, listing and scans fail with HTTP 401 until you enter a new one.

What we never need: push, delete, admin or user management rights.

Step-by-step setup

Publish a Wasm artifact (if you have not yet)

The CNCF TAG Runtime layout is a manifest with config media type application/vnd.wasm.config.v0+json and one application/wasm layer. wkg oci push writes it; with oras:

oras push registry.example.com/team/app:1.0.0 \
  --config config.json:application/vnd.wasm.config.v0+json \
  app.wasm:application/wasm

Build Rust components with cargo auditable build --target wasm32-wasip2 so the dependency list is embedded. Spin applications pushed with spin registry push are read too.

Create the credential

  1. In your registry, create a read-only token or robot account (see Permissions required).
  2. Copy the token. Most registries show it only once.

Connect it in Safeguard

  1. Open Integrations and choose the Project/Product tab.
  2. Click Connect on the WebAssembly (Wasm) card.
  3. Choose Public or Private.
  4. Public: enter a Name and the Repositories, one per line. Click Check repositories.
  5. Private: enter a Name, the Registry host (for example ghcr.io), the Username and the Password or token. Optionally list Repositories. Click Verify credentials.
  6. Click Next. Every repository is listed. Open a repository's tags: each tag shows its digest and whether it is a Wasm artifact.
  7. Select the repositories and tags and click Connect.

Or with the Safeguard CLI

safeguard wasm inspect ./app.wasm
safeguard wasm scan ./app.wasm -o app.cdx.json
safeguard wasm scan oci://registry.example.com/team/app:1.0.0 --key cosign.pub
echo "$TOKEN" | safeguard wasm scan registry.example.com/team/app:1.0.0 --username ci --password-stdin

See CLI usage.

Webhooks and triggers

This integration has no webhooks: the OCI distribution spec has no standard push notification. Scans run when you connect, on the schedule you set, or when you rescan by hand.

What we read and store

  • Read: repository and tag lists, the manifest of each tag (to classify it and pin the digest), the Wasm config blob, the Wasm layer, and cosign signature manifests and payloads.
  • Stored: the SBOM, the capability findings, vulnerability findings for embedded dependencies, the manifest digest and the signature status. Credentials are encrypted at rest and never shown again after you save them.
  • Not stored: the Wasm binary itself is not kept after the scan.
  • No customer data from this integration is sent to third-party LLMs.

Each scan resolves the tag to its manifest digest and reads that digest, so the result is for exactly that artifact. A tag that is a container image rather than a Wasm artifact is scanned as a container image.

Signatures

Safeguard looks for cosign signatures in two places: the sha256-<digest>.sig tag cosign has always used, and the OCI 1.1 referrers API.

  • Keyed signatures are verified with the public key you give (safeguard wasm inspect --key cosign.pub): the signature must verify and its payload must name this artifact's manifest digest. The result is "signed and verified" or "signature present but did not verify".
  • Keyless signatures (Fulcio certificate) and Sigstore bundles found through the referrers API are reported as "signature present, not verified". The certificate identity is not shown, because the certificate chain and Rekor entry are not verified. Use cosign verify --certificate-identity ... --certificate-oidc-issuer ... for that.
  • Hosted scans have no public key, so they report presence only.

Troubleshooting

Message you seeCauseFix
Plain http:// registries are not supportedThe registry address starts with http://Use the registry's https address. For a local test registry use the CLI with --plain-http.
The registry <host> needs a username and password or token. Connect it as Private.The registry refuses anonymous readsConnect as Private with a read-only token.
The registry <host> rejected the username '<user>' and its password or token (HTTP 401)Wrong username or token, or the token expiredCheck both; create a new token with pull access.
answered HTTP 401 for the repository catalog or does not offer a catalogThe registry does not let this credential list its catalogEnter the repositories by URL.
answered HTTP 404 for the manifestThe repository or tag does not exist, or the credential cannot see itCheck the name and tag, and the token's access.
Not a Wasm artifact (config application/vnd.oci.image.config.v1+json) on a tagThe tag is an ordinary container imageNothing to fix; it is scanned as a container image.
Not checked: ... on a tagThe manifest could not be read in timeOpen the next page of tags or scan the tag.
embedded dependencies: none embedded ... unknown, not emptyThe build did not embed a dependency listFor Rust, build with cargo auditable build. Other languages have no embedded list today.
signature present, not verifiedNo public key was given, or the signature is keylessPass --key cosign.pub, or verify keyless signatures with cosign verify.

Rotation and disconnect

  • Rotate: edit the connection, enter the username and the new token, verify, and save. Leave the password blank to keep the stored one. Then revoke the old token in the registry.
  • Disconnect: delete the connection under Integrations, then revoke the token or robot account in the registry.

Vendor references

On this page