Refuse a PDU whose optional parameters do not end on command_length (#87)

* Regression tests for a truncated TLV tail refused rather than accepted

* Refuse a PDU whose optional parameters do not end on command_length

* Assert the bare TLV header refusal against jsmpp instead of recording it as a defect

* Note the truncated TLV tail defect as fixed in the java-client findings

* Derive the padding position, share the bare TLV fixture and trim the decision record

* Regression tests for a PDU whose trailing C-Octet String a peer left out

* An absent trailing C-Octet String consumes no octet, so a bodyless PDU still parses

* Bound the TLV loop by the buffer it was given rather than a second spelling of its length

* Answer the stability review's questions in the record and pin the array contract
This commit is contained in:
2026-09-06 18:01:35 +02:00
committed by GitHub
parent c89005168d
commit 039951e69b
11 changed files with 215 additions and 85 deletions
+2 -1
View File
@@ -19,7 +19,8 @@ const bindParams = {
/**
* Key order inside each `params` object is the order the fields appear on the wire. Reordering
* them corrupts every PDU of that command.
* them corrupts every PDU of that command, and moving a field after `short_message` also stops the
* codec skipping the NULL octet some peers append to it.
*/
const specs = {
alert_notification: {
+3 -1
View File
@@ -146,7 +146,6 @@ function wantUnsuccessSmes(value: ParamValue): Result<{ smes: UnsuccessSme[] }>
}
function readCstring(buffer: Buffer, offset: number): Result<{ bytesRead: number; value: string }> {
// An offset at the end exactly is an absent trailing field, which real peers do send.
if (outOfRange(buffer, offset, 0)) {
return {
err: new Error(
@@ -155,6 +154,9 @@ function readCstring(buffer: Buffer, offset: number): Result<{ bytesRead: number
};
}
// An offset at the end exactly is an absent trailing field, which real peers do send.
if (offset === buffer.length) return { bytesRead: 0, value: '' };
let length = 0;
while (buffer[offset + length]) {