diff --git a/.jules/bolt.md b/.jules/bolt.md index 8ce7ce8f7..45c89ada9 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -77,3 +77,7 @@ ## 2024-11-20 - Optimize multiple tuple generation from a single collection **Learning:** `build_rule_metadata` derives exactly two collections, `owasp` and `cwe`, from the same references. Replacing its two generator traversals with one explicit loop reduces element visits from about 2N to N. Both versions remain O(N), so this is a constant-factor optimization rather than an asymptotic complexity improvement. **Action:** Combine repeated traversal when fixed derived collections share one source, while preserving ordering and classification semantics. Benchmark the production hot path before claiming a material wall-clock improvement. + +## 2025-03-01 - O(N*M) List Indexing in SARIF Generation +**Learning:** Calling `list(dict).index(key)` inside a loop over findings to determine the rule index creates an O(N*M) performance bottleneck, allocating a new list and scanning it linearly for every item. +**Action:** Use a companion dictionary `rule_indices: dict[str, int] = {}` to map rule IDs to their integer index (e.g., `len(rules)`) when first encountered. This reduces the lookup time complexity to O(1). diff --git a/appguardrail_core/sarif.py b/appguardrail_core/sarif.py index ca4a9fb6b..654592fab 100644 --- a/appguardrail_core/sarif.py +++ b/appguardrail_core/sarif.py @@ -39,6 +39,7 @@ def findings_to_sarif( normalized = normalize_findings(findings) rules: dict[str, dict[str, Any]] = {} + rule_indices: dict[str, int] = {} results: list[dict[str, Any]] = [] for f in normalized: rule_id = f["rule_id"] @@ -63,12 +64,13 @@ def findings_to_sarif( "security-severity": _SECURITY_SEVERITY.get(severity, "2.0"), }, } + rule_indices[rule_id] = len(rules) rules[rule_id] = rule results.append( { "ruleId": rule_id, - "ruleIndex": list(rules).index(rule_id), + "ruleIndex": rule_indices[rule_id], "level": _LEVEL.get(severity, "note"), "message": {"text": f["message"].strip()}, "locations": [