mirror of
https://github.com/tlsnotary/tlsn-extension.git
synced 2026-01-08 20:48:03 -05:00
Fix parser to return range for entire array when accessing array field (#212)
When calling parser.ranges.body('a', {type: 'json'}) for a body like
{a: [{b: 1}, {c: 2}]}, the parser now returns the range for the entire
array value instead of returning empty.
Changes:
- Store array field with full range (key + value) before processing elements
- hideKey option returns just the array value: [{b: 1}, {c: 2}]
- hideValue option returns just the key: "a"
Added tests for:
- Extracting entire array value directly
- hideKey/hideValue options for array fields
- Array of primitives
- Nested array fields (e.g., data.items)
This commit is contained in:
@@ -929,6 +929,85 @@ describe('Parser', () => {
|
||||
|
||||
expect(ranges).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should extract entire array value when accessing array field directly', () => {
|
||||
const request =
|
||||
'POST /api HTTP/1.1\r\n' +
|
||||
'Content-Type: application/json\r\n' +
|
||||
'\r\n' +
|
||||
'{"a":[{"b":1},{"c":2}]}';
|
||||
|
||||
const parser = new Parser(request);
|
||||
const ranges = parser.ranges.body('a', { type: 'json' });
|
||||
|
||||
expect(ranges).toHaveLength(1);
|
||||
const extracted = request.substring(ranges[0].start, ranges[0].end);
|
||||
// Should include the key and the entire array value
|
||||
expect(extracted).toContain('"a"');
|
||||
expect(extracted).toContain('[{"b":1},{"c":2}]');
|
||||
});
|
||||
|
||||
it('should extract only array value with hideKey option', () => {
|
||||
const request =
|
||||
'POST /api HTTP/1.1\r\n' +
|
||||
'Content-Type: application/json\r\n' +
|
||||
'\r\n' +
|
||||
'{"a":[{"b":1},{"c":2}]}';
|
||||
|
||||
const parser = new Parser(request);
|
||||
const ranges = parser.ranges.body('a', { type: 'json', hideKey: true });
|
||||
|
||||
expect(ranges).toHaveLength(1);
|
||||
const extracted = request.substring(ranges[0].start, ranges[0].end);
|
||||
expect(extracted).toBe('[{"b":1},{"c":2}]');
|
||||
expect(extracted).not.toContain('"a"');
|
||||
});
|
||||
|
||||
it('should extract only array key with hideValue option', () => {
|
||||
const request =
|
||||
'POST /api HTTP/1.1\r\n' +
|
||||
'Content-Type: application/json\r\n' +
|
||||
'\r\n' +
|
||||
'{"a":[{"b":1},{"c":2}]}';
|
||||
|
||||
const parser = new Parser(request);
|
||||
const ranges = parser.ranges.body('a', { type: 'json', hideValue: true });
|
||||
|
||||
expect(ranges).toHaveLength(1);
|
||||
const extracted = request.substring(ranges[0].start, ranges[0].end);
|
||||
expect(extracted).toBe('"a"');
|
||||
expect(extracted).not.toContain('[');
|
||||
});
|
||||
|
||||
it('should extract array of primitives', () => {
|
||||
const request =
|
||||
'POST /api HTTP/1.1\r\n' +
|
||||
'Content-Type: application/json\r\n' +
|
||||
'\r\n' +
|
||||
'{"numbers":[1,2,3,4,5]}';
|
||||
|
||||
const parser = new Parser(request);
|
||||
const ranges = parser.ranges.body('numbers', { type: 'json', hideKey: true });
|
||||
|
||||
expect(ranges).toHaveLength(1);
|
||||
const extracted = request.substring(ranges[0].start, ranges[0].end);
|
||||
expect(extracted).toBe('[1,2,3,4,5]');
|
||||
});
|
||||
|
||||
it('should extract nested array field', () => {
|
||||
const request =
|
||||
'POST /api HTTP/1.1\r\n' +
|
||||
'Content-Type: application/json\r\n' +
|
||||
'\r\n' +
|
||||
'{"data":{"items":[1,2,3]}}';
|
||||
|
||||
const parser = new Parser(request);
|
||||
const ranges = parser.ranges.body('data.items', { type: 'json', hideKey: true });
|
||||
|
||||
expect(ranges).toHaveLength(1);
|
||||
const extracted = request.substring(ranges[0].start, ranges[0].end);
|
||||
expect(extracted).toBe('[1,2,3]');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Mixed Paths (Objects and Arrays)', () => {
|
||||
|
||||
@@ -425,7 +425,33 @@ export class Parser {
|
||||
// Handle different value types
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
if (Array.isArray(value)) {
|
||||
// Handle array
|
||||
// Handle array - first store the array itself, then process elements
|
||||
const valueStr = JSON.stringify(value);
|
||||
const valueBytes = Buffer.from(valueStr, 'utf8');
|
||||
const valueByteIndex = textBytes.indexOf(valueBytes, actualValueByteStart);
|
||||
|
||||
if (valueByteIndex !== -1) {
|
||||
const valueByteEnd = valueByteIndex + valueBytes.length;
|
||||
|
||||
// Store the array itself as a field
|
||||
result[pathKey] = {
|
||||
value: value,
|
||||
ranges: {
|
||||
start: baseOffset + keyByteIndex,
|
||||
end: baseOffset + valueByteEnd,
|
||||
},
|
||||
keyRange: {
|
||||
start: baseOffset + keyByteIndex,
|
||||
end: baseOffset + keyByteIndex + keyBytes.length,
|
||||
},
|
||||
valueRange: {
|
||||
start: baseOffset + valueByteIndex,
|
||||
end: baseOffset + valueByteEnd,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Then recursively process array elements
|
||||
this.processJsonArray(
|
||||
value,
|
||||
textBytes,
|
||||
|
||||
Reference in New Issue
Block a user