2dadb5e0
scenario: reject size=0, out-of-bounds asserts, empty bytes-hex
a73x 2026-04-19 09:19
Commit message
src/scenario.zig
| Old | New | ||
|---|---|---|---|
| @@ -231,6 +231,10 @@ pub fn parse( | |||
| 231 | diag.message = "size rows must be a positive integer"; | 231 | diag.message = "size rows must be a positive integer"; |
| 232 | return error.ParseFailed; | 232 | return error.ParseFailed; |
| 233 | }; | 233 | }; |
| 234 | if (cols == 0 or rows == 0) { | ||
| 235 | diag.* = .{ .line = line_num, .message = "size dimensions must be non-zero" }; | ||
| 236 | return error.ParseFailed; | ||
| 237 | } | ||
| 234 | has_size = true; | 238 | has_size = true; |
| 235 | } else if (std.mem.eql(u8, kw, "timeout")) { | 239 | } else if (std.mem.eql(u8, kw, "timeout")) { |
| 236 | if (!has_size) { | 240 | if (!has_size) { |
| @@ -311,6 +315,10 @@ pub fn parse( | |||
| 311 | }; | 315 | }; |
| 312 | try hex_buf.append(alloc, byte); | 316 | try hex_buf.append(alloc, byte); |
| 313 | } | 317 | } |
| 318 | if (hex_buf.items.len == 0) { | ||
| 319 | diag.* = .{ .line = line_num, .message = "bytes-hex requires at least one token" }; | ||
| 320 | return error.ParseFailed; | ||
| 321 | } | ||
| 314 | try directives.append(alloc, .{ .bytes = try hex_buf.toOwnedSlice(alloc) }); | 322 | try directives.append(alloc, .{ .bytes = try hex_buf.toOwnedSlice(alloc) }); |
| 315 | } else if (std.mem.eql(u8, kw, "capture")) { | 323 | } else if (std.mem.eql(u8, kw, "capture")) { |
| 316 | const lbl_res = nextToken(line, pos) orelse { | 324 | const lbl_res = nextToken(line, pos) orelse { |
| @@ -367,6 +375,10 @@ pub fn parse( | |||
| 367 | diag.message = "unknown predicate"; | 375 | diag.message = "unknown predicate"; |
| 368 | return error.ParseFailed; | 376 | return error.ParseFailed; |
| 369 | }; | 377 | }; |
| 378 | if (row >= rows or col >= cols) { | ||
| 379 | diag.* = .{ .line = line_num, .message = "assert-cell row/col out of bounds for scenario size" }; | ||
| 380 | return error.ParseFailed; | ||
| 381 | } | ||
| 370 | try directives.append(alloc, .{ .assert_cell = .{ .row = row, .col = col, .pred = pred } }); | 382 | try directives.append(alloc, .{ .assert_cell = .{ .row = row, .col = col, .pred = pred } }); |
| 371 | } else if (std.mem.eql(u8, kw, "assert-cell-at")) { | 383 | } else if (std.mem.eql(u8, kw, "assert-cell-at")) { |
| 372 | const lbl_res = nextToken(line, pos) orelse { | 384 | const lbl_res = nextToken(line, pos) orelse { |
| @@ -416,6 +428,10 @@ pub fn parse( | |||
| 416 | diag.message = "unknown predicate"; | 428 | diag.message = "unknown predicate"; |
| 417 | return error.ParseFailed; | 429 | return error.ParseFailed; |
| 418 | }; | 430 | }; |
| 431 | if (row >= rows or col >= cols) { | ||
| 432 | diag.* = .{ .line = line_num, .message = "assert-cell row/col out of bounds for scenario size" }; | ||
| 433 | return error.ParseFailed; | ||
| 434 | } | ||
| 419 | try directives.append(alloc, .{ .assert_cell_at = .{ | 435 | try directives.append(alloc, .{ .assert_cell_at = .{ |
| 420 | .label = label_copy, | 436 | .label = label_copy, |
| 421 | .row = row, | 437 | .row = row, |
| @@ -697,3 +713,42 @@ test "parse: unknown predicate fails at line" { | |||
| 697 | ); | 713 | ); |
| 698 | try std.testing.expectEqual(@as(usize, 3), diag.line); | 714 | try std.testing.expectEqual(@as(usize, 3), diag.line); |
| 699 | } | 715 | } |
| 716 | |||
| 717 | test "parse: size 0 0 rejected" { | ||
| 718 | const diag = try parseErr( | ||
| 719 | \\size 0 0 | ||
| 720 | \\timeout 500ms | ||
| 721 | ); | ||
| 722 | try std.testing.expectEqual(@as(usize, 1), diag.line); | ||
| 723 | try std.testing.expect(std.mem.indexOf(u8, diag.message, "non-zero") != null); | ||
| 724 | } | ||
| 725 | |||
| 726 | test "parse: assert-cell row out of bounds" { | ||
| 727 | const diag = try parseErr( | ||
| 728 | \\size 80 24 | ||
| 729 | \\timeout 500ms | ||
| 730 | \\assert-cell 24 0 cell-empty | ||
| 731 | ); | ||
| 732 | try std.testing.expectEqual(@as(usize, 3), diag.line); | ||
| 733 | try std.testing.expect(std.mem.indexOf(u8, diag.message, "out of bounds") != null); | ||
| 734 | } | ||
| 735 | |||
| 736 | test "parse: assert-cell-at col out of bounds" { | ||
| 737 | const diag = try parseErr( | ||
| 738 | \\size 80 24 | ||
| 739 | \\timeout 500ms | ||
| 740 | \\assert-cell-at foo 0 80 cell-empty | ||
| 741 | ); | ||
| 742 | try std.testing.expectEqual(@as(usize, 3), diag.line); | ||
| 743 | try std.testing.expect(std.mem.indexOf(u8, diag.message, "out of bounds") != null); | ||
| 744 | } | ||
| 745 | |||
| 746 | test "parse: bytes-hex rejects empty token list" { | ||
| 747 | const diag = try parseErr( | ||
| 748 | \\size 80 24 | ||
| 749 | \\timeout 500ms | ||
| 750 | \\bytes-hex | ||
| 751 | ); | ||
| 752 | try std.testing.expectEqual(@as(usize, 3), diag.line); | ||
| 753 | try std.testing.expect(std.mem.indexOf(u8, diag.message, "at least one token") != null); | ||
| 754 | } | ||