1+ "Validate that the protoc binary is authentic and not spoofed by a malicious actor."
2+ load ("//bazel/common:proto_common.bzl" , "proto_common" )
3+ load ("//bazel/private:toolchain_helpers.bzl" , "toolchains" )
4+ load ("//bazel/private:prebuilt_tool_integrity.bzl" , "RELEASE_VERSION" )
5+
6+ def _protoc_authenticity_impl (ctx ):
7+ # When this flag is disabled, then users have no way to replace the protoc binary with their own toolchain registration.
8+ # Therefore there's no validation to perform.
9+ if not proto_common .INCOMPATIBLE_ENABLE_PROTO_TOOLCHAIN_RESOLUTION :
10+ return []
11+ toolchain = ctx .toolchains [toolchains .PROTO_TOOLCHAIN ]
12+ if not toolchain :
13+ fail ("Protocol compiler toolchain could not be resolved." )
14+ proto_lang_toolchain_info = toolchain .proto
15+ validation_output = ctx .actions .declare_file ("validation_output.txt" )
16+
17+ ctx .actions .run_shell (
18+ outputs = [validation_output ],
19+ tools = [proto_lang_toolchain_info .proto_compiler ],
20+ command = """\
21+ {protoc} --version > {validation_output}
22+ grep -q "^libprotoc {RELEASE_VERSION}" {validation_output} || {{
23+ echo >&2 'ERROR: protoc version does not match protobuf Bazel module; we do not support this. To suppress this error, run Bazel with --norun_validations'
24+ exit 1
25+ }}
26+ """ .format (protoc = proto_lang_toolchain_info .proto_compiler .executable .path , validation_output = validation_output .path , RELEASE_VERSION = RELEASE_VERSION .removeprefix ("v" )),
27+ )
28+ return [OutputGroupInfo (_validation = depset ([validation_output ]))]
29+
30+ protoc_authenticity = rule (
31+ implementation = _protoc_authenticity_impl ,
32+ fragments = ["proto" ],
33+ attrs = toolchains .if_legacy_toolchain ({
34+ "_proto_compiler" : attr .label (
35+ cfg = "exec" ,
36+ executable = True ,
37+ allow_files = True ,
38+ default = "//src/google/protobuf/compiler:protoc_minimal" ,
39+ ),
40+ }),
41+ toolchains = toolchains .use_toolchain (toolchains .PROTO_TOOLCHAIN ),
42+ )
0 commit comments