authorgravatar for techatrix@mailbox.orgTechatrix <techatrix@mailbox.org> 2023-09-11 23:00:06+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-09-11 17:00:06-04:00
log7827265ea81c72e44da9b7d9b91e68d557e4db6d
tree173f2120d984ce177110521d2613e423fe0da33a
parenta0968be83c1bc5cf6433e9dc7ebb2f94025c5aa5
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

json: respect max_value_len when parsing std.json.Value (#17107)


2 files changed, 25 insertions(+), 12 deletions(-)

lib/std/json/dynamic.zig+11-12
...@@ -81,7 +81,6 @@ pub const Value = union(enum) {...@@ -81,7 +81,6 @@ pub const Value = union(enum) {
81 }81 }
8282
83 pub fn jsonParse(allocator: Allocator, source: anytype, options: ParseOptions) ParseError(@TypeOf(source.*))!@This() {83 pub fn jsonParse(allocator: Allocator, source: anytype, options: ParseOptions) ParseError(@TypeOf(source.*))!@This() {
84 _ = options;
85 // The grammar of the stack is:84 // The grammar of the stack is:
86 // (.array | .object .string)*85 // (.array | .object .string)*
87 var stack = Array.init(allocator);86 var stack = Array.init(allocator);
...@@ -93,21 +92,21 @@ pub const Value = union(enum) {...@@ -93,21 +92,21 @@ pub const Value = union(enum) {
93 stack.items[stack.items.len - 1] == .array or92 stack.items[stack.items.len - 1] == .array or
94 (stack.items[stack.items.len - 2] == .object and stack.items[stack.items.len - 1] == .string));93 (stack.items[stack.items.len - 2] == .object and stack.items[stack.items.len - 1] == .string));
9594
96 switch (try source.nextAlloc(allocator, .alloc_always)) {95 switch (try source.nextAllocMax(allocator, .alloc_always, options.max_value_len.?)) {
97 .allocated_string => |s| {96 .allocated_string => |s| {
98 return try handleCompleteValue(&stack, allocator, source, Value{ .string = s }) orelse continue;97 return try handleCompleteValue(&stack, allocator, source, Value{ .string = s }, options) orelse continue;
99 },98 },
100 .allocated_number => |slice| {99 .allocated_number => |slice| {
101 return try handleCompleteValue(&stack, allocator, source, Value.parseFromNumberSlice(slice)) orelse continue;100 return try handleCompleteValue(&stack, allocator, source, Value.parseFromNumberSlice(slice), options) orelse continue;
102 },101 },
103102
104 .null => return try handleCompleteValue(&stack, allocator, source, .null) orelse continue,103 .null => return try handleCompleteValue(&stack, allocator, source, .null, options) orelse continue,
105 .true => return try handleCompleteValue(&stack, allocator, source, Value{ .bool = true }) orelse continue,104 .true => return try handleCompleteValue(&stack, allocator, source, Value{ .bool = true }, options) orelse continue,
106 .false => return try handleCompleteValue(&stack, allocator, source, Value{ .bool = false }) orelse continue,105 .false => return try handleCompleteValue(&stack, allocator, source, Value{ .bool = false }, options) orelse continue,
107106
108 .object_begin => {107 .object_begin => {
109 switch (try source.nextAlloc(allocator, .alloc_always)) {108 switch (try source.nextAllocMax(allocator, .alloc_always, options.max_value_len.?)) {
110 .object_end => return try handleCompleteValue(&stack, allocator, source, Value{ .object = ObjectMap.init(allocator) }) orelse continue,109 .object_end => return try handleCompleteValue(&stack, allocator, source, Value{ .object = ObjectMap.init(allocator) }, options) orelse continue,
111 .allocated_string => |key| {110 .allocated_string => |key| {
112 try stack.appendSlice(&[_]Value{111 try stack.appendSlice(&[_]Value{
113 Value{ .object = ObjectMap.init(allocator) },112 Value{ .object = ObjectMap.init(allocator) },
...@@ -120,7 +119,7 @@ pub const Value = union(enum) {...@@ -120,7 +119,7 @@ pub const Value = union(enum) {
120 .array_begin => {119 .array_begin => {
121 try stack.append(Value{ .array = Array.init(allocator) });120 try stack.append(Value{ .array = Array.init(allocator) });
122 },121 },
123 .array_end => return try handleCompleteValue(&stack, allocator, source, stack.pop()) orelse continue,122 .array_end => return try handleCompleteValue(&stack, allocator, source, stack.pop(), options) orelse continue,
124123
125 else => unreachable,124 else => unreachable,
126 }125 }
...@@ -134,7 +133,7 @@ pub const Value = union(enum) {...@@ -134,7 +133,7 @@ pub const Value = union(enum) {
134 }133 }
135};134};
136135
137fn handleCompleteValue(stack: *Array, allocator: Allocator, source: anytype, value_: Value) !?Value {136fn handleCompleteValue(stack: *Array, allocator: Allocator, source: anytype, value_: Value, options: ParseOptions) !?Value {
138 if (stack.items.len == 0) return value_;137 if (stack.items.len == 0) return value_;
139 var value = value_;138 var value = value_;
140 while (true) {139 while (true) {
...@@ -152,7 +151,7 @@ fn handleCompleteValue(stack: *Array, allocator: Allocator, source: anytype, val...@@ -152,7 +151,7 @@ fn handleCompleteValue(stack: *Array, allocator: Allocator, source: anytype, val
152151
153 // This is an invalid state to leave the stack in,152 // This is an invalid state to leave the stack in,
154 // so we have to process the next token before we return.153 // so we have to process the next token before we return.
155 switch (try source.nextAlloc(allocator, .alloc_always)) {154 switch (try source.nextAllocMax(allocator, .alloc_always, options.max_value_len.?)) {
156 .object_end => {155 .object_end => {
157 // This object is complete.156 // This object is complete.
158 value = stack.pop();157 value = stack.pop();
lib/std/json/dynamic_test.zig+14
...@@ -302,6 +302,20 @@ test "long object value" {...@@ -302,6 +302,20 @@ test "long object value" {
302 try testing.expectEqualStrings(value, parsed.value.object.get("key").?.string);302 try testing.expectEqualStrings(value, parsed.value.object.get("key").?.string);
303}303}
304304
305test "ParseOptions.max_value_len" {
306 var arena = ArenaAllocator.init(testing.allocator);
307 defer arena.deinit();
308
309 const str = "\"0800fc577294c34e0b28ad2839435945\"";
310
311 const value = try std.json.parseFromSliceLeaky(std.json.Value, arena.allocator(), str, .{ .max_value_len = 32 });
312
313 try testing.expect(value == .string);
314 try testing.expect(value.string.len == 32);
315
316 try testing.expectError(error.ValueTooLong, std.json.parseFromSliceLeaky(std.json.Value, arena.allocator(), str, .{ .max_value_len = 31 }));
317}
318
305test "many object keys" {319test "many object keys" {
306 const doc =320 const doc =
307 \\{321 \\{