{"record":{"id":"e24b72878e8ea201","repo":"shadow1ng/fscan","slug":"mssql-truncated-us-varchar","errorCode":null,"errorMessage":"mssql: truncated us varchar","messagePattern":"mssql: truncated us varchar","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugins/services/mssql_raw.go","lineNumber":398,"sourceCode":"\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}\n\tsize := int(binary.LittleEndian.Uint16(payload[pos : pos+2]))\n\tnext := pos + 2 + size\n\tif next > len(payload) {\n\t\treturn pos, fmt.Errorf(\"mssql: invalid token size\")\n\t}\n\treturn next, nil\n}\n\nfunc mssqlReadUSVarChar(payload []byte, pos int) (string, int, error) {\n\tif pos+2 > len(payload) {\n\t\treturn \"\", pos, fmt.Errorf(\"mssql: truncated us varchar\")\n\t}\n\tchars := int(binary.LittleEndian.Uint16(payload[pos : pos+2]))\n\tpos += 2\n\tsize := chars * 2\n\tif pos+size > len(payload) {\n\t\treturn \"\", pos, fmt.Errorf(\"mssql: invalid us varchar size\")\n\t}\n\treturn mssqlDecodeUCS2(payload[pos : pos+size]), pos + size, nil\n}\n\nfunc mssqlWritePacket(w io.Writer, packetType byte, payload []byte) error {\n\tif len(payload)+8 > 0xffff {\n\t\treturn fmt.Errorf(\"mssql: packet too large\")\n\t}\n\theader := []byte{packetType, tdsStatusEOM, 0, 0, 0, 0, 1, 0}\n\tbinary.BigEndian.PutUint16(header[2:4], uint16(len(payload)+8))\n\tif _, err := w.Write(header); err != nil {\n\t\treturn err","sourceCodeStart":380,"sourceCodeEnd":416,"githubUrl":"https://github.com/shadow1ng/fscan/blob/95cc12e753bf43de7004e5aef42a9ffba3934303/plugins/services/mssql_raw.go#L380-L416","documentation":"mssqlReadUSVarChar reads a UCS-2 string encoded as a 2-byte character count followed by count*2 bytes. It throws 'truncated us varchar' when fewer than 2 bytes remain to read even the length header. It is used by both mssqlParseErrorToken and mssqlSkipUSVarError, so this error means an ERROR/INFO token's message string header is missing from the payload.","triggerScenarios":"Either token parser positions the cursor at a US_VARCHAR whose 2-byte length header falls past the payload end (pos+2 > len(payload)).","commonSituations":"Server response truncated inside an ERROR/INFO token; man-in-the-middle or packet loss dropped the string header; fuzzed payloads ending mid-token; a custom packet reader assembled the login response incorrectly (e.g. wrong TDS packet reassembly).","solutions":["Retry the connection to rule out transient truncation.","Verify your TDS packet reassembly honors total packet length and continuation packets.","Capture the raw stream and confirm where the payload ends relative to the string header.","If parsing your own buffered payload, check the buffer was fully populated before parsing tokens."],"exampleFix":"// before: parse token strings assuming full payload\nmsg, _, err := mssqlReadUSVarChar(payload, pos)\n// after: verify the 2-byte header fits before calling\nif pos+2 > len(payload) {\n    return fmt.Errorf(\"cannot read error message: payload ends at %d\", len(payload))\n}\nmsg, _, err := mssqlReadUSVarChar(payload, pos)","handlingStrategy":"validation","validationCode":"if pos+2 > len(payload) {\n    return fmt.Errorf(\"US_VARCHAR header missing: need 2 bytes at offset %d, payload is %d\", pos, len(payload))\n}\nchars := int(binary.LittleEndian.Uint16(payload[pos : pos+2]))\nif pos+2+chars*2 > len(payload) {\n    return fmt.Errorf(\"US_VARCHAR body of %d chars overruns payload\", chars)\n}","typeGuard":"func hasUSVarChar(payload []byte, pos int) bool {\n    if pos+2 > len(payload) {\n        return false\n    }\n    return pos+2+int(binary.LittleEndian.Uint16(payload[pos:pos+2]))*2 <= len(payload)\n}","tryCatchPattern":"msg, next, err := mssqlReadUSVarChar(payload, pos)\nif err != nil {\n    return fmt.Errorf(\"reading server error message failed: %w\", err)\n}","preventionTips":["Ensure TDS packet reassembly completes before parsing (handle continuation packets).","Check both header and body bounds, not just the header.","Use TLS so the string bytes cannot be altered in transit.","Add parser unit tests with payloads cut exactly at string headers."],"tags":["mssql","tds-protocol","truncated-payload","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-14T00:17:10.932Z"}