Skip to content

Commit 2d78b23

Browse files
author
Tamas Vajk
committed
Add cargo-auditable support for dependency SBOM embedding
Embeds cargo-auditable compatible dependency metadata (.dep-v0 section) into Rust binaries and shared libraries. When enabled via --@rules_rust//rust/settings:auditable=true, the build generates a JSON dependency manifest, zlib-compresses it, and links it into the binary as a .dep-v0 ELF section. This enables vulnerability scanning tools like trivy/syft to extract dependency information from compiled binaries. Key changes: - New auditable_injector tool that generates platform-appropriate .dep-v0 object files from JSON dependency manifests - CrateInfo provider extended with pkg_name, version, and source fields - New bool_flag //rust/settings:auditable to control the feature - rust_binary and rust_shared_library gain an auditable_injector attribute - Analysis tests verify action graph correctness
1 parent 175bf94 commit 2d78b23

38 files changed

Lines changed: 2742 additions & 10 deletions

MODULE.bazel

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ bazel_dep(name = "apple_support", version = "1.24.1")
2020
internal_deps = use_extension("//rust/private:internal_extensions.bzl", "i")
2121
use_repo(
2222
internal_deps,
23+
"rra",
24+
"rra__miniz_oxide-0.9.1",
25+
"rra__object-0.39.1",
2326
"rrra",
2427
"rrra__anyhow-1.0.102",
2528
"rrra__camino-1.2.2",

cargo/private/BUILD.bazel

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
22
load("@bazel_skylib//rules:copy_file.bzl", "copy_file")
3-
load("//rust:defs.bzl", "rust_binary")
3+
4+
# Loads rust_binary directly from rust/private:rust.bzl (not //rust:defs.bzl)
5+
# to avoid a dependency cycle with the auditable_injector.
6+
# buildifier: disable=bzl-visibility
7+
load("//rust/private:rust.bzl", "rust_binary")
48

59
rust_binary(
610
name = "copy_file",

cargo/private/cargo_build_script_runner/BUILD.bazel

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
load("//rust:defs.bzl", "rust_binary", "rust_library", "rust_test")
1+
load("//rust:defs.bzl", "rust_library", "rust_test")
2+
3+
# Loads rust_binary directly from rust/private:rust.bzl (not //rust:defs.bzl)
4+
# to avoid a dependency cycle with the auditable_injector.
5+
# buildifier: disable=bzl-visibility
6+
load("//rust/private:rust.bzl", "rust_binary")
27

38
rust_library(
49
name = "cargo_build_script_runner",

cargo/private/cargo_build_script_wrapper.bzl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ load(
77
"name_to_pkg_name",
88
_build_script_run = "cargo_build_script",
99
)
10-
load("//rust:defs.bzl", "rust_binary")
10+
11+
# Loads rust_binary directly from rust/private:rust.bzl (not //rust:defs.bzl)
12+
# to avoid a dependency cycle with the auditable_injector.
13+
# buildifier: disable=bzl-visibility
14+
load("//rust/private:rust.bzl", "rust_binary")
1115

1216
def cargo_build_script(
1317
*,

cargo/private/cargo_toml_variable_extractor/BUILD.bazel

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
load("//rust:defs.bzl", "rust_binary")
1+
# Loads rust_binary directly from rust/private:rust.bzl (not //rust:defs.bzl)
2+
# to avoid a dependency cycle with the auditable_injector.
3+
# buildifier: disable=bzl-visibility
4+
load("//rust/private:rust.bzl", "rust_binary")
25

36
rust_binary(
47
name = "cargo_toml_variable_extractor",

rust/defs.bzl

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,23 @@ rust_library = _rust_library
8484
rust_static_library = _rust_static_library
8585
# See @rules_rust//rust/private:rust.bzl for a complete description.
8686

87-
rust_shared_library = _rust_shared_library
88-
# See @rules_rust//rust/private:rust.bzl for a complete description.
87+
_AUDITABLE_INJECTOR_SELECT = select({
88+
str(Label("//rust/settings:auditable_enabled")): str(Label("//tools/auditable:auditable_injector")),
89+
"//conditions:default": None,
90+
})
91+
92+
def rust_shared_library(name, **kwargs):
93+
"""Builds a Rust shared library. See @rules_rust//rust/private:rust.bzl for a complete description."""
94+
kwargs.setdefault("auditable_injector", _AUDITABLE_INJECTOR_SELECT)
95+
_rust_shared_library(name = name, **kwargs)
8996

9097
rust_proc_macro = _rust_proc_macro
9198
# See @rules_rust//rust/private:rust.bzl for a complete description.
9299

93-
rust_binary = _rust_binary
94-
# See @rules_rust//rust/private:rust.bzl for a complete description.
100+
def rust_binary(name, **kwargs):
101+
"""Builds a Rust binary crate. See @rules_rust//rust/private:rust.bzl for a complete description."""
102+
kwargs.setdefault("auditable_injector", _AUDITABLE_INJECTOR_SELECT)
103+
_rust_binary(name = name, **kwargs)
95104

96105
rust_library_group = _rust_library_group
97106
# See @rules_rust//rust/private:rust.bzl for a complete description.

rust/private/common.bzl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ def _create_crate_info(**kwargs):
6161
kwargs.update({"rustc_env_files": []})
6262
if not "data" in kwargs:
6363
kwargs.update({"data": depset([])})
64+
if not "pkg_name" in kwargs:
65+
kwargs.update({"pkg_name": kwargs.get("name", "")})
66+
if not "version" in kwargs:
67+
kwargs.update({"version": "0.0.0"})
68+
if not "source" in kwargs:
69+
kwargs.update({"source": "Local"})
6470
return CrateInfo(**kwargs)
6571

6672
rust_common = struct(

rust/private/internal_extensions.bzl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
load("@bazel_features//:features.bzl", "bazel_features")
44
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
55
load("//rust/private:repository_utils.bzl", "TINYJSON_KWARGS")
6+
load("//tools/auditable/3rdparty/crates:crates.bzl", _auditable_crate_repositories = "crate_repositories")
67
load("//tools/rust_analyzer/3rdparty/crates:crates.bzl", _rust_analyzer_crate_repositories = "crate_repositories")
78

89
def _internal_deps_impl(module_ctx):
910
direct_deps = [struct(repo = "rules_rust_tinyjson", is_dev_dep = False)]
1011
http_archive(**TINYJSON_KWARGS)
1112

13+
direct_deps.extend(_auditable_crate_repositories())
1214
direct_deps.extend(_rust_analyzer_crate_repositories())
1315

1416
# is_dev_dep is ignored here. It's not relevant for internal_deps, as dev

rust/private/providers.bzl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,21 @@ CrateInfo = provider(
3434
"name": "str: The name of this crate.",
3535
"output": "File: The output File that will be produced, depends on crate type.",
3636
"owner": "Label: The label of the target that produced this CrateInfo",
37+
"pkg_name": "str: The Cargo package name, which may differ from the Rust crate name (e.g. 'rustls-webpki' vs 'webpki').",
3738
"proc_macro_deps": "depset[DepVariantInfo]: This crate's rust proc_macro dependencies' providers.",
3839
"root": "File: The source File entrypoint to this crate, eg. lib.rs",
3940
"rustc_env": "Dict[String, String]: Additional `\"key\": \"value\"` environment variables to set for rustc.",
4041
"rustc_env_files": "[File]: Files containing additional environment variables to set for rustc.",
4142
"rustc_output": "File: The output from rustc from producing the output file. It is optional.",
4243
"rustc_rmeta_output": "File: The rmeta file produced for this crate. It is optional.",
44+
"source": "str: The source of this crate (e.g. 'CratesIo', 'Git', 'Local', 'Registry').",
4345
"srcs": "depset[File]: All source Files that are part of the crate.",
4446
"std_dylib": "File: libstd.so file",
4547
"type": (
4648
"str: The type of this crate " +
4749
"(see [rustc --crate-type](https://doc.rust-lang.org/rustc/command-line-arguments.html#--crate-type-a-list-of-types-of-crates-for-the-compiler-to-emit))."
4850
),
51+
"version": "str: The semver version of this crate.",
4952
"wrapped_crate_type": (
5053
"str, optional: The original crate type for targets generated using a previously defined " +
5154
"crate (typically tests using the `rust_test::crate` attribute)"

rust/private/rust.bzl

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,17 @@ load(
5757

5858
# TODO(marco): Separate each rule into its own file.
5959

60+
def _get_pkg_name(ctx):
61+
"""Extract the Cargo package name from a 'crate-name=...' tag.
62+
63+
crate_universe adds this tag to vendored crates. Returns empty string if
64+
not found, in which case _create_crate_info defaults to the crate name.
65+
"""
66+
for tag in getattr(ctx.attr, "tags", []):
67+
if tag.startswith("crate-name="):
68+
return tag[len("crate-name="):]
69+
return ""
70+
6071
def _assert_no_deprecated_attributes(_ctx):
6172
"""Forces a failure if any deprecated attributes were specified
6273
@@ -245,6 +256,9 @@ def _rust_library_common(ctx, crate_type):
245256
compile_data = depset(compile_data),
246257
compile_data_targets = depset(ctx.attr.compile_data),
247258
owner = ctx.label,
259+
version = getattr(ctx.attr, "version", "0.0.0"),
260+
source = getattr(ctx.attr, "source", "Local"),
261+
pkg_name = _get_pkg_name(ctx),
248262
cfgs = _collect_cfgs(ctx, toolchain, crate_root, crate_type, crate_is_test = False),
249263
),
250264
)
@@ -309,6 +323,9 @@ def _rust_binary_impl(ctx):
309323
compile_data = depset(compile_data),
310324
compile_data_targets = depset(ctx.attr.compile_data),
311325
owner = ctx.label,
326+
version = getattr(ctx.attr, "version", "0.0.0"),
327+
source = getattr(ctx.attr, "source", "Local"),
328+
pkg_name = _get_pkg_name(ctx),
312329
cfgs = _collect_cfgs(ctx, toolchain, crate_root, ctx.attr.crate_type, crate_is_test = False),
313330
),
314331
)
@@ -431,6 +448,9 @@ def _rust_test_impl(ctx):
431448
compile_data_targets = compile_data_targets,
432449
wrapped_crate_type = crate.type,
433450
owner = ctx.label,
451+
version = getattr(ctx.attr, "version", "0.0.0"),
452+
source = getattr(ctx.attr, "source", "Local"),
453+
pkg_name = _get_pkg_name(ctx),
434454
cfgs = _collect_cfgs(ctx, toolchain, crate.root, crate_type, crate_is_test = True),
435455
)
436456
else:
@@ -487,6 +507,9 @@ def _rust_test_impl(ctx):
487507
compile_data = depset(compile_data),
488508
compile_data_targets = depset(ctx.attr.compile_data),
489509
owner = ctx.label,
510+
version = getattr(ctx.attr, "version", "0.0.0"),
511+
source = getattr(ctx.attr, "source", "Local"),
512+
pkg_name = _get_pkg_name(ctx),
490513
cfgs = _collect_cfgs(ctx, toolchain, crate_root, crate_type, crate_is_test = True),
491514
)
492515

@@ -605,6 +628,9 @@ RUSTC_ATTRS = {
605628
"_always_enable_metadata_output_groups": attr.label(
606629
default = Label("//rust/settings:always_enable_metadata_output_groups"),
607630
),
631+
"_auditable": attr.label(
632+
default = Label("//rust/settings:auditable"),
633+
),
608634
"_error_format": attr.label(
609635
default = Label("//rust/settings:error_format"),
610636
),
@@ -791,6 +817,10 @@ _COMMON_ATTRS = {
791817
# "name": attr.string(
792818
# doc = "This name will also be used as the name of the crate built by this rule.",
793819
# `),
820+
"source": attr.string(
821+
doc = "The source of this crate (e.g. 'crates.io', 'git', 'local', 'registry'). Used for cargo-auditable dependency tracking.",
822+
default = "Local",
823+
),
794824
"srcs": attr.label_list(
795825
doc = dedent("""\
796826
List of Rust `.rs` source files used to build the library.
@@ -1082,9 +1112,17 @@ _rust_shared_library_transition = transition(
10821112
],
10831113
)
10841114

1115+
_AUDITABLE_INJECTOR_ATTR = {
1116+
"auditable_injector": attr.label(
1117+
doc = "The auditable_injector tool for embedding cargo-auditable metadata. " +
1118+
"Set to @rules_rust//tools/auditable:auditable_injector to enable.",
1119+
cfg = "exec",
1120+
),
1121+
}
1122+
10851123
rust_shared_library = rule(
10861124
implementation = _rust_shared_library_impl,
1087-
attrs = _COMMON_ATTRS | _PLATFORM_ATTRS | _EXPERIMENTAL_USE_CC_COMMON_LINK_ATTRS,
1125+
attrs = _COMMON_ATTRS | _PLATFORM_ATTRS | _EXPERIMENTAL_USE_CC_COMMON_LINK_ATTRS | _AUDITABLE_INJECTOR_ATTR,
10881126
fragments = ["cpp"],
10891127
cfg = _rust_shared_library_transition,
10901128
toolchains = [
@@ -1207,7 +1245,7 @@ _rust_binary_transition = transition(
12071245
rust_binary = rule(
12081246
implementation = _rust_binary_impl,
12091247
provides = COMMON_PROVIDERS,
1210-
attrs = _COMMON_ATTRS | _RUST_BINARY_ATTRS | _PLATFORM_ATTRS,
1248+
attrs = _COMMON_ATTRS | _RUST_BINARY_ATTRS | _PLATFORM_ATTRS | _AUDITABLE_INJECTOR_ATTR,
12111249
executable = True,
12121250
fragments = ["cpp"],
12131251
cfg = _rust_binary_transition,

0 commit comments

Comments
 (0)