{"record":{"id":"f4926f4927142a11","repo":"Tencent/WeKnora","slug":"sandbox-binding-session-must-not-contain-control-c","errorCode":null,"errorMessage":"sandbox binding session must not contain control characters","messagePattern":"sandbox binding session must not contain control characters","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/sandbox/session_binding.go","lineNumber":32,"sourceCode":"const SessionSandboxBindingVersion = 1\n\n// SessionSandboxKey identifies one tenant-scoped persistent sandbox.\ntype SessionSandboxKey struct {\n\tTenantID  uint64\n\tSessionID string\n}\n\n// Validate rejects keys that cannot identify a tenant session.\nfunc (k SessionSandboxKey) Validate() error {\n\tif k.TenantID == 0 || strings.TrimSpace(k.SessionID) == \"\" {\n\t\treturn errors.New(\"sandbox binding requires tenant and session\")\n\t}\n\tif strings.ContainsAny(k.SessionID, \"{}\") {\n\t\treturn errors.New(\"sandbox binding session must not contain braces\")\n\t}\n\tfor _, r := range k.SessionID {\n\t\tif unicode.IsControl(r) {\n\t\t\treturn errors.New(\"sandbox binding session must not contain control characters\")\n\t\t}\n\t}\n\treturn nil\n}\n\n// SessionSandboxBinding records the remote sandbox assigned to a session.\ntype SessionSandboxBinding struct {\n\tVersion    int            `json:\"version\"`\n\tProvider   RemoteProvider `json:\"provider,omitempty\"`\n\tTenantID   uint64         `json:\"tenant_id\"`\n\tSessionID  string         `json:\"session_id\"`\n\tSandboxID  string         `json:\"sandbox_id\"`\n\tTemplateID string         `json:\"template_id\"`\n\tCreatedAt  time.Time      `json:\"created_at\"`\n\n\t// ConfigID is the sandbox config the sandbox was created from. It is what\n\t// makes \"every sandbox of this config\" answerable from the binding store:\n\t// the sandbox itself carries the same value in provider metadata, but a","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/sandbox/session_binding.go#L14-L50","documentation":"Character-validation guard in SessionSandboxKey.Validate: fires when the SessionID of a tenant-scoped sandbox binding contains ASCII/Unicode control characters. Control characters in a session identifier could break binding serialization or enable injection into stored keys, so the key is rejected as unable to safely identify a tenant session.","triggerScenarios":"Passing a SessionSandboxKey whose SessionID contains control runes (\\n, \\t, \\x00, ...) to Validate, usually from raw header values, untrimmed input, or byte-level parsing mistakes.","commonSituations":"Session ID read from an HTTP header including a trailing newline; IDs split from a text blob retaining \\r\\n; accidental concatenation with NUL bytes.","solutions":["Trim and sanitize the session ID (strings.TrimSpace, strip non-printable runes) before building the key.","Validate identifiers at the API boundary with an allowlist (alphanumeric, hyphen, underscore).","Ensure generators/parsers produce printable IDs (bare hex UUIDs)."],"exampleFix":"// before\nkey := sandbox.SessionSandboxKey{TenantID: 7, SessionID: headerValue} // may contain \\n\n// after\nclean := strings.Map(func(r rune) rune { if unicode.IsControl(r) { return -1 }; return r }, strings.TrimSpace(headerValue))\nkey := sandbox.SessionSandboxKey{TenantID: 7, SessionID: clean}","handlingStrategy":"validation","validationCode":"func sanitizeSessionID(id string) string {\n    return strings.Map(func(r rune) rune {\n        if unicode.IsControl(r) { return -1 }\n        return r\n    }, strings.TrimSpace(id))\n}","typeGuard":"func printableSessionID(id string) bool {\n    for _, r := range id {\n        if unicode.IsControl(r) { return false }\n    }\n    return true\n}","tryCatchPattern":"if err := key.Validate(); err != nil {\n    if strings.Contains(err.Error(), \"control characters\") {\n        key.SessionID = sanitizeSessionID(key.SessionID)\n        return key.Validate()\n    }\n    return err\n}","preventionTips":["Trim header-derived values before use as IDs.","Use an allowlist regex for acceptable ID characters.","Log IDs with %q to spot hidden control chars during debugging."],"tags":["go","sandbox","validation","session-id"],"backgroundTag":"invalid-session-id","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}