FuzzJSONStructureAware is the structure-aware fuzzer. It combines: - Grammar-based JSON generation (always emits valid structures). - JSON-aware mutation operators (break structures at known-dangerous boundaries: value boundaries, mid-key, mid-structure, mid-escape). - Adversarial key-path generatio
(f *testing.F)
| 1507 | // GetBoolean, GetUnsafeString, ArrayEach, ObjectEach, EachKey, |
| 1508 | // ParseInt, ParseFloat |
| 1509 | func FuzzJSONStructureAware(f *testing.F) { |
| 1510 | // Seed with known-dangerous corpora (the actual bugs we've found). |
| 1511 | for _, s := range structureAwareSeeds { |
| 1512 | f.Add(s.data, s.path) |
| 1513 | } |
| 1514 | |
| 1515 | f.Fuzz(func(t *testing.T, data []byte, pathBytes []byte) { |
| 1516 | // Derive deterministic RNG from the fuzz inputs so generation and |
| 1517 | // mutation decisions are a pure function of the engine-supplied bytes. |
| 1518 | r := newRandFromSeed(data, pathBytes) |
| 1519 | |
| 1520 | // --- Structure-aware generation layer --- |
| 1521 | // When the fuzz engine mutates data into non-JSON garbage (or ~30% |
| 1522 | // of the time regardless), regenerate valid JSON from the grammar. |
| 1523 | // This maintains structural coverage that blind byte mutation cannot |
| 1524 | // reach in reasonable time. |
| 1525 | work := data |
| 1526 | if !looksLikeJSON(data) || r.Intn(10) < 3 { |
| 1527 | depth := r.Intn(4) // mostly depth 0–3 |
| 1528 | if r.Intn(10) == 0 { |
| 1529 | depth = 5 + r.Intn(3) // occasionally deep (stress recursion) |
| 1530 | } |
| 1531 | work = genJSON(r, depth) |
| 1532 | } |
| 1533 | |
| 1534 | // --- Deep-nesting injection (closes Dimension 5 DoS blind spot) --- |
| 1535 | // The grammar generator's maxGenDepth=5 cannot produce pathological |
| 1536 | // nesting. Occasionally replace work with deeply-nested arrays so the |
| 1537 | // parser's own stack-safety (not just encoding/json's) gets fuzzed. |
| 1538 | // Both closed ([[[...1...]]]) and unclosed ([[[...) shapes are tested; |
| 1539 | // the unclosed shape forces full descent before error recovery. |
| 1540 | // |
| 1541 | // Capped at 1000 levels in the fuzz body: deeper inputs make the |
| 1542 | // existing Get-family gates (6+ calls, each O(n)) dominate the |
| 1543 | // worker's per-exec budget. The non-fuzz TestDoSPerformance gate |
| 1544 | // covers 100k+ level nesting separately. |
| 1545 | if r.Intn(40) == 0 { |
| 1546 | depth := 50 * (1 << r.Intn(5)) // 50..800 |
| 1547 | work = genDeepNesting(depth, r.Intn(2) == 0) |
| 1548 | } |
| 1549 | |
| 1550 | // --- JSON-aware mutation layer --- |
| 1551 | // Apply 0–3 mutations at structural boundaries. Each operator |
| 1552 | // targets a specific bug class (truncation, duplication, corruption). |
| 1553 | for n := r.Intn(4); n > 0; n-- { |
| 1554 | work = applyMutation(r, work) |
| 1555 | } |
| 1556 | |
| 1557 | // --- Adversarial key-path layer --- |
| 1558 | // Derive the path from pathBytes (split on '/'); occasionally inject |
| 1559 | // a hazard component (empty key, OOB index) or regenerate from grammar. |
| 1560 | path := splitPathComponents(pathBytes) |
| 1561 | if r.Intn(5) == 0 { // 20% chance |
| 1562 | path = injectPathHazard(r, path) |
| 1563 | } |
| 1564 | |
| 1565 | // --- Gates --- |
| 1566 | runStructureAwareGates(t, work, path) |
nothing calls this directly
no test coverage detected