Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
fix(spec): reject @ in pod and member session-name components (#472)
Pod and member names must not contain '@' because canonical session names
are formatted as {pod}-{member}@{rig} and parseSessionName splits at the
first '@' to separate member from rig. If pod or member contained '@',
the canonical name would misparse, corrupting queue routing and rig identity.

Reject '@' in pod and member components during validateSessionComponents
while keeping '@' permitted in rig names (which consume the remainder).
  • Loading branch information
shravansumanthanan committed Oct 2, 2026
commit 162f2d3157be906417bde989a5ce04c911e917c5
15 changes: 12 additions & 3 deletions packages/daemon/src/domain/session-name.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,20 @@ export function validateSessionName(name: string): boolean {
/**
* Validate characters in a session name component.
* Returns null if valid, or an error string with the specific invalid character.
* When `opts.allowAt` is false, "@" is rejected because "@" is reserved as the
* canonical session separator ({pod}-{member}@{rig}).
*/
export function validateSessionNameChars(
value: string,
label: string
label: string,
opts?: { allowAt?: boolean }
): string | null {
if (!value) return `${label} must not be empty`;
const allowAt = opts?.allowAt ?? true;
for (const ch of value) {
if (ch === "@" && !allowAt) {
return `${label} "${value}" contains "@" — ${label} cannot contain "@" (reserved as session separator)`;
}
if (!ALLOWED_CHARS_PATTERN.test(ch)) {
return `${label} "${value}" contains "${ch}" — tmux session names allow: a-z, A-Z, 0-9, -, _, ., @`;
}
Expand All @@ -66,6 +73,8 @@ export function validateSessionNameChars(
/**
* Validate all three components of a canonical session name.
* Returns an array of errors (empty if valid).
* Pod name and member name must NOT contain "@", because the canonical session parser
* splits at the first "@" to separate {pod}-{member} from {rig}.
*/
export function validateSessionComponents(
podName: string,
Expand All @@ -77,14 +86,14 @@ export function validateSessionComponents(
if (!podName) {
errors.push("pod name must not be empty");
} else {
const err = validateSessionNameChars(podName, "pod name");
const err = validateSessionNameChars(podName, "pod name", { allowAt: false });
if (err) errors.push(err);
}

if (!memberName) {
errors.push("member name must not be empty");
} else {
const err = validateSessionNameChars(memberName, "member name");
const err = validateSessionNameChars(memberName, "member name", { allowAt: false });
if (err) errors.push(err);
}

Expand Down
22 changes: 22 additions & 0 deletions packages/daemon/test/session-name.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,26 @@ describe("session-name", () => {
// Valid
expect(validateSessionComponents("dev", "impl", "auth-feats")).toEqual([]);
});

// Test 7: validateSessionComponents rejects @ in pod and member names (#472)
it("validateSessionComponents rejects @ in pod and member names while accepting @ in rig name (#472)", () => {
const atInPod = validateSessionComponents("pod@alias", "impl", "my-rig");
expect(atInPod.length).toBeGreaterThan(0);
expect(atInPod[0]).toContain("pod name");
expect(atInPod[0]).toContain("@");

const atInMember = validateSessionComponents("dev", "impl@alias", "my-rig");
expect(atInMember.length).toBeGreaterThan(0);
expect(atInMember[0]).toContain("member name");
expect(atInMember[0]).toContain("@");

// Rig name can contain @ because parser splits at the first @
expect(validateSessionComponents("dev", "impl", "my@rig")).toEqual([]);

// validateSessionNameChars with allowAt: false rejects @
const err = validateSessionNameChars("pod@alias", "pod name", { allowAt: false });
expect(err).not.toBeNull();
expect(err).toContain("pod name");
expect(err).toContain("@");
});
});
Loading