crypto/x509: honor SSL_CERT_{FILE,DIR} on windows/darwin

When these env vars are set, load roots from on-disk, and don't use the
platform verifier.

Fixes #77865

Change-Id: I3183d1636238bed924252fe1288cfa263d17f61d
Reviewed-on: https://go-review.googlesource.com/c/go/+/776940
Reviewed-by: Neal Patel <nealpatel@google.com>
Auto-Submit: Roland Shoemaker <roland@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: David Chase <drchase@google.com>
This commit is contained in:
Roland Shoemaker 2026-05-11 12:30:48 -07:00 committed by Gopher Robot
parent 93da30397d
commit 4bf23b51b8
12 changed files with 404 additions and 366 deletions

View file

@ -170,6 +170,14 @@ to `1`. This opt-out is expected to be kept indefinitely in case goroutine
labels acquire sensitive information that shouldn't be made available in
tracebacks.
Go 1.27 added a new `x509sslcertoverrideplatform` setting that controls whether
crypto/x509 will load roots from disk on Windows and Darwin when `SSL_CERT_FILE`
or `SSL_CERT_DIR` are set. The default value `x509sslcertoverrideplatform=1` will
cause roots to be loaded from disk when these environment variables are set.
Setting `x509sslcertoverrideplatform=0` disables this behavior in favor of using
the platform certificate store instead of honoring the environment variables. We
plan to remove this setting in Go 1.31.
### Go 1.26
Go 1.26 added a new `httpcookiemaxnum` setting that controls the maximum number

View file

@ -0,0 +1,5 @@
[SystemCertPool] now respects SSL_CERT_FILE and SSL_CERT_DIR on Windows and
Darwin. When these environment variables are set, roots are loaded from disk and
instead of using the platform certificate verification APIs, the native Go
verifier is used. This behavior can be disabled with
`GODEBUG=x509sslcertoverrideplatform=0`.

View file

@ -105,10 +105,14 @@ func (s *CertPool) Clone() *CertPool {
// SystemCertPool returns a copy of the system cert pool.
//
// On Unix systems other than macOS the environment variables SSL_CERT_FILE and
// SSL_CERT_DIR can be used to override the system default locations for the SSL
// certificate file and SSL certificate files directory, respectively. The
// latter can be a colon-separated list.
// The environment variables SSL_CERT_FILE and SSL_CERT_DIR can be used to
// override the system default locations for the SSL certificate file and SSL
// certificate files directory, respectively. The latter can be a
// colon-separated list, or a semicolon-separated list on Windows. On platforms
// which have system APIs for certificate verification (macOS and Windows),
// setting SSL_CERT_FILE or SSL_CERT_DIR will prevent those APIs from being
// used, unless the x509sslcertoverrideplatform=0 GODEBUG setting is used. (This
// changed in Go 1.27.)
//
// Any mutations to the returned pool are not written to disk and do not affect
// any other pool returned by SystemCertPool.

View file

@ -6,6 +6,11 @@ package x509
import (
"internal/godebug"
"io/fs"
"os"
"path/filepath"
"runtime"
"strings"
"sync"
_ "unsafe" // for linkname
)
@ -115,3 +120,111 @@ func SetFallbackRoots(roots *CertPool) {
systemRoots, systemRootsErr = roots, nil
}
const (
// certFileEnv is the environment variable which identifies where to locate
// the SSL certificate file. If set this overrides the system default.
certFileEnv = "SSL_CERT_FILE"
// certDirEnv is the environment variable which identifies which directory
// to check for SSL certificate files. If set this overrides the system default.
// See https://docs.openssl.org/4.0/man1/openssl-rehash/#environment.
certDirEnv = "SSL_CERT_DIR"
)
var x509sslcertoverrideplatform = godebug.New("x509sslcertoverrideplatform")
func loadSystemRoots() (*CertPool, error) {
certFilePath, certDirPath := os.Getenv(certFileEnv), os.Getenv(certDirEnv)
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
if certFilePath == "" && certDirPath == "" {
return &CertPool{systemPool: true}, nil
}
if x509sslcertoverrideplatform.Value() == "0" {
x509sslcertoverrideplatform.IncNonDefault()
return &CertPool{systemPool: true}, nil
}
}
return loadOnDiskRoots(certFilePath, certDirPath)
}
func loadOnDiskRoots(certFilePath, certDirPath string) (*CertPool, error) {
roots := NewCertPool()
files := certFiles
if certFilePath != "" {
files = []string{certFilePath}
}
var firstErr error
for _, file := range files {
data, err := os.ReadFile(file)
if err == nil {
roots.AppendCertsFromPEM(data)
break
}
if firstErr == nil && !os.IsNotExist(err) {
firstErr = err
}
}
dirs := certDirectories
if certDirPath != "" {
// OpenSSL and BoringSSL both use ":" as the SSL_CERT_DIR separator on
// Unix-like systems, and ";" on Windows.
// See:
// * https://golang.org/issue/35325
// * https://docs.openssl.org/4.0/man1/openssl-rehash/#environment
dirs = filepath.SplitList(certDirPath)
}
for _, directory := range dirs {
fis, err := readUniqueDirectoryEntries(directory)
if err != nil {
if firstErr == nil && !os.IsNotExist(err) {
firstErr = err
}
continue
}
for _, fi := range fis {
data, err := os.ReadFile(filepath.Join(directory, fi.Name()))
if err == nil {
roots.AppendCertsFromPEM(data)
}
}
}
if roots.len() > 0 || firstErr == nil {
return roots, nil
}
return nil, firstErr
}
// readUniqueDirectoryEntries is like os.ReadDir but omits
// symlinks that point within the directory.
func readUniqueDirectoryEntries(dir string) ([]fs.DirEntry, error) {
files, err := os.ReadDir(dir)
if err != nil {
return nil, err
}
uniq := files[:0]
for _, f := range files {
if !isSameDirSymlink(f, dir) {
uniq = append(uniq, f)
}
}
return uniq, nil
}
// isSameDirSymlink reports whether f in dir is a symlink with a
// target not containing a slash.
func isSameDirSymlink(f fs.DirEntry, dir string) bool {
if f.Type()&fs.ModeSymlink == 0 {
return false
}
target, err := os.Readlink(filepath.Join(dir, f.Name()))
return err == nil && !strings.ContainsRune(target, filepath.Separator)
}

View file

@ -10,6 +10,9 @@ import (
"fmt"
)
// macOS has no default SSL_CERT_{FILE,DIR} paths.
var certFiles, certDirectories []string
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
certs := macos.CFArrayCreateMutable()
defer macos.ReleaseCFArray(certs)
@ -125,7 +128,3 @@ func exportCertificate(cert macos.CFRef) (*Certificate, error) {
}
return ParseCertificate(data)
}
func loadSystemRoots() (*CertPool, error) {
return &CertPool{systemPool: true}, nil
}

View file

@ -6,34 +6,13 @@
package x509
import (
"os"
)
// Possible certificate files; stop after finding one.
var certFiles = []string{
"/sys/lib/tls/ca.pem",
}
var certDirectories = []string{}
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
return nil, nil
}
func loadSystemRoots() (*CertPool, error) {
roots := NewCertPool()
var bestErr error
for _, file := range certFiles {
data, err := os.ReadFile(file)
if err == nil {
roots.AppendCertsFromPEM(data)
return roots, nil
}
if bestErr == nil || (os.IsNotExist(bestErr) && !os.IsNotExist(err)) {
bestErr = err
}
}
if bestErr == nil {
return roots, nil
}
return nil, bestErr
}

View file

@ -5,6 +5,14 @@
package x509
import (
"bytes"
"fmt"
"internal/testenv"
"os"
"path/filepath"
"runtime"
"slices"
"strings"
"testing"
)
@ -108,3 +116,252 @@ func TestFallback(t *testing.T) {
})
}
}
const (
testDirCN = "test-dir"
testFile = "test-file.crt"
testFileCN = "test-file"
testMissing = "missing"
)
func TestEnvVars(t *testing.T) {
tmpDir := t.TempDir()
testCert, err := os.ReadFile("testdata/test-dir.crt")
if err != nil {
t.Fatalf("failed to read test cert: %s", err)
}
if err := os.WriteFile(filepath.Join(tmpDir, testFile), testCert, 0644); err != nil {
t.Fatalf("failed to write test cert: %s", err)
}
testCases := []struct {
name string
fileEnv string
dirEnv string
files []string
dirs []string
cns []string
}{
{
// Environment variables override the default locations preventing fall through.
name: "override-defaults",
fileEnv: testMissing,
dirEnv: testMissing,
files: []string{testFile},
dirs: []string{tmpDir},
cns: nil,
},
{
// File environment overrides default file locations.
name: "file",
fileEnv: testFile,
dirEnv: "",
files: nil,
dirs: nil,
cns: []string{testFileCN},
},
{
// Directory environment overrides default directory locations.
name: "dir",
fileEnv: "",
dirEnv: tmpDir,
files: nil,
dirs: nil,
cns: []string{testDirCN},
},
{
// File & directory environment overrides both default locations.
name: "file+dir",
fileEnv: testFile,
dirEnv: tmpDir,
files: nil,
dirs: nil,
cns: []string{testFileCN, testDirCN},
},
{
// Environment variable empty / unset uses default locations.
name: "empty-fall-through",
fileEnv: "",
dirEnv: "",
files: []string{testFile},
dirs: []string{tmpDir},
cns: []string{testFileCN, testDirCN},
},
}
// Save old settings so we can restore before the test ends.
origCertFiles, origCertDirectories := certFiles, certDirectories
origFile, origDir := os.Getenv(certFileEnv), os.Getenv(certDirEnv)
defer func() {
certFiles = origCertFiles
certDirectories = origCertDirectories
os.Setenv(certFileEnv, origFile)
os.Setenv(certDirEnv, origDir)
}()
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
if err := os.Setenv(certFileEnv, tc.fileEnv); err != nil {
t.Fatalf("setenv %q failed: %v", certFileEnv, err)
}
if err := os.Setenv(certDirEnv, tc.dirEnv); err != nil {
t.Fatalf("setenv %q failed: %v", certDirEnv, err)
}
certFiles, certDirectories = tc.files, tc.dirs
r, err := loadSystemRoots()
if err != nil {
t.Fatal("unexpected failure:", err)
}
if r == nil {
t.Fatal("nil roots")
}
wantSystemPool := (runtime.GOOS == "darwin" || runtime.GOOS == "windows") && tc.dirEnv == "" && tc.fileEnv == ""
if wantSystemPool {
if !r.systemPool {
t.Fatal("expected returned cert pool to be a system pool")
}
if r.len() != 0 {
t.Fatalf("expected empty system pool, pool has %d roots", r.len())
}
return
}
// Verify that the returned certs match, otherwise report where the mismatch is.
for i, cn := range tc.cns {
if i >= r.len() {
t.Errorf("missing cert %v @ %v", cn, i)
} else if r.mustCert(t, i).Subject.CommonName != cn {
fmt.Printf("%#v\n", r.mustCert(t, 0).Subject)
t.Errorf("unexpected cert common name %q, want %q", r.mustCert(t, i).Subject.CommonName, cn)
}
}
if r.len() > len(tc.cns) {
t.Errorf("got %v certs, which is more than %v wanted", r.len(), len(tc.cns))
}
})
}
}
// Ensure that "SSL_CERT_DIR" when used as the environment variable delimited by
// colons on Unix-like systems, and semicolons on Windows, allows
// loadSystemRoots to load all the roots from the respective directories.
// See https://golang.org/issue/35325.
func TestLoadSystemCertsLoadColonSeparatedDirs(t *testing.T) {
origFile, origDir := os.Getenv(certFileEnv), os.Getenv(certDirEnv)
origCertFiles := certFiles[:]
// To prevent any other certs from being loaded in
// through "SSL_CERT_FILE" or from known "certFiles",
// clear them all, and they'll be reverted on defer.
certFiles = certFiles[:0]
os.Setenv(certFileEnv, "")
defer func() {
certFiles = origCertFiles[:]
os.Setenv(certDirEnv, origDir)
os.Setenv(certFileEnv, origFile)
}()
tmpDir := t.TempDir()
rootPEMs := []string{
gtsRoot,
googleLeaf,
}
var certDirs []string
for i, certPEM := range rootPEMs {
certDir := filepath.Join(tmpDir, fmt.Sprintf("cert-%d", i))
if err := os.MkdirAll(certDir, 0755); err != nil {
t.Fatalf("failed to create certificate dir: %v", err)
}
certOutFile := filepath.Join(certDir, "cert.crt")
if err := os.WriteFile(certOutFile, []byte(certPEM), 0655); err != nil {
t.Fatalf("failed to write certificate to file: %v", err)
}
certDirs = append(certDirs, certDir)
}
// Sanity check: the number of certDirs should be equal to the number of roots.
if g, w := len(certDirs), len(rootPEMs); g != w {
t.Fatalf("failed sanity check: len(certsDir)=%d is not equal to len(rootsPEMS)=%d", g, w)
}
// Now finally concatenate them with a colon/semicolon.
concatCertDirs := strings.Join(certDirs, string(filepath.ListSeparator))
os.Setenv(certDirEnv, concatCertDirs)
gotPool, err := loadSystemRoots()
if err != nil {
t.Fatalf("failed to load system roots: %v", err)
}
subjects := gotPool.Subjects()
// We expect exactly len(rootPEMs) subjects back.
if g, w := len(subjects), len(rootPEMs); g != w {
t.Fatalf("invalid number of subjects: got %d want %d", g, w)
}
wantPool := NewCertPool()
for _, certPEM := range rootPEMs {
wantPool.AppendCertsFromPEM([]byte(certPEM))
}
strCertPool := func(p *CertPool) string {
return string(bytes.Join(p.Subjects(), []byte("\n")))
}
if !certPoolEqual(gotPool, wantPool) {
got, want := strCertPool(gotPool), strCertPool(wantPool)
t.Fatalf("mismatched certPools\nGot:\n%s\n\nWant:\n%s", got, want)
}
}
func TestReadUniqueDirectoryEntries(t *testing.T) {
baseTmpDir := t.TempDir()
path := func(base string) string { return filepath.Join(baseTmpDir, base) }
if f, err := os.Create(path("file")); err != nil {
t.Fatal(err)
} else {
f.Close()
}
if err := os.Symlink("target-in", path("link-in")); err != nil {
t.Fatal(err)
}
if err := os.Symlink("../target-out", path("link-out")); err != nil {
t.Fatal(err)
}
got, err := readUniqueDirectoryEntries(baseTmpDir)
if err != nil {
t.Fatal(err)
}
gotNames := []string{}
for _, fi := range got {
gotNames = append(gotNames, fi.Name())
}
wantNames := []string{"file", "link-out"}
if !slices.Equal(gotNames, wantNames) {
t.Errorf("got %q; want %q", gotNames, wantNames)
}
}
func TestSSLCertEnvOverride(t *testing.T) {
testenv.SetGODEBUG(t, "x509sslcertoverrideplatform=0")
t.Setenv(certFileEnv, "/tmp/nope")
t.Setenv(certDirEnv, "/tmp/nope")
p, err := loadSystemRoots()
if err != nil {
t.Fatalf("unexpected failure: %s", err)
}
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
if !p.systemPool {
t.Fatal("x509sslcertoverrideplatform did not override SSL_CERT_{FILE,DIR}")
}
} else if p.systemPool {
t.Fatal("x509sslcertoverrideplatform caused a systemPool to be returned on OS other than windows or darwin")
}
}

View file

@ -6,103 +6,6 @@
package x509
import (
"io/fs"
"os"
"path/filepath"
"strings"
)
const (
// certFileEnv is the environment variable which identifies where to locate
// the SSL certificate file. If set this overrides the system default.
certFileEnv = "SSL_CERT_FILE"
// certDirEnv is the environment variable which identifies which directory
// to check for SSL certificate files. If set this overrides the system default.
// It is a colon separated list of directories.
// See https://www.openssl.org/docs/man1.0.2/man1/c_rehash.html.
certDirEnv = "SSL_CERT_DIR"
)
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
return nil, nil
}
func loadSystemRoots() (*CertPool, error) {
roots := NewCertPool()
files := certFiles
if f := os.Getenv(certFileEnv); f != "" {
files = []string{f}
}
var firstErr error
for _, file := range files {
data, err := os.ReadFile(file)
if err == nil {
roots.AppendCertsFromPEM(data)
break
}
if firstErr == nil && !os.IsNotExist(err) {
firstErr = err
}
}
dirs := certDirectories
if d := os.Getenv(certDirEnv); d != "" {
// OpenSSL and BoringSSL both use ":" as the SSL_CERT_DIR separator.
// See:
// * https://golang.org/issue/35325
// * https://www.openssl.org/docs/man1.0.2/man1/c_rehash.html
dirs = strings.Split(d, ":")
}
for _, directory := range dirs {
fis, err := readUniqueDirectoryEntries(directory)
if err != nil {
if firstErr == nil && !os.IsNotExist(err) {
firstErr = err
}
continue
}
for _, fi := range fis {
data, err := os.ReadFile(directory + "/" + fi.Name())
if err == nil {
roots.AppendCertsFromPEM(data)
}
}
}
if roots.len() > 0 || firstErr == nil {
return roots, nil
}
return nil, firstErr
}
// readUniqueDirectoryEntries is like os.ReadDir but omits
// symlinks that point within the directory.
func readUniqueDirectoryEntries(dir string) ([]fs.DirEntry, error) {
files, err := os.ReadDir(dir)
if err != nil {
return nil, err
}
uniq := files[:0]
for _, f := range files {
if !isSameDirSymlink(f, dir) {
uniq = append(uniq, f)
}
}
return uniq, nil
}
// isSameDirSymlink reports whether fi in dir is a symlink with a
// target not containing a slash.
func isSameDirSymlink(f fs.DirEntry, dir string) bool {
if f.Type()&fs.ModeSymlink == 0 {
return false
}
target, err := os.Readlink(filepath.Join(dir, f.Name()))
return err == nil && !strings.Contains(target, "/")
}

View file

@ -1,235 +0,0 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build dragonfly || freebsd || linux || netbsd || openbsd || solaris
package x509
import (
"bytes"
"fmt"
"os"
"path/filepath"
"slices"
"strings"
"testing"
)
const (
testDirCN = "test-dir"
testFile = "test-file.crt"
testFileCN = "test-file"
testMissing = "missing"
)
func TestEnvVars(t *testing.T) {
tmpDir := t.TempDir()
testCert, err := os.ReadFile("testdata/test-dir.crt")
if err != nil {
t.Fatalf("failed to read test cert: %s", err)
}
if err := os.WriteFile(filepath.Join(tmpDir, testFile), testCert, 0644); err != nil {
t.Fatalf("failed to write test cert: %s", err)
}
testCases := []struct {
name string
fileEnv string
dirEnv string
files []string
dirs []string
cns []string
}{
{
// Environment variables override the default locations preventing fall through.
name: "override-defaults",
fileEnv: testMissing,
dirEnv: testMissing,
files: []string{testFile},
dirs: []string{tmpDir},
cns: nil,
},
{
// File environment overrides default file locations.
name: "file",
fileEnv: testFile,
dirEnv: "",
files: nil,
dirs: nil,
cns: []string{testFileCN},
},
{
// Directory environment overrides default directory locations.
name: "dir",
fileEnv: "",
dirEnv: tmpDir,
files: nil,
dirs: nil,
cns: []string{testDirCN},
},
{
// File & directory environment overrides both default locations.
name: "file+dir",
fileEnv: testFile,
dirEnv: tmpDir,
files: nil,
dirs: nil,
cns: []string{testFileCN, testDirCN},
},
{
// Environment variable empty / unset uses default locations.
name: "empty-fall-through",
fileEnv: "",
dirEnv: "",
files: []string{testFile},
dirs: []string{tmpDir},
cns: []string{testFileCN, testDirCN},
},
}
// Save old settings so we can restore before the test ends.
origCertFiles, origCertDirectories := certFiles, certDirectories
origFile, origDir := os.Getenv(certFileEnv), os.Getenv(certDirEnv)
defer func() {
certFiles = origCertFiles
certDirectories = origCertDirectories
os.Setenv(certFileEnv, origFile)
os.Setenv(certDirEnv, origDir)
}()
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
if err := os.Setenv(certFileEnv, tc.fileEnv); err != nil {
t.Fatalf("setenv %q failed: %v", certFileEnv, err)
}
if err := os.Setenv(certDirEnv, tc.dirEnv); err != nil {
t.Fatalf("setenv %q failed: %v", certDirEnv, err)
}
certFiles, certDirectories = tc.files, tc.dirs
r, err := loadSystemRoots()
if err != nil {
t.Fatal("unexpected failure:", err)
}
if r == nil {
t.Fatal("nil roots")
}
// Verify that the returned certs match, otherwise report where the mismatch is.
for i, cn := range tc.cns {
if i >= r.len() {
t.Errorf("missing cert %v @ %v", cn, i)
} else if r.mustCert(t, i).Subject.CommonName != cn {
fmt.Printf("%#v\n", r.mustCert(t, 0).Subject)
t.Errorf("unexpected cert common name %q, want %q", r.mustCert(t, i).Subject.CommonName, cn)
}
}
if r.len() > len(tc.cns) {
t.Errorf("got %v certs, which is more than %v wanted", r.len(), len(tc.cns))
}
})
}
}
// Ensure that "SSL_CERT_DIR" when used as the environment
// variable delimited by colons, allows loadSystemRoots to
// load all the roots from the respective directories.
// See https://golang.org/issue/35325.
func TestLoadSystemCertsLoadColonSeparatedDirs(t *testing.T) {
origFile, origDir := os.Getenv(certFileEnv), os.Getenv(certDirEnv)
origCertFiles := certFiles[:]
// To prevent any other certs from being loaded in
// through "SSL_CERT_FILE" or from known "certFiles",
// clear them all, and they'll be reverting on defer.
certFiles = certFiles[:0]
os.Setenv(certFileEnv, "")
defer func() {
certFiles = origCertFiles[:]
os.Setenv(certDirEnv, origDir)
os.Setenv(certFileEnv, origFile)
}()
tmpDir := t.TempDir()
rootPEMs := []string{
gtsRoot,
googleLeaf,
}
var certDirs []string
for i, certPEM := range rootPEMs {
certDir := filepath.Join(tmpDir, fmt.Sprintf("cert-%d", i))
if err := os.MkdirAll(certDir, 0755); err != nil {
t.Fatalf("Failed to create certificate dir: %v", err)
}
certOutFile := filepath.Join(certDir, "cert.crt")
if err := os.WriteFile(certOutFile, []byte(certPEM), 0655); err != nil {
t.Fatalf("Failed to write certificate to file: %v", err)
}
certDirs = append(certDirs, certDir)
}
// Sanity check: the number of certDirs should be equal to the number of roots.
if g, w := len(certDirs), len(rootPEMs); g != w {
t.Fatalf("Failed sanity check: len(certsDir)=%d is not equal to len(rootsPEMS)=%d", g, w)
}
// Now finally concatenate them with a colon.
colonConcatCertDirs := strings.Join(certDirs, ":")
os.Setenv(certDirEnv, colonConcatCertDirs)
gotPool, err := loadSystemRoots()
if err != nil {
t.Fatalf("Failed to load system roots: %v", err)
}
subjects := gotPool.Subjects()
// We expect exactly len(rootPEMs) subjects back.
if g, w := len(subjects), len(rootPEMs); g != w {
t.Fatalf("Invalid number of subjects: got %d want %d", g, w)
}
wantPool := NewCertPool()
for _, certPEM := range rootPEMs {
wantPool.AppendCertsFromPEM([]byte(certPEM))
}
strCertPool := func(p *CertPool) string {
return string(bytes.Join(p.Subjects(), []byte("\n")))
}
if !certPoolEqual(gotPool, wantPool) {
g, w := strCertPool(gotPool), strCertPool(wantPool)
t.Fatalf("Mismatched certPools\nGot:\n%s\n\nWant:\n%s", g, w)
}
}
func TestReadUniqueDirectoryEntries(t *testing.T) {
tmp := t.TempDir()
temp := func(base string) string { return filepath.Join(tmp, base) }
if f, err := os.Create(temp("file")); err != nil {
t.Fatal(err)
} else {
f.Close()
}
if err := os.Symlink("target-in", temp("link-in")); err != nil {
t.Fatal(err)
}
if err := os.Symlink("../target-out", temp("link-out")); err != nil {
t.Fatal(err)
}
got, err := readUniqueDirectoryEntries(tmp)
if err != nil {
t.Fatal(err)
}
gotNames := []string{}
for _, fi := range got {
gotNames = append(gotNames, fi.Name())
}
wantNames := []string{"file", "link-out"}
if !slices.Equal(gotNames, wantNames) {
t.Errorf("got %q; want %q", gotNames, wantNames)
}
}

View file

@ -12,9 +12,8 @@ import (
"unsafe"
)
func loadSystemRoots() (*CertPool, error) {
return &CertPool{systemPool: true}, nil
}
// Windows has no default SSL_CERT_{FILE,DIR} paths.
var certFiles, certDirectories []string
// Creates a new *syscall.CertContext representing the leaf certificate in an in-memory
// certificate store containing itself and all of the intermediate certificates specified

View file

@ -81,6 +81,7 @@ var All = []Info{
{Name: "x509negativeserial", Package: "crypto/x509", Changed: 23, Old: "1"},
{Name: "x509rsacrt", Package: "crypto/x509", Changed: 24, Old: "0"},
{Name: "x509sha256skid", Package: "crypto/x509", Changed: 25, Old: "0"},
{Name: "x509sslcertoverrideplatform", Package: "crypto/x509", Changed: 27, Old: "0"},
{Name: "x509usefallbackroots", Package: "crypto/x509"},
{Name: "x509usepolicies", Package: "crypto/x509", Changed: 24, Old: "0"},
{Name: "zipinsecurepath", Package: "archive/zip"},

View file

@ -441,6 +441,11 @@ Below is the full list of supported metrics, ordered lexicographically.
The number of non-default behaviors executed by the crypto/x509
package due to a non-default GODEBUG=x509sha256skid=... setting.
/godebug/non-default-behavior/x509sslcertoverrideplatform:events
The number of non-default behaviors executed by
the crypto/x509 package due to a non-default
GODEBUG=x509sslcertoverrideplatform=... setting.
/godebug/non-default-behavior/x509usefallbackroots:events
The number of non-default behaviors executed by the crypto/x509
package due to a non-default GODEBUG=x509usefallbackroots=...