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
12 changes: 6 additions & 6 deletions tools/wasmsign2_wrapper/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ load("@rules_go//go:def.bzl", "go_binary")

package(default_visibility = ["//visibility:public"])

# Wrapper binary that executes wasmsign2.wasm with path resolution
# Wrapper binary that executes wasmsign2.wasm with path resolution.
# The wasmtime binary and wasmsign2.wasm are passed by the calling rule as
# --bazel-* arguments (and staged as action inputs); wasmtime opens both
# natively. They are NOT resolved via runfiles, which would embed a canonical
# repo name that breaks when rules_wasm_component is consumed as a dependency
# (issue #501).
go_binary(
name = "wasmsign2_wrapper",
srcs = ["main.go"],
data = [
"@wasmsign2_cli_wasm//file",
"@wasmtime_toolchain//:wasmtime",
],
pure = "on", # Pure Go for cross-platform compatibility
visibility = ["//visibility:public"],
deps = ["@rules_go//go/runfiles"],
)

# Export for easy access in toolchains
Expand Down
38 changes: 19 additions & 19 deletions tools/wasmsign2_wrapper/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import (
"os/exec"
"path/filepath"
"strings"

"github.com/bazelbuild/rules_go/go/runfiles"
)

// Wrapper for wasmsign2 WASM component
Expand All @@ -19,6 +17,8 @@ func main() {
}

// Internal-only Bazel coordination flags. These never reach wsc.
// --bazel-wasmtime=PATH Path to the wasmtime binary to exec.
// --bazel-wasm-component=PATH Path to the wasmsign2 WASM component.
// --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
Expand All @@ -29,12 +29,25 @@ func main() {
// inheriting this process's stdout. Used
// by show-chain to produce a Bazel output
// artifact.
//
// wasmtime and the wasm component are passed by the calling rule (and staged
// as action inputs) rather than located via runfiles: a hardcoded runfiles
// Rlocation embeds the canonical repo name, which differs when
// rules_wasm_component is consumed as a dependency (it gains a
// `rules_wasm_component+` prefix), breaking downstream signing (issue #501,
// same fix as #490/#497). wasmtime opens both files natively.
var markerFile string
var stageSource string
var captureStdout string
var wasmtimeBinary string
var wasmsign2Wasm string
filteredArgs := make([]string, 0, len(os.Args))
for i, arg := range os.Args {
switch {
case strings.HasPrefix(arg, "--bazel-wasmtime="):
wasmtimeBinary = strings.TrimPrefix(arg, "--bazel-wasmtime=")
case strings.HasPrefix(arg, "--bazel-wasm-component="):
wasmsign2Wasm = strings.TrimPrefix(arg, "--bazel-wasm-component=")
case strings.HasPrefix(arg, "--bazel-marker-file="):
markerFile = strings.TrimPrefix(arg, "--bazel-marker-file=")
case strings.HasPrefix(arg, "--bazel-stage-source="):
Expand All @@ -48,28 +61,15 @@ func main() {
}
}

// Initialize Bazel runfiles
r, err := runfiles.New()
if err != nil {
log.Fatalf("Failed to initialize runfiles: %v", err)
if wasmtimeBinary == "" {
log.Fatal("Missing required --bazel-wasmtime=PATH")
}

// Locate wasmtime binary
wasmtimeBinary, err := r.Rlocation("+wasmtime+wasmtime_toolchain/wasmtime")
if err != nil {
log.Fatalf("Failed to locate wasmtime: %v", err)
if wasmsign2Wasm == "" {
log.Fatal("Missing required --bazel-wasm-component=PATH")
}

if _, err := os.Stat(wasmtimeBinary); err != nil {
log.Fatalf("Wasmtime binary not found at %s: %v", wasmtimeBinary, err)
}

// Locate wasmsign2 WASM component
wasmsign2Wasm, err := r.Rlocation("+_repo_rules+wasmsign2_cli_wasm/file/wasmsign2.wasm")
if err != nil {
log.Fatalf("Failed to locate wasmsign2.wasm: %v", err)
}

if _, err := os.Stat(wasmsign2Wasm); err != nil {
log.Fatalf("wasmsign2.wasm not found at %s: %v", wasmsign2Wasm, err)
}
Expand Down
19 changes: 17 additions & 2 deletions wasm/private/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,26 @@ bzl_library(
deps = ["//providers"],
)

bzl_library(
name = "wasmsign2_tools",
srcs = ["wasmsign2_tools.bzl"],
visibility = [
"//docs:__pkg__",
"//wasm:__pkg__",
],
)

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

bzl_library(
Expand Down Expand Up @@ -173,5 +185,8 @@ bzl_library(
"//docs:__pkg__",
"//wasm:__pkg__",
],
deps = ["//providers"],
deps = [
":wasmsign2_tools",
"//providers",
],
)
19 changes: 16 additions & 3 deletions wasm/private/wasm_signing.bzl
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
"""WebAssembly signing rules using wasmsign2"""

load("//providers:providers.bzl", "WasmComponentInfo", "WasmKeyInfo", "WasmSignatureInfo")
load(
":wasmsign2_tools.bzl",
"WASMSIGN2_WASM_ATTR",
"WASMTIME_TOOLCHAIN",
"add_wrapper_tools",
)

def _wasm_keygen_impl(ctx):
"""Implementation of wasm_keygen rule"""
Expand All @@ -17,12 +23,14 @@ def _wasm_keygen_impl(ctx):
args.add("keygen")
args.add("--public-key", public_key)
args.add("--secret-key", secret_key)
tool_inputs = add_wrapper_tools(ctx, args)

# Run key generation via Go wrapper
# The wrapper handles symlink resolution and WASI directory mapping
ctx.actions.run(
executable = wasmsign2_wrapper,
arguments = [args],
inputs = tool_inputs,
outputs = [public_key, secret_key],
mnemonic = "WasmKeyGen",
progress_message = "Generating WASM signing keys %s" % ctx.label,
Expand Down Expand Up @@ -69,7 +77,8 @@ wasm_keygen = rule(
executable = True,
cfg = "exec",
),
},
} | WASMSIGN2_WASM_ATTR,
toolchains = [WASMTIME_TOOLCHAIN],
doc = """
Generates a key pair for signing WebAssembly components in compact format.

Expand Down Expand Up @@ -150,6 +159,7 @@ def _wasm_sign_impl(ctx):
inputs = [input_wasm, secret_key]
if public_key:
inputs.append(public_key)
inputs += add_wrapper_tools(ctx, args)

# Prepare outputs
outputs = [signed_wasm]
Expand Down Expand Up @@ -238,7 +248,8 @@ wasm_sign = rule(
executable = True,
cfg = "exec",
),
},
} | WASMSIGN2_WASM_ATTR,
toolchains = [WASMTIME_TOOLCHAIN],
doc = """
Signs a WebAssembly component with a cryptographic signature.

Expand Down Expand Up @@ -323,6 +334,7 @@ def _wasm_verify_impl(ctx):
inputs.append(public_key)
if signature_file:
inputs.append(signature_file)
inputs += add_wrapper_tools(ctx, args)

# Run verification via Go wrapper (no shell scripts!)
# The wrapper will create the marker file on success
Expand Down Expand Up @@ -394,7 +406,8 @@ wasm_verify = rule(
executable = True,
cfg = "exec",
),
},
} | WASMSIGN2_WASM_ATTR,
toolchains = [WASMTIME_TOOLCHAIN],
doc = """
Verifies the cryptographic signature of a WebAssembly component.

Expand Down
36 changes: 36 additions & 0 deletions wasm/private/wasmsign2_tools.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""Shared helper for invoking the wasmsign2 Go wrapper.

The wrapper (`//tools/wasmsign2_wrapper`) runs the wasmsign2 WASM component under
wasmtime. Both the wasmtime binary and the component are passed to it as
`--bazel-*` arguments (and staged as action inputs) rather than located via the
wrapper's runfiles: a hardcoded runfiles Rlocation embeds the canonical repo
name, which differs when rules_wasm_component is consumed as a dependency (it
gains a `rules_wasm_component+` prefix), breaking downstream signing/attestation
(issue #501, same fix as #490/#497).
"""

# wasmtime toolchain that runs the wasmsign2 component.
WASMTIME_TOOLCHAIN = "@rules_wasm_component//toolchains:wasmtime_toolchain_type"

# The wasmsign2 WASM component, passed to the wrapper as an action input.
# Merge into a rule's attrs; the rule must also set
# `toolchains = [WASMTIME_TOOLCHAIN]`.
WASMSIGN2_WASM_ATTR = {
"_wasmsign2_wasm": attr.label(
default = "@wasmsign2_cli_wasm//file:file",
allow_single_file = True,
doc = "wasmsign2 WASM component, passed to the wrapper as an action input",
),
}

def add_wrapper_tools(ctx, args):
"""Add `--bazel-wasmtime=`/`--bazel-wasm-component=` to a wrapper invocation.

Returns the extra action inputs (the wasmtime binary and the component) to
include in `ctx.actions.run(inputs = ...)`.
"""
wasmtime = ctx.toolchains[WASMTIME_TOOLCHAIN].wasmtime
wasm = ctx.file._wasmsign2_wasm
args.add(wasmtime, format = "--bazel-wasmtime=%s")
args.add(wasm, format = "--bazel-wasm-component=%s")
return [wasmtime, wasm]
22 changes: 17 additions & 5 deletions wasm/private/wsc_attestation.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ useful for CI diagnostics.
"""

load("//providers:providers.bzl", "WasmComponentInfo")
load(
":wasmsign2_tools.bzl",
"WASMSIGN2_WASM_ATTR",
"WASMTIME_TOOLCHAIN",
"add_wrapper_tools",
)

_TRANSFORMATION_TYPES = [
"optimization",
Expand Down Expand Up @@ -69,11 +75,12 @@ def _wasm_attest_impl(ctx):
args.add("--tool-name", ctx.attr.tool_name)
args.add("--tool-version", ctx.attr.tool_version)
args.add("--type", ctx.attr.transformation_type)
tool_inputs = add_wrapper_tools(ctx, args)

ctx.actions.run(
executable = wrapper,
arguments = [args],
inputs = [input_wasm, output_input],
inputs = [input_wasm, output_input] + tool_inputs,
outputs = [attested_wasm],
mnemonic = "WasmAttest",
progress_message = "Recording transformation attestation %s on %s" % (
Expand Down Expand Up @@ -141,7 +148,8 @@ wasm_attest = rule(
executable = True,
cfg = "exec",
),
},
} | WASMSIGN2_WASM_ATTR,
toolchains = [WASMTIME_TOOLCHAIN],
doc = """Record a transformation attestation on a WebAssembly module.

Use this when you run a transformation outside of the built-in pipeline rules
Expand Down Expand Up @@ -197,6 +205,7 @@ def _wasm_verify_chain_impl(ctx):
args.add("--report-only")

args.add("--bazel-marker-file=" + marker.path)
inputs += add_wrapper_tools(ctx, args)

ctx.actions.run(
executable = wrapper,
Expand Down Expand Up @@ -254,7 +263,8 @@ wasm_verify_chain = rule(
executable = True,
cfg = "exec",
),
},
} | WASMSIGN2_WASM_ATTR,
toolchains = [WASMTIME_TOOLCHAIN],
doc = """Verify a WebAssembly module's transformation attestation chain.

Emits a marker file on success; the build fails if verification fails (unless
Expand Down Expand Up @@ -306,11 +316,12 @@ def _wasm_show_chain_impl(ctx):
args.add("--input-file", input_wasm)
if ctx.attr.as_json:
args.add("--json")
tool_inputs = add_wrapper_tools(ctx, args)

ctx.actions.run(
executable = wrapper,
arguments = [args],
inputs = [input_wasm],
inputs = [input_wasm] + tool_inputs,
outputs = [out],
mnemonic = "WasmShowChain",
progress_message = "Extracting attestation chain from %s" % input_wasm.short_path,
Expand Down Expand Up @@ -339,7 +350,8 @@ wasm_show_chain = rule(
executable = True,
cfg = "exec",
),
},
} | WASMSIGN2_WASM_ATTR,
toolchains = [WASMTIME_TOOLCHAIN],
doc = """Extract a WebAssembly module's transformation attestation chain to a file.

Produces a JSON (default) or text rendering of the transformation chain stored
Expand Down
Loading