From da797c907cb2d614f438d98d9197ef5080e41ec5 Mon Sep 17 00:00:00 2001 From: Codex Date: Wed, 3 Jun 2026 19:47:29 +0800 Subject: [PATCH] fix(v0.1): sanitize session id for PVC name (RFC 1123 subdomain compliance) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit session id 允许任意字符(含下划线/大写/点),但 PVC name 必须符合 RFC 1123 subdomain(小写字母数字 + '-' + '.',首尾必须 alphanumeric)。 sanitizeSessionIdForPvc 把非法字符替换为 '-',全空 fallback 到 'default'。 selftest 增加 3 case 覆盖下划线/大写/纯符号。 --- src/mgr/session-pvc.ts | 6 +++++- src/selftest/cases/10-mgr-session-pvc.ts | 5 ++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/mgr/session-pvc.ts b/src/mgr/session-pvc.ts index 5d76d6d..e9d75f3 100644 --- a/src/mgr/session-pvc.ts +++ b/src/mgr/session-pvc.ts @@ -38,9 +38,13 @@ const defaultPvcSize = "1Gi"; const defaultSubdir = "sessions"; export function sessionPvcNameFor(sessionId: string): string { - return `agentrun-v01-session-${sessionId}`; + const sanitized = sanitizeSessionIdForPvc(sessionId); + return `agentrun-v01-session-${sanitized.length > 0 ? sanitized : "default"}`; } +export function sanitizeSessionIdForPvc(sessionId: string): string { + return sessionId.toLowerCase().replace(/[^a-z0-9-]+/gu, "-").replace(/^-+|-+$/gu, ""); +} export function buildSessionPvcSpec(input: { sessionId: string; namespace?: string; options: SessionPvcOptions }): SessionPvcSpec { const namespace = input.namespace ?? "agentrun-v01"; return { diff --git a/src/selftest/cases/10-mgr-session-pvc.ts b/src/selftest/cases/10-mgr-session-pvc.ts index 7c6035c..e755712 100644 --- a/src/selftest/cases/10-mgr-session-pvc.ts +++ b/src/selftest/cases/10-mgr-session-pvc.ts @@ -2,7 +2,7 @@ import assert from "node:assert/strict"; import { startManagerServer } from "../../mgr/server.js"; import { MemoryAgentRunStore } from "../../mgr/store.js"; import { ManagerClient } from "../../mgr/client.js"; -import { createSessionPvc, deleteSessionPvc, getSessionPvcSummary, refreshSessionPvcSummary, runSessionStorageGc, sessionPvcNameFor } from "../../mgr/session-pvc.js"; +import { createSessionPvc, deleteSessionPvc, getSessionPvcSummary, refreshSessionPvcSummary, runSessionStorageGc, sessionPvcNameFor, sanitizeSessionIdForPvc } from "../../mgr/session-pvc.js"; import type { KubectlHandler, SessionPvcOptions } from "../../mgr/session-pvc.js"; import type { SelfTestCase } from "../harness.js"; @@ -32,6 +32,9 @@ const selfTest: SelfTestCase = async () => { assert.equal(summary.pvcName, sessionPvcNameFor(sessionId)); assert.equal(summary.pvcPhase, "Bound"); assert.equal(summary.codexRolloutSubdir, "sessions"); + assert.equal(sessionPvcNameFor("sess_with_underscores_001"), "agentrun-v01-session-sess-with-underscores-001"); + assert.equal(sessionPvcNameFor("Sess.UPPER.001"), "agentrun-v01-session-sess-upper-001"); + assert.equal(sessionPvcNameFor("---"), "agentrun-v01-session-default"); const after = await store.getSession(sessionId); assert.equal(after?.storageKind, "pvc"); assert.equal(after?.storagePvcName, summary.pvcName);