This commit is contained in:
OpenClaw-User 2026-03-14 17:10:39 +08:00
commit 5c926d2937
2 changed files with 63 additions and 6 deletions

View file

@ -4,12 +4,15 @@ import (
"bufio"
"bytes"
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"net/url"
"os"
"strings"
"time"
@ -54,22 +57,48 @@ func WithRequestTimeout(timeout time.Duration) Option {
}
}
func NewProvider(apiKey, apiBase, proxy string, opts ...Option) *Provider {
client := &http.Client{
Timeout: defaultRequestTimeout,
// buildCertPool returns the system cert pool supplemented with CA bundles from
// well-known Termux paths. On Android/Termux, Go's x509.SystemCertPool returns
// an empty pool because it does not probe Termux-specific locations, causing
// TLS handshakes to fail with "certificate signed by unknown authority".
// InsecureSkipVerify is never set.
func buildCertPool() *x509.CertPool {
pool, err := x509.SystemCertPool()
if err != nil || pool == nil {
pool = x509.NewCertPool()
}
for _, p := range []string{
"/data/data/com.termux/files/usr/etc/tls/cert.pem",
"/data/data/com.termux/files/usr/etc/ssl/certs/ca-bundle.crt",
"/data/data/com.termux/files/usr/etc/ssl/certs/ca-certificates.crt",
} {
if pem, e := os.ReadFile(p); e == nil {
pool.AppendCertsFromPEM(pem)
}
}
return pool
}
func NewProvider(apiKey, apiBase, proxy string, opts ...Option) *Provider {
// Clone preserves all http.DefaultTransport defaults (connection pooling,
// dial/TLS handshake timeouts, HTTP/2, env proxy). We only patch RootCAs.
transport := http.DefaultTransport.(*http.Transport).Clone()
transport.TLSClientConfig = &tls.Config{RootCAs: buildCertPool()}
if proxy != "" {
parsed, err := url.Parse(proxy)
if err == nil {
client.Transport = &http.Transport{
Proxy: http.ProxyURL(parsed),
}
transport.Proxy = http.ProxyURL(parsed)
} else {
log.Printf("openai_compat: invalid proxy URL %q: %v", proxy, err)
}
}
client := &http.Client{
Timeout: defaultRequestTimeout,
Transport: transport,
}
p := &Provider{
apiKey: apiKey,
apiBase: strings.TrimRight(apiBase, "/"),

View file

@ -841,3 +841,31 @@ func TestSerializeMessages_StripsSystemParts(t *testing.T) {
t.Fatal("system_parts should not appear in serialized output")
}
}
// TestNewProvider_TLSTransportNeverInsecure ensures every provider — whether
// configured with a proxy or not — has an explicit TLS transport that never
// sets InsecureSkipVerify. This is the security guard for issue #1375.
func TestNewProvider_TLSTransportNeverInsecure(t *testing.T) {
tests := []struct {
name string
proxy string
}{
{name: "no proxy", proxy: ""},
{name: "with proxy", proxy: "http://127.0.0.1:8080"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := NewProvider("key", "https://example.com", tt.proxy)
tr, ok := p.httpClient.Transport.(*http.Transport)
if !ok || tr == nil {
t.Fatalf("Transport = %T, want *http.Transport", p.httpClient.Transport)
}
if tr.TLSClientConfig == nil {
t.Fatal("TLSClientConfig is nil")
}
if tr.TLSClientConfig.InsecureSkipVerify {
t.Fatal("InsecureSkipVerify must never be true")
}
})
}
}