Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion examples/wasm_signing/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,14 @@ signature verification.
"""

load("@rules_wasm_component//rust:defs.bzl", "rust_wasm_component_bindgen")
load("@rules_wasm_component//wasm:defs.bzl", "wasm_keygen", "wasm_sign", "wasm_validate", "wasm_verify")
load(
"@rules_wasm_component//wasm:defs.bzl",
"wasm_keygen",
"wasm_show_chain",
"wasm_sign",
"wasm_validate",
"wasm_verify",
)
load("@rules_wasm_component//wit:defs.bzl", "wit_library")

package(default_visibility = ["//visibility:public"])
Expand Down Expand Up @@ -90,6 +97,21 @@ wasm_validate(
verify_signature = True,
)

# Step 9b: Extract the transformation chain from the signed component as JSON.
# A freshly signed component has an empty chain ("No transformation
# attestations found in module"); meaningful output appears only after
# transformations like meld_fuse or wasm_optimize have run. This target proves
# the wasm_show_chain rule wires end-to-end.
#
# wasm_verify_chain is the natural ship-gate but requires at least one
# transformation attestation in the chain — demo it in a pipeline that includes
# meld_fuse or wasm_optimize, not a pure-sign flow.
wasm_show_chain(
name = "signed_component_chain_json",
as_json = True,
component = ":signed_component_embedded",
)

# Step 10: Demonstrate signing a raw WASM file
wasm_sign(
name = "signed_raw_wasm",
Expand Down
93 changes: 86 additions & 7 deletions tools/wasmsign2_wrapper/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,33 @@ func main() {
log.Fatal("Usage: wasmsign2_wrapper <command> [args...]")
}

// Check for --bazel-marker-file flag (internal use only)
// Internal-only Bazel coordination flags. These never reach wsc.
// --bazel-marker-file=PATH Write "Verification passed\n" on success.
// --bazel-stage-source=PATH Copy PATH to the --output-file location
// before running wsc. Lets rules pass the
// post-transformation WASM as a separate
// input and have the wrapper stage it so
// wsc can read-modify-write the output.
// --bazel-capture-stdout=PATH Write wsc's stdout to PATH instead of
// inheriting this process's stdout. Used
// by show-chain to produce a Bazel output
// artifact.
var markerFile string
var stageSource string
var captureStdout string
filteredArgs := make([]string, 0, len(os.Args))
for i, arg := range os.Args {
if strings.HasPrefix(arg, "--bazel-marker-file=") {
switch {
case strings.HasPrefix(arg, "--bazel-marker-file="):
markerFile = strings.TrimPrefix(arg, "--bazel-marker-file=")
} else if i > 0 { // Skip program name
filteredArgs = append(filteredArgs, arg)
case strings.HasPrefix(arg, "--bazel-stage-source="):
stageSource = strings.TrimPrefix(arg, "--bazel-stage-source=")
case strings.HasPrefix(arg, "--bazel-capture-stdout="):
captureStdout = strings.TrimPrefix(arg, "--bazel-capture-stdout=")
default:
if i > 0 { // Skip program name
filteredArgs = append(filteredArgs, arg)
}
}
}

Expand Down Expand Up @@ -65,6 +84,22 @@ func main() {
log.Fatalf("Failed to resolve paths: %v", err)
}

// Stage the post-transformation WASM into the declared Bazel output before
// invoking wsc, which reads and rewrites --output-file in place.
if stageSource != "" {
outPath := findFlagValue(resolvedArgs, "--output-file", "-o")
if outPath == "" {
log.Fatal("--bazel-stage-source requires a resolvable --output-file or -o in the wsc command")
}
data, readErr := os.ReadFile(stageSource)
if readErr != nil {
log.Fatalf("Failed to read stage source %s: %v", stageSource, readErr)
}
if writeErr := os.WriteFile(outPath, data, 0644); writeErr != nil {
log.Fatalf("Failed to stage %s -> %s: %v", stageSource, outPath, writeErr)
}
}

// Build wasmtime command with directory mappings
wasmtimeArgs := []string{
"run",
Expand All @@ -84,10 +119,20 @@ func main() {

// Execute wasmtime
cmd := exec.Command(wasmtimeBinary, wasmtimeArgs...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin

if captureStdout != "" {
outFile, createErr := os.Create(captureStdout)
if createErr != nil {
log.Fatalf("Failed to create stdout capture file %s: %v", captureStdout, createErr)
}
defer outFile.Close()
cmd.Stdout = outFile
} else {
cmd.Stdout = os.Stdout
}

if err := cmd.Run(); err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
os.Exit(exitErr.ExitCode())
Expand All @@ -109,7 +154,9 @@ func resolvePathsInArgs(command string, args []string) ([]string, []string, erro
resolvedArgs := make([]string, 0, len(args))
dirs := make([]string, 0)

// Track which flags expect file paths
// Track which flags expect file paths.
// Covers sign/verify/keygen (legacy) plus attest/verify-chain/show-chain
// (wsc 0.7.0+ attestation commands).
pathFlags := map[string]bool{
"-i": true, "--input": true,
"-o": true, "--output": true,
Expand All @@ -118,6 +165,11 @@ func resolvePathsInArgs(command string, args []string) ([]string, []string, erro
"-S": true, "--signature": true,
"--public-key-name": true,
"--secret-key-name": true,
"--input-file": true,
"--output-file": true,
"-p": true, "--policy": true,
"--trusted-tools": true,
"--audit-file": true,
}

for i := 0; i < len(args); i++ {
Expand Down Expand Up @@ -149,7 +201,12 @@ func resolvePathsInArgs(command string, args []string) ([]string, []string, erro
strings.HasPrefix(arg, "--secret-key-name=") ||
strings.HasPrefix(arg, "-i=") || strings.HasPrefix(arg, "-o=") ||
strings.HasPrefix(arg, "-k=") || strings.HasPrefix(arg, "-K=") ||
strings.HasPrefix(arg, "-S=")) {
strings.HasPrefix(arg, "-S=") ||
strings.HasPrefix(arg, "--input-file=") ||
strings.HasPrefix(arg, "--output-file=") ||
strings.HasPrefix(arg, "-p=") || strings.HasPrefix(arg, "--policy=") ||
strings.HasPrefix(arg, "--trusted-tools=") ||
strings.HasPrefix(arg, "--audit-file=")) {
// Handle --flag=value format
parts := strings.SplitN(arg, "=", 2)
if len(parts) == 2 {
Expand Down Expand Up @@ -180,6 +237,28 @@ func resolvePathsInArgs(command string, args []string) ([]string, []string, erro
return resolvedArgs, dirs, nil
}

// findFlagValue returns the value of a long or short flag in the resolved
// arg list, supporting both "--flag val" and "--flag=val" forms. Returns ""
// if neither form is present.
func findFlagValue(args []string, longFlag, shortFlag string) string {
for i := 0; i < len(args); i++ {
a := args[i]
if a == longFlag || (shortFlag != "" && a == shortFlag) {
if i+1 < len(args) {
return args[i+1]
}
return ""
}
if strings.HasPrefix(a, longFlag+"=") {
return strings.TrimPrefix(a, longFlag+"=")
}
if shortFlag != "" && strings.HasPrefix(a, shortFlag+"=") {
return strings.TrimPrefix(a, shortFlag+"=")
}
}
return ""
}

// uniqueStrings returns unique strings from a slice
func uniqueStrings(strs []string) []string {
seen := make(map[string]bool)
Expand Down
1 change: 1 addition & 0 deletions wasm/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ bzl_library(
"//wasm/private:wasm_run",
"//wasm/private:wasm_signing",
"//wasm/private:wasm_validate",
"//wasm/private:wsc_attestation",
],
)

Expand Down
12 changes: 12 additions & 0 deletions wasm/defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ This module provides utilities for:
- AOT embedding (wasm_embed_aot, wasm_extract_aot)
- Component fusion (meld_fuse)
- ARM cross-compilation (synth_compile)
- Sigil transformation attestation (wasm_attest, wasm_verify_chain, wasm_show_chain)

Example usage:

Expand Down Expand Up @@ -105,6 +106,12 @@ load(
_wasm_sign = "wasm_sign",
_wasm_verify = "wasm_verify",
)
load(
"//wasm/private:wsc_attestation.bzl",
_wasm_attest = "wasm_attest",
_wasm_show_chain = "wasm_show_chain",
_wasm_verify_chain = "wasm_verify_chain",
)
load(
"//wasm/private:wasm_validate.bzl",
_wasm_validate = "wasm_validate",
Expand All @@ -121,6 +128,11 @@ wasm_keygen = _wasm_keygen
wasm_sign = _wasm_sign
wasm_verify = _wasm_verify

# Sigil transformation attestation rules (wsc 0.7.0+)
wasm_attest = _wasm_attest
wasm_verify_chain = _wasm_verify_chain
wasm_show_chain = _wasm_show_chain

# WebAssembly AOT compilation rules
wasm_precompile = _wasm_precompile
wasm_precompile_multi = _wasm_precompile_multi
Expand Down
10 changes: 10 additions & 0 deletions wasm/private/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,13 @@ bzl_library(
],
deps = ["//providers"],
)

bzl_library(
name = "wsc_attestation",
srcs = ["wsc_attestation.bzl"],
visibility = [
"//docs:__pkg__",
"//wasm:__pkg__",
],
deps = ["//providers"],
)
Loading