{"record":{"id":"55510294461a53ca","repo":"shadow1ng/fscan","slug":"mssql-truncated-string-in-error-token","errorCode":null,"errorMessage":"mssql: truncated string in error token","messagePattern":"mssql: truncated string in error token","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugins/services/mssql_raw.go","lineNumber":369,"sourceCode":"}\n\nfunc mssqlSkipUSVarError(payload []byte, pos int) (int, error) {\n\tif pos+2 > len(payload) {\n\t\treturn pos, fmt.Errorf(\"mssql: truncated info token\")\n\t}\n\tsize := int(binary.LittleEndian.Uint16(payload[pos : pos+2]))\n\tend := pos + 2 + size\n\tif size < 6 || end > len(payload) || pos+8 > len(payload) {\n\t\treturn pos, fmt.Errorf(\"mssql: invalid info token size\")\n\t}\n\t_, _, err := mssqlReadUSVarChar(payload, pos+8)\n\treturn end, err\n}\n\nfunc mssqlEnsureSkipBVarStrings(payload []byte, pos, end int) error {\n\tfor i := 0; i < 2; i++ {\n\t\tif pos >= end {\n\t\t\treturn fmt.Errorf(\"mssql: truncated string in error token\")\n\t\t}\n\t\tlength := int(payload[pos]) * 2\n\t\tpos++\n\t\tif pos+length > end {\n\t\t\treturn fmt.Errorf(\"mssql: invalid string in error token\")\n\t\t}\n\t\tpos += length\n\t}\n\tif pos+4 > end {\n\t\treturn fmt.Errorf(\"mssql: truncated error line number\")\n\t}\n\treturn nil\n}\n\nfunc mssqlSkipLen16(payload []byte, pos int) (int, error) {\n\tif pos+2 > len(payload) {\n\t\treturn pos, fmt.Errorf(\"mssql: truncated token\")\n\t}","sourceCodeStart":351,"sourceCodeEnd":387,"githubUrl":"https://github.com/shadow1ng/fscan/blob/95cc12e753bf43de7004e5aef42a9ffba3934303/plugins/services/mssql_raw.go#L351-L387","documentation":"mssqlEnsureSkipBVarStrings walks the two BVarChar strings (server name and error message) inside a TDS ERROR token. It throws this when the cursor has already reached the token end (pos >= end) before reading one of those strings, meaning the token is shorter than the spec requires. This is a defensive check against malformed ERROR tokens.","triggerScenarios":"mssqlParseErrorToken finishes the fixed header and calls mssqlEnsureSkipBVarStrings, but after skipping earlier fields the position equals or exceeds the token end before both length-prefixed strings are consumed.","commonSituations":"Server (or man-in-the-middle) emits an ERROR token with missing server-name/message strings; fuzzer probes sending minimal-length tokens; corruption from a buggy TCP middlebox dropping bytes inside the token.","solutions":["Capture and hex-dump the login response to confirm which string is missing from the ERROR token.","Retry the login; if reproducible, treat the server as misbehaving and report it.","Check for proxies or firewalls rewriting the TDS payload in transit.","In callers, treat this as a fatal parse error and close the connection rather than resuming at a guessed offset."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"end := pos + 2 + int(binary.LittleEndian.Uint16(payload[pos:pos+2]))\nif pos+8+1+2+1+2+4 > end {\n    return fmt.Errorf(\"ERROR token too small to contain header+strings+line number\")\n}","typeGuard":null,"tryCatchPattern":"if err := mssqlEnsureSkipBVarStrings(payload, pos, end); err != nil {\n    return fmt.Errorf(\"incomplete ERROR token (offset %d..%d): %w\", pos, end, err)\n}","preventionTips":["Enforce a minimum token size covering all mandatory ERROR-token fields.","Fail closed: reject the whole response when a token is incomplete.","Test against fuzzed servers with missing string fields.","Monitor for repeated occurrences from a single server as a sign of server-side bugs."],"tags":["mssql","tds-protocol","malformed-packet","wire-parsing"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"95cc12e753bf43de7004e5aef42a9ffba3934303","analyzedAt":"2026-09-06T17:07:30.094Z","contentChangedAt":"2026-09-06T17:07:30.094Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}