Ð޸´ȫÏîĿÉó²éÎÊÌⲢ·¢²¼ v1.1.9

¸ÄÓñ¾»ú¶ÀÁ¢ CA£¬»ָ´ CLI TLS УÑ飬±£»¤ƾ¾ÝÓëÅäÖò¢·¢±£´档
Ð޸´ Cursor Êý¾ݡ¢»Ự·¾¶¡¢´úÀíÉúÃüÖÜÆڡ¢ģÐÍÇëÇóÓë Skills Îļþ²Ù×÷¡£
Ϊ¶ÀÁ¢ Tab ·þÎñÔö¼ӿͻ§¶˼øȨ£¬Éý¼¶ÒÀÀµÓ빤¾ßÁ´£¬ÍêÉƷ¢²¼ԪÐÅϢͬ²½¡£

Ñé֤£ºÁ½¸ö Go ģ¿éµIJâÊÔ/vet¡¢¹ؼüģ¿é race¡¢ǰ¶˲âÊÔ/Éú²ú¹¹½¨/Éó¼ơ¢
Go ¿ɴï©¶´ɨÃèÓë macOS/Linux ¿ÉÐÐģ¿齻²æ±àÒë¾ùͨ¹ý¡£
Windows ¸ôÀëÆô¶¯ͨ¹ý£»δÍê³ɵĵã»÷ðÑ̺ÍԭÉúƽ̨Ñé֤¼ûÉó²鱨¸档
This commit is contained in:
FuDie0915
2026-11-10 09:59:34 +08:00
parent db73cf5a39
commit 188225fae6
98 changed files with 3324 additions and 930 deletions

View File

@@ -25,11 +25,6 @@ import (
//go:embed ca.crt
var embeddedCACertPEM []byte
// embeddedCAKeyPEM 表示当前模块中的 embeddedCAKeyPEM 状态值。
//
//go:embed ca.key
var embeddedCAKeyPEM []byte
// Manager 定义了当前模块中的 Manager 类型。
type Manager struct {
// caCert 表示当前声明中的 caCert。
@@ -52,27 +47,23 @@ func NewManager(caCertPath, caKeyPath string) (*Manager, error) {
return NewManagerFromPEM(certPEM, keyPEM)
}
// NewEmbeddedManager 用于处理与 NewEmbeddedManager 相关的逻辑。
func NewEmbeddedManager() (*Manager, error) {
return NewManagerFromPEM(embeddedCACertPEM, embeddedCAKeyPEM)
}
// EmbeddedCACertPEM 用于处理与 EmbeddedCACertPEM 相关的逻辑。
func EmbeddedCACertPEM() []byte {
return cloneBytes(embeddedCACertPEM)
}
// EmbeddedCAKeyPEM 用于处理与 EmbeddedCAKeyPEM 相关的逻辑。
func EmbeddedCAKeyPEM() []byte {
return cloneBytes(embeddedCAKeyPEM)
}
// NewManagerFromPEM 用于处理与 NewManagerFromPEM 相关的逻辑。
func NewManagerFromPEM(caCertPEM, caKeyPEM []byte) (*Manager, error) {
if _, err := tls.X509KeyPair(caCertPEM, caKeyPEM); err != nil {
return nil, err
}
caCert, caKey, err := loadCAFromPEM(caCertPEM, caKeyPEM)
if err != nil {
return nil, err
}
if !caCert.IsCA || caCert.KeyUsage&x509.KeyUsageCertSign == 0 || time.Now().Before(caCert.NotBefore) || !time.Now().Before(caCert.NotAfter) {
return nil, errors.New("invalid or expired CA certificate")
}
return &Manager{caCert: caCert, caKey: caKey, cache: make(map[string]*tls.Certificate)}, nil
}
@@ -98,7 +89,7 @@ func (m *Manager) CertificateForServerName(serverName string) (*tls.Certificate,
m.mu.Lock()
defer m.mu.Unlock()
if cert, ok := m.cache[host]; ok {
if cert, ok := m.cache[host]; ok && cert.Leaf != nil && time.Now().Add(time.Minute).Before(cert.Leaf.NotAfter) {
return cert, nil
}
@@ -119,6 +110,12 @@ func (m *Manager) CertificateForServerName(serverName string) (*tls.Certificate,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}
if !time.Now().Before(m.caCert.NotAfter) {
return nil, errors.New("CA certificate expired")
}
if leaf.NotAfter.After(m.caCert.NotAfter) {
leaf.NotAfter = m.caCert.NotAfter
}
if len(m.caCert.SubjectKeyId) > 0 {
leaf.AuthorityKeyId = append([]byte(nil), m.caCert.SubjectKeyId...)
}

View File

@@ -1,27 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAyh3jND/aFusuRjGTmhQtX2hkF1qroNjEEKCxWPlfprvdl8Tx
upxNv1TQcm+K9KsS7OxnKYP4Qtv068XLbaCCGuoA/xpor6enrT85KulBq0j8z/g1
y0VWxjz3xN9F9ND13h9yxDZCn76egbRkhFxpXow67jLsIlrqWDSltERlTKh2cJ1g
hTRuQNSr7jtKQgFAR3aQ6dTzOP4fCOtLYn63jL4+YcxdGoK66tx8eFHq8oBLYvsc
LNjjkaAHilZFwA4Jr7zHBofTRBg04eZum2UTaRqmSyT65ifXm4vRdQa4k1FS1gnq
hPMhIjST8b6RkxvbLNmFpkOfI9eCMRpFG7y+NQIDAQABAoIBAATU9ZVOcHmLSkop
zcBJerM09O2dAIziGb/XA55fqdJ728aQ0gGW0oIANlKCCaWjQFrTJP04VzNL/F01
l5EpnOqlTPxMRpPqc2cAI677sBL29fpH0gtnvzUSiI7Xkp3RcAtNH6qCrJGSlkn+
BMgoSGmW+yKuK3h/yWnt6kc2umA8fN+bHKhS3pI56PMW8qVnny9n92RaCA7Uf/4j
XDewIreiH5jRqRwrPbOpjDFmv+W18LWZQiTwwxfY6sRZiZpsfsHidzfFGUFZXMlq
2P3FCqoF4oMM1rRgBlHhDR7JHmSkFpZG639HJTXLllpyDbj3H86nsjIj9WZYKn+h
B9k9bcECgYEA1HDzRCqoRZh6cL46KiYjv+LwmEKMa3nPb4ljywgLbRkzTMyVs0MK
fDsDoTLFY9PBhveypU5gjTbQqtyBsDtbU+dH9Eks1FdL9P8bJf0ZOJFRiEB7uB4a
z3V9tcXHwH4l2bCWbWThQGFwRudDAoY0EH89oSA/WHayjOKe1Wi4ovUCgYEA848B
cYi+Qbkk+fOv9gSJn8KS1LH/jE28S/e7E4YkTfYUuu+7wr8bdRKUNpPIsLX0Fo9R
KpJX0Oyjjady9n/8ARZRmD8Upl+F7Sl7Ro6F8+nfqQUrxbVDiIL3b+aZF+cDfFrB
/xL5kyZZqTtFfP360tfYnlS6Sssd4E2Jsj0fJkECgYEA0gG+WbqZkgMDtwQ114jQ
elZLZRkUWwKVjzsQDZssQHNTBS6RJh619M0Z73aTLvYcL+IZFdT/GVoAuYc2JRLo
W28c8F6OFHMfwVeWbN1g20y8fqbQJtiLxF3vIYwcxStvG123tvisu8oXBeCDm7Ez
MsO2FtwcAsWECEXWojzdmSkCgYEA18GvPawtHnuszd+Z2Q5b/DKZb+Hex6N1Ura5
+qmyL333D0Kfyf0RjbxPn6l690+4UuPSuyu4r1Nx72KO7N6jlzL2RTBcUqX8NgOx
OOe4skJT5561EAdrM9sQ5wgYRpxW8ipUAGoGvNwUQV5ISFmVgIHFWz0jam5UoQcP
G94ZYgECgYBN4PB8BuAwMRqhTCLL5RJKtcdC/Ls9xdWrmswtQQ4OSBUTLGdeMhEN
E23F0d+NdtiTWDYRDJ7z6KAF8CcOwWMt2sNbrxgNRuDzyfLqbuVLOM8iY3xA50Nl
clZWujKILWp47+gA6/AqJLv2lA2LK8gM8zYzzm/nCuLCBLYH/hOiCA==
-----END RSA PRIVATE KEY-----

78
internal/certs/local.go Normal file
View File

@@ -0,0 +1,78 @@
package certs
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"errors"
"fmt"
"math/big"
"os"
"path/filepath"
"time"
"cursor/internal/securefile"
)
// LoadOrCreateLocalCA persists one private CA per application data directory.
// A single private bundle is the authority; ca.crt is a public export for clients.
func LoadOrCreateLocalCA(dir string) (*Manager, error) {
if err := securefile.PrivateDir(dir); err != nil {
return nil, err
}
unlock, err := securefile.Lock(filepath.Join(dir, "ca.lock"))
if err != nil {
return nil, err
}
defer unlock()
bundlePath := filepath.Join(dir, "local-ca.pem")
bundle, err := os.ReadFile(bundlePath)
if errors.Is(err, os.ErrNotExist) {
key, keyErr := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if keyErr != nil {
return nil, keyErr
}
serial, keyErr := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
if keyErr != nil {
return nil, keyErr
}
now := time.Now()
template := &x509.Certificate{SerialNumber: serial, Subject: pkix.Name{CommonName: "Local-Cursor Private CA", Organization: []string{"Local-Cursor"}}, NotBefore: now.Add(-time.Hour), NotAfter: now.AddDate(10, 0, 0), IsCA: true, BasicConstraintsValid: true, KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign}
der, keyErr := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key)
if keyErr != nil {
return nil, keyErr
}
keyPEM, keyErr := marshalPrivateKeyPEM(key)
if keyErr != nil {
return nil, keyErr
}
bundle = append(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der}), keyPEM...)
if err = securefile.Write(bundlePath, bundle); err != nil {
return nil, err
}
} else if err != nil {
return nil, err
}
if err = securefile.Protect(bundlePath); err != nil {
return nil, err
}
certBlock, rest := pem.Decode(bundle)
if certBlock == nil {
return nil, fmt.Errorf("本机 CA 文件损坏,请先备份并修复")
}
manager, err := NewManagerFromPEM(pem.EncodeToMemory(certBlock), rest)
if err != nil {
return nil, fmt.Errorf("本机 CA 校验失败(不会自动轮换): %w", err)
}
if err = securefile.Write(filepath.Join(dir, "ca.crt"), manager.CACertPEM()); err != nil {
return nil, err
}
return manager, nil
}
func (m *Manager) CACertPEM() []byte {
return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: m.caCert.Raw})
}

View File

@@ -0,0 +1,70 @@
package certs
import (
"bytes"
"encoding/pem"
"os"
"path/filepath"
"testing"
)
func TestLocalCAStablePrivateAndIndependent(t *testing.T) {
dir := t.TempDir()
a, err := LoadOrCreateLocalCA(dir)
if err != nil {
t.Fatal(err)
}
b, err := LoadOrCreateLocalCA(dir)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(a.CACertPEM(), b.CACertPEM()) {
t.Fatal("CA rotated on restart")
}
c, err := LoadOrCreateLocalCA(t.TempDir())
if err != nil {
t.Fatal(err)
}
if bytes.Equal(a.CACertPEM(), c.CACertPEM()) {
t.Fatal("installations share a CA")
}
if bytes.Equal(a.CACertPEM(), EmbeddedCACertPEM()) {
t.Fatal("legacy CA reused")
}
leaf, err := a.CertificateForServerName("api2.cursor.sh")
if err != nil {
t.Fatal(err)
}
if err := leaf.Leaf.CheckSignatureFrom(a.caCert); err != nil {
t.Fatal(err)
}
if err := leaf.Leaf.VerifyHostname("api2.cursor.sh"); err != nil {
t.Fatal(err)
}
if err := leaf.Leaf.VerifyHostname("unrelated.example"); err == nil {
t.Fatal("hostname mismatch accepted")
}
bundle, err := os.ReadFile(filepath.Join(dir, "local-ca.pem"))
if err != nil {
t.Fatal(err)
}
_, key := pem.Decode(bundle)
if _, err = NewManagerFromPEM(c.CACertPEM(), key); err == nil {
t.Fatal("mismatched key accepted")
}
}
func TestCorruptCADoesNotRotateOrOverwrite(t *testing.T) {
dir := t.TempDir()
bad := []byte("broken private authority")
if err := os.WriteFile(filepath.Join(dir, "local-ca.pem"), bad, 0600); err != nil {
t.Fatal(err)
}
if _, err := LoadOrCreateLocalCA(dir); err == nil {
t.Fatal("corrupt CA accepted")
}
after, _ := os.ReadFile(filepath.Join(dir, "local-ca.pem"))
if !bytes.Equal(bad, after) {
t.Fatal("corrupt CA silently replaced")
}
}