diff --git a/tools/wasmsign2_wrapper/BUILD.bazel b/tools/wasmsign2_wrapper/BUILD.bazel index 6d5d9d72..54e74bb6 100644 --- a/tools/wasmsign2_wrapper/BUILD.bazel +++ b/tools/wasmsign2_wrapper/BUILD.bazel @@ -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 diff --git a/tools/wasmsign2_wrapper/main.go b/tools/wasmsign2_wrapper/main.go index e342993c..dd8bd9a3 100644 --- a/tools/wasmsign2_wrapper/main.go +++ b/tools/wasmsign2_wrapper/main.go @@ -7,8 +7,6 @@ import ( "os/exec" "path/filepath" "strings" - - "github.com/bazelbuild/rules_go/go/runfiles" ) // Wrapper for wasmsign2 WASM component @@ -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 @@ -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="): @@ -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) } diff --git a/wasm/private/BUILD.bazel b/wasm/private/BUILD.bazel index 8f812a11..3a09f646 100644 --- a/wasm/private/BUILD.bazel +++ b/wasm/private/BUILD.bazel @@ -68,6 +68,15 @@ 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"], @@ -75,7 +84,10 @@ bzl_library( "//docs:__pkg__", "//wasm:__pkg__", ], - deps = ["//providers"], + deps = [ + ":wasmsign2_tools", + "//providers", + ], ) bzl_library( @@ -173,5 +185,8 @@ bzl_library( "//docs:__pkg__", "//wasm:__pkg__", ], - deps = ["//providers"], + deps = [ + ":wasmsign2_tools", + "//providers", + ], ) diff --git a/wasm/private/wasm_signing.bzl b/wasm/private/wasm_signing.bzl index 7d14650a..3d77594a 100644 --- a/wasm/private/wasm_signing.bzl +++ b/wasm/private/wasm_signing.bzl @@ -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""" @@ -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, @@ -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. @@ -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] @@ -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. @@ -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 @@ -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. diff --git a/wasm/private/wasmsign2_tools.bzl b/wasm/private/wasmsign2_tools.bzl new file mode 100644 index 00000000..55c88db2 --- /dev/null +++ b/wasm/private/wasmsign2_tools.bzl @@ -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] diff --git a/wasm/private/wsc_attestation.bzl b/wasm/private/wsc_attestation.bzl index 18d2a18a..4b1090d2 100644 --- a/wasm/private/wsc_attestation.bzl +++ b/wasm/private/wsc_attestation.bzl @@ -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", @@ -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" % ( @@ -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 @@ -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, @@ -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 @@ -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, @@ -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