Lars Talian commited on
Commit
e8df2e1
·
1 Parent(s): 4c52b20

Harden TemplateOnlyBuilder difficulty bounds

Browse files

- Clamp malformed min/max vulns to safe bounds

- Preserve at least one selected vulnerability

- Add regression coverage for non-schema difficulty envelopes

src/open_range/builder/builder.py CHANGED
@@ -963,9 +963,14 @@ class TemplateOnlyBuilder:
963
  avg_extra = 4
964
  tier_max_vulns = max(1, 1 + (max_steps_hi - avg_first) // avg_extra)
965
 
966
- max_vulns = manifest.get("difficulty", {}).get("max_vulns", 2)
967
- min_vulns = manifest.get("difficulty", {}).get("min_vulns", 1)
968
- effective_max = min(max_vulns, tier_max_vulns, len(candidates))
 
 
 
 
 
969
  effective_min = min(min_vulns, effective_max)
970
  count = rng.randint(effective_min, effective_max)
971
  chosen = rng.sample(candidates, count)
 
963
  avg_extra = 4
964
  tier_max_vulns = max(1, 1 + (max_steps_hi - avg_first) // avg_extra)
965
 
966
+ max_v_raw = manifest.get("difficulty", {}).get("max_vulns", 2)
967
+ min_v_raw = manifest.get("difficulty", {}).get("min_vulns", 1)
968
+ max_vulns = max(1, int(max_v_raw))
969
+ min_vulns = max(1, int(min_v_raw))
970
+ if min_vulns > max_vulns:
971
+ min_vulns = max_vulns
972
+
973
+ effective_max = max(1, min(max_vulns, tier_max_vulns, len(candidates)))
974
  effective_min = min(min_vulns, effective_max)
975
  count = rng.randint(effective_min, effective_max)
976
  chosen = rng.sample(candidates, count)
tests/test_builder.py CHANGED
@@ -82,6 +82,22 @@ async def test_template_builder_clamps_min_vulns_to_candidate_pool(tier1_manifes
82
  assert all(v.type == "sqli" for v in spec.truth_graph.vulns)
83
 
84
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  @pytest.mark.asyncio
86
  async def test_template_builder_avoids_previous_vulns(tier1_manifest):
87
  from open_range.builder.builder import TemplateOnlyBuilder
 
82
  assert all(v.type == "sqli" for v in spec.truth_graph.vulns)
83
 
84
 
85
+ @pytest.mark.asyncio
86
+ async def test_template_builder_handles_non_schema_difficulty_bounds(tier1_manifest):
87
+ from open_range.builder.builder import TemplateOnlyBuilder
88
+
89
+ builder = TemplateOnlyBuilder()
90
+ manifest = {
91
+ **tier1_manifest,
92
+ "bug_families": ["sqli"],
93
+ "difficulty": {**tier1_manifest.get("difficulty", {}), "min_vulns": -2, "max_vulns": 0},
94
+ }
95
+
96
+ spec = await builder.build(manifest, BuildContext(seed=9, tier=1))
97
+ assert len(spec.truth_graph.vulns) == 1
98
+ assert spec.truth_graph.vulns[0].type == "sqli"
99
+
100
+
101
  @pytest.mark.asyncio
102
  async def test_template_builder_avoids_previous_vulns(tier1_manifest):
103
  from open_range.builder.builder import TemplateOnlyBuilder