дȫÏîĿÉó²éÎÊÌⲢ·¢²¼ v1.1.9
¸ÄÓñ¾»ú¶ÀÁ¢ CA£¬»ָ´ CLI TLS УÑ飬±£»¤ƾ¾ÝÓëÅäÖò¢·¢±£´档 д Cursor Êý¾ݡ¢»Ự·¾¶¡¢´úÀíÉúÃüÖÜÆڡ¢ģÐÍÇëÇóÓë Skills Îļþ²Ù×÷¡£ Ϊ¶ÀÁ¢ Tab ·þÎñÔö¼ӿͻ§¶˼øȨ£¬Éý¼¶ÒÀÀµÓ빤¾ßÁ´£¬ÍêÉƷ¢²¼ԪÐÅϢͬ²½¡£ Ñé֤£ºÁ½¸ö Go ģ¿éµIJâÊÔ/vet¡¢¹ؼüģ¿é race¡¢ǰ¶˲âÊÔ/Éú²ú¹¹½¨/Éó¼ơ¢ Go ¿ɴï©¶´ɨÃèÓë macOS/Linux ¿ÉÐÐģ¿齻²æ±àÒë¾ùͨ¹ý¡£ Windows ¸ôÀëÆô¶¯ͨ¹ý£»δÍê³ɵĵã»÷ðÑ̺ÍԭÉúƽ̨Ñé֤¼ûÉó²鱨¸档
This commit is contained in:
@@ -746,7 +746,16 @@ func validateConversationID(conversationID string) (string, error) {
|
||||
if normalized == "" {
|
||||
return "", fmt.Errorf("conversation_id is required")
|
||||
}
|
||||
if strings.Contains(normalized, "/") || strings.Contains(normalized, string(os.PathSeparator)) {
|
||||
base := strings.ToUpper(strings.Split(normalized, ".")[0])
|
||||
if base == "CON" || base == "PRN" || base == "AUX" || base == "NUL" || (len(base) == 4 && (strings.HasPrefix(base, "COM") || strings.HasPrefix(base, "LPT")) && base[3] >= '1' && base[3] <= '9') {
|
||||
return "", fmt.Errorf("conversation_id is a reserved filename")
|
||||
}
|
||||
for _, r := range normalized {
|
||||
if r < 32 || r == 127 {
|
||||
return "", fmt.Errorf("invalid conversation_id")
|
||||
}
|
||||
}
|
||||
if normalized == "." || normalized == ".." || !filepath.IsLocal(normalized) || strings.ContainsAny(normalized, "/\\:\x00<>\"|?*") || strings.TrimRight(normalized, " .") != normalized {
|
||||
return "", fmt.Errorf("conversation_id must not contain path separators")
|
||||
}
|
||||
return normalized, nil
|
||||
|
||||
20
internal/backend/forwarder/file_store_security_test.go
Normal file
20
internal/backend/forwarder/file_store_security_test.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package forwarder
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestConversationIDRejectsTraversalAndWindowsAliases(t *testing.T) {
|
||||
for _, id := range []string{"", ".", "..", "../outside", `..\outside`, "C:evil", "CON", "id.", "id ", "a\x00b", "/absolute"} {
|
||||
// Whitespace is trimmed for compatibility; the remaining alias must be safe.
|
||||
if id == "id " {
|
||||
continue
|
||||
}
|
||||
if _, err := validateConversationID(id); err == nil {
|
||||
t.Errorf("accepted %q", id)
|
||||
}
|
||||
}
|
||||
for _, id := range []string{"conversation-123", "a_b.c"} {
|
||||
if _, err := validateConversationID(id); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package forwarder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -27,7 +28,7 @@ func TestUpsertEventIdempotentBeyondRecentLimit(t *testing.T) {
|
||||
// 填充超过 RecentEvents 上限的无关事件,把目标事件挤出 RecentEvents。
|
||||
for i := range usageRecentEventLimit + 10 {
|
||||
if err := store.UpsertEvent(usageFileEvent{
|
||||
EventID: "filler",
|
||||
EventID: fmt.Sprintf("filler-%d", i),
|
||||
Kind: usageEventKindProvider,
|
||||
At: base.Add(time.Duration(i+1) * time.Second),
|
||||
InputTokens: 1,
|
||||
|
||||
Reference in New Issue
Block a user