Files
Local-Cursor/internal/cursor/security_regression_test.go
FuDie0915 188225fae6 Ð޸´ȫÏîĿÉó²éÎÊÌⲢ·¢²¼ v1.1.9
¸ÄÓñ¾»ú¶ÀÁ¢ CA£¬»ָ´ CLI TLS УÑ飬±£»¤ƾ¾ÝÓëÅäÖò¢·¢±£´档
Ð޸´ Cursor Êý¾ݡ¢»Ự·¾¶¡¢´úÀíÉúÃüÖÜÆڡ¢ģÐÍÇëÇóÓë Skills Îļþ²Ù×÷¡£
Ϊ¶ÀÁ¢ Tab ·þÎñÔö¼ӿͻ§¶˼øȨ£¬Éý¼¶ÒÀÀµÓ빤¾ßÁ´£¬ÍêÉƷ¢²¼ԪÐÅϢͬ²½¡£

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

168 lines
5.4 KiB
Go

package cursor
import (
"bytes"
"database/sql"
"encoding/json"
"errors"
"os"
"path/filepath"
"testing"
"cursor/internal/certs"
)
func isolatedHome(t *testing.T) string {
t.Helper()
dir := t.TempDir()
for _, key := range []string{"HOME", "USERPROFILE", "APPDATA", "XDG_CONFIG_HOME"} {
t.Setenv(key, dir)
}
return dir
}
func TestProxySettingsRestoreAndPreserveConcurrentUserChange(t *testing.T) {
isolatedHome(t)
path, err := resolveCursorSettingsPath()
if err != nil {
t.Fatal(err)
}
if err = os.MkdirAll(filepath.Dir(path), 0700); err != nil {
t.Fatal(err)
}
original := []byte(`{"http.proxy":"http://original:8080","editor.fontSize":15,"custom":{"enabled":true}}`)
if err = os.WriteFile(path, original, 0600); err != nil {
t.Fatal(err)
}
for range 2 {
if err = WriteUserProxySettings("http://127.0.0.1:28080"); err != nil {
t.Fatal(err)
}
}
current, err := loadSettings(path)
if err != nil {
t.Fatal(err)
}
current["http.proxySupport"] = json.RawMessage(`"override"`)
data, _ := json.Marshal(current)
if err = os.WriteFile(path, data, 0600); err != nil {
t.Fatal(err)
}
if err = ClearUserProxySettings(); err != nil {
t.Fatal(err)
}
got, err := loadSettings(path)
if err != nil {
t.Fatal(err)
}
if !rawEqual(got["http.proxy"], json.RawMessage(`"http://original:8080"`)) {
t.Fatal("original proxy not restored")
}
if !rawEqual(got["http.proxySupport"], json.RawMessage(`"override"`)) {
t.Fatal("user edit overwritten")
}
if !rawEqual(got["custom"], json.RawMessage(`{"enabled":true}`)) {
t.Fatal("unrelated settings lost")
}
if _, ok := got["cursor.general.disableHttp2"]; ok {
t.Fatal("injected key not removed")
}
if err = ClearUserProxySettings(); err != nil {
t.Fatal(err)
}
}
func TestInvalidSettingsNeverDeleted(t *testing.T) {
isolatedHome(t)
path, _ := resolveCursorSettingsPath()
_ = os.MkdirAll(filepath.Dir(path), 0700)
bad := []byte("{broken user settings // keep me")
if err := os.WriteFile(path, bad, 0600); err != nil {
t.Fatal(err)
}
if err := WriteUserProxySettings("http://127.0.0.1:28080"); err == nil {
t.Fatal("invalid settings accepted")
}
after, _ := os.ReadFile(path)
if !bytes.Equal(bad, after) {
t.Fatal("user settings destroyed")
}
}
func TestCertificateMigrationCancellationAndRetry(t *testing.T) {
newCert := []byte("local CA")
current, legacy := false, true
trusted := func(cert []byte) (bool, error) {
if bytes.Equal(cert, certs.EmbeddedCACertPEM()) {
return legacy, nil
}
return current, nil
}
denied := errors.New("authorization canceled")
if err := migrateCATrust(newCert, trusted, func() error { return denied }); !errors.Is(err, denied) {
t.Fatal(err)
}
if current || !legacy {
t.Fatal("cancellation changed trust")
}
if err := migrateCATrust(newCert, trusted, func() error { current = true; legacy = false; return nil }); err != nil {
t.Fatal(err)
}
if err := migrateCATrust(newCert, trusted, func() error { t.Fatal("migration not idempotent"); return nil }); err != nil {
t.Fatal(err)
}
legacy = true
if err := migrateCATrust(newCert, trusted, func() error { return nil }); err == nil {
t.Fatal("legacy trust silently retained")
}
}
func TestRecentlyOpenedPreservesUnknownFields(t *testing.T) {
dir := t.TempDir()
createStateDB(t, dir, []string{"file:///d:/Project/A", "file:///d:/Project/B"})
db, err := sql.Open("sqlite", filepath.Join(dir, "globalStorage", "state.vscdb"))
if err != nil {
t.Fatal(err)
}
defer db.Close()
raw := `{"version":3,"metadata":{"keep":true},"entries":[{"folderUri":"file:///d:/Project/A","label":"remove"},{"folderUri":"file:///d:/Project/B","label":"keep","remoteAuthority":"ssh-remote+host"},{"fileUri":"file:///d:/readme.md","label":"file"}]}`
if _, err = db.Exec("UPDATE ItemTable SET value = ? WHERE key = 'history.recentlyOpenedPathsList'", raw); err != nil {
t.Fatal(err)
}
if n, err := removeRecentlyOpenedEntry(dir, "d:/Project/A"); err != nil || n != 1 {
t.Fatalf("removed=%d err=%v", n, err)
}
var updated string
if err = db.QueryRow("SELECT value FROM ItemTable WHERE key = 'history.recentlyOpenedPathsList'").Scan(&updated); err != nil {
t.Fatal(err)
}
var got map[string]json.RawMessage
if err = json.Unmarshal([]byte(updated), &got); err != nil {
t.Fatal(err)
}
if !rawEqual(got["metadata"], json.RawMessage(`{"keep":true}`)) || !rawEqual(got["version"], json.RawMessage(`3`)) {
t.Fatal("top-level metadata lost")
}
var entries []map[string]any
_ = json.Unmarshal(got["entries"], &entries)
if len(entries) != 2 || entries[0]["remoteAuthority"] != "ssh-remote+host" || entries[1]["fileUri"] != "file:///d:/readme.md" {
t.Fatalf("unmatched entries changed: %s", updated)
}
}
func TestDatabaseFailurePreservesWorkspaceChatData(t *testing.T) {
dir := t.TempDir()
workspace := filepath.Join(dir, "workspaceStorage", "hash")
_ = os.MkdirAll(workspace, 0700)
_ = os.WriteFile(filepath.Join(workspace, "workspace.json"), []byte(`{"folder":"file:///d:/Project/A"}`), 0600)
_ = os.WriteFile(filepath.Join(workspace, "chat.db"), []byte("important user chat"), 0600)
_ = os.MkdirAll(filepath.Join(dir, "globalStorage"), 0700)
_ = os.WriteFile(filepath.Join(dir, "globalStorage", "state.vscdb"), []byte("corrupt database"), 0600)
if err := deleteProjectRecords(dir, "d:/Project/A"); err == nil {
t.Fatal("corrupt database accepted")
}
if data, err := os.ReadFile(filepath.Join(workspace, "chat.db")); err != nil || string(data) != "important user chat" {
t.Fatal("chat data removed before database update")
}
}