{"record":{"id":"98562c6a675cec88","repo":"projectdiscovery/nuclei","slug":"truncated-pl-option-token","errorCode":null,"errorMessage":"truncated PL option token","messagePattern":"truncated PL option token","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/js/libs/mssql/fingerprint.go","lineNumber":208,"sourceCode":"\tif len(response) != packetLength {\n\t\treturn out, fmt.Errorf(\"packet length mismatch: header=%d body=%d\", packetLength, len(response))\n\t}\n\n\ttype optionToken struct {\n\t\ttoken  byte\n\t\toffset uint16\n\t\tlength uint16\n\t\tdata   []byte\n\t}\n\n\tposition := 8\n\tvar tokens []optionToken\n\tfor position < len(response) {\n\t\tif response[position] == tdsTerminator {\n\t\t\tbreak\n\t\t}\n\t\tif position+5 > len(response) {\n\t\t\treturn out, fmt.Errorf(\"truncated PL option token\")\n\t\t}\n\t\ttoken := response[position]\n\t\toffset := binary.BigEndian.Uint16(response[position+1 : position+3])\n\t\tlength := binary.BigEndian.Uint16(response[position+3 : position+5])\n\t\tvar data []byte\n\t\tif length > 0 {\n\t\t\tstart := int(offset) + 8\n\t\t\tend := start + int(length)\n\t\t\tif start < 8 || end > len(response) {\n\t\t\t\treturn out, fmt.Errorf(\"invalid PL option data range\")\n\t\t\t}\n\t\t\tdata = response[start:end]\n\t\t}\n\t\ttokens = append(tokens, optionToken{token: token, offset: offset, length: length, data: data})\n\t\tposition += 5\n\t}\n\tif position >= len(response) || response[position] != tdsTerminator {\n\t\treturn out, fmt.Errorf(\"missing PL option terminator\")","sourceCodeStart":190,"sourceCodeEnd":226,"githubUrl":"https://github.com/projectdiscovery/nuclei/blob/265b3a3dec374741614e342f813c10f8b38d2bb7/pkg/js/libs/mssql/fingerprint.go#L190-L226","documentation":"A TDS pre-login option table entry is a 5-byte header (option token byte + 2-byte offset + 2-byte length) and the table ends with a 0xff terminator. This error means fewer than 5 bytes remained before end of the response while a non-terminator byte was present, so an option header cannot be read completely. The reply is truncated mid-option and the pre-login data is unusable; FingerprintMssql re-wraps it as 'not a mssql service: truncated PL option token'.","triggerScenarios":"mssql.FingerprintMssql / mssql.IsMssql against a service that replies with a plausible 8-byte TDS header (type 0x04, status 0x01, consistent length) but whose option table is cut off without the 0xff terminator — crafted banners, protocol emulation honeypots, or truncated reads.","commonSituations":"Honeypots emulating MSSQL; misbehaving TDS proxies; custom fuzzing or unit tests feeding partial pre-login payloads; targeting non-MSSQL services that happen to start their reply with 0x04 0x01.","solutions":["Confirm the service with mssql.IsMssql — it maps this parse failure to a clean false","Check with a real TDS client (sqlcmd) that the server completes a pre-login handshake","Capture the raw bytes and verify the option table ends with 0xff within the declared packet length","If the target is behind a TLS-terminating proxy, note pre-login expects plaintext first; encryption is negotiated after, so a TLS-immediate endpoint yields garbage frames"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if (!mssql.IsMssql(host, port)) { log('not mssql, skip: ' + host); return; }","typeGuard":null,"tryCatchPattern":"try { const info = mssql.FingerprintMssql(host, port); }\ncatch (e) { if (String(e).includes('truncated PL option token')) log('malformed TDS reply from ' + host); else throw e; }","preventionTips":["Probe with IsMssql first — it swallows errNotMssql-wrapped parse failures","Do not point mssql templates at unverified ports","Capture replies from persistent offenders to build a skip-list"],"tags":["mssql","tds","protocol-parsing","fingerprint","truncated-response","network"],"backgroundTag":null,"analyzedSha":"265b3a3dec374741614e342f813c10f8b38d2bb7","analyzedAt":"2026-08-15T20:05:51.855Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}