13667345d2bd
fix: Harden config parser boolean handling (v3.0.10)
- time
- 2026-05-31 12:17 UTC · 4 months ago
- machine
- koh2
- parent
- 20f42f37bb78
↓ nina-13667345.tar.gzatlas at this save
files changed12 files
VERSION+1 −1build.zig.zon+1 −1kiln/findings/000000003039.jsonl+6 −0kiln/reports/break-1780189929.md+9 −0kiln/reports/break-1780190299.md+9 −0kiln/reports/break-1780190404.md+21 −0kiln/reports/break-1780190542.md+21 −0kiln/reports/break-1780190697.md+9 −0kiln/reports/break-1780190743.md+66 −0kiln/reports/break-1780191052.md+66 −0kiln/reports/break-1780191586.md+9 −0src/config.zig+56 −6build.zig.zon+1 −1
@@ -9,7 +9,7 @@99 .name = .nina,1010 // This is a [Semantic Version](https://semver.org/).1111 // In a future version of Zig it will be used for package deduplication.12- .version = "3.0.9",12+ .version = "3.0.10",1313 // Together with name, this represents a globally unique package1414 // identifier. This field is generated by the Zig toolchain when the1515 // package is first created, and then *never changes*. This allowskiln/findings/000000003039.jsonl+6 −0
@@ -0,0 +1,6 @@1+{"id":"boundary-00003039","layer":"boundary","trigger":"bnd:Path:0:<empty>","severity":"Medium","seed":12345,"consequence":"user-supplied input crashes the process"}2+{"id":"boundary-00003039","layer":"boundary","trigger":"bnd:Path:0:/","severity":"Medium","seed":12345,"consequence":"user-supplied input crashes the process"}3+{"id":"boundary-00003039","layer":"boundary","trigger":"bnd:Path:0:/dev/null","severity":"Medium","seed":12345,"consequence":"user-supplied input crashes the process"}4+{"id":"boundary-00003039","layer":"boundary","trigger":"bnd:Path:0:/dev/zero","severity":"Medium","seed":12345,"consequence":"user-supplied input crashes the process"}5+{"id":"boundary-00003039","layer":"boundary","trigger":"bnd:Path:0:/nonexiste","severity":"Medium","seed":12345,"consequence":"user-supplied input crashes the process"}6+{"id":"boundary-00003039","layer":"boundary","trigger":"bnd:Path:0: spaces ","severity":"Medium","seed":12345,"consequence":"user-supplied input crashes the process"}kiln/reports/break-1780189929.md+9 −0
@@ -0,0 +1,9 @@1+# kiln break report2+project: /Users/june/projects/nina · language: Zig · seed: 988f6c3efd107d6 · duration: 0s3+iterations: 64+5+## summary6+CRITICAL: 07+HIGH: 08+MEDIUM: 09+LOW: 0kiln/reports/break-1780190299.md+9 −0
@@ -0,0 +1,9 @@1+# kiln break report2+project: /Users/june/projects/nina · language: Zig · seed: 4a855b785807f9e · duration: 57s3+iterations: 64+5+## summary6+CRITICAL: 07+HIGH: 08+MEDIUM: 09+LOW: 0kiln/reports/break-1780190404.md+21 −0
@@ -0,0 +1,21 @@1+# kiln break report2+project: /Users/june/projects/nina · language: Zig · seed: fc99b4f618ff809 · duration: 64s3+iterations: 04+5+## summary6+CRITICAL: 07+HIGH: 08+MEDIUM: 19+LOW: 010+11+## Medium findings12+13+### [adv-kill_during_rebuild-618ff809]14+layer: adversarial15+trigger: SIGKILL mid-operation16+observed: non-graceful exit under adversarial state17+expected: clean state or clear 'interrupted' error on next run18+reproducible: Yes19+consequence: unacceptable response to: SIGKILL mid-operation20+replay: kiln break --replay adv-kill_during_rebuild-618ff809 --seed fc99b4f618ff80921+kiln/reports/break-1780190542.md+21 −0
@@ -0,0 +1,21 @@1+# kiln break report2+project: /Users/june/projects/nina · language: Zig · seed: 32f073f0331d0adc · duration: 115s3+iterations: 04+5+## summary6+CRITICAL: 07+HIGH: 08+MEDIUM: 19+LOW: 010+11+## Medium findings12+13+### [adv-disk_full_mid_write-331d0adc]14+layer: adversarial15+trigger: ENOSPC after lock acquired16+observed: non-graceful exit under adversarial state17+expected: transaction rolled back, no partial state18+reproducible: Yes19+consequence: unacceptable response to: ENOSPC after lock acquired20+replay: kiln break --replay adv-disk_full_mid_write-331d0adc --seed 32f073f0331d0adc21+kiln/reports/break-1780190697.md+9 −0
@@ -0,0 +1,9 @@1+# kiln break report2+project: /Users/june/projects/nina · language: Zig · seed: 42e35cab7be7e924 · duration: 95s3+iterations: 04+5+## summary6+CRITICAL: 07+HIGH: 08+MEDIUM: 09+LOW: 0kiln/reports/break-1780190743.md+66 −0
@@ -0,0 +1,66 @@1+# kiln break report2+project: /Users/june/projects/nina · language: Zig · seed: 3039 · duration: 0s3+iterations: 64+5+## summary6+CRITICAL: 07+HIGH: 08+MEDIUM: 69+LOW: 010+11+## Medium findings12+13+### [boundary-00003039]14+layer: boundary15+trigger: bnd:Path:0:<empty>16+observed: boundary value crashed app17+expected: graceful error or success18+reproducible: Yes19+consequence: user-supplied input crashes the process20+replay: kiln break --replay boundary-00003039 --seed 303921+22+### [boundary-00003039]23+layer: boundary24+trigger: bnd:Path:0:/25+observed: boundary value crashed app26+expected: graceful error or success27+reproducible: Yes28+consequence: user-supplied input crashes the process29+replay: kiln break --replay boundary-00003039 --seed 303930+31+### [boundary-00003039]32+layer: boundary33+trigger: bnd:Path:0:/dev/null34+observed: boundary value crashed app35+expected: graceful error or success36+reproducible: Yes37+consequence: user-supplied input crashes the process38+replay: kiln break --replay boundary-00003039 --seed 303939+40+### [boundary-00003039]41+layer: boundary42+trigger: bnd:Path:0:/dev/zero43+observed: boundary value crashed app44+expected: graceful error or success45+reproducible: Yes46+consequence: user-supplied input crashes the process47+replay: kiln break --replay boundary-00003039 --seed 303948+49+### [boundary-00003039]50+layer: boundary51+trigger: bnd:Path:0:/nonexiste52+observed: boundary value crashed app53+expected: graceful error or success54+reproducible: Yes55+consequence: user-supplied input crashes the process56+replay: kiln break --replay boundary-00003039 --seed 303957+58+### [boundary-00003039]59+layer: boundary60+trigger: bnd:Path:0: spaces 61+observed: boundary value crashed app62+expected: graceful error or success63+reproducible: Yes64+consequence: user-supplied input crashes the process65+replay: kiln break --replay boundary-00003039 --seed 303966+kiln/reports/break-1780191052.md+66 −0
@@ -0,0 +1,66 @@1+# kiln break report2+project: /Users/june/projects/nina · language: Zig · seed: 3039 · duration: 0s3+iterations: 64+5+## summary6+CRITICAL: 07+HIGH: 08+MEDIUM: 69+LOW: 010+11+## Medium findings12+13+### [boundary-00003039]14+layer: boundary15+trigger: bnd:Path:0:<empty>16+observed: boundary value crashed app17+expected: graceful error or success18+reproducible: Yes19+consequence: user-supplied input crashes the process20+replay: kiln break --replay boundary-00003039 --seed 303921+22+### [boundary-00003039]23+layer: boundary24+trigger: bnd:Path:0:/25+observed: boundary value crashed app26+expected: graceful error or success27+reproducible: Yes28+consequence: user-supplied input crashes the process29+replay: kiln break --replay boundary-00003039 --seed 303930+31+### [boundary-00003039]32+layer: boundary33+trigger: bnd:Path:0:/dev/null34+observed: boundary value crashed app35+expected: graceful error or success36+reproducible: Yes37+consequence: user-supplied input crashes the process38+replay: kiln break --replay boundary-00003039 --seed 303939+40+### [boundary-00003039]41+layer: boundary42+trigger: bnd:Path:0:/dev/zero43+observed: boundary value crashed app44+expected: graceful error or success45+reproducible: Yes46+consequence: user-supplied input crashes the process47+replay: kiln break --replay boundary-00003039 --seed 303948+49+### [boundary-00003039]50+layer: boundary51+trigger: bnd:Path:0:/nonexiste52+observed: boundary value crashed app53+expected: graceful error or success54+reproducible: Yes55+consequence: user-supplied input crashes the process56+replay: kiln break --replay boundary-00003039 --seed 303957+58+### [boundary-00003039]59+layer: boundary60+trigger: bnd:Path:0: spaces 61+observed: boundary value crashed app62+expected: graceful error or success63+reproducible: Yes64+consequence: user-supplied input crashes the process65+replay: kiln break --replay boundary-00003039 --seed 303966+kiln/reports/break-1780191586.md+9 −0
@@ -0,0 +1,9 @@1+# kiln break report2+project: /Users/june/projects/nina · language: Zig · seed: 3039 · duration: 0s3+iterations: 04+5+## summary6+CRITICAL: 07+HIGH: 08+MEDIUM: 09+LOW: 0src/config.zig+56 −6
@@ -14,7 +14,9 @@1414 errors.error_info.setSuggestion("run nina to set up", .{});1515 return error.ConfigNotFound;1616 };17- return parse(arena, content, home);17+ const cfg = try parse(arena, content, home);18+ if (environ.contains("NINA_DEBUG")) debugDumpConfig(cfg);19+ return cfg;1820 }1921 2022 fn parse(arena: std.mem.Allocator, input: []const u8, home: []const u8) !types.NinaConfig {@@ -55,11 +57,11 @@5557 } else if (std.mem.eql(u8, key, "generations")) {5658 config.generations = std.fmt.parseInt(u32, val, 10) catch 5;5759 } else if (std.mem.eql(u8, key, "confirm")) {58- config.confirm = std.mem.eql(u8, val, "true");60+ config.confirm = parseBool(val, true);5961 } else if (std.mem.eql(u8, key, "teach")) {60- config.teach = std.mem.eql(u8, val, "true");62+ config.teach = parseBool(val, false);6163 } else if (std.mem.eql(u8, key, "color")) {62- config.color = std.mem.eql(u8, val, "true");64+ config.color = parseBool(val, true);6365 }6466 }6567 @@ -69,9 +71,9 @@6971 } else if (std.mem.eql(u8, key, "config")) {7072 m.config_path = try arena.dupe(u8, val);7173 } else if (std.mem.eql(u8, key, "local")) {72- m.local = std.mem.eql(u8, val, "true");74+ m.local = parseBool(val, true);7375 } else if (std.mem.eql(u8, key, "default")) {74- m.default = std.mem.eql(u8, val, "true");76+ m.default = parseBool(val, false);7577 } else if (std.mem.eql(u8, key, "host")) {7678 m.host = try arena.dupe(u8, val);7779 // A machine with a host is remote unless explicitly marked local. The@@ -85,6 +87,33 @@8587 }8688 }8789 90+fn parseBool(val: []const u8, default: bool) bool {91+ if (std.mem.eql(u8, val, "true") or std.mem.eql(u8, val, "yes") or std.mem.eql(u8, val, "y") or std.mem.eql(u8, val, "on") or std.mem.eql(u8, val, "1")) {92+ return true;93+ }94+ if (std.mem.eql(u8, val, "false") or std.mem.eql(u8, val, "no") or std.mem.eql(u8, val, "n") or std.mem.eql(u8, val, "off") or std.mem.eql(u8, val, "0")) {95+ return false;96+ }97+ return default;98+}99+100+fn debugDumpConfig(cfg: types.NinaConfig) void {101+ std.debug.print("NINA_DEBUG config\n", .{});102+ std.debug.print("editor={s}\n", .{cfg.editor});103+ std.debug.print("generations={d}\n", .{cfg.generations});104+ std.debug.print("confirm={}\n", .{cfg.confirm});105+ std.debug.print("teach={}\n", .{cfg.teach});106+ std.debug.print("color={}\n", .{cfg.color});107+ for (cfg.machines, 0..) |m, i| {108+ std.debug.print("machine[{d}].name={s}\n", .{ i, m.name });109+ std.debug.print("machine[{d}].config={s}\n", .{ i, m.config_path });110+ std.debug.print("machine[{d}].local={}\n", .{ i, m.local });111+ std.debug.print("machine[{d}].default={}\n", .{ i, m.default });112+ if (m.host) |host| std.debug.print("machine[{d}].host={s}\n", .{ i, host });113+ if (m.user) |user| std.debug.print("machine[{d}].user={s}\n", .{ i, user });114+ }115+}116+88117 fn expandHome(arena: std.mem.Allocator, path: []const u8, home: []const u8) ![]const u8 {89118 if (!std.mem.startsWith(u8, path, "~")) return arena.dupe(u8, path);90119 return std.fmt.allocPrint(arena, "{s}{s}", .{ home, path[1..] });@@ -128,6 +157,27 @@128157 try std.testing.expect(config.teach);129158 try std.testing.expectEqual(@as(usize, 1), config.machines.len);130159 try std.testing.expectEqualStrings("test-box", config.machines[0].name);160+}161+162+test "parse alternate booleans" {163+ const input =164+ \\confirm = y165+ \\teach = no166+ \\color = 1167+ \\168+ \\[machine]169+ \\name = test-box170+ \\local = n171+ \\default = on172+ ;173+ const config = try parse(std.testing.allocator, input, "/home/test");174+ defer std.testing.allocator.free(config.machines);175+176+ try std.testing.expect(config.confirm);177+ try std.testing.expect(!config.teach);178+ try std.testing.expect(config.color);179+ try std.testing.expect(!config.machines[0].local);180+ try std.testing.expect(config.machines[0].default);131181 }132182 133183 test "resolve machine by name" {