Skip to content

Commit 2b91776

Browse files
committed
Accept a session label character newer than this build's Unicode tables
bc3 refuses exactly control, format, private-use, surrogate and line or paragraph separator characters in a launch label, and no longer refuses unassigned code points: a host's Unicode tables can be newer than the server's, and a refused label costs the launch its whole token. The local check now refuses the same set, named category by category, since Go's unicode.C table also covers code points it doesn't know.
1 parent f1e2e3d commit 2b91776

2 files changed

Lines changed: 23 additions & 20 deletions

File tree

‎internal/auth/agent_launch.go‎

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -88,16 +88,15 @@ func ValidateSession(id, label string) error {
8888
return nil
8989
}
9090

91-
// labelRune reports whether r may appear in a session label: an assigned
92-
// character outside the "other" category (control, format, private use,
93-
// surrogate) and the line and paragraph separators. The label is shown to
94-
// people, so nothing that reorders, hides or breaks the text around it.
91+
// labelRune reports whether r may appear in a session label: anything but
92+
// the "other" category (control, format, private use, surrogate) and the
93+
// line and paragraph separators — exactly the set bc3 refuses. The label is
94+
// shown to people, so nothing that reorders, hides or breaks the text around
95+
// it. Unassigned code points are deliberately allowed: a host's Unicode
96+
// tables can be newer than this build's or the server's, and a label refused
97+
// over that skew costs the launch its whole token.
9598
func labelRune(r rune) bool {
96-
if unicode.In(r, unicode.C, unicode.Zl, unicode.Zp) {
97-
return false
98-
}
99-
// Unassigned code points are in no category table at all.
100-
return unicode.In(r, unicode.L, unicode.M, unicode.N, unicode.P, unicode.S, unicode.Zs)
99+
return !unicode.In(r, unicode.Cc, unicode.Cf, unicode.Co, unicode.Cs, unicode.Zl, unicode.Zp)
101100
}
102101

103102
// addTo puts the session on a client_credentials form. A nil launch is the

‎internal/auth/agent_launch_test.go‎

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -183,17 +183,16 @@ func TestSessionTokenNeedsAnAgentProfile(t *testing.T) {
183183
// server holds a session to are checked before anything goes out.
184184
func TestSessionTokenValidatesItsIdentityBeforeSending(t *testing.T) {
185185
for name, tc := range map[string]struct{ id, label string }{
186-
"empty id": {"", "coworker@box"},
187-
"short id": {"0123456789abcdef", ""},
188-
"uppercase id": {strings.ToUpper(testSessionID), ""},
189-
"non-hex id": {"0123456789abcdef0123456789abcdeg", ""},
190-
"label too long": {testSessionID, strings.Repeat("x", 101)},
191-
"blank label": {testSessionID, " "},
192-
"control in label": {testSessionID, "coworker\nbox"},
193-
"bidi override": {testSessionID, "coworker\u202ebox"},
194-
"line separator": {testSessionID, "coworker\u2028box"},
195-
"invalid utf-8 label": {testSessionID, "coworker\xffbox"},
196-
"unassigned codepoint": {testSessionID, "coworker\U000E0080box"},
186+
"empty id": {"", "coworker@box"},
187+
"short id": {"0123456789abcdef", ""},
188+
"uppercase id": {strings.ToUpper(testSessionID), ""},
189+
"non-hex id": {"0123456789abcdef0123456789abcdeg", ""},
190+
"label too long": {testSessionID, strings.Repeat("x", 101)},
191+
"blank label": {testSessionID, " "},
192+
"control in label": {testSessionID, "coworker\nbox"},
193+
"bidi override": {testSessionID, "coworker\u202ebox"},
194+
"line separator": {testSessionID, "coworker\u2028box"},
195+
"invalid utf-8 label": {testSessionID, "coworker\xffbox"},
197196
} {
198197
t.Run(name, func(t *testing.T) {
199198
as := startDeviceAS(t)
@@ -209,4 +208,9 @@ func TestSessionTokenValidatesItsIdentityBeforeSending(t *testing.T) {
209208

210209
assert.NoError(t, ValidateSession(testSessionID, strings.Repeat("é", 100)), "100 characters, not bytes")
211210
assert.NoError(t, ValidateSession(testSessionID, "claude · coworker"))
211+
// A character newer than this build's Unicode tables is accepted, as bc3
212+
// accepts it: refusing it would cost the launch its whole token over a
213+
// table the two sides need not share.
214+
assert.NoError(t, ValidateSession(testSessionID, "coworker \U0001FAE9"))
215+
assert.NoError(t, ValidateSession(testSessionID, "coworker\U000E0080box"))
212216
}

0 commit comments

Comments
 (0)