// Copyright (c) 2026 Lark Technologies Pte. Ltd. // SPDX-License-Identifier: MIT package transport import ( "crypto/rand" "crypto/rsa" "crypto/tls" "crypto/x509" "crypto/x509/pkix" "encoding/pem" "math/big" "net/http" "os" "path/filepath" "strings" "testing" "time" ) // mustCreateTestCertPEM generates a short-lived self-signed CA certificate for tests. func mustCreateTestCertPEM(t *testing.T) []byte { t.Helper() key, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { t.Fatalf("GenerateKey() error = %v", err) } der, err := x509.CreateCertificate(rand.Reader, &x509.Certificate{ SerialNumber: big.NewInt(1), Subject: pkix.Name{ CommonName: "proxyplugin-test-ca", }, NotBefore: time.Now().Add(-time.Hour), NotAfter: time.Now().Add(time.Hour), KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, IsCA: true, BasicConstraintsValid: true, }, &x509.Certificate{ SerialNumber: big.NewInt(1), Subject: pkix.Name{ CommonName: "proxyplugin-test-ca", }, NotBefore: time.Now().Add(-time.Hour), NotAfter: time.Now().Add(time.Hour), KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, IsCA: true, BasicConstraintsValid: true, }, &key.PublicKey, key) if err != nil { t.Fatalf("CreateCertificate() error = %v", err) } return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der}) } // TestApplyExtraRootCA_EmptyPathIsNoop verifies that an empty CA path leaves the transport unchanged. func TestApplyExtraRootCA_EmptyPathIsNoop(t *testing.T) { tr := &http.Transport{} if err := applyExtraRootCA(tr, " "); err != nil { t.Fatalf("applyExtraRootCA() error = %v", err) } if tr.TLSClientConfig != nil { t.Fatalf("TLSClientConfig = %#v, want nil", tr.TLSClientConfig) } } // TestApplyExtraRootCA_RejectsRelativePath verifies that CA paths must be absolute. func TestApplyExtraRootCA_RejectsRelativePath(t *testing.T) { tr := &http.Transport{} err := applyExtraRootCA(tr, "ca.pem") if err == nil || !strings.Contains(err.Error(), "must be an absolute path") { t.Fatalf("applyExtraRootCA() error = %v, want absolute-path error", err) } } // TestApplyExtraRootCA_RejectsMissingFile verifies missing PEM bundles fail before file reads. func TestApplyExtraRootCA_RejectsMissingFile(t *testing.T) { tr := &http.Transport{} err := applyExtraRootCA(tr, filepath.Join(t.TempDir(), "missing.pem")) if err == nil || !strings.Contains(err.Error(), "unsafe") { t.Fatalf("applyExtraRootCA() error = %v, want unsafe path error", err) } } // TestApplyExtraRootCA_RejectsInvalidPEM verifies validation of malformed PEM bundles. func TestApplyExtraRootCA_RejectsInvalidPEM(t *testing.T) { caPath := filepath.Join(t.TempDir(), "invalid.pem") writeFile(t, caPath, []byte("not a pem"), 0600) tr := &http.Transport{} err := applyExtraRootCA(tr, caPath) if err == nil || !strings.Contains(err.Error(), "no certificates parsed from PEM") { t.Fatalf("applyExtraRootCA() error = %v, want invalid PEM error", err) } } // TestApplyExtraRootCA_RejectsInsecureCAPath verifies CA paths are safety-checked // before reading the configured file. func TestApplyExtraRootCA_RejectsInsecureCAPath(t *testing.T) { caPath := filepath.Join(t.TempDir(), "ca.pem") writeFile(t, caPath, mustCreateTestCertPEM(t), 0600) if err := os.Chmod(caPath, 0666); err != nil { t.Fatalf("Chmod() error = %v", err) } tr := &http.Transport{} err := applyExtraRootCA(tr, caPath) if err == nil || !strings.Contains(err.Error(), "unsafe") { t.Fatalf("applyExtraRootCA() error = %v, want unsafe path error", err) } if tr.TLSClientConfig != nil { t.Fatalf("TLSClientConfig = %#v, want nil", tr.TLSClientConfig) } } // TestApplyExtraRootCA_SetsTLSConfigWhenMissing verifies initialization of TLSClientConfig when absent. func TestApplyExtraRootCA_SetsTLSConfigWhenMissing(t *testing.T) { caPath := filepath.Join(t.TempDir(), "ca.pem") writeFile(t, caPath, mustCreateTestCertPEM(t), 0600) tr := &http.Transport{} if err := applyExtraRootCA(tr, caPath); err != nil { t.Fatalf("applyExtraRootCA() error = %v", err) } if tr.TLSClientConfig == nil { t.Fatal("TLSClientConfig = nil, want initialized config") } if tr.TLSClientConfig.RootCAs == nil { t.Fatal("RootCAs = nil, want cert pool") } } // TestApplyExtraRootCA_ClonesExistingTLSConfig verifies cloning when the base transport already has TLS settings. func TestApplyExtraRootCA_ClonesExistingTLSConfig(t *testing.T) { caPath := filepath.Join(t.TempDir(), "ca.pem") writeFile(t, caPath, mustCreateTestCertPEM(t), 0600) original := &tls.Config{ServerName: "open.feishu.cn"} tr := &http.Transport{TLSClientConfig: original} if err := applyExtraRootCA(tr, caPath); err != nil { t.Fatalf("applyExtraRootCA() error = %v", err) } if tr.TLSClientConfig == original { t.Fatal("TLSClientConfig pointer reused, want clone") } if tr.TLSClientConfig.ServerName != original.ServerName { t.Fatalf("ServerName = %q, want %q", tr.TLSClientConfig.ServerName, original.ServerName) } if tr.TLSClientConfig.RootCAs == nil { t.Fatal("RootCAs = nil, want cert pool") } } // TestApplyExtraRootCA_PreservesHigherTLSMinVersion verifies that adding a CA // does not relax an existing stricter TLS version floor. func TestApplyExtraRootCA_PreservesHigherTLSMinVersion(t *testing.T) { caPath := filepath.Join(t.TempDir(), "ca.pem") writeFile(t, caPath, mustCreateTestCertPEM(t), 0600) tr := &http.Transport{TLSClientConfig: &tls.Config{MinVersion: tls.VersionTLS13}} if err := applyExtraRootCA(tr, caPath); err != nil { t.Fatalf("applyExtraRootCA() error = %v", err) } if tr.TLSClientConfig.MinVersion != tls.VersionTLS13 { t.Fatalf("MinVersion = %x, want %x", tr.TLSClientConfig.MinVersion, tls.VersionTLS13) } }