diff --git a/BUILD.bazel b/BUILD.bazel
index 51c7d4351f..64a745a1aa 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -130,6 +130,7 @@ go_config(
export_stdlib = "//go/config:export_stdlib",
gc_goopts = "//go/config:gc_goopts",
gc_linkopts = "//go/config:gc_linkopts",
+ gofips140 = "//go/config:gofips140",
gotags = "//go/config:tags",
linkmode = "//go/config:linkmode",
msan = "//go/config:msan",
diff --git a/docs/go/core/rules.bzl b/docs/go/core/rules.bzl
index 8fdb90e2b2..6c15afb5bc 100644
--- a/docs/go/core/rules.bzl
+++ b/docs/go/core/rules.bzl
@@ -11,6 +11,7 @@
[config_setting]: https://docs.bazel.build/versions/master/be/general.html#config_setting
[data dependencies]: https://bazel.build/concepts/dependencies#data-dependencies
[goarch]: /go/modes.rst#goarch
+ [gofips140]: /go/modes.rst#gofips140
[goos]: /go/modes.rst#goos
[mode attributes]: /go/modes.rst#mode-attributes
[nogo]: /go/nogo.rst#nogo
@@ -58,6 +59,7 @@ sufficient to match the capabilities of the normal go tools.
- [config_setting]
- [data dependencies]
- [goarch]
+- [gofips140]
- [goos]
- [mode attributes]
- [nogo]
diff --git a/docs/go/core/rules.md b/docs/go/core/rules.md
index f084cc81d0..504b08352b 100644
--- a/docs/go/core/rules.md
+++ b/docs/go/core/rules.md
@@ -13,6 +13,7 @@
[config_setting]: https://docs.bazel.build/versions/master/be/general.html#config_setting
[data dependencies]: https://bazel.build/concepts/dependencies#data-dependencies
[goarch]: /go/modes.rst#goarch
+ [gofips140]: /go/modes.rst#gofips140
[goos]: /go/modes.rst#goos
[mode attributes]: /go/modes.rst#mode-attributes
[nogo]: /go/nogo.rst#nogo
@@ -60,6 +61,7 @@ sufficient to match the capabilities of the normal go tools.
- [config_setting]
- [data dependencies]
- [goarch]
+- [gofips140]
- [goos]
- [mode attributes]
- [nogo]
@@ -124,8 +126,8 @@ Rules
go_binary(name, basename, cdeps, cgo, clinkopts, copts, cppopts, cxxopts, data, deps, embed,
- embedsrcs, env, gc_goopts, gc_linkopts, goarch, goos, gotags, importpath, linkmode, msan,
- out, pgoprofile, pure, race, srcs, static, x_defs)
+ embedsrcs, env, gc_goopts, gc_linkopts, goarch, gofips140, goos, gotags, importpath,
+ linkmode, msan, out, pgoprofile, pure, race, srcs, static, x_defs)
This builds an executable from a set of source files,
@@ -136,7 +138,7 @@ This builds an executable from a set of source files,
-
+
### **Attributes**
@@ -159,6 +161,7 @@ This builds an executable from a set of source files,
| gc_goopts | List of flags to add to the Go compilation command when using the gc compiler. Subject to ["Make variable"] substitution and [Bourne shell tokenization]. | List of strings | optional | [] |
| gc_linkopts | List of flags to add to the Go link command when using the gc compiler. Subject to ["Make variable"] substitution and [Bourne shell tokenization]. | List of strings | optional | [] |
| goarch | Forces a binary to be cross-compiled for a specific architecture. It's usually better to control this on the command line with --platforms.
This disables cgo by default, since a cross-compiling C/C++ toolchain is rarely available. To force cgo, set pure = off.
See [Cross compilation] for more information. | String | optional | "auto" |
+| gofips140 | Controls the GOFIPS140 environment variable. May be any string value. Common values include "off" (default), "latest", and specific versions like "v1.0.0". See [mode attributes], specifically [gofips140]. | String | optional | "off" |
| goos | Forces a binary to be cross-compiled for a specific operating system. It's usually better to control this on the command line with --platforms.
This disables cgo by default, since a cross-compiling C/C++ toolchain is rarely available. To force cgo, set pure = off.
See [Cross compilation] for more information. | String | optional | "auto" |
| gotags | Enables a list of build tags when evaluating [build constraints]. Useful for conditional compilation. | List of strings | optional | [] |
| importpath | The import path of this binary. Binaries can't actually be imported, but this may be used by [go_path] and other tools to report the location of source files. This may be inferred from embedded libraries. | String | optional | "" |
@@ -191,7 +194,7 @@ This wraps an executable built by `go_binary` to cross compile it
-
+
### **Attributes**
@@ -226,7 +229,7 @@ This builds a Go library from a set of source files that are all part of
[GoInfo]
[GoArchive]
-
+
### **Attributes**
@@ -269,7 +272,7 @@ go_path(name, data,
[GoInfo]
-
+
### **Attributes**
@@ -363,8 +366,8 @@ This declares a set of source files and related dependencies that can be embedde
go_test(name, cdeps, cgo, clinkopts, copts, cppopts, cxxopts, data, deps, embed, embedsrcs, env,
- env_inherit, gc_goopts, gc_linkopts, goarch, goos, gotags, importpath, linkmode, msan, pure,
- race, rundir, srcs, static, x_defs)
+ env_inherit, gc_goopts, gc_linkopts, goarch, gofips140, goos, gotags, importpath, linkmode,
+ msan, pure, race, rundir, srcs, static, x_defs)
This builds a set of tests that can be run with `bazel test`.
@@ -396,7 +399,7 @@ This builds a set of tests that can be run with `bazel test`.
the name based on the last component of the path. For example, a test
in `//foo/bar` is named `bar_test`, and uses internal and external
sources.
-
+
### **Attributes**
@@ -419,6 +422,7 @@ This builds a set of tests that can be run with `bazel test`.
| gc_goopts | List of flags to add to the Go compilation command when using the gc compiler. Subject to ["Make variable"] substitution and [Bourne shell tokenization]. | List of strings | optional | [] |
| gc_linkopts | List of flags to add to the Go link command when using the gc compiler. Subject to ["Make variable"] substitution and [Bourne shell tokenization]. | List of strings | optional | [] |
| goarch | Forces a binary to be cross-compiled for a specific architecture. It's usually better to control this on the command line with --platforms.
This disables cgo by default, since a cross-compiling C/C++ toolchain is rarely available. To force cgo, set pure = off.
See [Cross compilation] for more information. | String | optional | "auto" |
+| gofips140 | Controls the GOFIPS140 environment variable. May be any string value. Common values include "off" (default), "latest", and specific versions like "v1.0.0". See [mode attributes], specifically [gofips140]. | String | optional | "off" |
| goos | Forces a binary to be cross-compiled for a specific operating system. It's usually better to control this on the command line with --platforms.
This disables cgo by default, since a cross-compiling C/C++ toolchain is rarely available. To force cgo, set pure = off.
See [Cross compilation] for more information. | String | optional | "auto" |
| gotags | Enables a list of build tags when evaluating [build constraints]. Useful for conditional compilation. | List of strings | optional | [] |
| importpath | The import path of this test. Tests can't actually be imported, but this may be used by [go_path] and other tools to report the location of source files. This may be inferred from embedded libraries. | String | optional | "" |
diff --git a/go/config/BUILD.bazel b/go/config/BUILD.bazel
index 5b1ccc0087..d19f801b38 100644
--- a/go/config/BUILD.bazel
+++ b/go/config/BUILD.bazel
@@ -33,6 +33,12 @@ bool_flag(
visibility = ["//visibility:public"],
)
+string_flag(
+ name = "gofips140",
+ build_setting_default = "off",
+ visibility = ["//visibility:public"],
+)
+
bool_flag(
name = "debug",
build_setting_default = False,
diff --git a/go/core.rst b/go/core.rst
index 03fddb4d02..89f2bf183f 100644
--- a/go/core.rst
+++ b/go/core.rst
@@ -13,6 +13,7 @@ Core Go rules
.. _config_setting: https://docs.bazel.build/versions/master/be/general.html#config_setting
.. _data dependencies: https://bazel.build/concepts/dependencies#data-dependencies
.. _goarch: modes.rst#goarch
+.. _gofips140: modes.rst#gofips140
.. _goos: modes.rst#goos
.. _mode attributes: modes.rst#mode-attributes
.. _nogo: nogo.rst#nogo
diff --git a/go/modes.rst b/go/modes.rst
index 044d2eb19f..55b1e04e9b 100644
--- a/go/modes.rst
+++ b/go/modes.rst
@@ -69,6 +69,13 @@ or using `Bazel configuration transitions`_.
| ``CGO_ENABLED=0``). Packages that contain cgo code may still be built, but |
| the cgo code will be filtered out, and the ``cgo`` build tag will be false. |
+------------------------+---------------------+-------------------------------+
+| :param:`gofips140` | :type:`string` | :value:`"off"` |
++------------------------+---------------------+-------------------------------+
+| Controls the ``GOFIPS140`` environment variable used by Go 1.24+ to select |
+| the version of the Go Cryptographic Module. Can be set to ``"off"`` |
+| (default), ``"latest"``, or a specific version like ``"v1.0.0"``. |
+| See the `Go 1.24 FIPS 140-3 documentation`_ for more details. |
++------------------------+---------------------+-------------------------------+
| :param:`debug` | :type:`bool` | :value:`false` |
+------------------------+---------------------+-------------------------------+
| Includes debugging information in compiled packages (using the ``-N`` and |
diff --git a/go/private/context.bzl b/go/private/context.bzl
index f12fd0070b..6b463cc87c 100644
--- a/go/private/context.bzl
+++ b/go/private/context.bzl
@@ -452,6 +452,7 @@ default_go_config_info = GoConfigInfo(
race = False,
msan = False,
pure = False,
+ gofips140 = "off",
strip = False,
debug = False,
linkmode = LINKMODE_NORMAL,
@@ -541,6 +542,7 @@ def go_context(
"GOROOT": goroot,
"GOROOT_FINAL": "GOROOT",
"CGO_ENABLED": "0" if mode.pure else "1",
+ "GOFIPS140": mode.gofips140,
# If we use --action_env=GOPATH, or in other cases where environment
# variables are passed through to this builder, the SDK build will try
@@ -988,6 +990,7 @@ def _go_config_impl(ctx):
race = race,
msan = msan,
pure = ctx.attr.pure[BuildSettingInfo].value,
+ gofips140 = ctx.attr.gofips140[BuildSettingInfo].value,
strip = ctx.attr.strip,
debug = ctx.attr.debug[BuildSettingInfo].value,
linkmode = ctx.attr.linkmode[BuildSettingInfo].value,
@@ -1024,6 +1027,10 @@ go_config = rule(
mandatory = True,
providers = [BuildSettingInfo],
),
+ "gofips140": attr.label(
+ mandatory = True,
+ providers = [BuildSettingInfo],
+ ),
"strip": attr.bool(mandatory = True),
"debug": attr.label(
mandatory = True,
diff --git a/go/private/mode.bzl b/go/private/mode.bzl
index 19e288dd50..b1368fcda2 100644
--- a/go/private/mode.bzl
+++ b/go/private/mode.bzl
@@ -48,6 +48,8 @@ def mode_string(mode):
result.append("msan")
if mode.pure:
result.append("pure")
+ if mode.gofips140 != "off":
+ result.append("gofips140=" + mode.gofips140)
if mode.debug:
result.append("debug")
if mode.strip:
diff --git a/go/private/rules/binary.bzl b/go/private/rules/binary.bzl
index 0ec4b26c4a..e197fec6d2 100644
--- a/go/private/rules/binary.bzl
+++ b/go/private/rules/binary.bzl
@@ -373,6 +373,13 @@ def _go_binary_kwargs(go_cc_aspects = []):
[pure].
""",
),
+ "gofips140": attr.string(
+ default = "off",
+ doc = """Controls the GOFIPS140 environment variable. May be any string value.
+ Common values include `"off"` (default), `"latest"`, and specific versions like `"v1.0.0"`.
+ See [mode attributes], specifically [gofips140].
+ """,
+ ),
"static": attr.string(
default = "auto",
doc = """Controls whether a binary is statically linked. May be one of `on`,
diff --git a/go/private/rules/test.bzl b/go/private/rules/test.bzl
index 46ae669633..997b935582 100644
--- a/go/private/rules/test.bzl
+++ b/go/private/rules/test.bzl
@@ -407,6 +407,13 @@ _go_test_kwargs = {
[pure].
""",
),
+ "gofips140": attr.string(
+ default = "off",
+ doc = """Controls the GOFIPS140 environment variable. May be any string value.
+ Common values include `"off"` (default), `"latest"`, and specific versions like `"v1.0.0"`.
+ See [mode attributes], specifically [gofips140].
+ """,
+ ),
"static": attr.string(
default = "auto",
doc = """Controls whether a binary is statically linked. May be one of `on`,
diff --git a/go/private/rules/transition.bzl b/go/private/rules/transition.bzl
index 83909ec53c..7816d5e66f 100644
--- a/go/private/rules/transition.bzl
+++ b/go/private/rules/transition.bzl
@@ -40,6 +40,7 @@ TRANSITIONED_GO_SETTING_KEYS = [
"//go/config:msan",
"//go/config:race",
"//go/config:pure",
+ "//go/config:gofips140",
"//go/config:linkmode",
"//go/config:tags",
"//go/config:pgoprofile",
@@ -109,6 +110,10 @@ def _go_transition_impl(settings, attr):
if tags:
settings["//go/config:tags"] = _deduped_and_sorted(tags)
+ gofips140 = getattr(attr, "gofips140", "off")
+ if gofips140 != "off":
+ settings["//go/config:gofips140"] = gofips140
+
linkmode = getattr(attr, "linkmode", "auto")
if linkmode != "auto":
if linkmode not in LINKMODES:
@@ -198,6 +203,7 @@ _common_reset_transition_dict = dict({
"//go/config:msan": False,
"//go/config:race": False,
"//go/config:pure": False,
+ "//go/config:gofips140": "off",
"//go/config:debug": False,
"//go/config:linkmode": LINKMODE_NORMAL,
"//go/config:tags": [],
@@ -214,6 +220,7 @@ _stdlib_keep_keys = sorted([
"//go/config:msan",
"//go/config:race",
"//go/config:pure",
+ "//go/config:gofips140",
"//go/config:linkmode",
"//go/config:tags",
"//go/config:pgoprofile",
diff --git a/tests/core/go_binary/BUILD.bazel b/tests/core/go_binary/BUILD.bazel
index d437868350..f1db80d2e9 100644
--- a/tests/core/go_binary/BUILD.bazel
+++ b/tests/core/go_binary/BUILD.bazel
@@ -233,6 +233,11 @@ go_bazel_test(
embedsrcs = ["pgo.pprof"],
)
+go_bazel_test(
+ name = "gofips140_test",
+ srcs = ["gofips140_test.go"],
+)
+
# Tests using .syso files in go_binary both transitively and directly.
go_binary(
name = "meaning",
diff --git a/tests/core/go_binary/gofips140_test.go b/tests/core/go_binary/gofips140_test.go
new file mode 100644
index 0000000000..b5d4381e0e
--- /dev/null
+++ b/tests/core/go_binary/gofips140_test.go
@@ -0,0 +1,87 @@
+// Copyright 2025 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package gofips140_test
+
+import (
+ "bytes"
+ "testing"
+
+ "github.com/bazelbuild/rules_go/go/tools/bazel_testing"
+)
+
+func TestMain(m *testing.M) {
+ bazel_testing.TestMain(m, bazel_testing.Args{
+ Main: `
+-- BUILD.bazel --
+load("@io_bazel_rules_go//go:def.bzl", "go_binary")
+
+go_binary(
+ name = "gofips140_off",
+ srcs = ["gofips140.go"],
+ gofips140 = "off",
+)
+
+go_binary(
+ name = "gofips140_latest",
+ srcs = ["gofips140.go"],
+ gofips140 = "latest",
+)
+
+go_binary(
+ name = "gofips140_version",
+ srcs = ["gofips140.go"],
+ gofips140 = "v1.0.0",
+)
+
+-- gofips140.go --
+package main
+
+import (
+ "crypto/fips140"
+ "fmt"
+)
+
+func main() {
+ fmt.Printf("%t", fips140.Enabled())
+}
+`,
+ })
+}
+
+// TestGOFIPS140Attribute checks that the gofips140 attribute on go_binary
+// controls the GOFIPS140 environment variable.
+func TestGOFIPS140Attribute(t *testing.T) {
+ tests := []struct {
+ target string
+ want string
+ }{
+ {"//:gofips140_off", "false"},
+ {"//:gofips140_latest", "true"},
+ {"//:gofips140_version", "true"},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.target, func(t *testing.T) {
+ out, err := bazel_testing.BazelOutput("run", tt.target)
+ if err != nil {
+ t.Fatalf("running %s: %v", tt.target, err)
+ }
+ got := string(bytes.TrimSpace(out))
+ if got != tt.want {
+ t.Fatalf("got %q; want %q", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/tests/core/transition/cmdline_test.go b/tests/core/transition/cmdline_test.go
index 1de0b3f593..69f52e3db6 100644
--- a/tests/core/transition/cmdline_test.go
+++ b/tests/core/transition/cmdline_test.go
@@ -35,6 +35,11 @@ go_binary(
],
)
+go_binary(
+ name = "gofips140_test",
+ srcs = ["gofips140.go"],
+)
+
-- not_pure.go --
// +build cgo
@@ -56,6 +61,18 @@ import "fmt"
func main() {
fmt.Println("pure")
}
+
+-- gofips140.go --
+package main
+
+import (
+ "crypto/fips140"
+ "fmt"
+)
+
+func main() {
+ fmt.Printf("%t", fips140.Enabled())
+}
`,
})
}
@@ -82,3 +99,37 @@ func TestPure(t *testing.T) {
t.Fatalf("got %q; want %q", got, want)
}
}
+
+// TestGOFIPS140 checks that the --@io_bazel_rules_go//go/config:gofips140 flag
+// controls the GOFIPS140 environment variable.
+func TestGOFIPS140(t *testing.T) {
+ // Test default value (should be "off")
+ out, err := bazel_testing.BazelOutput("run", "//:gofips140_test")
+ if err != nil {
+ t.Fatalf("running //:gofips140_test without flag: %v", err)
+ }
+ got := string(bytes.TrimSpace(out))
+ if want := "false"; got != want {
+ t.Fatalf("got %q; want %q", got, want)
+ }
+
+ // Test with "latest" value
+ out, err = bazel_testing.BazelOutput("run", "--@io_bazel_rules_go//go/config:gofips140=latest", "//:gofips140_test")
+ if err != nil {
+ t.Fatalf("running //:gofips140_test with gofips140=latest: %v", err)
+ }
+ got = string(bytes.TrimSpace(out))
+ if want := "true"; got != want {
+ t.Fatalf("got %q; want %q", got, want)
+ }
+
+ // Test with specific version
+ out, err = bazel_testing.BazelOutput("run", "--@io_bazel_rules_go//go/config:gofips140=v1.0.0", "//:gofips140_test")
+ if err != nil {
+ t.Fatalf("running //:gofips140_test with gofips140=v1.0.0: %v", err)
+ }
+ got = string(bytes.TrimSpace(out))
+ if want := "true"; got != want {
+ t.Fatalf("got %q; want %q", got, want)
+ }
+}