harden: reject ".", "..", and absolute paths in ValidateSessionID · Entire

harden: reject ".", "..", and absolute paths in ValidateSessionID

71790ea→main·

Soph·1mo ago·2 files·+29 added/-0 removed

ValidateSessionID previously rejected only path separators and empty input. A bare "." or ".." is separator-free yet still traverses when an agent uses the ID as a path segment (e.g. Copilot CLI builds

//events.jsonl), and the separator check can miss platform-specific absolute forms (Windows drive paths). Reject both.

Because the same validator guards both checkpoint writes and the resume/rewind restore boundaries, this tightens the whole class without affecting UUID-style IDs.

Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

Sessions

388c3d8a3b31View transcript

Changes

2

  • cmd/entire/cli/validation

  • Mvalidators.go+11

  • Mvalidators_test.go+18

4 unmodified lines

5
6
7
8
9
10
11
12 unmodified lines

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

4 unmodified lines

import (
    "errors"
    "fmt"
    "path/filepath"
    "regexp"
    "strings"
)
12 unmodified lines

if strings.ContainsAny(id, "/\\") {
        return fmt.Errorf("invalid session ID %q: contains path separators", id)
    }
    // A bare "." or ".." is separator-free but still traverses when used as a
    // path segment (e.g. an agent that uses the ID as a directory component).
    if id == "." || id == ".." {
        return fmt.Errorf("invalid session ID %q: reserved path segment", id)
    }
    // Defense in depth against platform-specific absolute forms (e.g. Windows
    // drive paths) that the separator check above may not catch.
    if filepath.IsAbs(id) {
        return fmt.Errorf("invalid session ID %q: must not be an absolute path", id)
    }
    return nil
}

Mcmd/entire/cli/validation/validators.go+11

71 unmodified lines

72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95

71 unmodified lines

wantErr:   true,
            errMsg:    "contains path separators",
        },
        // Bare path segments (separator-free but still traverse as a path component)
        {
            name:      "single dot",
            sessionID: ".",
            wantErr:   true,
            errMsg:    "reserved path segment",
        },
        {
            name:      "double dot",
            sessionID: "..",
            wantErr:   true,
            errMsg:    "reserved path segment",
        },
        {
            name:      "dot in the middle is allowed",
            sessionID: "a..b",
            wantErr:   false,
        },
    }

for _, tt := range tests {

Mcmd/entire/cli/validation/validators_test.go+18