Remove dep on safeopen (#1522)
This repo's readme recommends using the newer APIs from go itself for
this instead.
diff --git a/MODULE.bazel b/MODULE.bazel
index 4ce46ea..9aeecba 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -20,15 +20,10 @@
go_deps = use_extension("@bazel_gazelle//:extensions.bzl", "go_deps")
go_deps.from_file(go_mod = "//:go.mod")
-go_deps.gazelle_override(
- build_file_generation = "clean",
- path = "github.com/google/safeopen",
-)
use_repo(
go_deps,
"com_github_golang_protobuf",
"com_github_google_go_cmp",
- "com_github_google_safeopen",
"net_starlark_go",
"org_golang_google_protobuf",
"org_golang_x_tools",
diff --git a/WORKSPACE b/WORKSPACE
index db09952..5383fc1 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -15,7 +15,7 @@
go_rules_dependencies()
-go_register_toolchains(version = "1.20.3")
+go_register_toolchains(version = "1.24.0")
http_archive(
name = "bazel_gazelle",
diff --git a/file/BUILD.bazel b/file/BUILD.bazel
index edaafa2..21f5552 100644
--- a/file/BUILD.bazel
+++ b/file/BUILD.bazel
@@ -1,14 +1,11 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library")
+load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "file",
srcs = ["file.go"],
importpath = "github.com/bazel-contrib/buildtools/v10/file",
visibility = ["//visibility:public"],
- deps = [
- "//wspace",
- "@com_github_google_safeopen//:safeopen",
- ],
+ deps = ["//wspace"],
)
alias(
@@ -16,3 +13,9 @@
actual = ":file",
visibility = ["//visibility:public"],
)
+
+go_test(
+ name = "file_test",
+ srcs = ["file_test.go"],
+ embed = [":file"],
+)
diff --git a/file/file.go b/file/file.go
index cbf387a..5b2331f 100644
--- a/file/file.go
+++ b/file/file.go
@@ -25,7 +25,6 @@
"runtime"
"github.com/bazel-contrib/buildtools/v10/wspace"
- "github.com/google/safeopen"
)
// ReadFile can be updated from the caller to change the API
@@ -74,7 +73,7 @@
if err != nil {
return err
}
- return safeopen.WriteFileBeneath(wsRoot, relPath, data, mode)
+ return writeFileBeneath(wsRoot, relPath, data, mode)
}
dir, file := filepath.Split(name)
absDir, err := filepath.Abs(dir)
@@ -82,7 +81,26 @@
return err
}
// If we are not in a workspace, we only allow writes to the directory where the file is located.
- return safeopen.WriteFileBeneath(absDir, file, data, mode)
+ return writeFileBeneath(absDir, file, data, mode)
+}
+
+func writeFileBeneath(dir, name string, data []byte, mode os.FileMode) error {
+ root, err := os.OpenRoot(dir)
+ if err != nil {
+ return err
+ }
+ defer root.Close()
+
+ f, err := root.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode)
+ if err != nil {
+ return err
+ }
+ _, err = f.Write(data)
+ if err != nil {
+ f.Close()
+ return err
+ }
+ return f.Close()
}
// openReadFile is like os.Open.
diff --git a/file/file_test.go b/file/file_test.go
new file mode 100644
index 0000000..531dd68
--- /dev/null
+++ b/file/file_test.go
@@ -0,0 +1,51 @@
+/*
+Copyright 2026 Google LLC
+
+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
+
+ https://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 file
+
+import (
+ "os"
+ "path/filepath"
+ "testing"
+)
+
+func TestWriteFileBeneath(t *testing.T) {
+ root := t.TempDir()
+ outside := t.TempDir()
+ if err := os.Mkdir(filepath.Join(root, "subdir"), 0755); err != nil {
+ t.Fatal(err)
+ }
+ if err := os.Symlink("subdir", filepath.Join(root, "inside")); err != nil {
+ t.Fatal(err)
+ }
+ if err := os.Symlink(outside, filepath.Join(root, "outside")); err != nil {
+ t.Fatal(err)
+ }
+
+ if err := writeFileBeneath(root, "inside/file", []byte("data"), 0644); err != nil {
+ t.Fatalf("write through symlink inside root: %v", err)
+ }
+ if got, err := os.ReadFile(filepath.Join(root, "subdir", "file")); err != nil || string(got) != "data" {
+ t.Fatalf("read written file: got %q, %v", got, err)
+ }
+
+ if err := writeFileBeneath(root, "outside/file", []byte("data"), 0644); err == nil {
+ t.Fatal("write through symlink outside root succeeded")
+ }
+ if _, err := os.Stat(filepath.Join(outside, "file")); !os.IsNotExist(err) {
+ t.Fatalf("file was created outside root: %v", err)
+ }
+}
diff --git a/go.mod b/go.mod
index b925f56..de02dda 100644
--- a/go.mod
+++ b/go.mod
@@ -7,14 +7,10 @@
require (
github.com/golang/protobuf v1.5.4
github.com/google/go-cmp v0.7.0
- github.com/google/safeopen v0.0.0-20260327150837-43626d6f4685
go.starlark.net v0.0.0-20210223155950-e043a3d3c984
google.golang.org/protobuf v1.36.10
)
-require (
- golang.org/x/sys v0.48.0 // indirect
- golang.org/x/tools v0.50.0 // indirect
-)
+require golang.org/x/tools v0.50.0 // indirect
tool golang.org/x/tools/cmd/goyacc
diff --git a/go.sum b/go.sum
index eb4b975..5022688 100644
--- a/go.sum
+++ b/go.sum
@@ -27,8 +27,6 @@
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
-github.com/google/safeopen v0.0.0-20260327150837-43626d6f4685 h1:NcJjfIYRDHuboRtptwjtdQflVDKgXSqTIfwu9PpE9uo=
-github.com/google/safeopen v0.0.0-20260327150837-43626d6f4685/go.mod h1:D59KewtQCiD2Avi8N/v2zb/xTYaefwJl+ux2ejB58GQ=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
go.starlark.net v0.0.0-20210223155950-e043a3d3c984 h1:xwwDQW5We85NaTk2APgoN9202w/l0DVGp+GZMfsrh7s=
go.starlark.net v0.0.0-20210223155950-e043a3d3c984/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0=