hecks 3.6.0 → 3.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/hecks/bluebook/meta_validator/modes.rb +14 -0
- data/lib/hecks/bluebook/meta_validator/syntax_boot/disk_cache.rb +28 -3
- data/lib/hecks/bluebook/meta_validator/verdict_cache/prebuilt.rb +44 -0
- data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +3 -17
- data/lib/hecks/bluebook/meta_validator.rb +4 -2
- data/lib/hecks/cache_dir.rb +10 -0
- data/lib/hecks/corpus/routes.rb +1 -1
- data/lib/hecks/deploy/bluebook/deploy.bluebook +109 -0
- data/lib/hecks/deploy/oidc.json +10 -0
- data/lib/hecks/doors/usage_cache.rb +19 -9
- data/lib/hecks/fuzzing/coverage_campaign.rb +42 -17
- data/lib/hecks/fuzzing/failure_triage.rb +80 -0
- data/lib/hecks/fuzzing/form_census/chapter_forms.rb +38 -0
- data/lib/hecks/fuzzing/form_census/reference_forms.rb +49 -0
- data/lib/hecks/fuzzing/form_census.rb +28 -52
- data/lib/hecks/fuzzing/isolated_boot/postgres_scratch.rb +22 -7
- data/lib/hecks/fuzzing/isolated_boot/scratch_schema.rb +27 -0
- data/lib/hecks/fuzzing/isolated_boot.rb +6 -2
- data/lib/hecks/fuzzing/mutation/checks.rb +76 -0
- data/lib/hecks/fuzzing/mutation/finders.rb +136 -0
- data/lib/hecks/fuzzing/mutation/judge.rb +94 -0
- data/lib/hecks/fuzzing/mutation/observation.rb +38 -0
- data/lib/hecks/fuzzing/mutation/operators.rb +52 -0
- data/lib/hecks/fuzzing/mutation/report.rb +78 -0
- data/lib/hecks/fuzzing/mutation/run.rb +99 -0
- data/lib/hecks/fuzzing/mutation/selection.rb +32 -0
- data/lib/hecks/fuzzing/mutation/site.rb +21 -0
- data/lib/hecks/fuzzing/mutation/source_map.rb +59 -0
- data/lib/hecks/fuzzing/mutation.rb +12 -0
- data/lib/hecks/fuzzing/properties/catalog.rb +10 -59
- data/lib/hecks/fuzzing/properties/guaranteed_by_construction.rb +63 -0
- data/lib/hecks/fuzzing/properties/read_model_names.rb +50 -0
- data/lib/hecks/fuzzing/properties/read_models.rb +138 -0
- data/lib/hecks/fuzzing/properties/role_checks.rb +50 -0
- data/lib/hecks/fuzzing/properties.rb +7 -0
- data/lib/hecks/fuzzing/replay/observation.rb +1 -1
- data/lib/hecks/fuzzing/replay/role_check.rb +49 -0
- data/lib/hecks/fuzzing/replay/session.rb +22 -6
- data/lib/hecks/fuzzing/replay.rb +1 -0
- data/lib/hecks/fuzzing/runtime_coverage.rb +70 -0
- data/lib/hecks/fuzzing/shrinker/value_simplification.rb +64 -0
- data/lib/hecks/fuzzing/shrinker.rb +3 -0
- data/lib/hecks/fuzzing.rb +3 -0
- data/lib/hecks/gate/stages.yml +12 -2
- data/lib/hecks/hecks/adapters/deploy_toolchain/answers.rb +2 -1
- data/lib/hecks/hecks/adapters/github_rulesets.rb +7 -5
- data/lib/hecks/hecks/adapters/process_pool.rb +26 -4
- data/lib/hecks/hecks/adapters/site_toolchain/prober.rb +64 -0
- data/lib/hecks/hecks/adapters/site_toolchain.rb +41 -5
- data/lib/hecks/hecks/custodian.behaviors +6 -0
- data/lib/hecks/hecks/custodian.bluebook +37 -0
- data/lib/hecks/hecks/hecks.hecksagon +6 -0
- data/lib/hecks/language/bluebook/vocabulary.bluebook +4 -1
- data/lib/hecks/projections/deploy/box/roll_scripts.rb +6 -4
- data/lib/hecks/projections/deploy/box/settings/patterns.rb +4 -0
- data/lib/hecks/projections/deploy/box/settings/types.rb +7 -3
- data/lib/hecks/projections/deploy/box/settings.rb +18 -0
- data/lib/hecks/projections/deploy/box/shared_database.rb +69 -0
- data/lib/hecks/projections/deploy/box/templates/Makefile.tmpl +6 -7
- data/lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl +1 -1
- data/lib/hecks/projections/deploy/box/templates/provision-database.sh.tmpl +97 -0
- data/lib/hecks/projections/deploy/box/templates/restore-to-rds.sh.tmpl +4 -2
- data/lib/hecks/projections/deploy/box/templates/verify-copy.sh.tmpl +2 -1
- data/lib/hecks/projections/deploy/box.rb +14 -2
- data/lib/hecks/projections/deploy/shared_database_instance/templates/Makefile.tmpl +11 -0
- data/lib/hecks/projections/deploy/shared_database_instance/templates/README.md.tmpl +25 -0
- data/lib/hecks/projections/deploy/shared_database_instance/templates/rds.yaml.tmpl +180 -0
- data/lib/hecks/projections/deploy/shared_database_instance.rb +106 -0
- data/lib/hecks/projections/deploy/vercel/settings.rb +83 -0
- data/lib/hecks/projections/deploy/vercel.rb +134 -0
- data/lib/hecks/projections/site/probe.rb +131 -0
- data/lib/hecks/projections/site/site_host/dockerfile.rb +1 -1
- data/lib/hecks/projections/site/site_host/templates.rb +12 -4
- data/lib/hecks/projections.rb +2 -0
- data/lib/hecks/quality_control/adapters/github_checks.rb +9 -2
- data/lib/hecks/quality_control/cli/qa_sweep/dials.rb +1 -0
- data/lib/hecks/quality_control/cli/qa_sweep/seed_loop.rb +10 -2
- data/lib/hecks/runtime/value/validation.rb +34 -3
- data/lib/hecks/rust_build/native_build.rb +29 -1
- data/lib/hecks/rust_build/wasm.rb +16 -3
- data/lib/hecks/site/bluebook/site.bluebook +31 -0
- data/lib/hecks/site/bluebook/site.ports.hecksagon +6 -0
- data/lib/hecks/tools/deploy_recipe/generation.rb +59 -0
- data/lib/hecks/tools/deploy_recipe/targets.rb +56 -2
- data/lib/hecks/tools/deploy_recipe/worlds.rb +2 -1
- data/lib/hecks/tools/deploy_recipe.rb +20 -10
- data/lib/hecks/tools/fuzz_sweep/options.rb +21 -10
- data/lib/hecks/tools/fuzz_sweep/reporting.rb +25 -3
- data/lib/hecks/tools/fuzz_sweep.rb +26 -9
- data/lib/hecks/tools/lanes/files.rb +2 -4
- data/lib/hecks/tools/lanes/live.rb +3 -4
- data/lib/hecks/tools/lanes/ruleset.rb +23 -1
- data/lib/hecks/tools/lanes.rb +12 -1
- data/lib/hecks/tools/mutation_run.rb +73 -0
- data/lib/hecks/tools.rb +1 -0
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks/vocabulary.rb +3 -1
- data/prebuilt/4e6c3cd1f578a38732ea5454dae52562d03d8e9886216077a5c34cbf589f3463.marshal +0 -0
- data/prebuilt/911cdcc56b5a094ed26583a4c76401dba6455b1f434722ed22b850bc1b1bdd28.marshal +0 -0
- data/prebuilt/verdicts-3230a251570aa03666708d16f92e646feab6a3f8cc2a3540c017d748e41d41a1.json +1 -0
- data/qa/settings.yml +1 -0
- data/rust/codegen/src/fielded.rs +3 -3
- data/rust/codegen/src/json_codec.rs +2 -2
- data/rust/codegen/src/naming.rs +20 -0
- data/rust/host/Cargo.toml +6 -2
- data/rust/host/HECKS_RELEASE +1 -1
- data/rust/host/src/api.rs +28 -5
- data/rust/host/src/auth.rs +11 -1
- data/rust/host/src/boundary_fuzz/api.rs +260 -0
- data/rust/host/src/boundary_fuzz/auth.rs +119 -0
- data/rust/host/src/boundary_fuzz/expr_json.rs +158 -0
- data/rust/host/src/boundary_fuzz/ir.rs +79 -0
- data/rust/host/src/boundary_fuzz/server.rs +227 -0
- data/rust/host/src/boundary_fuzz/web.rs +125 -0
- data/rust/host/src/commerce_ir.rs +459 -0
- data/rust/host/src/extension.rs +100 -0
- data/rust/host/src/fuzz_support.rs +165 -0
- data/rust/host/src/ir.rs +4 -448
- data/rust/host/src/journal.rs +1 -1
- data/rust/host/src/lib.rs +39 -0
- data/rust/host/src/main.rs +10 -44
- data/rust/host/src/mint.rs +1 -1
- data/rust/host/src/payments/tests.rs +8 -8
- data/rust/host/src/payments.rs +10 -10
- data/rust/host/src/rate_limit.rs +27 -41
- data/rust/host/src/secrets.rs +44 -0
- data/rust/host/src/server.rs +6 -1
- data/rust/host/src/web/commerce.rs +113 -0
- data/rust/host/src/web/newsletter.rs +2 -1
- data/rust/host/src/web/newsletter_send.rs +1 -1
- data/rust/host/src/web/registration_receipt.rs +2 -2
- data/rust/host/src/web/registrations.rs +49 -49
- data/rust/host/src/web/resend_webhook.rs +299 -0
- data/rust/host/src/web.rs +55 -49
- data/rust/parser/fuzz/.gitignore +4 -0
- data/rust/parser/fuzz/Cargo.lock +89 -0
- data/rust/parser/fuzz/Cargo.toml +27 -0
- data/rust/parser/fuzz/README.md +19 -0
- data/rust/parser/fuzz/fuzz_targets/chapter.rs +50 -0
- data/rust/parser/fuzz/fuzz_targets/resolve.rs +35 -0
- data/rust/parser/src/parse/command.rs +14 -2
- data/rust/parser/src/parse/mod.rs +25 -0
- data/rust/parser/src/ruby_value.rs +95 -6
- data/rust/src/kernel/json.rs +34 -3
- metadata +58 -2
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
//! Coverage-guided target for `parse::chapter::resolve_hecksagon_dependencies`.
|
|
2
|
+
//!
|
|
3
|
+
//! Run from `rust/parser` with a nightly toolchain: `cargo +nightly fuzz run resolve`.
|
|
4
|
+
#![no_main]
|
|
5
|
+
#![allow(dead_code)]
|
|
6
|
+
|
|
7
|
+
#[path = "../../src/build/mod.rs"]
|
|
8
|
+
mod build;
|
|
9
|
+
#[path = "../../src/canonical.rs"]
|
|
10
|
+
mod canonical;
|
|
11
|
+
#[path = "../../src/diag.rs"]
|
|
12
|
+
mod diag;
|
|
13
|
+
#[path = "../../src/emit.rs"]
|
|
14
|
+
mod emit;
|
|
15
|
+
#[path = "../../src/expr/mod.rs"]
|
|
16
|
+
mod expr;
|
|
17
|
+
#[path = "../../src/ir.rs"]
|
|
18
|
+
mod ir;
|
|
19
|
+
#[path = "../../src/keywords.rs"]
|
|
20
|
+
mod keywords;
|
|
21
|
+
#[path = "../../src/lex.rs"]
|
|
22
|
+
mod lex;
|
|
23
|
+
#[path = "../../src/parse/mod.rs"]
|
|
24
|
+
mod parse;
|
|
25
|
+
#[path = "../../src/ruby_value.rs"]
|
|
26
|
+
mod ruby_value;
|
|
27
|
+
|
|
28
|
+
use libfuzzer_sys::fuzz_target;
|
|
29
|
+
|
|
30
|
+
fuzz_target!(|data: &[u8]| {
|
|
31
|
+
let Ok(source) = std::str::from_utf8(data) else {
|
|
32
|
+
return;
|
|
33
|
+
};
|
|
34
|
+
let _ = parse::chapter::resolve_hecksagon_dependencies("Fuzzed", "fuzz.hecksagon", source);
|
|
35
|
+
});
|
|
@@ -171,10 +171,22 @@ pub fn parse_body(
|
|
|
171
171
|
|
|
172
172
|
match gated.row.word {
|
|
173
173
|
"role" => {
|
|
174
|
-
command.role = Some(super::
|
|
174
|
+
command.role = Some(super::positional_literal_text(
|
|
175
|
+
file,
|
|
176
|
+
line,
|
|
177
|
+
"role",
|
|
178
|
+
&gated.args,
|
|
179
|
+
1,
|
|
180
|
+
)?)
|
|
175
181
|
}
|
|
176
182
|
"goal" => {
|
|
177
|
-
command.goal = Some(super::
|
|
183
|
+
command.goal = Some(super::positional_literal_text(
|
|
184
|
+
file,
|
|
185
|
+
line,
|
|
186
|
+
"goal",
|
|
187
|
+
&gated.args,
|
|
188
|
+
1,
|
|
189
|
+
)?)
|
|
178
190
|
}
|
|
179
191
|
// A synthesized inline `one_of(...)` closed set is dropped; `CommandBuilder#build`
|
|
180
192
|
// ignores it.
|
|
@@ -743,6 +743,31 @@ pub(crate) fn positional_text(
|
|
|
743
743
|
})
|
|
744
744
|
}
|
|
745
745
|
|
|
746
|
+
/// Like `positional_text`, but the argument must be a literal. Ruby evaluates what follows
|
|
747
|
+
/// `role` or `goal`, so a bare word or a call there (`role sets :name`) is an expression this
|
|
748
|
+
/// parser cannot evaluate, and reading its source text as the value would invent one.
|
|
749
|
+
pub(crate) fn positional_literal_text(
|
|
750
|
+
file: &str,
|
|
751
|
+
line: usize,
|
|
752
|
+
word: &str,
|
|
753
|
+
args: &ArgumentGateResult,
|
|
754
|
+
at: usize,
|
|
755
|
+
) -> ParseResult<String> {
|
|
756
|
+
let raw = positional_raw(file, line, word, args, at)?;
|
|
757
|
+
match ruby_value::read(raw) {
|
|
758
|
+
ruby_value::Value::Bare(_) => Err(Diagnostic::new(
|
|
759
|
+
file,
|
|
760
|
+
line,
|
|
761
|
+
format!(
|
|
762
|
+
"'{word}'s positional argument {at} ('{}') is not a literal",
|
|
763
|
+
raw.trim()
|
|
764
|
+
),
|
|
765
|
+
)),
|
|
766
|
+
ruby_value::Value::Str(s) => Ok(s),
|
|
767
|
+
literal => Ok(ruby_value::to_s(&literal)),
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
|
|
746
771
|
/// A required symbol positional without its colon (`attribute :name`), bare or quoted (`:"a.b"`).
|
|
747
772
|
/// The quoted form spells dotted paths, e.g. `correlates_by :"reference.value"`.
|
|
748
773
|
pub(crate) fn positional_symbol(
|
|
@@ -192,21 +192,97 @@ fn unquote(raw: &str) -> String {
|
|
|
192
192
|
unescape_double_quoted_inner(&raw[1..raw.len() - 1])
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
+
/// Unescapes a Ruby double-quoted body: `\n`, `\t`, `\e` and the other single-letter escapes,
|
|
196
|
+
/// octal `\NNN`, `\xHH`, `\uHHHH` and `\u{H H ...}`, and `\<newline>` as nothing. Any other
|
|
197
|
+
/// escaped character stands for itself, as in Ruby. `\c`, `\C-` and `\M-` control and meta
|
|
198
|
+
/// escapes are left as the bare letter, since no bluebook spells them.
|
|
195
199
|
fn unescape_double_quoted_inner(inner: &str) -> String {
|
|
196
200
|
let mut out = String::with_capacity(inner.len());
|
|
197
|
-
let mut chars = inner.chars();
|
|
201
|
+
let mut chars = inner.chars().peekable();
|
|
198
202
|
while let Some(ch) = chars.next() {
|
|
199
|
-
if ch
|
|
200
|
-
if let Some(next) = chars.next() {
|
|
201
|
-
out.push(next);
|
|
202
|
-
}
|
|
203
|
-
} else {
|
|
203
|
+
if ch != '\\' {
|
|
204
204
|
out.push(ch);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
let Some(next) = chars.next() else {
|
|
208
|
+
continue;
|
|
209
|
+
};
|
|
210
|
+
match next {
|
|
211
|
+
'a' => out.push('\u{7}'),
|
|
212
|
+
'b' => out.push('\u{8}'),
|
|
213
|
+
'e' => out.push('\u{1b}'),
|
|
214
|
+
'f' => out.push('\u{c}'),
|
|
215
|
+
'n' => out.push('\n'),
|
|
216
|
+
'r' => out.push('\r'),
|
|
217
|
+
's' => out.push(' '),
|
|
218
|
+
't' => out.push('\t'),
|
|
219
|
+
'v' => out.push('\u{b}'),
|
|
220
|
+
'\n' => {}
|
|
221
|
+
'0'..='7' => out.push(read_octal_escape(next, &mut chars)),
|
|
222
|
+
'x' => match read_hex_digits(&mut chars, 2) {
|
|
223
|
+
Some(code) => out.push(char::from_u32(code).unwrap_or('\u{fffd}')),
|
|
224
|
+
None => out.push('x'),
|
|
225
|
+
},
|
|
226
|
+
'u' => read_unicode_escape(&mut chars, &mut out),
|
|
227
|
+
other => out.push(other),
|
|
205
228
|
}
|
|
206
229
|
}
|
|
207
230
|
out
|
|
208
231
|
}
|
|
209
232
|
|
|
233
|
+
/// Up to three octal digits starting with `first`, as one character.
|
|
234
|
+
fn read_octal_escape(first: char, chars: &mut std::iter::Peekable<std::str::Chars>) -> char {
|
|
235
|
+
let mut code = first.to_digit(8).unwrap_or(0);
|
|
236
|
+
for _ in 0..2 {
|
|
237
|
+
match chars.peek().and_then(|c| c.to_digit(8)) {
|
|
238
|
+
Some(digit) => {
|
|
239
|
+
code = code * 8 + digit;
|
|
240
|
+
chars.next();
|
|
241
|
+
}
|
|
242
|
+
None => break,
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
char::from_u32(code & 0xff).unwrap_or('\u{fffd}')
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/// Between one and `max` hex digits, or `None` when the next character is not one.
|
|
249
|
+
fn read_hex_digits(chars: &mut std::iter::Peekable<std::str::Chars>, max: usize) -> Option<u32> {
|
|
250
|
+
let mut code: Option<u32> = None;
|
|
251
|
+
for _ in 0..max {
|
|
252
|
+
match chars.peek().and_then(|c| c.to_digit(16)) {
|
|
253
|
+
Some(digit) => {
|
|
254
|
+
code = Some(code.unwrap_or(0) * 16 + digit);
|
|
255
|
+
chars.next();
|
|
256
|
+
}
|
|
257
|
+
None => break,
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
code
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/// `\uHHHH` (exactly four digits) or `\u{H H ...}` (one or more codepoints), after the `u`.
|
|
264
|
+
fn read_unicode_escape(chars: &mut std::iter::Peekable<std::str::Chars>, out: &mut String) {
|
|
265
|
+
if chars.peek() == Some(&'{') {
|
|
266
|
+
chars.next();
|
|
267
|
+
loop {
|
|
268
|
+
while chars.peek().is_some_and(|c| *c == ' ') {
|
|
269
|
+
chars.next();
|
|
270
|
+
}
|
|
271
|
+
match read_hex_digits(chars, 6) {
|
|
272
|
+
Some(code) => out.push(char::from_u32(code).unwrap_or('\u{fffd}')),
|
|
273
|
+
None => break,
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
if chars.peek() == Some(&'}') {
|
|
277
|
+
chars.next();
|
|
278
|
+
}
|
|
279
|
+
} else if let Some(code) = read_hex_digits(chars, 4) {
|
|
280
|
+
out.push(char::from_u32(code).unwrap_or('\u{fffd}'));
|
|
281
|
+
} else {
|
|
282
|
+
out.push('u');
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
210
286
|
/// Scans adjacent quoted literals (either quote style, whitespace-separated) from index 0.
|
|
211
287
|
/// Returns their unescaped concatenation and the chars consumed, or `None` if `raw` does not
|
|
212
288
|
/// start with a closed literal. Callers compare `consumed` to the full length to tell a whole
|
|
@@ -408,6 +484,19 @@ mod tests {
|
|
|
408
484
|
);
|
|
409
485
|
}
|
|
410
486
|
|
|
487
|
+
#[test]
|
|
488
|
+
fn double_quoted_escapes_read_as_ruby_reads_them() {
|
|
489
|
+
assert_eq!(read(r#""a\tb\nc""#), Value::Str("a\tb\nc".to_string()));
|
|
490
|
+
assert_eq!(read(r#""\e\s\a\0""#), Value::Str("\u{1b} \u{7}\0".to_string()));
|
|
491
|
+
assert_eq!(
|
|
492
|
+
read(r#""\x41\101é\u{1F4A5 41}""#),
|
|
493
|
+
Value::Str("AA\u{e9}\u{1F4A5}A".to_string())
|
|
494
|
+
);
|
|
495
|
+
assert_eq!(read(r#""q\q\"\\""#), Value::Str("qq\"\\".to_string()));
|
|
496
|
+
assert_eq!(read("\"a\\\nb\""), Value::Str("ab".to_string()));
|
|
497
|
+
assert_eq!(read(r#""\xZ""#), Value::Str("xZ".to_string()));
|
|
498
|
+
}
|
|
499
|
+
|
|
411
500
|
#[test]
|
|
412
501
|
fn concatenates_three_adjacent_literals_mixing_quote_styles() {
|
|
413
502
|
assert_eq!(read("\"a\" 'b' \"c\""), Value::Str("abc".to_string()));
|
data/rust/src/kernel/json.rs
CHANGED
|
@@ -346,7 +346,7 @@ impl Json {
|
|
|
346
346
|
}
|
|
347
347
|
|
|
348
348
|
pub fn parse(input: &str) -> Result<Json, String> {
|
|
349
|
-
let mut parser = Parser { chars: input.chars().peekable() };
|
|
349
|
+
let mut parser = Parser { chars: input.chars().peekable(), depth: 0 };
|
|
350
350
|
let value = parser.parse_value()?;
|
|
351
351
|
parser.skip_ws();
|
|
352
352
|
Ok(value)
|
|
@@ -611,8 +611,14 @@ fn write_escaped_string(s: &str, out: &mut String) {
|
|
|
611
611
|
// Recursive descent over chars; the input is a valid `&str`, so no UTF-8 byte handling is needed.
|
|
612
612
|
struct Parser<'a> {
|
|
613
613
|
chars: std::iter::Peekable<std::str::Chars<'a>>,
|
|
614
|
+
depth: usize,
|
|
614
615
|
}
|
|
615
616
|
|
|
617
|
+
/// How deeply arrays and objects may nest before `parse` refuses. Each level is a recursive call,
|
|
618
|
+
/// so an unbounded document (`[[[[...`) overflows the stack, which aborts the process rather than
|
|
619
|
+
/// refusing. 128 is `serde_json`'s own recursion limit, so the host and this parser refuse alike.
|
|
620
|
+
const MAX_DEPTH: usize = 128;
|
|
621
|
+
|
|
616
622
|
impl<'a> Parser<'a> {
|
|
617
623
|
fn skip_ws(&mut self) {
|
|
618
624
|
while matches!(self.chars.peek(), Some(c) if c.is_whitespace()) {
|
|
@@ -651,7 +657,26 @@ impl<'a> Parser<'a> {
|
|
|
651
657
|
}
|
|
652
658
|
}
|
|
653
659
|
|
|
660
|
+
// Counts one level of nesting for the duration of `body`; refuses past `MAX_DEPTH`.
|
|
661
|
+
fn nested(&mut self, body: impl FnOnce(&mut Self) -> Result<Json, String>) -> Result<Json, String> {
|
|
662
|
+
if self.depth >= MAX_DEPTH {
|
|
663
|
+
return Err(format!("JSON nests deeper than {MAX_DEPTH} levels"));
|
|
664
|
+
}
|
|
665
|
+
self.depth += 1;
|
|
666
|
+
let result = body(self);
|
|
667
|
+
self.depth -= 1;
|
|
668
|
+
result
|
|
669
|
+
}
|
|
670
|
+
|
|
654
671
|
fn parse_object(&mut self) -> Result<Json, String> {
|
|
672
|
+
self.nested(Self::parse_object_body)
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
fn parse_array(&mut self) -> Result<Json, String> {
|
|
676
|
+
self.nested(Self::parse_array_body)
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
fn parse_object_body(&mut self) -> Result<Json, String> {
|
|
655
680
|
self.expect('{')?;
|
|
656
681
|
let mut fields = Vec::new();
|
|
657
682
|
self.skip_ws();
|
|
@@ -676,7 +701,7 @@ impl<'a> Parser<'a> {
|
|
|
676
701
|
Ok(Json::Object(fields))
|
|
677
702
|
}
|
|
678
703
|
|
|
679
|
-
fn
|
|
704
|
+
fn parse_array_body(&mut self) -> Result<Json, String> {
|
|
680
705
|
self.expect('[')?;
|
|
681
706
|
let mut items = Vec::new();
|
|
682
707
|
self.skip_ws();
|
|
@@ -773,7 +798,13 @@ impl<'a> Parser<'a> {
|
|
|
773
798
|
// A literal with a `.`/exponent is a genuine `Float`, and Ruby's `Float` is the same
|
|
774
799
|
// `f64` Rust's is, so there is nothing to preserve there.
|
|
775
800
|
let is_plain_integer = !s.contains('.') && !s.contains(['e', 'E']);
|
|
776
|
-
let
|
|
801
|
+
let keeps_source = is_plain_integer && canonical_integer_digits(value) != s;
|
|
802
|
+
// `1e999` parses to infinity with no source text kept, and `write` would print it as `inf`:
|
|
803
|
+
// not JSON, so refuse it. A plain integer keeps its digits, which `write` re-emits as-is.
|
|
804
|
+
if !value.is_finite() && !keeps_source {
|
|
805
|
+
return Err(format!("number {s:?} is out of range"));
|
|
806
|
+
}
|
|
807
|
+
let raw = keeps_source.then_some(s);
|
|
777
808
|
Ok(Json::Num(value, raw))
|
|
778
809
|
}
|
|
779
810
|
}
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: hecks
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chris Young
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: prism
|
|
@@ -271,6 +271,7 @@ files:
|
|
|
271
271
|
- lib/hecks/bluebook/meta_validator/translation_judge.rb
|
|
272
272
|
- lib/hecks/bluebook/meta_validator/translation_judge/rules.rb
|
|
273
273
|
- lib/hecks/bluebook/meta_validator/verdict_cache.rb
|
|
274
|
+
- lib/hecks/bluebook/meta_validator/verdict_cache/prebuilt.rb
|
|
274
275
|
- lib/hecks/bluebook/meta_validator/verdict_cache/tagging.rb
|
|
275
276
|
- lib/hecks/bluebook/meta_validator/world_judge.rb
|
|
276
277
|
- lib/hecks/bluebook/model_check.rb
|
|
@@ -473,13 +474,28 @@ files:
|
|
|
473
474
|
- lib/hecks/fuzzing/domain_generator/rendering.rb
|
|
474
475
|
- lib/hecks/fuzzing/domain_generator/tokens.rb
|
|
475
476
|
- lib/hecks/fuzzing/era_boundary.rb
|
|
477
|
+
- lib/hecks/fuzzing/failure_triage.rb
|
|
476
478
|
- lib/hecks/fuzzing/form_census.rb
|
|
479
|
+
- lib/hecks/fuzzing/form_census/chapter_forms.rb
|
|
480
|
+
- lib/hecks/fuzzing/form_census/reference_forms.rb
|
|
477
481
|
- lib/hecks/fuzzing/generated_domain_check.rb
|
|
478
482
|
- lib/hecks/fuzzing/invalid_value_generator.rb
|
|
479
483
|
- lib/hecks/fuzzing/isolated_boot.rb
|
|
480
484
|
- lib/hecks/fuzzing/isolated_boot/copying.rb
|
|
481
485
|
- lib/hecks/fuzzing/isolated_boot/postgres_scratch.rb
|
|
486
|
+
- lib/hecks/fuzzing/isolated_boot/scratch_schema.rb
|
|
482
487
|
- lib/hecks/fuzzing/isolated_boot/worlds.rb
|
|
488
|
+
- lib/hecks/fuzzing/mutation.rb
|
|
489
|
+
- lib/hecks/fuzzing/mutation/checks.rb
|
|
490
|
+
- lib/hecks/fuzzing/mutation/finders.rb
|
|
491
|
+
- lib/hecks/fuzzing/mutation/judge.rb
|
|
492
|
+
- lib/hecks/fuzzing/mutation/observation.rb
|
|
493
|
+
- lib/hecks/fuzzing/mutation/operators.rb
|
|
494
|
+
- lib/hecks/fuzzing/mutation/report.rb
|
|
495
|
+
- lib/hecks/fuzzing/mutation/run.rb
|
|
496
|
+
- lib/hecks/fuzzing/mutation/selection.rb
|
|
497
|
+
- lib/hecks/fuzzing/mutation/site.rb
|
|
498
|
+
- lib/hecks/fuzzing/mutation/source_map.rb
|
|
483
499
|
- lib/hecks/fuzzing/nondeterministic.rb
|
|
484
500
|
- lib/hecks/fuzzing/outside_world.rb
|
|
485
501
|
- lib/hecks/fuzzing/persistence_parity.rb
|
|
@@ -489,6 +505,7 @@ files:
|
|
|
489
505
|
- lib/hecks/fuzzing/properties/corrections.rb
|
|
490
506
|
- lib/hecks/fuzzing/properties/dispatch_and_mutations.rb
|
|
491
507
|
- lib/hecks/fuzzing/properties/group_by_recompute.rb
|
|
508
|
+
- lib/hecks/fuzzing/properties/guaranteed_by_construction.rb
|
|
492
509
|
- lib/hecks/fuzzing/properties/guards.rb
|
|
493
510
|
- lib/hecks/fuzzing/properties/invariants_and_aggregation.rb
|
|
494
511
|
- lib/hecks/fuzzing/properties/lifecycle_and_replay.rb
|
|
@@ -498,7 +515,10 @@ files:
|
|
|
498
515
|
- lib/hecks/fuzzing/properties/policy_wiring.rb
|
|
499
516
|
- lib/hecks/fuzzing/properties/query_rows.rb
|
|
500
517
|
- lib/hecks/fuzzing/properties/querying.rb
|
|
518
|
+
- lib/hecks/fuzzing/properties/read_model_names.rb
|
|
519
|
+
- lib/hecks/fuzzing/properties/read_models.rb
|
|
501
520
|
- lib/hecks/fuzzing/properties/references.rb
|
|
521
|
+
- lib/hecks/fuzzing/properties/role_checks.rb
|
|
502
522
|
- lib/hecks/fuzzing/properties/tenant_scope.rb
|
|
503
523
|
- lib/hecks/fuzzing/qa_settings.rb
|
|
504
524
|
- lib/hecks/fuzzing/replay.rb
|
|
@@ -508,8 +528,10 @@ files:
|
|
|
508
528
|
- lib/hecks/fuzzing/replay/mutation_trace.rb
|
|
509
529
|
- lib/hecks/fuzzing/replay/observation.rb
|
|
510
530
|
- lib/hecks/fuzzing/replay/read_only_steps.rb
|
|
531
|
+
- lib/hecks/fuzzing/replay/role_check.rb
|
|
511
532
|
- lib/hecks/fuzzing/replay/session.rb
|
|
512
533
|
- lib/hecks/fuzzing/rotation_priority.rb
|
|
534
|
+
- lib/hecks/fuzzing/runtime_coverage.rb
|
|
513
535
|
- lib/hecks/fuzzing/rust_gap_manifest.rb
|
|
514
536
|
- lib/hecks/fuzzing/self_consistency.rb
|
|
515
537
|
- lib/hecks/fuzzing/self_consistency/saga_redelivery.rb
|
|
@@ -530,6 +552,7 @@ files:
|
|
|
530
552
|
- lib/hecks/fuzzing/sequence_generator/query_binding.rb
|
|
531
553
|
- lib/hecks/fuzzing/sequence_generator/step_builder.rb
|
|
532
554
|
- lib/hecks/fuzzing/shrinker.rb
|
|
555
|
+
- lib/hecks/fuzzing/shrinker/value_simplification.rb
|
|
533
556
|
- lib/hecks/fuzzing/structural_skips.rb
|
|
534
557
|
- lib/hecks/fuzzing/sweep_depth.rb
|
|
535
558
|
- lib/hecks/fuzzing/target_capabilities.rb
|
|
@@ -621,6 +644,7 @@ files:
|
|
|
621
644
|
- lib/hecks/hecks/adapters/shell.rb
|
|
622
645
|
- lib/hecks/hecks/adapters/site_toolchain.adapter
|
|
623
646
|
- lib/hecks/hecks/adapters/site_toolchain.rb
|
|
647
|
+
- lib/hecks/hecks/adapters/site_toolchain/prober.rb
|
|
624
648
|
- lib/hecks/hecks/adapters/terminal.adapter
|
|
625
649
|
- lib/hecks/hecks/adapters/terminal.rb
|
|
626
650
|
- lib/hecks/hecks/codebase.behaviors
|
|
@@ -793,6 +817,7 @@ files:
|
|
|
793
817
|
- lib/hecks/projections/deploy/box/settings/origin.rb
|
|
794
818
|
- lib/hecks/projections/deploy/box/settings/patterns.rb
|
|
795
819
|
- lib/hecks/projections/deploy/box/settings/types.rb
|
|
820
|
+
- lib/hecks/projections/deploy/box/shared_database.rb
|
|
796
821
|
- lib/hecks/projections/deploy/box/stack_yaml.rb
|
|
797
822
|
- lib/hecks/projections/deploy/box/templates/MIGRATION.md.tmpl
|
|
798
823
|
- lib/hecks/projections/deploy/box/templates/Makefile.tmpl
|
|
@@ -809,6 +834,7 @@ files:
|
|
|
809
834
|
- lib/hecks/projections/deploy/box/templates/hosting.mk.tmpl
|
|
810
835
|
- lib/hecks/projections/deploy/box/templates/mode-summary-services.tmpl
|
|
811
836
|
- lib/hecks/projections/deploy/box/templates/mode-summary-taskdef.tmpl
|
|
837
|
+
- lib/hecks/projections/deploy/box/templates/provision-database.sh.tmpl
|
|
812
838
|
- lib/hecks/projections/deploy/box/templates/rds.yaml.tmpl
|
|
813
839
|
- lib/hecks/projections/deploy/box/templates/render-compose-taskdef.sh.tmpl
|
|
814
840
|
- lib/hecks/projections/deploy/box/templates/render-compose.sh.tmpl
|
|
@@ -879,6 +905,10 @@ files:
|
|
|
879
905
|
- lib/hecks/projections/deploy/scripts/smoke-after-deploy.sh.tmpl
|
|
880
906
|
- lib/hecks/projections/deploy/shared.rb
|
|
881
907
|
- lib/hecks/projections/deploy/shared/rows.rb
|
|
908
|
+
- lib/hecks/projections/deploy/shared_database_instance.rb
|
|
909
|
+
- lib/hecks/projections/deploy/shared_database_instance/templates/Makefile.tmpl
|
|
910
|
+
- lib/hecks/projections/deploy/shared_database_instance/templates/README.md.tmpl
|
|
911
|
+
- lib/hecks/projections/deploy/shared_database_instance/templates/rds.yaml.tmpl
|
|
882
912
|
- lib/hecks/projections/deploy/smoke.rb
|
|
883
913
|
- lib/hecks/projections/deploy/smoke/harness.js
|
|
884
914
|
- lib/hecks/projections/deploy/smoke/workflow.yml.tmpl
|
|
@@ -971,6 +1001,8 @@ files:
|
|
|
971
1001
|
- lib/hecks/projections/deploy/templates/shared/plain_db.tmpl
|
|
972
1002
|
- lib/hecks/projections/deploy/templates/shared/vpc_and_database.tmpl
|
|
973
1003
|
- lib/hecks/projections/deploy/text_template.rb
|
|
1004
|
+
- lib/hecks/projections/deploy/vercel.rb
|
|
1005
|
+
- lib/hecks/projections/deploy/vercel/settings.rb
|
|
974
1006
|
- lib/hecks/projections/diagrams.rb
|
|
975
1007
|
- lib/hecks/projections/diagrams/dispatch.rb
|
|
976
1008
|
- lib/hecks/projections/diagrams/statecharts.rb
|
|
@@ -1030,6 +1062,7 @@ files:
|
|
|
1030
1062
|
- lib/hecks/projections/site/payload/specs.rb
|
|
1031
1063
|
- lib/hecks/projections/site/payload/specs/wire.rb
|
|
1032
1064
|
- lib/hecks/projections/site/payload_driver.rb
|
|
1065
|
+
- lib/hecks/projections/site/probe.rb
|
|
1033
1066
|
- lib/hecks/projections/site/regions.rb
|
|
1034
1067
|
- lib/hecks/projections/site/root_rows.rb
|
|
1035
1068
|
- lib/hecks/projections/site/routes_ts.rb
|
|
@@ -1375,6 +1408,7 @@ files:
|
|
|
1375
1408
|
- lib/hecks/tools/comment_style/walker.rb
|
|
1376
1409
|
- lib/hecks/tools/corpus_report.rb
|
|
1377
1410
|
- lib/hecks/tools/deploy_recipe.rb
|
|
1411
|
+
- lib/hecks/tools/deploy_recipe/generation.rb
|
|
1378
1412
|
- lib/hecks/tools/deploy_recipe/targets.rb
|
|
1379
1413
|
- lib/hecks/tools/deploy_recipe/worlds.rb
|
|
1380
1414
|
- lib/hecks/tools/deploy_recipe_lint.rb
|
|
@@ -1405,6 +1439,7 @@ files:
|
|
|
1405
1439
|
- lib/hecks/tools/lanes/ruleset.rb
|
|
1406
1440
|
- lib/hecks/tools/lanes/watch_workflow.rb
|
|
1407
1441
|
- lib/hecks/tools/lanes/workflow.rb
|
|
1442
|
+
- lib/hecks/tools/mutation_run.rb
|
|
1408
1443
|
- lib/hecks/tools/oidc_manifests.rb
|
|
1409
1444
|
- lib/hecks/tools/query_ir_run.rb
|
|
1410
1445
|
- lib/hecks/tools/regeneration_run.rb
|
|
@@ -1439,6 +1474,9 @@ files:
|
|
|
1439
1474
|
- lib/rubocop/cop/hecks/fallback_hash_lookup.rb
|
|
1440
1475
|
- lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb
|
|
1441
1476
|
- lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb
|
|
1477
|
+
- prebuilt/4e6c3cd1f578a38732ea5454dae52562d03d8e9886216077a5c34cbf589f3463.marshal
|
|
1478
|
+
- prebuilt/911cdcc56b5a094ed26583a4c76401dba6455b1f434722ed22b850bc1b1bdd28.marshal
|
|
1479
|
+
- prebuilt/verdicts-3230a251570aa03666708d16f92e646feab6a3f8cc2a3540c017d748e41d41a1.json
|
|
1442
1480
|
- qa/settings.yml
|
|
1443
1481
|
- rust/.cargo/config.toml
|
|
1444
1482
|
- rust/.cargo/mutants.toml
|
|
@@ -1505,13 +1543,23 @@ files:
|
|
|
1505
1543
|
- rust/host/src/bin/expr_harness.rs
|
|
1506
1544
|
- rust/host/src/bin/lineage_harness.rs
|
|
1507
1545
|
- rust/host/src/bin/mint_harness.rs
|
|
1546
|
+
- rust/host/src/boundary_fuzz/api.rs
|
|
1547
|
+
- rust/host/src/boundary_fuzz/auth.rs
|
|
1548
|
+
- rust/host/src/boundary_fuzz/expr_json.rs
|
|
1549
|
+
- rust/host/src/boundary_fuzz/ir.rs
|
|
1550
|
+
- rust/host/src/boundary_fuzz/server.rs
|
|
1551
|
+
- rust/host/src/boundary_fuzz/web.rs
|
|
1508
1552
|
- rust/host/src/checkout.rs
|
|
1553
|
+
- rust/host/src/commerce_ir.rs
|
|
1509
1554
|
- rust/host/src/dispatch.rs
|
|
1510
1555
|
- rust/host/src/expr_json.rs
|
|
1556
|
+
- rust/host/src/extension.rs
|
|
1511
1557
|
- rust/host/src/field_hints.rs
|
|
1558
|
+
- rust/host/src/fuzz_support.rs
|
|
1512
1559
|
- rust/host/src/ir.rs
|
|
1513
1560
|
- rust/host/src/journal.rs
|
|
1514
1561
|
- rust/host/src/lambda_client.rs
|
|
1562
|
+
- rust/host/src/lib.rs
|
|
1515
1563
|
- rust/host/src/log.rs
|
|
1516
1564
|
- rust/host/src/main.rs
|
|
1517
1565
|
- rust/host/src/mint.rs
|
|
@@ -1533,6 +1581,7 @@ files:
|
|
|
1533
1581
|
- rust/host/src/ui_schema.rs
|
|
1534
1582
|
- rust/host/src/wasm_runner.rs
|
|
1535
1583
|
- rust/host/src/web.rs
|
|
1584
|
+
- rust/host/src/web/commerce.rs
|
|
1536
1585
|
- rust/host/src/web/newsletter.rs
|
|
1537
1586
|
- rust/host/src/web/newsletter/cooldown.rs
|
|
1538
1587
|
- rust/host/src/web/newsletter/template.rs
|
|
@@ -1541,6 +1590,7 @@ files:
|
|
|
1541
1590
|
- rust/host/src/web/registration_receipt.rs
|
|
1542
1591
|
- rust/host/src/web/registration_receipt/tests.rs
|
|
1543
1592
|
- rust/host/src/web/registrations.rs
|
|
1593
|
+
- rust/host/src/web/resend_webhook.rs
|
|
1544
1594
|
- rust/lsp/.gitignore
|
|
1545
1595
|
- rust/lsp/Cargo.lock
|
|
1546
1596
|
- rust/lsp/Cargo.toml
|
|
@@ -1554,6 +1604,12 @@ files:
|
|
|
1554
1604
|
- rust/parser/.gitignore
|
|
1555
1605
|
- rust/parser/Cargo.lock
|
|
1556
1606
|
- rust/parser/Cargo.toml
|
|
1607
|
+
- rust/parser/fuzz/.gitignore
|
|
1608
|
+
- rust/parser/fuzz/Cargo.lock
|
|
1609
|
+
- rust/parser/fuzz/Cargo.toml
|
|
1610
|
+
- rust/parser/fuzz/README.md
|
|
1611
|
+
- rust/parser/fuzz/fuzz_targets/chapter.rs
|
|
1612
|
+
- rust/parser/fuzz/fuzz_targets/resolve.rs
|
|
1557
1613
|
- rust/parser/src/build/closed_sets.rs
|
|
1558
1614
|
- rust/parser/src/build/identity.rs
|
|
1559
1615
|
- rust/parser/src/build/mod.rs
|