[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-trail-of-bits-sarif-parsing":3,"mdc-g8z4sc-key":35,"related-repo-trail-of-bits-sarif-parsing":3444,"related-org-trail-of-bits-sarif-parsing":3537},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":12,"stars":23,"repoUrl":24,"updatedAt":25,"license":26,"forks":27,"topics":28,"repo":30,"sourceUrl":33,"mdContent":34},"sarif-parsing","parse and process SARIF scan results","Parses and processes SARIF files from static analysis tools like CodeQL, Semgrep, or other scanners. Triggers on \"parse sarif\", \"read scan results\", \"aggregate findings\", \"deduplicate alerts\", or \"process sarif output\". Handles filtering, deduplication, format conversion, and CI\u002FCD integration of SARIF data. Does NOT run scans — use the Semgrep or CodeQL skills for that.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},"trail-of-bits","Trail of Bits","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Ftrail-of-bits.png","trailofbits",[13,17,20],{"name":14,"slug":15,"type":16},"Security","security","tag",{"name":18,"slug":19,"type":16},"Audit","audit",{"name":21,"slug":22,"type":16},"Code Analysis","code-analysis",6139,"https:\u002F\u002Fgithub.com\u002Ftrailofbits\u002Fskills","2026-07-17T06:05:30.353625",null,541,[29],"agent-skills",{"repoUrl":24,"stars":23,"forks":27,"topics":31,"description":32},[29],"Trail of Bits Claude Code skills for security research, vulnerability detection, and audit workflows","https:\u002F\u002Fgithub.com\u002Ftrailofbits\u002Fskills\u002Ftree\u002FHEAD\u002Fplugins\u002Fstatic-analysis\u002Fskills\u002Fsarif-parsing","---\nname: sarif-parsing\ndescription: >-\n  Parses and processes SARIF files from static analysis tools like CodeQL, Semgrep, or other\n  scanners. Triggers on \"parse sarif\", \"read scan results\", \"aggregate findings\", \"deduplicate\n  alerts\", or \"process sarif output\". Handles filtering, deduplication, format conversion, and\n  CI\u002FCD integration of SARIF data. Does NOT run scans — use the Semgrep or CodeQL skills for that.\nallowed-tools: Bash Read Glob Grep\n---\n\n# SARIF Parsing Best Practices\n\nYou are a SARIF parsing expert. Your role is to help users effectively read, analyze, and process SARIF files from static analysis tools.\n\n## When to Use\n\nUse this skill when:\n- Reading or interpreting static analysis scan results in SARIF format\n- Aggregating findings from multiple security tools\n- Deduplicating or filtering security alerts\n- Extracting specific vulnerabilities from SARIF files\n- Integrating SARIF data into CI\u002FCD pipelines\n- Converting SARIF output to other formats\n\n## When NOT to Use\n\nDo NOT use this skill for:\n- Running static analysis scans (use CodeQL or Semgrep skills instead)\n- Writing CodeQL or Semgrep rules (use their respective skills)\n- Analyzing source code directly (SARIF is for processing existing scan results)\n- Triaging findings without SARIF input (use variant-analysis or audit skills)\n\n## SARIF Structure Overview\n\nSARIF 2.1.0 is the current OASIS standard. Every SARIF file has this hierarchical structure:\n\n```\nsarifLog\n├── version: \"2.1.0\"\n├── $schema: (optional, enables IDE validation)\n└── runs[] (array of analysis runs)\n    ├── tool\n    │   ├── driver\n    │   │   ├── name (required)\n    │   │   ├── version\n    │   │   └── rules[] (rule definitions)\n    │   └── extensions[] (plugins)\n    ├── results[] (findings)\n    │   ├── ruleId\n    │   ├── level (error\u002Fwarning\u002Fnote)\n    │   ├── message.text\n    │   ├── locations[]\n    │   │   └── physicalLocation\n    │   │       ├── artifactLocation.uri\n    │   │       └── region (startLine, startColumn, etc.)\n    │   ├── fingerprints{}\n    │   └── partialFingerprints{}\n    └── artifacts[] (scanned files metadata)\n```\n\n### Why Fingerprinting Matters\n\nWithout stable fingerprints, you can't track findings across runs:\n\n- **Baseline comparison**: \"Is this a new finding or did we see it before?\"\n- **Regression detection**: \"Did this PR introduce new vulnerabilities?\"\n- **Suppression**: \"Ignore this known false positive in future runs\"\n\nTools report different paths (`\u002Fpath\u002Fto\u002Fproject\u002F` vs `\u002Fgithub\u002Fworkspace\u002F`), so path-based matching fails. Fingerprints hash the *content* (code snippet, rule ID, relative location) to create stable identifiers regardless of environment.\n\n## Tool Selection Guide\n\n| Use Case | Tool | Installation |\n|----------|------|--------------|\n| Quick CLI queries | jq | `brew install jq` \u002F `apt install jq` |\n| Python scripting (simple) | pysarif | `pip install pysarif` |\n| Python scripting (advanced) | sarif-tools | `pip install sarif-tools` |\n| .NET applications | SARIF SDK | NuGet package |\n| JavaScript\u002FNode.js | sarif-js | npm package |\n| Go applications | garif | `go get github.com\u002Fchavacava\u002Fgarif` |\n| Validation | SARIF Validator | sarifweb.azurewebsites.net |\n\n## Strategy 1: Quick Analysis with jq\n\nFor rapid exploration and one-off queries:\n\n```bash\n# Pretty print the file\njq '.' results.sarif\n\n# Count total findings\njq '[.runs[].results[]] | length' results.sarif\n\n# List all rule IDs triggered\njq '[.runs[].results[].ruleId] | unique' results.sarif\n\n# Extract errors only\njq '.runs[].results[] | select(.level == \"error\")' results.sarif\n\n# Get findings with file locations\njq '.runs[].results[] | {\n  rule: .ruleId,\n  message: .message.text,\n  file: .locations[0].physicalLocation.artifactLocation.uri,\n  line: .locations[0].physicalLocation.region.startLine\n}' results.sarif\n\n# Filter by severity and get count per rule\njq '[.runs[].results[] | select(.level == \"error\")] | group_by(.ruleId) | map({rule: .[0].ruleId, count: length})' results.sarif\n\n# Extract findings for a specific file\njq --arg file \"src\u002Fauth.py\" '.runs[].results[] | select(.locations[].physicalLocation.artifactLocation.uri | contains($file))' results.sarif\n```\n\n## Strategy 2: Python with pysarif\n\nFor programmatic access with full object model:\n\n```python\nfrom pysarif import load_from_file, save_to_file\n\n# Load SARIF file\nsarif = load_from_file(\"results.sarif\")\n\n# Iterate through runs and results\nfor run in sarif.runs:\n    tool_name = run.tool.driver.name\n    print(f\"Tool: {tool_name}\")\n\n    for result in run.results:\n        print(f\"  [{result.level}] {result.rule_id}: {result.message.text}\")\n\n        if result.locations:\n            loc = result.locations[0].physical_location\n            if loc and loc.artifact_location:\n                print(f\"    File: {loc.artifact_location.uri}\")\n                if loc.region:\n                    print(f\"    Line: {loc.region.start_line}\")\n\n# Save modified SARIF\nsave_to_file(sarif, \"modified.sarif\")\n```\n\n## Strategy 3: Python with sarif-tools\n\nFor aggregation, reporting, and CI\u002FCD integration:\n\n```python\nfrom sarif import loader\n\n# Load single file\nsarif_data = loader.load_sarif_file(\"results.sarif\")\n\n# Or load multiple files\nsarif_set = loader.load_sarif_files([\"tool1.sarif\", \"tool2.sarif\"])\n\n# Get summary report\nreport = sarif_data.get_report()\n\n# Get histogram by severity\nerrors = report.get_issue_type_histogram_for_severity(\"error\")\nwarnings = report.get_issue_type_histogram_for_severity(\"warning\")\n\n# Filter results\nhigh_severity = [r for r in sarif_data.get_results()\n                 if r.get(\"level\") == \"error\"]\n```\n\n**sarif-tools CLI commands:**\n\n```bash\n# Summary of findings\nsarif summary results.sarif\n\n# List all results with details\nsarif ls results.sarif\n\n# Get results by severity\nsarif ls --level error results.sarif\n\n# Diff two SARIF files (find new\u002Ffixed issues)\nsarif diff baseline.sarif current.sarif\n\n# Convert to other formats\nsarif csv results.sarif > results.csv\nsarif html results.sarif > report.html\n```\n\n## Strategy 4: Aggregating Multiple SARIF Files\n\nWhen combining results from multiple tools:\n\n```python\nimport json\nfrom pathlib import Path\n\ndef aggregate_sarif_files(sarif_paths: list[str]) -> dict:\n    \"\"\"Combine multiple SARIF files into one.\"\"\"\n    aggregated = {\n        \"version\": \"2.1.0\",\n        \"$schema\": \"https:\u002F\u002Fjson.schemastore.org\u002Fsarif-2.1.0.json\",\n        \"runs\": []\n    }\n\n    for path in sarif_paths:\n        with open(path) as f:\n            sarif = json.load(f)\n            aggregated[\"runs\"].extend(sarif.get(\"runs\", []))\n\n    return aggregated\n\ndef deduplicate_results(sarif: dict) -> dict:\n    \"\"\"Remove duplicate findings based on fingerprints.\"\"\"\n    seen_fingerprints = set()\n\n    for run in sarif[\"runs\"]:\n        unique_results = []\n        for result in run.get(\"results\", []):\n            # Use partialFingerprints or create key from location\n            fp = None\n            if result.get(\"partialFingerprints\"):\n                fp = tuple(sorted(result[\"partialFingerprints\"].items()))\n            elif result.get(\"fingerprints\"):\n                fp = tuple(sorted(result[\"fingerprints\"].items()))\n            else:\n                # Fallback: create fingerprint from rule + location\n                loc = result.get(\"locations\", [{}])[0]\n                phys = loc.get(\"physicalLocation\", {})\n                fp = (\n                    result.get(\"ruleId\"),\n                    phys.get(\"artifactLocation\", {}).get(\"uri\"),\n                    phys.get(\"region\", {}).get(\"startLine\")\n                )\n\n            if fp not in seen_fingerprints:\n                seen_fingerprints.add(fp)\n                unique_results.append(result)\n\n        run[\"results\"] = unique_results\n\n    return sarif\n```\n\n## Strategy 5: Extracting Actionable Data\n\n```python\nimport json\nfrom dataclasses import dataclass\nfrom typing import Optional\n\n@dataclass\nclass Finding:\n    rule_id: str\n    level: str\n    message: str\n    file_path: Optional[str]\n    start_line: Optional[int]\n    end_line: Optional[int]\n    fingerprint: Optional[str]\n\ndef extract_findings(sarif_path: str) -> list[Finding]:\n    \"\"\"Extract structured findings from SARIF file.\"\"\"\n    with open(sarif_path) as f:\n        sarif = json.load(f)\n\n    findings = []\n    for run in sarif.get(\"runs\", []):\n        for result in run.get(\"results\", []):\n            loc = result.get(\"locations\", [{}])[0]\n            phys = loc.get(\"physicalLocation\", {})\n            region = phys.get(\"region\", {})\n\n            findings.append(Finding(\n                rule_id=result.get(\"ruleId\", \"unknown\"),\n                level=result.get(\"level\", \"warning\"),\n                message=result.get(\"message\", {}).get(\"text\", \"\"),\n                file_path=phys.get(\"artifactLocation\", {}).get(\"uri\"),\n                start_line=region.get(\"startLine\"),\n                end_line=region.get(\"endLine\"),\n                fingerprint=next(iter(result.get(\"partialFingerprints\", {}).values()), None)\n            ))\n\n    return findings\n\n# Filter and prioritize\ndef prioritize_findings(findings: list[Finding]) -> list[Finding]:\n    \"\"\"Sort findings by severity.\"\"\"\n    severity_order = {\"error\": 0, \"warning\": 1, \"note\": 2, \"none\": 3}\n    return sorted(findings, key=lambda f: severity_order.get(f.level, 99))\n```\n\n## Common Pitfalls and Solutions\n\n### 1. Path Normalization Issues\n\nDifferent tools report paths differently (absolute, relative, URI-encoded):\n\n```python\nfrom urllib.parse import unquote\nfrom pathlib import Path\n\ndef normalize_path(uri: str, base_path: str = \"\") -> str:\n    \"\"\"Normalize SARIF artifact URI to consistent path.\"\"\"\n    # Remove file:\u002F\u002F prefix if present\n    if uri.startswith(\"file:\u002F\u002F\"):\n        uri = uri[7:]\n\n    # URL decode\n    uri = unquote(uri)\n\n    # Handle relative paths\n    if not Path(uri).is_absolute() and base_path:\n        uri = str(Path(base_path) \u002F uri)\n\n    # Normalize separators\n    return str(Path(uri))\n```\n\n### 2. Fingerprint Mismatch Across Runs\n\nFingerprints may not match if:\n- File paths differ between environments\n- Tool versions changed fingerprinting algorithm\n- Code was reformatted (changing line numbers)\n\n**Solution:** Use multiple fingerprint strategies:\n\n```python\ndef compute_stable_fingerprint(result: dict, file_content: str = None) -> str:\n    \"\"\"Compute environment-independent fingerprint.\"\"\"\n    import hashlib\n\n    components = [\n        result.get(\"ruleId\", \"\"),\n        result.get(\"message\", {}).get(\"text\", \"\")[:100],  # First 100 chars\n    ]\n\n    # Add code snippet if available\n    if file_content and result.get(\"locations\"):\n        region = result[\"locations\"][0].get(\"physicalLocation\", {}).get(\"region\", {})\n        if region.get(\"startLine\"):\n            lines = file_content.split(\"\\n\")\n            line_idx = region[\"startLine\"] - 1\n            if 0 \u003C= line_idx \u003C len(lines):\n                # Normalize whitespace\n                components.append(lines[line_idx].strip())\n\n    return hashlib.sha256(\"\".join(components).encode()).hexdigest()[:16]\n```\n\n### 3. Missing or Incomplete Data\n\nSARIF allows many optional fields. Always use defensive access:\n\n```python\ndef safe_get_location(result: dict) -> tuple[str, int]:\n    \"\"\"Safely extract file and line from result.\"\"\"\n    try:\n        loc = result.get(\"locations\", [{}])[0]\n        phys = loc.get(\"physicalLocation\", {})\n        file_path = phys.get(\"artifactLocation\", {}).get(\"uri\", \"unknown\")\n        line = phys.get(\"region\", {}).get(\"startLine\", 0)\n        return file_path, line\n    except (IndexError, KeyError, TypeError):\n        return \"unknown\", 0\n```\n\n### 4. Large File Performance\n\nFor very large SARIF files (100MB+):\n\n```python\nimport ijson  # pip install ijson\n\ndef stream_results(sarif_path: str):\n    \"\"\"Stream results without loading entire file.\"\"\"\n    with open(sarif_path, \"rb\") as f:\n        # Stream through results arrays\n        for result in ijson.items(f, \"runs.item.results.item\"):\n            yield result\n```\n\n### 5. Schema Validation\n\nValidate before processing to catch malformed files:\n\n```bash\n# Using ajv-cli\nnpm install -g ajv-cli\najv validate -s sarif-schema-2.1.0.json -d results.sarif\n\n# Using Python jsonschema\npip install jsonschema\n```\n\n```python\nfrom jsonschema import validate, ValidationError\nimport json\n\ndef validate_sarif(sarif_path: str, schema_path: str) -> bool:\n    \"\"\"Validate SARIF file against schema.\"\"\"\n    with open(sarif_path) as f:\n        sarif = json.load(f)\n    with open(schema_path) as f:\n        schema = json.load(f)\n\n    try:\n        validate(sarif, schema)\n        return True\n    except ValidationError as e:\n        print(f\"Validation error: {e.message}\")\n        return False\n```\n\n## CI\u002FCD Integration Patterns\n\n### GitHub Actions\n\n```yaml\n- name: Upload SARIF\n  uses: github\u002Fcodeql-action\u002Fupload-sarif@v3\n  with:\n    sarif_file: results.sarif\n\n- name: Check for high severity\n  run: |\n    HIGH_COUNT=$(jq '[.runs[].results[] | select(.level == \"error\")] | length' results.sarif)\n    if [ \"$HIGH_COUNT\" -gt 0 ]; then\n      echo \"Found $HIGH_COUNT high severity issues\"\n      exit 1\n    fi\n```\n\n### Fail on New Issues\n\n```python\nfrom sarif import loader\n\ndef check_for_regressions(baseline: str, current: str) -> int:\n    \"\"\"Return count of new issues not in baseline.\"\"\"\n    baseline_data = loader.load_sarif_file(baseline)\n    current_data = loader.load_sarif_file(current)\n\n    baseline_fps = {get_fingerprint(r) for r in baseline_data.get_results()}\n    new_issues = [r for r in current_data.get_results()\n                  if get_fingerprint(r) not in baseline_fps]\n\n    return len(new_issues)\n```\n\n## Key Principles\n\n1. **Validate first**: Check SARIF structure before processing\n2. **Handle optionals**: Many fields are optional; use defensive access\n3. **Normalize paths**: Tools report paths differently; normalize early\n4. **Fingerprint wisely**: Combine multiple strategies for stable deduplication\n5. **Stream large files**: Use ijson or similar for 100MB+ files\n6. **Aggregate thoughtfully**: Preserve tool metadata when combining files\n\n## Skill Resources\n\nFor ready-to-use query templates, see [{baseDir}\u002Fresources\u002Fjq-queries.md]({baseDir}\u002Fresources\u002Fjq-queries.md):\n- 40+ jq queries for common SARIF operations\n- Severity filtering, rule extraction, aggregation patterns\n\nFor Python utilities, see [{baseDir}\u002Fresources\u002Fsarif_helpers.py]({baseDir}\u002Fresources\u002Fsarif_helpers.py):\n- `normalize_path()` - Handle tool-specific path formats\n- `compute_fingerprint()` - Stable fingerprinting ignoring paths\n- `deduplicate_results()` - Remove duplicates across runs\n\n## Reference Links\n\n- [OASIS SARIF 2.1.0 Specification](https:\u002F\u002Fdocs.oasis-open.org\u002Fsarif\u002Fsarif\u002Fv2.1.0\u002Fsarif-v2.1.0.html)\n- [Microsoft SARIF Tutorials](https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fsarif-tutorials)\n- [SARIF SDK (.NET)](https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fsarif-sdk)\n- [sarif-tools (Python)](https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fsarif-tools)\n- [pysarif (Python)](https:\u002F\u002Fgithub.com\u002FKjeld-P\u002Fpysarif)\n- [GitHub SARIF Support](https:\u002F\u002Fdocs.github.com\u002Fen\u002Fcode-security\u002Fcode-scanning\u002Fintegrating-with-code-scanning\u002Fsarif-support-for-code-scanning)\n- [SARIF Validator](https:\u002F\u002Fsarifweb.azurewebsites.net\u002F)\n",{"data":36,"body":38},{"name":4,"description":6,"allowed-tools":37},"Bash Read Glob Grep",{"type":39,"children":40},"root",[41,50,56,63,68,103,109,114,137,143,148,161,168,173,207,236,242,425,431,436,812,818,823,1003,1009,1014,1160,1168,1375,1381,1386,1792,1798,2141,2147,2153,2158,2304,2310,2315,2333,2343,2507,2513,2518,2605,2611,2616,2686,2692,2697,2799,2928,2934,2940,3104,3110,3209,3215,3279,3285,3298,3311,3323,3359,3365,3438],{"type":42,"tag":43,"props":44,"children":46},"element","h1",{"id":45},"sarif-parsing-best-practices",[47],{"type":48,"value":49},"text","SARIF Parsing Best Practices",{"type":42,"tag":51,"props":52,"children":53},"p",{},[54],{"type":48,"value":55},"You are a SARIF parsing expert. Your role is to help users effectively read, analyze, and process SARIF files from static analysis tools.",{"type":42,"tag":57,"props":58,"children":60},"h2",{"id":59},"when-to-use",[61],{"type":48,"value":62},"When to Use",{"type":42,"tag":51,"props":64,"children":65},{},[66],{"type":48,"value":67},"Use this skill when:",{"type":42,"tag":69,"props":70,"children":71},"ul",{},[72,78,83,88,93,98],{"type":42,"tag":73,"props":74,"children":75},"li",{},[76],{"type":48,"value":77},"Reading or interpreting static analysis scan results in SARIF format",{"type":42,"tag":73,"props":79,"children":80},{},[81],{"type":48,"value":82},"Aggregating findings from multiple security tools",{"type":42,"tag":73,"props":84,"children":85},{},[86],{"type":48,"value":87},"Deduplicating or filtering security alerts",{"type":42,"tag":73,"props":89,"children":90},{},[91],{"type":48,"value":92},"Extracting specific vulnerabilities from SARIF files",{"type":42,"tag":73,"props":94,"children":95},{},[96],{"type":48,"value":97},"Integrating SARIF data into CI\u002FCD pipelines",{"type":42,"tag":73,"props":99,"children":100},{},[101],{"type":48,"value":102},"Converting SARIF output to other formats",{"type":42,"tag":57,"props":104,"children":106},{"id":105},"when-not-to-use",[107],{"type":48,"value":108},"When NOT to Use",{"type":42,"tag":51,"props":110,"children":111},{},[112],{"type":48,"value":113},"Do NOT use this skill for:",{"type":42,"tag":69,"props":115,"children":116},{},[117,122,127,132],{"type":42,"tag":73,"props":118,"children":119},{},[120],{"type":48,"value":121},"Running static analysis scans (use CodeQL or Semgrep skills instead)",{"type":42,"tag":73,"props":123,"children":124},{},[125],{"type":48,"value":126},"Writing CodeQL or Semgrep rules (use their respective skills)",{"type":42,"tag":73,"props":128,"children":129},{},[130],{"type":48,"value":131},"Analyzing source code directly (SARIF is for processing existing scan results)",{"type":42,"tag":73,"props":133,"children":134},{},[135],{"type":48,"value":136},"Triaging findings without SARIF input (use variant-analysis or audit skills)",{"type":42,"tag":57,"props":138,"children":140},{"id":139},"sarif-structure-overview",[141],{"type":48,"value":142},"SARIF Structure Overview",{"type":42,"tag":51,"props":144,"children":145},{},[146],{"type":48,"value":147},"SARIF 2.1.0 is the current OASIS standard. Every SARIF file has this hierarchical structure:",{"type":42,"tag":149,"props":150,"children":154},"pre",{"className":151,"code":153,"language":48},[152],"language-text","sarifLog\n├── version: \"2.1.0\"\n├── $schema: (optional, enables IDE validation)\n└── runs[] (array of analysis runs)\n    ├── tool\n    │   ├── driver\n    │   │   ├── name (required)\n    │   │   ├── version\n    │   │   └── rules[] (rule definitions)\n    │   └── extensions[] (plugins)\n    ├── results[] (findings)\n    │   ├── ruleId\n    │   ├── level (error\u002Fwarning\u002Fnote)\n    │   ├── message.text\n    │   ├── locations[]\n    │   │   └── physicalLocation\n    │   │       ├── artifactLocation.uri\n    │   │       └── region (startLine, startColumn, etc.)\n    │   ├── fingerprints{}\n    │   └── partialFingerprints{}\n    └── artifacts[] (scanned files metadata)\n",[155],{"type":42,"tag":156,"props":157,"children":159},"code",{"__ignoreMap":158},"",[160],{"type":48,"value":153},{"type":42,"tag":162,"props":163,"children":165},"h3",{"id":164},"why-fingerprinting-matters",[166],{"type":48,"value":167},"Why Fingerprinting Matters",{"type":42,"tag":51,"props":169,"children":170},{},[171],{"type":48,"value":172},"Without stable fingerprints, you can't track findings across runs:",{"type":42,"tag":69,"props":174,"children":175},{},[176,187,197],{"type":42,"tag":73,"props":177,"children":178},{},[179,185],{"type":42,"tag":180,"props":181,"children":182},"strong",{},[183],{"type":48,"value":184},"Baseline comparison",{"type":48,"value":186},": \"Is this a new finding or did we see it before?\"",{"type":42,"tag":73,"props":188,"children":189},{},[190,195],{"type":42,"tag":180,"props":191,"children":192},{},[193],{"type":48,"value":194},"Regression detection",{"type":48,"value":196},": \"Did this PR introduce new vulnerabilities?\"",{"type":42,"tag":73,"props":198,"children":199},{},[200,205],{"type":42,"tag":180,"props":201,"children":202},{},[203],{"type":48,"value":204},"Suppression",{"type":48,"value":206},": \"Ignore this known false positive in future runs\"",{"type":42,"tag":51,"props":208,"children":209},{},[210,212,218,220,226,228,234],{"type":48,"value":211},"Tools report different paths (",{"type":42,"tag":156,"props":213,"children":215},{"className":214},[],[216],{"type":48,"value":217},"\u002Fpath\u002Fto\u002Fproject\u002F",{"type":48,"value":219}," vs ",{"type":42,"tag":156,"props":221,"children":223},{"className":222},[],[224],{"type":48,"value":225},"\u002Fgithub\u002Fworkspace\u002F",{"type":48,"value":227},"), so path-based matching fails. Fingerprints hash the ",{"type":42,"tag":229,"props":230,"children":231},"em",{},[232],{"type":48,"value":233},"content",{"type":48,"value":235}," (code snippet, rule ID, relative location) to create stable identifiers regardless of environment.",{"type":42,"tag":57,"props":237,"children":239},{"id":238},"tool-selection-guide",[240],{"type":48,"value":241},"Tool Selection Guide",{"type":42,"tag":243,"props":244,"children":245},"table",{},[246,270],{"type":42,"tag":247,"props":248,"children":249},"thead",{},[250],{"type":42,"tag":251,"props":252,"children":253},"tr",{},[254,260,265],{"type":42,"tag":255,"props":256,"children":257},"th",{},[258],{"type":48,"value":259},"Use Case",{"type":42,"tag":255,"props":261,"children":262},{},[263],{"type":48,"value":264},"Tool",{"type":42,"tag":255,"props":266,"children":267},{},[268],{"type":48,"value":269},"Installation",{"type":42,"tag":271,"props":272,"children":273},"tbody",{},[274,305,327,349,367,385,407],{"type":42,"tag":251,"props":275,"children":276},{},[277,283,288],{"type":42,"tag":278,"props":279,"children":280},"td",{},[281],{"type":48,"value":282},"Quick CLI queries",{"type":42,"tag":278,"props":284,"children":285},{},[286],{"type":48,"value":287},"jq",{"type":42,"tag":278,"props":289,"children":290},{},[291,297,299],{"type":42,"tag":156,"props":292,"children":294},{"className":293},[],[295],{"type":48,"value":296},"brew install jq",{"type":48,"value":298}," \u002F ",{"type":42,"tag":156,"props":300,"children":302},{"className":301},[],[303],{"type":48,"value":304},"apt install jq",{"type":42,"tag":251,"props":306,"children":307},{},[308,313,318],{"type":42,"tag":278,"props":309,"children":310},{},[311],{"type":48,"value":312},"Python scripting (simple)",{"type":42,"tag":278,"props":314,"children":315},{},[316],{"type":48,"value":317},"pysarif",{"type":42,"tag":278,"props":319,"children":320},{},[321],{"type":42,"tag":156,"props":322,"children":324},{"className":323},[],[325],{"type":48,"value":326},"pip install pysarif",{"type":42,"tag":251,"props":328,"children":329},{},[330,335,340],{"type":42,"tag":278,"props":331,"children":332},{},[333],{"type":48,"value":334},"Python scripting (advanced)",{"type":42,"tag":278,"props":336,"children":337},{},[338],{"type":48,"value":339},"sarif-tools",{"type":42,"tag":278,"props":341,"children":342},{},[343],{"type":42,"tag":156,"props":344,"children":346},{"className":345},[],[347],{"type":48,"value":348},"pip install sarif-tools",{"type":42,"tag":251,"props":350,"children":351},{},[352,357,362],{"type":42,"tag":278,"props":353,"children":354},{},[355],{"type":48,"value":356},".NET applications",{"type":42,"tag":278,"props":358,"children":359},{},[360],{"type":48,"value":361},"SARIF SDK",{"type":42,"tag":278,"props":363,"children":364},{},[365],{"type":48,"value":366},"NuGet package",{"type":42,"tag":251,"props":368,"children":369},{},[370,375,380],{"type":42,"tag":278,"props":371,"children":372},{},[373],{"type":48,"value":374},"JavaScript\u002FNode.js",{"type":42,"tag":278,"props":376,"children":377},{},[378],{"type":48,"value":379},"sarif-js",{"type":42,"tag":278,"props":381,"children":382},{},[383],{"type":48,"value":384},"npm package",{"type":42,"tag":251,"props":386,"children":387},{},[388,393,398],{"type":42,"tag":278,"props":389,"children":390},{},[391],{"type":48,"value":392},"Go applications",{"type":42,"tag":278,"props":394,"children":395},{},[396],{"type":48,"value":397},"garif",{"type":42,"tag":278,"props":399,"children":400},{},[401],{"type":42,"tag":156,"props":402,"children":404},{"className":403},[],[405],{"type":48,"value":406},"go get github.com\u002Fchavacava\u002Fgarif",{"type":42,"tag":251,"props":408,"children":409},{},[410,415,420],{"type":42,"tag":278,"props":411,"children":412},{},[413],{"type":48,"value":414},"Validation",{"type":42,"tag":278,"props":416,"children":417},{},[418],{"type":48,"value":419},"SARIF Validator",{"type":42,"tag":278,"props":421,"children":422},{},[423],{"type":48,"value":424},"sarifweb.azurewebsites.net",{"type":42,"tag":57,"props":426,"children":428},{"id":427},"strategy-1-quick-analysis-with-jq",[429],{"type":48,"value":430},"Strategy 1: Quick Analysis with jq",{"type":42,"tag":51,"props":432,"children":433},{},[434],{"type":48,"value":435},"For rapid exploration and one-off queries:",{"type":42,"tag":149,"props":437,"children":441},{"className":438,"code":439,"language":440,"meta":158,"style":158},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# Pretty print the file\njq '.' results.sarif\n\n# Count total findings\njq '[.runs[].results[]] | length' results.sarif\n\n# List all rule IDs triggered\njq '[.runs[].results[].ruleId] | unique' results.sarif\n\n# Extract errors only\njq '.runs[].results[] | select(.level == \"error\")' results.sarif\n\n# Get findings with file locations\njq '.runs[].results[] | {\n  rule: .ruleId,\n  message: .message.text,\n  file: .locations[0].physicalLocation.artifactLocation.uri,\n  line: .locations[0].physicalLocation.region.startLine\n}' results.sarif\n\n# Filter by severity and get count per rule\njq '[.runs[].results[] | select(.level == \"error\")] | group_by(.ruleId) | map({rule: .[0].ruleId, count: length})' results.sarif\n\n# Extract findings for a specific file\njq --arg file \"src\u002Fauth.py\" '.runs[].results[] | select(.locations[].physicalLocation.artifactLocation.uri | contains($file))' results.sarif\n","bash",[442],{"type":42,"tag":156,"props":443,"children":444},{"__ignoreMap":158},[445,457,488,498,507,532,540,549,574,582,591,616,624,633,650,659,668,677,686,703,711,720,745,753,762],{"type":42,"tag":446,"props":447,"children":450},"span",{"class":448,"line":449},"line",1,[451],{"type":42,"tag":446,"props":452,"children":454},{"style":453},"--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic",[455],{"type":48,"value":456},"# Pretty print the file\n",{"type":42,"tag":446,"props":458,"children":460},{"class":448,"line":459},2,[461,466,472,478,483],{"type":42,"tag":446,"props":462,"children":464},{"style":463},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B",[465],{"type":48,"value":287},{"type":42,"tag":446,"props":467,"children":469},{"style":468},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[470],{"type":48,"value":471}," '",{"type":42,"tag":446,"props":473,"children":475},{"style":474},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[476],{"type":48,"value":477},".",{"type":42,"tag":446,"props":479,"children":480},{"style":468},[481],{"type":48,"value":482},"'",{"type":42,"tag":446,"props":484,"children":485},{"style":474},[486],{"type":48,"value":487}," results.sarif\n",{"type":42,"tag":446,"props":489,"children":491},{"class":448,"line":490},3,[492],{"type":42,"tag":446,"props":493,"children":495},{"emptyLinePlaceholder":494},true,[496],{"type":48,"value":497},"\n",{"type":42,"tag":446,"props":499,"children":501},{"class":448,"line":500},4,[502],{"type":42,"tag":446,"props":503,"children":504},{"style":453},[505],{"type":48,"value":506},"# Count total findings\n",{"type":42,"tag":446,"props":508,"children":510},{"class":448,"line":509},5,[511,515,519,524,528],{"type":42,"tag":446,"props":512,"children":513},{"style":463},[514],{"type":48,"value":287},{"type":42,"tag":446,"props":516,"children":517},{"style":468},[518],{"type":48,"value":471},{"type":42,"tag":446,"props":520,"children":521},{"style":474},[522],{"type":48,"value":523},"[.runs[].results[]] | length",{"type":42,"tag":446,"props":525,"children":526},{"style":468},[527],{"type":48,"value":482},{"type":42,"tag":446,"props":529,"children":530},{"style":474},[531],{"type":48,"value":487},{"type":42,"tag":446,"props":533,"children":535},{"class":448,"line":534},6,[536],{"type":42,"tag":446,"props":537,"children":538},{"emptyLinePlaceholder":494},[539],{"type":48,"value":497},{"type":42,"tag":446,"props":541,"children":543},{"class":448,"line":542},7,[544],{"type":42,"tag":446,"props":545,"children":546},{"style":453},[547],{"type":48,"value":548},"# List all rule IDs triggered\n",{"type":42,"tag":446,"props":550,"children":552},{"class":448,"line":551},8,[553,557,561,566,570],{"type":42,"tag":446,"props":554,"children":555},{"style":463},[556],{"type":48,"value":287},{"type":42,"tag":446,"props":558,"children":559},{"style":468},[560],{"type":48,"value":471},{"type":42,"tag":446,"props":562,"children":563},{"style":474},[564],{"type":48,"value":565},"[.runs[].results[].ruleId] | unique",{"type":42,"tag":446,"props":567,"children":568},{"style":468},[569],{"type":48,"value":482},{"type":42,"tag":446,"props":571,"children":572},{"style":474},[573],{"type":48,"value":487},{"type":42,"tag":446,"props":575,"children":577},{"class":448,"line":576},9,[578],{"type":42,"tag":446,"props":579,"children":580},{"emptyLinePlaceholder":494},[581],{"type":48,"value":497},{"type":42,"tag":446,"props":583,"children":585},{"class":448,"line":584},10,[586],{"type":42,"tag":446,"props":587,"children":588},{"style":453},[589],{"type":48,"value":590},"# Extract errors only\n",{"type":42,"tag":446,"props":592,"children":594},{"class":448,"line":593},11,[595,599,603,608,612],{"type":42,"tag":446,"props":596,"children":597},{"style":463},[598],{"type":48,"value":287},{"type":42,"tag":446,"props":600,"children":601},{"style":468},[602],{"type":48,"value":471},{"type":42,"tag":446,"props":604,"children":605},{"style":474},[606],{"type":48,"value":607},".runs[].results[] | select(.level == \"error\")",{"type":42,"tag":446,"props":609,"children":610},{"style":468},[611],{"type":48,"value":482},{"type":42,"tag":446,"props":613,"children":614},{"style":474},[615],{"type":48,"value":487},{"type":42,"tag":446,"props":617,"children":619},{"class":448,"line":618},12,[620],{"type":42,"tag":446,"props":621,"children":622},{"emptyLinePlaceholder":494},[623],{"type":48,"value":497},{"type":42,"tag":446,"props":625,"children":627},{"class":448,"line":626},13,[628],{"type":42,"tag":446,"props":629,"children":630},{"style":453},[631],{"type":48,"value":632},"# Get findings with file locations\n",{"type":42,"tag":446,"props":634,"children":636},{"class":448,"line":635},14,[637,641,645],{"type":42,"tag":446,"props":638,"children":639},{"style":463},[640],{"type":48,"value":287},{"type":42,"tag":446,"props":642,"children":643},{"style":468},[644],{"type":48,"value":471},{"type":42,"tag":446,"props":646,"children":647},{"style":474},[648],{"type":48,"value":649},".runs[].results[] | {\n",{"type":42,"tag":446,"props":651,"children":653},{"class":448,"line":652},15,[654],{"type":42,"tag":446,"props":655,"children":656},{"style":474},[657],{"type":48,"value":658},"  rule: .ruleId,\n",{"type":42,"tag":446,"props":660,"children":662},{"class":448,"line":661},16,[663],{"type":42,"tag":446,"props":664,"children":665},{"style":474},[666],{"type":48,"value":667},"  message: .message.text,\n",{"type":42,"tag":446,"props":669,"children":671},{"class":448,"line":670},17,[672],{"type":42,"tag":446,"props":673,"children":674},{"style":474},[675],{"type":48,"value":676},"  file: .locations[0].physicalLocation.artifactLocation.uri,\n",{"type":42,"tag":446,"props":678,"children":680},{"class":448,"line":679},18,[681],{"type":42,"tag":446,"props":682,"children":683},{"style":474},[684],{"type":48,"value":685},"  line: .locations[0].physicalLocation.region.startLine\n",{"type":42,"tag":446,"props":687,"children":689},{"class":448,"line":688},19,[690,695,699],{"type":42,"tag":446,"props":691,"children":692},{"style":474},[693],{"type":48,"value":694},"}",{"type":42,"tag":446,"props":696,"children":697},{"style":468},[698],{"type":48,"value":482},{"type":42,"tag":446,"props":700,"children":701},{"style":474},[702],{"type":48,"value":487},{"type":42,"tag":446,"props":704,"children":706},{"class":448,"line":705},20,[707],{"type":42,"tag":446,"props":708,"children":709},{"emptyLinePlaceholder":494},[710],{"type":48,"value":497},{"type":42,"tag":446,"props":712,"children":714},{"class":448,"line":713},21,[715],{"type":42,"tag":446,"props":716,"children":717},{"style":453},[718],{"type":48,"value":719},"# Filter by severity and get count per rule\n",{"type":42,"tag":446,"props":721,"children":723},{"class":448,"line":722},22,[724,728,732,737,741],{"type":42,"tag":446,"props":725,"children":726},{"style":463},[727],{"type":48,"value":287},{"type":42,"tag":446,"props":729,"children":730},{"style":468},[731],{"type":48,"value":471},{"type":42,"tag":446,"props":733,"children":734},{"style":474},[735],{"type":48,"value":736},"[.runs[].results[] | select(.level == \"error\")] | group_by(.ruleId) | map({rule: .[0].ruleId, count: length})",{"type":42,"tag":446,"props":738,"children":739},{"style":468},[740],{"type":48,"value":482},{"type":42,"tag":446,"props":742,"children":743},{"style":474},[744],{"type":48,"value":487},{"type":42,"tag":446,"props":746,"children":748},{"class":448,"line":747},23,[749],{"type":42,"tag":446,"props":750,"children":751},{"emptyLinePlaceholder":494},[752],{"type":48,"value":497},{"type":42,"tag":446,"props":754,"children":756},{"class":448,"line":755},24,[757],{"type":42,"tag":446,"props":758,"children":759},{"style":453},[760],{"type":48,"value":761},"# Extract findings for a specific file\n",{"type":42,"tag":446,"props":763,"children":765},{"class":448,"line":764},25,[766,770,775,780,785,790,795,799,804,808],{"type":42,"tag":446,"props":767,"children":768},{"style":463},[769],{"type":48,"value":287},{"type":42,"tag":446,"props":771,"children":772},{"style":474},[773],{"type":48,"value":774}," --arg",{"type":42,"tag":446,"props":776,"children":777},{"style":474},[778],{"type":48,"value":779}," file",{"type":42,"tag":446,"props":781,"children":782},{"style":468},[783],{"type":48,"value":784}," \"",{"type":42,"tag":446,"props":786,"children":787},{"style":474},[788],{"type":48,"value":789},"src\u002Fauth.py",{"type":42,"tag":446,"props":791,"children":792},{"style":468},[793],{"type":48,"value":794},"\"",{"type":42,"tag":446,"props":796,"children":797},{"style":468},[798],{"type":48,"value":471},{"type":42,"tag":446,"props":800,"children":801},{"style":474},[802],{"type":48,"value":803},".runs[].results[] | select(.locations[].physicalLocation.artifactLocation.uri | contains($file))",{"type":42,"tag":446,"props":805,"children":806},{"style":468},[807],{"type":48,"value":482},{"type":42,"tag":446,"props":809,"children":810},{"style":474},[811],{"type":48,"value":487},{"type":42,"tag":57,"props":813,"children":815},{"id":814},"strategy-2-python-with-pysarif",[816],{"type":48,"value":817},"Strategy 2: Python with pysarif",{"type":42,"tag":51,"props":819,"children":820},{},[821],{"type":48,"value":822},"For programmatic access with full object model:",{"type":42,"tag":149,"props":824,"children":828},{"className":825,"code":826,"language":827,"meta":158,"style":158},"language-python shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","from pysarif import load_from_file, save_to_file\n\n# Load SARIF file\nsarif = load_from_file(\"results.sarif\")\n\n# Iterate through runs and results\nfor run in sarif.runs:\n    tool_name = run.tool.driver.name\n    print(f\"Tool: {tool_name}\")\n\n    for result in run.results:\n        print(f\"  [{result.level}] {result.rule_id}: {result.message.text}\")\n\n        if result.locations:\n            loc = result.locations[0].physical_location\n            if loc and loc.artifact_location:\n                print(f\"    File: {loc.artifact_location.uri}\")\n                if loc.region:\n                    print(f\"    Line: {loc.region.start_line}\")\n\n# Save modified SARIF\nsave_to_file(sarif, \"modified.sarif\")\n","python",[829],{"type":42,"tag":156,"props":830,"children":831},{"__ignoreMap":158},[832,840,847,855,863,870,878,886,894,902,909,917,925,932,940,948,956,964,972,980,987,995],{"type":42,"tag":446,"props":833,"children":834},{"class":448,"line":449},[835],{"type":42,"tag":446,"props":836,"children":837},{},[838],{"type":48,"value":839},"from pysarif import load_from_file, save_to_file\n",{"type":42,"tag":446,"props":841,"children":842},{"class":448,"line":459},[843],{"type":42,"tag":446,"props":844,"children":845},{"emptyLinePlaceholder":494},[846],{"type":48,"value":497},{"type":42,"tag":446,"props":848,"children":849},{"class":448,"line":490},[850],{"type":42,"tag":446,"props":851,"children":852},{},[853],{"type":48,"value":854},"# Load SARIF file\n",{"type":42,"tag":446,"props":856,"children":857},{"class":448,"line":500},[858],{"type":42,"tag":446,"props":859,"children":860},{},[861],{"type":48,"value":862},"sarif = load_from_file(\"results.sarif\")\n",{"type":42,"tag":446,"props":864,"children":865},{"class":448,"line":509},[866],{"type":42,"tag":446,"props":867,"children":868},{"emptyLinePlaceholder":494},[869],{"type":48,"value":497},{"type":42,"tag":446,"props":871,"children":872},{"class":448,"line":534},[873],{"type":42,"tag":446,"props":874,"children":875},{},[876],{"type":48,"value":877},"# Iterate through runs and results\n",{"type":42,"tag":446,"props":879,"children":880},{"class":448,"line":542},[881],{"type":42,"tag":446,"props":882,"children":883},{},[884],{"type":48,"value":885},"for run in sarif.runs:\n",{"type":42,"tag":446,"props":887,"children":888},{"class":448,"line":551},[889],{"type":42,"tag":446,"props":890,"children":891},{},[892],{"type":48,"value":893},"    tool_name = run.tool.driver.name\n",{"type":42,"tag":446,"props":895,"children":896},{"class":448,"line":576},[897],{"type":42,"tag":446,"props":898,"children":899},{},[900],{"type":48,"value":901},"    print(f\"Tool: {tool_name}\")\n",{"type":42,"tag":446,"props":903,"children":904},{"class":448,"line":584},[905],{"type":42,"tag":446,"props":906,"children":907},{"emptyLinePlaceholder":494},[908],{"type":48,"value":497},{"type":42,"tag":446,"props":910,"children":911},{"class":448,"line":593},[912],{"type":42,"tag":446,"props":913,"children":914},{},[915],{"type":48,"value":916},"    for result in run.results:\n",{"type":42,"tag":446,"props":918,"children":919},{"class":448,"line":618},[920],{"type":42,"tag":446,"props":921,"children":922},{},[923],{"type":48,"value":924},"        print(f\"  [{result.level}] {result.rule_id}: {result.message.text}\")\n",{"type":42,"tag":446,"props":926,"children":927},{"class":448,"line":626},[928],{"type":42,"tag":446,"props":929,"children":930},{"emptyLinePlaceholder":494},[931],{"type":48,"value":497},{"type":42,"tag":446,"props":933,"children":934},{"class":448,"line":635},[935],{"type":42,"tag":446,"props":936,"children":937},{},[938],{"type":48,"value":939},"        if result.locations:\n",{"type":42,"tag":446,"props":941,"children":942},{"class":448,"line":652},[943],{"type":42,"tag":446,"props":944,"children":945},{},[946],{"type":48,"value":947},"            loc = result.locations[0].physical_location\n",{"type":42,"tag":446,"props":949,"children":950},{"class":448,"line":661},[951],{"type":42,"tag":446,"props":952,"children":953},{},[954],{"type":48,"value":955},"            if loc and loc.artifact_location:\n",{"type":42,"tag":446,"props":957,"children":958},{"class":448,"line":670},[959],{"type":42,"tag":446,"props":960,"children":961},{},[962],{"type":48,"value":963},"                print(f\"    File: {loc.artifact_location.uri}\")\n",{"type":42,"tag":446,"props":965,"children":966},{"class":448,"line":679},[967],{"type":42,"tag":446,"props":968,"children":969},{},[970],{"type":48,"value":971},"                if loc.region:\n",{"type":42,"tag":446,"props":973,"children":974},{"class":448,"line":688},[975],{"type":42,"tag":446,"props":976,"children":977},{},[978],{"type":48,"value":979},"                    print(f\"    Line: {loc.region.start_line}\")\n",{"type":42,"tag":446,"props":981,"children":982},{"class":448,"line":705},[983],{"type":42,"tag":446,"props":984,"children":985},{"emptyLinePlaceholder":494},[986],{"type":48,"value":497},{"type":42,"tag":446,"props":988,"children":989},{"class":448,"line":713},[990],{"type":42,"tag":446,"props":991,"children":992},{},[993],{"type":48,"value":994},"# Save modified SARIF\n",{"type":42,"tag":446,"props":996,"children":997},{"class":448,"line":722},[998],{"type":42,"tag":446,"props":999,"children":1000},{},[1001],{"type":48,"value":1002},"save_to_file(sarif, \"modified.sarif\")\n",{"type":42,"tag":57,"props":1004,"children":1006},{"id":1005},"strategy-3-python-with-sarif-tools",[1007],{"type":48,"value":1008},"Strategy 3: Python with sarif-tools",{"type":42,"tag":51,"props":1010,"children":1011},{},[1012],{"type":48,"value":1013},"For aggregation, reporting, and CI\u002FCD integration:",{"type":42,"tag":149,"props":1015,"children":1017},{"className":825,"code":1016,"language":827,"meta":158,"style":158},"from sarif import loader\n\n# Load single file\nsarif_data = loader.load_sarif_file(\"results.sarif\")\n\n# Or load multiple files\nsarif_set = loader.load_sarif_files([\"tool1.sarif\", \"tool2.sarif\"])\n\n# Get summary report\nreport = sarif_data.get_report()\n\n# Get histogram by severity\nerrors = report.get_issue_type_histogram_for_severity(\"error\")\nwarnings = report.get_issue_type_histogram_for_severity(\"warning\")\n\n# Filter results\nhigh_severity = [r for r in sarif_data.get_results()\n                 if r.get(\"level\") == \"error\"]\n",[1018],{"type":42,"tag":156,"props":1019,"children":1020},{"__ignoreMap":158},[1021,1029,1036,1044,1052,1059,1067,1075,1082,1090,1098,1105,1113,1121,1129,1136,1144,1152],{"type":42,"tag":446,"props":1022,"children":1023},{"class":448,"line":449},[1024],{"type":42,"tag":446,"props":1025,"children":1026},{},[1027],{"type":48,"value":1028},"from sarif import loader\n",{"type":42,"tag":446,"props":1030,"children":1031},{"class":448,"line":459},[1032],{"type":42,"tag":446,"props":1033,"children":1034},{"emptyLinePlaceholder":494},[1035],{"type":48,"value":497},{"type":42,"tag":446,"props":1037,"children":1038},{"class":448,"line":490},[1039],{"type":42,"tag":446,"props":1040,"children":1041},{},[1042],{"type":48,"value":1043},"# Load single file\n",{"type":42,"tag":446,"props":1045,"children":1046},{"class":448,"line":500},[1047],{"type":42,"tag":446,"props":1048,"children":1049},{},[1050],{"type":48,"value":1051},"sarif_data = loader.load_sarif_file(\"results.sarif\")\n",{"type":42,"tag":446,"props":1053,"children":1054},{"class":448,"line":509},[1055],{"type":42,"tag":446,"props":1056,"children":1057},{"emptyLinePlaceholder":494},[1058],{"type":48,"value":497},{"type":42,"tag":446,"props":1060,"children":1061},{"class":448,"line":534},[1062],{"type":42,"tag":446,"props":1063,"children":1064},{},[1065],{"type":48,"value":1066},"# Or load multiple files\n",{"type":42,"tag":446,"props":1068,"children":1069},{"class":448,"line":542},[1070],{"type":42,"tag":446,"props":1071,"children":1072},{},[1073],{"type":48,"value":1074},"sarif_set = loader.load_sarif_files([\"tool1.sarif\", \"tool2.sarif\"])\n",{"type":42,"tag":446,"props":1076,"children":1077},{"class":448,"line":551},[1078],{"type":42,"tag":446,"props":1079,"children":1080},{"emptyLinePlaceholder":494},[1081],{"type":48,"value":497},{"type":42,"tag":446,"props":1083,"children":1084},{"class":448,"line":576},[1085],{"type":42,"tag":446,"props":1086,"children":1087},{},[1088],{"type":48,"value":1089},"# Get summary report\n",{"type":42,"tag":446,"props":1091,"children":1092},{"class":448,"line":584},[1093],{"type":42,"tag":446,"props":1094,"children":1095},{},[1096],{"type":48,"value":1097},"report = sarif_data.get_report()\n",{"type":42,"tag":446,"props":1099,"children":1100},{"class":448,"line":593},[1101],{"type":42,"tag":446,"props":1102,"children":1103},{"emptyLinePlaceholder":494},[1104],{"type":48,"value":497},{"type":42,"tag":446,"props":1106,"children":1107},{"class":448,"line":618},[1108],{"type":42,"tag":446,"props":1109,"children":1110},{},[1111],{"type":48,"value":1112},"# Get histogram by severity\n",{"type":42,"tag":446,"props":1114,"children":1115},{"class":448,"line":626},[1116],{"type":42,"tag":446,"props":1117,"children":1118},{},[1119],{"type":48,"value":1120},"errors = report.get_issue_type_histogram_for_severity(\"error\")\n",{"type":42,"tag":446,"props":1122,"children":1123},{"class":448,"line":635},[1124],{"type":42,"tag":446,"props":1125,"children":1126},{},[1127],{"type":48,"value":1128},"warnings = report.get_issue_type_histogram_for_severity(\"warning\")\n",{"type":42,"tag":446,"props":1130,"children":1131},{"class":448,"line":652},[1132],{"type":42,"tag":446,"props":1133,"children":1134},{"emptyLinePlaceholder":494},[1135],{"type":48,"value":497},{"type":42,"tag":446,"props":1137,"children":1138},{"class":448,"line":661},[1139],{"type":42,"tag":446,"props":1140,"children":1141},{},[1142],{"type":48,"value":1143},"# Filter results\n",{"type":42,"tag":446,"props":1145,"children":1146},{"class":448,"line":670},[1147],{"type":42,"tag":446,"props":1148,"children":1149},{},[1150],{"type":48,"value":1151},"high_severity = [r for r in sarif_data.get_results()\n",{"type":42,"tag":446,"props":1153,"children":1154},{"class":448,"line":679},[1155],{"type":42,"tag":446,"props":1156,"children":1157},{},[1158],{"type":48,"value":1159},"                 if r.get(\"level\") == \"error\"]\n",{"type":42,"tag":51,"props":1161,"children":1162},{},[1163],{"type":42,"tag":180,"props":1164,"children":1165},{},[1166],{"type":48,"value":1167},"sarif-tools CLI commands:",{"type":42,"tag":149,"props":1169,"children":1171},{"className":438,"code":1170,"language":440,"meta":158,"style":158},"# Summary of findings\nsarif summary results.sarif\n\n# List all results with details\nsarif ls results.sarif\n\n# Get results by severity\nsarif ls --level error results.sarif\n\n# Diff two SARIF files (find new\u002Ffixed issues)\nsarif diff baseline.sarif current.sarif\n\n# Convert to other formats\nsarif csv results.sarif > results.csv\nsarif html results.sarif > report.html\n",[1172],{"type":42,"tag":156,"props":1173,"children":1174},{"__ignoreMap":158},[1175,1183,1200,1207,1215,1231,1238,1246,1271,1278,1286,1308,1315,1323,1350],{"type":42,"tag":446,"props":1176,"children":1177},{"class":448,"line":449},[1178],{"type":42,"tag":446,"props":1179,"children":1180},{"style":453},[1181],{"type":48,"value":1182},"# Summary of findings\n",{"type":42,"tag":446,"props":1184,"children":1185},{"class":448,"line":459},[1186,1191,1196],{"type":42,"tag":446,"props":1187,"children":1188},{"style":463},[1189],{"type":48,"value":1190},"sarif",{"type":42,"tag":446,"props":1192,"children":1193},{"style":474},[1194],{"type":48,"value":1195}," summary",{"type":42,"tag":446,"props":1197,"children":1198},{"style":474},[1199],{"type":48,"value":487},{"type":42,"tag":446,"props":1201,"children":1202},{"class":448,"line":490},[1203],{"type":42,"tag":446,"props":1204,"children":1205},{"emptyLinePlaceholder":494},[1206],{"type":48,"value":497},{"type":42,"tag":446,"props":1208,"children":1209},{"class":448,"line":500},[1210],{"type":42,"tag":446,"props":1211,"children":1212},{"style":453},[1213],{"type":48,"value":1214},"# List all results with details\n",{"type":42,"tag":446,"props":1216,"children":1217},{"class":448,"line":509},[1218,1222,1227],{"type":42,"tag":446,"props":1219,"children":1220},{"style":463},[1221],{"type":48,"value":1190},{"type":42,"tag":446,"props":1223,"children":1224},{"style":474},[1225],{"type":48,"value":1226}," ls",{"type":42,"tag":446,"props":1228,"children":1229},{"style":474},[1230],{"type":48,"value":487},{"type":42,"tag":446,"props":1232,"children":1233},{"class":448,"line":534},[1234],{"type":42,"tag":446,"props":1235,"children":1236},{"emptyLinePlaceholder":494},[1237],{"type":48,"value":497},{"type":42,"tag":446,"props":1239,"children":1240},{"class":448,"line":542},[1241],{"type":42,"tag":446,"props":1242,"children":1243},{"style":453},[1244],{"type":48,"value":1245},"# Get results by severity\n",{"type":42,"tag":446,"props":1247,"children":1248},{"class":448,"line":551},[1249,1253,1257,1262,1267],{"type":42,"tag":446,"props":1250,"children":1251},{"style":463},[1252],{"type":48,"value":1190},{"type":42,"tag":446,"props":1254,"children":1255},{"style":474},[1256],{"type":48,"value":1226},{"type":42,"tag":446,"props":1258,"children":1259},{"style":474},[1260],{"type":48,"value":1261}," --level",{"type":42,"tag":446,"props":1263,"children":1264},{"style":474},[1265],{"type":48,"value":1266}," error",{"type":42,"tag":446,"props":1268,"children":1269},{"style":474},[1270],{"type":48,"value":487},{"type":42,"tag":446,"props":1272,"children":1273},{"class":448,"line":576},[1274],{"type":42,"tag":446,"props":1275,"children":1276},{"emptyLinePlaceholder":494},[1277],{"type":48,"value":497},{"type":42,"tag":446,"props":1279,"children":1280},{"class":448,"line":584},[1281],{"type":42,"tag":446,"props":1282,"children":1283},{"style":453},[1284],{"type":48,"value":1285},"# Diff two SARIF files (find new\u002Ffixed issues)\n",{"type":42,"tag":446,"props":1287,"children":1288},{"class":448,"line":593},[1289,1293,1298,1303],{"type":42,"tag":446,"props":1290,"children":1291},{"style":463},[1292],{"type":48,"value":1190},{"type":42,"tag":446,"props":1294,"children":1295},{"style":474},[1296],{"type":48,"value":1297}," diff",{"type":42,"tag":446,"props":1299,"children":1300},{"style":474},[1301],{"type":48,"value":1302}," baseline.sarif",{"type":42,"tag":446,"props":1304,"children":1305},{"style":474},[1306],{"type":48,"value":1307}," current.sarif\n",{"type":42,"tag":446,"props":1309,"children":1310},{"class":448,"line":618},[1311],{"type":42,"tag":446,"props":1312,"children":1313},{"emptyLinePlaceholder":494},[1314],{"type":48,"value":497},{"type":42,"tag":446,"props":1316,"children":1317},{"class":448,"line":626},[1318],{"type":42,"tag":446,"props":1319,"children":1320},{"style":453},[1321],{"type":48,"value":1322},"# Convert to other formats\n",{"type":42,"tag":446,"props":1324,"children":1325},{"class":448,"line":635},[1326,1330,1335,1340,1345],{"type":42,"tag":446,"props":1327,"children":1328},{"style":463},[1329],{"type":48,"value":1190},{"type":42,"tag":446,"props":1331,"children":1332},{"style":474},[1333],{"type":48,"value":1334}," csv",{"type":42,"tag":446,"props":1336,"children":1337},{"style":474},[1338],{"type":48,"value":1339}," results.sarif",{"type":42,"tag":446,"props":1341,"children":1342},{"style":468},[1343],{"type":48,"value":1344}," >",{"type":42,"tag":446,"props":1346,"children":1347},{"style":474},[1348],{"type":48,"value":1349}," results.csv\n",{"type":42,"tag":446,"props":1351,"children":1352},{"class":448,"line":652},[1353,1357,1362,1366,1370],{"type":42,"tag":446,"props":1354,"children":1355},{"style":463},[1356],{"type":48,"value":1190},{"type":42,"tag":446,"props":1358,"children":1359},{"style":474},[1360],{"type":48,"value":1361}," html",{"type":42,"tag":446,"props":1363,"children":1364},{"style":474},[1365],{"type":48,"value":1339},{"type":42,"tag":446,"props":1367,"children":1368},{"style":468},[1369],{"type":48,"value":1344},{"type":42,"tag":446,"props":1371,"children":1372},{"style":474},[1373],{"type":48,"value":1374}," report.html\n",{"type":42,"tag":57,"props":1376,"children":1378},{"id":1377},"strategy-4-aggregating-multiple-sarif-files",[1379],{"type":48,"value":1380},"Strategy 4: Aggregating Multiple SARIF Files",{"type":42,"tag":51,"props":1382,"children":1383},{},[1384],{"type":48,"value":1385},"When combining results from multiple tools:",{"type":42,"tag":149,"props":1387,"children":1389},{"className":825,"code":1388,"language":827,"meta":158,"style":158},"import json\nfrom pathlib import Path\n\ndef aggregate_sarif_files(sarif_paths: list[str]) -> dict:\n    \"\"\"Combine multiple SARIF files into one.\"\"\"\n    aggregated = {\n        \"version\": \"2.1.0\",\n        \"$schema\": \"https:\u002F\u002Fjson.schemastore.org\u002Fsarif-2.1.0.json\",\n        \"runs\": []\n    }\n\n    for path in sarif_paths:\n        with open(path) as f:\n            sarif = json.load(f)\n            aggregated[\"runs\"].extend(sarif.get(\"runs\", []))\n\n    return aggregated\n\ndef deduplicate_results(sarif: dict) -> dict:\n    \"\"\"Remove duplicate findings based on fingerprints.\"\"\"\n    seen_fingerprints = set()\n\n    for run in sarif[\"runs\"]:\n        unique_results = []\n        for result in run.get(\"results\", []):\n            # Use partialFingerprints or create key from location\n            fp = None\n            if result.get(\"partialFingerprints\"):\n                fp = tuple(sorted(result[\"partialFingerprints\"].items()))\n            elif result.get(\"fingerprints\"):\n                fp = tuple(sorted(result[\"fingerprints\"].items()))\n            else:\n                # Fallback: create fingerprint from rule + location\n                loc = result.get(\"locations\", [{}])[0]\n                phys = loc.get(\"physicalLocation\", {})\n                fp = (\n                    result.get(\"ruleId\"),\n                    phys.get(\"artifactLocation\", {}).get(\"uri\"),\n                    phys.get(\"region\", {}).get(\"startLine\")\n                )\n\n            if fp not in seen_fingerprints:\n                seen_fingerprints.add(fp)\n                unique_results.append(result)\n\n        run[\"results\"] = unique_results\n\n    return sarif\n",[1390],{"type":42,"tag":156,"props":1391,"children":1392},{"__ignoreMap":158},[1393,1401,1409,1416,1424,1432,1440,1448,1456,1464,1472,1479,1487,1495,1503,1511,1518,1526,1533,1541,1549,1557,1564,1572,1580,1588,1597,1606,1615,1624,1633,1642,1651,1660,1669,1678,1687,1696,1705,1714,1723,1731,1740,1749,1758,1766,1775,1783],{"type":42,"tag":446,"props":1394,"children":1395},{"class":448,"line":449},[1396],{"type":42,"tag":446,"props":1397,"children":1398},{},[1399],{"type":48,"value":1400},"import json\n",{"type":42,"tag":446,"props":1402,"children":1403},{"class":448,"line":459},[1404],{"type":42,"tag":446,"props":1405,"children":1406},{},[1407],{"type":48,"value":1408},"from pathlib import Path\n",{"type":42,"tag":446,"props":1410,"children":1411},{"class":448,"line":490},[1412],{"type":42,"tag":446,"props":1413,"children":1414},{"emptyLinePlaceholder":494},[1415],{"type":48,"value":497},{"type":42,"tag":446,"props":1417,"children":1418},{"class":448,"line":500},[1419],{"type":42,"tag":446,"props":1420,"children":1421},{},[1422],{"type":48,"value":1423},"def aggregate_sarif_files(sarif_paths: list[str]) -> dict:\n",{"type":42,"tag":446,"props":1425,"children":1426},{"class":448,"line":509},[1427],{"type":42,"tag":446,"props":1428,"children":1429},{},[1430],{"type":48,"value":1431},"    \"\"\"Combine multiple SARIF files into one.\"\"\"\n",{"type":42,"tag":446,"props":1433,"children":1434},{"class":448,"line":534},[1435],{"type":42,"tag":446,"props":1436,"children":1437},{},[1438],{"type":48,"value":1439},"    aggregated = {\n",{"type":42,"tag":446,"props":1441,"children":1442},{"class":448,"line":542},[1443],{"type":42,"tag":446,"props":1444,"children":1445},{},[1446],{"type":48,"value":1447},"        \"version\": \"2.1.0\",\n",{"type":42,"tag":446,"props":1449,"children":1450},{"class":448,"line":551},[1451],{"type":42,"tag":446,"props":1452,"children":1453},{},[1454],{"type":48,"value":1455},"        \"$schema\": \"https:\u002F\u002Fjson.schemastore.org\u002Fsarif-2.1.0.json\",\n",{"type":42,"tag":446,"props":1457,"children":1458},{"class":448,"line":576},[1459],{"type":42,"tag":446,"props":1460,"children":1461},{},[1462],{"type":48,"value":1463},"        \"runs\": []\n",{"type":42,"tag":446,"props":1465,"children":1466},{"class":448,"line":584},[1467],{"type":42,"tag":446,"props":1468,"children":1469},{},[1470],{"type":48,"value":1471},"    }\n",{"type":42,"tag":446,"props":1473,"children":1474},{"class":448,"line":593},[1475],{"type":42,"tag":446,"props":1476,"children":1477},{"emptyLinePlaceholder":494},[1478],{"type":48,"value":497},{"type":42,"tag":446,"props":1480,"children":1481},{"class":448,"line":618},[1482],{"type":42,"tag":446,"props":1483,"children":1484},{},[1485],{"type":48,"value":1486},"    for path in sarif_paths:\n",{"type":42,"tag":446,"props":1488,"children":1489},{"class":448,"line":626},[1490],{"type":42,"tag":446,"props":1491,"children":1492},{},[1493],{"type":48,"value":1494},"        with open(path) as f:\n",{"type":42,"tag":446,"props":1496,"children":1497},{"class":448,"line":635},[1498],{"type":42,"tag":446,"props":1499,"children":1500},{},[1501],{"type":48,"value":1502},"            sarif = json.load(f)\n",{"type":42,"tag":446,"props":1504,"children":1505},{"class":448,"line":652},[1506],{"type":42,"tag":446,"props":1507,"children":1508},{},[1509],{"type":48,"value":1510},"            aggregated[\"runs\"].extend(sarif.get(\"runs\", []))\n",{"type":42,"tag":446,"props":1512,"children":1513},{"class":448,"line":661},[1514],{"type":42,"tag":446,"props":1515,"children":1516},{"emptyLinePlaceholder":494},[1517],{"type":48,"value":497},{"type":42,"tag":446,"props":1519,"children":1520},{"class":448,"line":670},[1521],{"type":42,"tag":446,"props":1522,"children":1523},{},[1524],{"type":48,"value":1525},"    return aggregated\n",{"type":42,"tag":446,"props":1527,"children":1528},{"class":448,"line":679},[1529],{"type":42,"tag":446,"props":1530,"children":1531},{"emptyLinePlaceholder":494},[1532],{"type":48,"value":497},{"type":42,"tag":446,"props":1534,"children":1535},{"class":448,"line":688},[1536],{"type":42,"tag":446,"props":1537,"children":1538},{},[1539],{"type":48,"value":1540},"def deduplicate_results(sarif: dict) -> dict:\n",{"type":42,"tag":446,"props":1542,"children":1543},{"class":448,"line":705},[1544],{"type":42,"tag":446,"props":1545,"children":1546},{},[1547],{"type":48,"value":1548},"    \"\"\"Remove duplicate findings based on fingerprints.\"\"\"\n",{"type":42,"tag":446,"props":1550,"children":1551},{"class":448,"line":713},[1552],{"type":42,"tag":446,"props":1553,"children":1554},{},[1555],{"type":48,"value":1556},"    seen_fingerprints = set()\n",{"type":42,"tag":446,"props":1558,"children":1559},{"class":448,"line":722},[1560],{"type":42,"tag":446,"props":1561,"children":1562},{"emptyLinePlaceholder":494},[1563],{"type":48,"value":497},{"type":42,"tag":446,"props":1565,"children":1566},{"class":448,"line":747},[1567],{"type":42,"tag":446,"props":1568,"children":1569},{},[1570],{"type":48,"value":1571},"    for run in sarif[\"runs\"]:\n",{"type":42,"tag":446,"props":1573,"children":1574},{"class":448,"line":755},[1575],{"type":42,"tag":446,"props":1576,"children":1577},{},[1578],{"type":48,"value":1579},"        unique_results = []\n",{"type":42,"tag":446,"props":1581,"children":1582},{"class":448,"line":764},[1583],{"type":42,"tag":446,"props":1584,"children":1585},{},[1586],{"type":48,"value":1587},"        for result in run.get(\"results\", []):\n",{"type":42,"tag":446,"props":1589,"children":1591},{"class":448,"line":1590},26,[1592],{"type":42,"tag":446,"props":1593,"children":1594},{},[1595],{"type":48,"value":1596},"            # Use partialFingerprints or create key from location\n",{"type":42,"tag":446,"props":1598,"children":1600},{"class":448,"line":1599},27,[1601],{"type":42,"tag":446,"props":1602,"children":1603},{},[1604],{"type":48,"value":1605},"            fp = None\n",{"type":42,"tag":446,"props":1607,"children":1609},{"class":448,"line":1608},28,[1610],{"type":42,"tag":446,"props":1611,"children":1612},{},[1613],{"type":48,"value":1614},"            if result.get(\"partialFingerprints\"):\n",{"type":42,"tag":446,"props":1616,"children":1618},{"class":448,"line":1617},29,[1619],{"type":42,"tag":446,"props":1620,"children":1621},{},[1622],{"type":48,"value":1623},"                fp = tuple(sorted(result[\"partialFingerprints\"].items()))\n",{"type":42,"tag":446,"props":1625,"children":1627},{"class":448,"line":1626},30,[1628],{"type":42,"tag":446,"props":1629,"children":1630},{},[1631],{"type":48,"value":1632},"            elif result.get(\"fingerprints\"):\n",{"type":42,"tag":446,"props":1634,"children":1636},{"class":448,"line":1635},31,[1637],{"type":42,"tag":446,"props":1638,"children":1639},{},[1640],{"type":48,"value":1641},"                fp = tuple(sorted(result[\"fingerprints\"].items()))\n",{"type":42,"tag":446,"props":1643,"children":1645},{"class":448,"line":1644},32,[1646],{"type":42,"tag":446,"props":1647,"children":1648},{},[1649],{"type":48,"value":1650},"            else:\n",{"type":42,"tag":446,"props":1652,"children":1654},{"class":448,"line":1653},33,[1655],{"type":42,"tag":446,"props":1656,"children":1657},{},[1658],{"type":48,"value":1659},"                # Fallback: create fingerprint from rule + location\n",{"type":42,"tag":446,"props":1661,"children":1663},{"class":448,"line":1662},34,[1664],{"type":42,"tag":446,"props":1665,"children":1666},{},[1667],{"type":48,"value":1668},"                loc = result.get(\"locations\", [{}])[0]\n",{"type":42,"tag":446,"props":1670,"children":1672},{"class":448,"line":1671},35,[1673],{"type":42,"tag":446,"props":1674,"children":1675},{},[1676],{"type":48,"value":1677},"                phys = loc.get(\"physicalLocation\", {})\n",{"type":42,"tag":446,"props":1679,"children":1681},{"class":448,"line":1680},36,[1682],{"type":42,"tag":446,"props":1683,"children":1684},{},[1685],{"type":48,"value":1686},"                fp = (\n",{"type":42,"tag":446,"props":1688,"children":1690},{"class":448,"line":1689},37,[1691],{"type":42,"tag":446,"props":1692,"children":1693},{},[1694],{"type":48,"value":1695},"                    result.get(\"ruleId\"),\n",{"type":42,"tag":446,"props":1697,"children":1699},{"class":448,"line":1698},38,[1700],{"type":42,"tag":446,"props":1701,"children":1702},{},[1703],{"type":48,"value":1704},"                    phys.get(\"artifactLocation\", {}).get(\"uri\"),\n",{"type":42,"tag":446,"props":1706,"children":1708},{"class":448,"line":1707},39,[1709],{"type":42,"tag":446,"props":1710,"children":1711},{},[1712],{"type":48,"value":1713},"                    phys.get(\"region\", {}).get(\"startLine\")\n",{"type":42,"tag":446,"props":1715,"children":1717},{"class":448,"line":1716},40,[1718],{"type":42,"tag":446,"props":1719,"children":1720},{},[1721],{"type":48,"value":1722},"                )\n",{"type":42,"tag":446,"props":1724,"children":1726},{"class":448,"line":1725},41,[1727],{"type":42,"tag":446,"props":1728,"children":1729},{"emptyLinePlaceholder":494},[1730],{"type":48,"value":497},{"type":42,"tag":446,"props":1732,"children":1734},{"class":448,"line":1733},42,[1735],{"type":42,"tag":446,"props":1736,"children":1737},{},[1738],{"type":48,"value":1739},"            if fp not in seen_fingerprints:\n",{"type":42,"tag":446,"props":1741,"children":1743},{"class":448,"line":1742},43,[1744],{"type":42,"tag":446,"props":1745,"children":1746},{},[1747],{"type":48,"value":1748},"                seen_fingerprints.add(fp)\n",{"type":42,"tag":446,"props":1750,"children":1752},{"class":448,"line":1751},44,[1753],{"type":42,"tag":446,"props":1754,"children":1755},{},[1756],{"type":48,"value":1757},"                unique_results.append(result)\n",{"type":42,"tag":446,"props":1759,"children":1761},{"class":448,"line":1760},45,[1762],{"type":42,"tag":446,"props":1763,"children":1764},{"emptyLinePlaceholder":494},[1765],{"type":48,"value":497},{"type":42,"tag":446,"props":1767,"children":1769},{"class":448,"line":1768},46,[1770],{"type":42,"tag":446,"props":1771,"children":1772},{},[1773],{"type":48,"value":1774},"        run[\"results\"] = unique_results\n",{"type":42,"tag":446,"props":1776,"children":1778},{"class":448,"line":1777},47,[1779],{"type":42,"tag":446,"props":1780,"children":1781},{"emptyLinePlaceholder":494},[1782],{"type":48,"value":497},{"type":42,"tag":446,"props":1784,"children":1786},{"class":448,"line":1785},48,[1787],{"type":42,"tag":446,"props":1788,"children":1789},{},[1790],{"type":48,"value":1791},"    return sarif\n",{"type":42,"tag":57,"props":1793,"children":1795},{"id":1794},"strategy-5-extracting-actionable-data",[1796],{"type":48,"value":1797},"Strategy 5: Extracting Actionable Data",{"type":42,"tag":149,"props":1799,"children":1801},{"className":825,"code":1800,"language":827,"meta":158,"style":158},"import json\nfrom dataclasses import dataclass\nfrom typing import Optional\n\n@dataclass\nclass Finding:\n    rule_id: str\n    level: str\n    message: str\n    file_path: Optional[str]\n    start_line: Optional[int]\n    end_line: Optional[int]\n    fingerprint: Optional[str]\n\ndef extract_findings(sarif_path: str) -> list[Finding]:\n    \"\"\"Extract structured findings from SARIF file.\"\"\"\n    with open(sarif_path) as f:\n        sarif = json.load(f)\n\n    findings = []\n    for run in sarif.get(\"runs\", []):\n        for result in run.get(\"results\", []):\n            loc = result.get(\"locations\", [{}])[0]\n            phys = loc.get(\"physicalLocation\", {})\n            region = phys.get(\"region\", {})\n\n            findings.append(Finding(\n                rule_id=result.get(\"ruleId\", \"unknown\"),\n                level=result.get(\"level\", \"warning\"),\n                message=result.get(\"message\", {}).get(\"text\", \"\"),\n                file_path=phys.get(\"artifactLocation\", {}).get(\"uri\"),\n                start_line=region.get(\"startLine\"),\n                end_line=region.get(\"endLine\"),\n                fingerprint=next(iter(result.get(\"partialFingerprints\", {}).values()), None)\n            ))\n\n    return findings\n\n# Filter and prioritize\ndef prioritize_findings(findings: list[Finding]) -> list[Finding]:\n    \"\"\"Sort findings by severity.\"\"\"\n    severity_order = {\"error\": 0, \"warning\": 1, \"note\": 2, \"none\": 3}\n    return sorted(findings, key=lambda f: severity_order.get(f.level, 99))\n",[1802],{"type":42,"tag":156,"props":1803,"children":1804},{"__ignoreMap":158},[1805,1812,1820,1828,1835,1843,1851,1859,1867,1875,1883,1891,1899,1907,1914,1922,1930,1938,1946,1953,1961,1969,1976,1984,1992,2000,2007,2015,2023,2031,2039,2047,2055,2063,2071,2079,2086,2094,2101,2109,2117,2125,2133],{"type":42,"tag":446,"props":1806,"children":1807},{"class":448,"line":449},[1808],{"type":42,"tag":446,"props":1809,"children":1810},{},[1811],{"type":48,"value":1400},{"type":42,"tag":446,"props":1813,"children":1814},{"class":448,"line":459},[1815],{"type":42,"tag":446,"props":1816,"children":1817},{},[1818],{"type":48,"value":1819},"from dataclasses import dataclass\n",{"type":42,"tag":446,"props":1821,"children":1822},{"class":448,"line":490},[1823],{"type":42,"tag":446,"props":1824,"children":1825},{},[1826],{"type":48,"value":1827},"from typing import Optional\n",{"type":42,"tag":446,"props":1829,"children":1830},{"class":448,"line":500},[1831],{"type":42,"tag":446,"props":1832,"children":1833},{"emptyLinePlaceholder":494},[1834],{"type":48,"value":497},{"type":42,"tag":446,"props":1836,"children":1837},{"class":448,"line":509},[1838],{"type":42,"tag":446,"props":1839,"children":1840},{},[1841],{"type":48,"value":1842},"@dataclass\n",{"type":42,"tag":446,"props":1844,"children":1845},{"class":448,"line":534},[1846],{"type":42,"tag":446,"props":1847,"children":1848},{},[1849],{"type":48,"value":1850},"class Finding:\n",{"type":42,"tag":446,"props":1852,"children":1853},{"class":448,"line":542},[1854],{"type":42,"tag":446,"props":1855,"children":1856},{},[1857],{"type":48,"value":1858},"    rule_id: str\n",{"type":42,"tag":446,"props":1860,"children":1861},{"class":448,"line":551},[1862],{"type":42,"tag":446,"props":1863,"children":1864},{},[1865],{"type":48,"value":1866},"    level: str\n",{"type":42,"tag":446,"props":1868,"children":1869},{"class":448,"line":576},[1870],{"type":42,"tag":446,"props":1871,"children":1872},{},[1873],{"type":48,"value":1874},"    message: str\n",{"type":42,"tag":446,"props":1876,"children":1877},{"class":448,"line":584},[1878],{"type":42,"tag":446,"props":1879,"children":1880},{},[1881],{"type":48,"value":1882},"    file_path: Optional[str]\n",{"type":42,"tag":446,"props":1884,"children":1885},{"class":448,"line":593},[1886],{"type":42,"tag":446,"props":1887,"children":1888},{},[1889],{"type":48,"value":1890},"    start_line: Optional[int]\n",{"type":42,"tag":446,"props":1892,"children":1893},{"class":448,"line":618},[1894],{"type":42,"tag":446,"props":1895,"children":1896},{},[1897],{"type":48,"value":1898},"    end_line: Optional[int]\n",{"type":42,"tag":446,"props":1900,"children":1901},{"class":448,"line":626},[1902],{"type":42,"tag":446,"props":1903,"children":1904},{},[1905],{"type":48,"value":1906},"    fingerprint: Optional[str]\n",{"type":42,"tag":446,"props":1908,"children":1909},{"class":448,"line":635},[1910],{"type":42,"tag":446,"props":1911,"children":1912},{"emptyLinePlaceholder":494},[1913],{"type":48,"value":497},{"type":42,"tag":446,"props":1915,"children":1916},{"class":448,"line":652},[1917],{"type":42,"tag":446,"props":1918,"children":1919},{},[1920],{"type":48,"value":1921},"def extract_findings(sarif_path: str) -> list[Finding]:\n",{"type":42,"tag":446,"props":1923,"children":1924},{"class":448,"line":661},[1925],{"type":42,"tag":446,"props":1926,"children":1927},{},[1928],{"type":48,"value":1929},"    \"\"\"Extract structured findings from SARIF file.\"\"\"\n",{"type":42,"tag":446,"props":1931,"children":1932},{"class":448,"line":670},[1933],{"type":42,"tag":446,"props":1934,"children":1935},{},[1936],{"type":48,"value":1937},"    with open(sarif_path) as f:\n",{"type":42,"tag":446,"props":1939,"children":1940},{"class":448,"line":679},[1941],{"type":42,"tag":446,"props":1942,"children":1943},{},[1944],{"type":48,"value":1945},"        sarif = json.load(f)\n",{"type":42,"tag":446,"props":1947,"children":1948},{"class":448,"line":688},[1949],{"type":42,"tag":446,"props":1950,"children":1951},{"emptyLinePlaceholder":494},[1952],{"type":48,"value":497},{"type":42,"tag":446,"props":1954,"children":1955},{"class":448,"line":705},[1956],{"type":42,"tag":446,"props":1957,"children":1958},{},[1959],{"type":48,"value":1960},"    findings = []\n",{"type":42,"tag":446,"props":1962,"children":1963},{"class":448,"line":713},[1964],{"type":42,"tag":446,"props":1965,"children":1966},{},[1967],{"type":48,"value":1968},"    for run in sarif.get(\"runs\", []):\n",{"type":42,"tag":446,"props":1970,"children":1971},{"class":448,"line":722},[1972],{"type":42,"tag":446,"props":1973,"children":1974},{},[1975],{"type":48,"value":1587},{"type":42,"tag":446,"props":1977,"children":1978},{"class":448,"line":747},[1979],{"type":42,"tag":446,"props":1980,"children":1981},{},[1982],{"type":48,"value":1983},"            loc = result.get(\"locations\", [{}])[0]\n",{"type":42,"tag":446,"props":1985,"children":1986},{"class":448,"line":755},[1987],{"type":42,"tag":446,"props":1988,"children":1989},{},[1990],{"type":48,"value":1991},"            phys = loc.get(\"physicalLocation\", {})\n",{"type":42,"tag":446,"props":1993,"children":1994},{"class":448,"line":764},[1995],{"type":42,"tag":446,"props":1996,"children":1997},{},[1998],{"type":48,"value":1999},"            region = phys.get(\"region\", {})\n",{"type":42,"tag":446,"props":2001,"children":2002},{"class":448,"line":1590},[2003],{"type":42,"tag":446,"props":2004,"children":2005},{"emptyLinePlaceholder":494},[2006],{"type":48,"value":497},{"type":42,"tag":446,"props":2008,"children":2009},{"class":448,"line":1599},[2010],{"type":42,"tag":446,"props":2011,"children":2012},{},[2013],{"type":48,"value":2014},"            findings.append(Finding(\n",{"type":42,"tag":446,"props":2016,"children":2017},{"class":448,"line":1608},[2018],{"type":42,"tag":446,"props":2019,"children":2020},{},[2021],{"type":48,"value":2022},"                rule_id=result.get(\"ruleId\", \"unknown\"),\n",{"type":42,"tag":446,"props":2024,"children":2025},{"class":448,"line":1617},[2026],{"type":42,"tag":446,"props":2027,"children":2028},{},[2029],{"type":48,"value":2030},"                level=result.get(\"level\", \"warning\"),\n",{"type":42,"tag":446,"props":2032,"children":2033},{"class":448,"line":1626},[2034],{"type":42,"tag":446,"props":2035,"children":2036},{},[2037],{"type":48,"value":2038},"                message=result.get(\"message\", {}).get(\"text\", \"\"),\n",{"type":42,"tag":446,"props":2040,"children":2041},{"class":448,"line":1635},[2042],{"type":42,"tag":446,"props":2043,"children":2044},{},[2045],{"type":48,"value":2046},"                file_path=phys.get(\"artifactLocation\", {}).get(\"uri\"),\n",{"type":42,"tag":446,"props":2048,"children":2049},{"class":448,"line":1644},[2050],{"type":42,"tag":446,"props":2051,"children":2052},{},[2053],{"type":48,"value":2054},"                start_line=region.get(\"startLine\"),\n",{"type":42,"tag":446,"props":2056,"children":2057},{"class":448,"line":1653},[2058],{"type":42,"tag":446,"props":2059,"children":2060},{},[2061],{"type":48,"value":2062},"                end_line=region.get(\"endLine\"),\n",{"type":42,"tag":446,"props":2064,"children":2065},{"class":448,"line":1662},[2066],{"type":42,"tag":446,"props":2067,"children":2068},{},[2069],{"type":48,"value":2070},"                fingerprint=next(iter(result.get(\"partialFingerprints\", {}).values()), None)\n",{"type":42,"tag":446,"props":2072,"children":2073},{"class":448,"line":1671},[2074],{"type":42,"tag":446,"props":2075,"children":2076},{},[2077],{"type":48,"value":2078},"            ))\n",{"type":42,"tag":446,"props":2080,"children":2081},{"class":448,"line":1680},[2082],{"type":42,"tag":446,"props":2083,"children":2084},{"emptyLinePlaceholder":494},[2085],{"type":48,"value":497},{"type":42,"tag":446,"props":2087,"children":2088},{"class":448,"line":1689},[2089],{"type":42,"tag":446,"props":2090,"children":2091},{},[2092],{"type":48,"value":2093},"    return findings\n",{"type":42,"tag":446,"props":2095,"children":2096},{"class":448,"line":1698},[2097],{"type":42,"tag":446,"props":2098,"children":2099},{"emptyLinePlaceholder":494},[2100],{"type":48,"value":497},{"type":42,"tag":446,"props":2102,"children":2103},{"class":448,"line":1707},[2104],{"type":42,"tag":446,"props":2105,"children":2106},{},[2107],{"type":48,"value":2108},"# Filter and prioritize\n",{"type":42,"tag":446,"props":2110,"children":2111},{"class":448,"line":1716},[2112],{"type":42,"tag":446,"props":2113,"children":2114},{},[2115],{"type":48,"value":2116},"def prioritize_findings(findings: list[Finding]) -> list[Finding]:\n",{"type":42,"tag":446,"props":2118,"children":2119},{"class":448,"line":1725},[2120],{"type":42,"tag":446,"props":2121,"children":2122},{},[2123],{"type":48,"value":2124},"    \"\"\"Sort findings by severity.\"\"\"\n",{"type":42,"tag":446,"props":2126,"children":2127},{"class":448,"line":1733},[2128],{"type":42,"tag":446,"props":2129,"children":2130},{},[2131],{"type":48,"value":2132},"    severity_order = {\"error\": 0, \"warning\": 1, \"note\": 2, \"none\": 3}\n",{"type":42,"tag":446,"props":2134,"children":2135},{"class":448,"line":1742},[2136],{"type":42,"tag":446,"props":2137,"children":2138},{},[2139],{"type":48,"value":2140},"    return sorted(findings, key=lambda f: severity_order.get(f.level, 99))\n",{"type":42,"tag":57,"props":2142,"children":2144},{"id":2143},"common-pitfalls-and-solutions",[2145],{"type":48,"value":2146},"Common Pitfalls and Solutions",{"type":42,"tag":162,"props":2148,"children":2150},{"id":2149},"_1-path-normalization-issues",[2151],{"type":48,"value":2152},"1. Path Normalization Issues",{"type":42,"tag":51,"props":2154,"children":2155},{},[2156],{"type":48,"value":2157},"Different tools report paths differently (absolute, relative, URI-encoded):",{"type":42,"tag":149,"props":2159,"children":2161},{"className":825,"code":2160,"language":827,"meta":158,"style":158},"from urllib.parse import unquote\nfrom pathlib import Path\n\ndef normalize_path(uri: str, base_path: str = \"\") -> str:\n    \"\"\"Normalize SARIF artifact URI to consistent path.\"\"\"\n    # Remove file:\u002F\u002F prefix if present\n    if uri.startswith(\"file:\u002F\u002F\"):\n        uri = uri[7:]\n\n    # URL decode\n    uri = unquote(uri)\n\n    # Handle relative paths\n    if not Path(uri).is_absolute() and base_path:\n        uri = str(Path(base_path) \u002F uri)\n\n    # Normalize separators\n    return str(Path(uri))\n",[2162],{"type":42,"tag":156,"props":2163,"children":2164},{"__ignoreMap":158},[2165,2173,2180,2187,2195,2203,2211,2219,2227,2234,2242,2250,2257,2265,2273,2281,2288,2296],{"type":42,"tag":446,"props":2166,"children":2167},{"class":448,"line":449},[2168],{"type":42,"tag":446,"props":2169,"children":2170},{},[2171],{"type":48,"value":2172},"from urllib.parse import unquote\n",{"type":42,"tag":446,"props":2174,"children":2175},{"class":448,"line":459},[2176],{"type":42,"tag":446,"props":2177,"children":2178},{},[2179],{"type":48,"value":1408},{"type":42,"tag":446,"props":2181,"children":2182},{"class":448,"line":490},[2183],{"type":42,"tag":446,"props":2184,"children":2185},{"emptyLinePlaceholder":494},[2186],{"type":48,"value":497},{"type":42,"tag":446,"props":2188,"children":2189},{"class":448,"line":500},[2190],{"type":42,"tag":446,"props":2191,"children":2192},{},[2193],{"type":48,"value":2194},"def normalize_path(uri: str, base_path: str = \"\") -> str:\n",{"type":42,"tag":446,"props":2196,"children":2197},{"class":448,"line":509},[2198],{"type":42,"tag":446,"props":2199,"children":2200},{},[2201],{"type":48,"value":2202},"    \"\"\"Normalize SARIF artifact URI to consistent path.\"\"\"\n",{"type":42,"tag":446,"props":2204,"children":2205},{"class":448,"line":534},[2206],{"type":42,"tag":446,"props":2207,"children":2208},{},[2209],{"type":48,"value":2210},"    # Remove file:\u002F\u002F prefix if present\n",{"type":42,"tag":446,"props":2212,"children":2213},{"class":448,"line":542},[2214],{"type":42,"tag":446,"props":2215,"children":2216},{},[2217],{"type":48,"value":2218},"    if uri.startswith(\"file:\u002F\u002F\"):\n",{"type":42,"tag":446,"props":2220,"children":2221},{"class":448,"line":551},[2222],{"type":42,"tag":446,"props":2223,"children":2224},{},[2225],{"type":48,"value":2226},"        uri = uri[7:]\n",{"type":42,"tag":446,"props":2228,"children":2229},{"class":448,"line":576},[2230],{"type":42,"tag":446,"props":2231,"children":2232},{"emptyLinePlaceholder":494},[2233],{"type":48,"value":497},{"type":42,"tag":446,"props":2235,"children":2236},{"class":448,"line":584},[2237],{"type":42,"tag":446,"props":2238,"children":2239},{},[2240],{"type":48,"value":2241},"    # URL decode\n",{"type":42,"tag":446,"props":2243,"children":2244},{"class":448,"line":593},[2245],{"type":42,"tag":446,"props":2246,"children":2247},{},[2248],{"type":48,"value":2249},"    uri = unquote(uri)\n",{"type":42,"tag":446,"props":2251,"children":2252},{"class":448,"line":618},[2253],{"type":42,"tag":446,"props":2254,"children":2255},{"emptyLinePlaceholder":494},[2256],{"type":48,"value":497},{"type":42,"tag":446,"props":2258,"children":2259},{"class":448,"line":626},[2260],{"type":42,"tag":446,"props":2261,"children":2262},{},[2263],{"type":48,"value":2264},"    # Handle relative paths\n",{"type":42,"tag":446,"props":2266,"children":2267},{"class":448,"line":635},[2268],{"type":42,"tag":446,"props":2269,"children":2270},{},[2271],{"type":48,"value":2272},"    if not Path(uri).is_absolute() and base_path:\n",{"type":42,"tag":446,"props":2274,"children":2275},{"class":448,"line":652},[2276],{"type":42,"tag":446,"props":2277,"children":2278},{},[2279],{"type":48,"value":2280},"        uri = str(Path(base_path) \u002F uri)\n",{"type":42,"tag":446,"props":2282,"children":2283},{"class":448,"line":661},[2284],{"type":42,"tag":446,"props":2285,"children":2286},{"emptyLinePlaceholder":494},[2287],{"type":48,"value":497},{"type":42,"tag":446,"props":2289,"children":2290},{"class":448,"line":670},[2291],{"type":42,"tag":446,"props":2292,"children":2293},{},[2294],{"type":48,"value":2295},"    # Normalize separators\n",{"type":42,"tag":446,"props":2297,"children":2298},{"class":448,"line":679},[2299],{"type":42,"tag":446,"props":2300,"children":2301},{},[2302],{"type":48,"value":2303},"    return str(Path(uri))\n",{"type":42,"tag":162,"props":2305,"children":2307},{"id":2306},"_2-fingerprint-mismatch-across-runs",[2308],{"type":48,"value":2309},"2. Fingerprint Mismatch Across Runs",{"type":42,"tag":51,"props":2311,"children":2312},{},[2313],{"type":48,"value":2314},"Fingerprints may not match if:",{"type":42,"tag":69,"props":2316,"children":2317},{},[2318,2323,2328],{"type":42,"tag":73,"props":2319,"children":2320},{},[2321],{"type":48,"value":2322},"File paths differ between environments",{"type":42,"tag":73,"props":2324,"children":2325},{},[2326],{"type":48,"value":2327},"Tool versions changed fingerprinting algorithm",{"type":42,"tag":73,"props":2329,"children":2330},{},[2331],{"type":48,"value":2332},"Code was reformatted (changing line numbers)",{"type":42,"tag":51,"props":2334,"children":2335},{},[2336,2341],{"type":42,"tag":180,"props":2337,"children":2338},{},[2339],{"type":48,"value":2340},"Solution:",{"type":48,"value":2342}," Use multiple fingerprint strategies:",{"type":42,"tag":149,"props":2344,"children":2346},{"className":825,"code":2345,"language":827,"meta":158,"style":158},"def compute_stable_fingerprint(result: dict, file_content: str = None) -> str:\n    \"\"\"Compute environment-independent fingerprint.\"\"\"\n    import hashlib\n\n    components = [\n        result.get(\"ruleId\", \"\"),\n        result.get(\"message\", {}).get(\"text\", \"\")[:100],  # First 100 chars\n    ]\n\n    # Add code snippet if available\n    if file_content and result.get(\"locations\"):\n        region = result[\"locations\"][0].get(\"physicalLocation\", {}).get(\"region\", {})\n        if region.get(\"startLine\"):\n            lines = file_content.split(\"\\n\")\n            line_idx = region[\"startLine\"] - 1\n            if 0 \u003C= line_idx \u003C len(lines):\n                # Normalize whitespace\n                components.append(lines[line_idx].strip())\n\n    return hashlib.sha256(\"\".join(components).encode()).hexdigest()[:16]\n",[2347],{"type":42,"tag":156,"props":2348,"children":2349},{"__ignoreMap":158},[2350,2358,2366,2374,2381,2389,2397,2405,2413,2420,2428,2436,2444,2452,2460,2468,2476,2484,2492,2499],{"type":42,"tag":446,"props":2351,"children":2352},{"class":448,"line":449},[2353],{"type":42,"tag":446,"props":2354,"children":2355},{},[2356],{"type":48,"value":2357},"def compute_stable_fingerprint(result: dict, file_content: str = None) -> str:\n",{"type":42,"tag":446,"props":2359,"children":2360},{"class":448,"line":459},[2361],{"type":42,"tag":446,"props":2362,"children":2363},{},[2364],{"type":48,"value":2365},"    \"\"\"Compute environment-independent fingerprint.\"\"\"\n",{"type":42,"tag":446,"props":2367,"children":2368},{"class":448,"line":490},[2369],{"type":42,"tag":446,"props":2370,"children":2371},{},[2372],{"type":48,"value":2373},"    import hashlib\n",{"type":42,"tag":446,"props":2375,"children":2376},{"class":448,"line":500},[2377],{"type":42,"tag":446,"props":2378,"children":2379},{"emptyLinePlaceholder":494},[2380],{"type":48,"value":497},{"type":42,"tag":446,"props":2382,"children":2383},{"class":448,"line":509},[2384],{"type":42,"tag":446,"props":2385,"children":2386},{},[2387],{"type":48,"value":2388},"    components = [\n",{"type":42,"tag":446,"props":2390,"children":2391},{"class":448,"line":534},[2392],{"type":42,"tag":446,"props":2393,"children":2394},{},[2395],{"type":48,"value":2396},"        result.get(\"ruleId\", \"\"),\n",{"type":42,"tag":446,"props":2398,"children":2399},{"class":448,"line":542},[2400],{"type":42,"tag":446,"props":2401,"children":2402},{},[2403],{"type":48,"value":2404},"        result.get(\"message\", {}).get(\"text\", \"\")[:100],  # First 100 chars\n",{"type":42,"tag":446,"props":2406,"children":2407},{"class":448,"line":551},[2408],{"type":42,"tag":446,"props":2409,"children":2410},{},[2411],{"type":48,"value":2412},"    ]\n",{"type":42,"tag":446,"props":2414,"children":2415},{"class":448,"line":576},[2416],{"type":42,"tag":446,"props":2417,"children":2418},{"emptyLinePlaceholder":494},[2419],{"type":48,"value":497},{"type":42,"tag":446,"props":2421,"children":2422},{"class":448,"line":584},[2423],{"type":42,"tag":446,"props":2424,"children":2425},{},[2426],{"type":48,"value":2427},"    # Add code snippet if available\n",{"type":42,"tag":446,"props":2429,"children":2430},{"class":448,"line":593},[2431],{"type":42,"tag":446,"props":2432,"children":2433},{},[2434],{"type":48,"value":2435},"    if file_content and result.get(\"locations\"):\n",{"type":42,"tag":446,"props":2437,"children":2438},{"class":448,"line":618},[2439],{"type":42,"tag":446,"props":2440,"children":2441},{},[2442],{"type":48,"value":2443},"        region = result[\"locations\"][0].get(\"physicalLocation\", {}).get(\"region\", {})\n",{"type":42,"tag":446,"props":2445,"children":2446},{"class":448,"line":626},[2447],{"type":42,"tag":446,"props":2448,"children":2449},{},[2450],{"type":48,"value":2451},"        if region.get(\"startLine\"):\n",{"type":42,"tag":446,"props":2453,"children":2454},{"class":448,"line":635},[2455],{"type":42,"tag":446,"props":2456,"children":2457},{},[2458],{"type":48,"value":2459},"            lines = file_content.split(\"\\n\")\n",{"type":42,"tag":446,"props":2461,"children":2462},{"class":448,"line":652},[2463],{"type":42,"tag":446,"props":2464,"children":2465},{},[2466],{"type":48,"value":2467},"            line_idx = region[\"startLine\"] - 1\n",{"type":42,"tag":446,"props":2469,"children":2470},{"class":448,"line":661},[2471],{"type":42,"tag":446,"props":2472,"children":2473},{},[2474],{"type":48,"value":2475},"            if 0 \u003C= line_idx \u003C len(lines):\n",{"type":42,"tag":446,"props":2477,"children":2478},{"class":448,"line":670},[2479],{"type":42,"tag":446,"props":2480,"children":2481},{},[2482],{"type":48,"value":2483},"                # Normalize whitespace\n",{"type":42,"tag":446,"props":2485,"children":2486},{"class":448,"line":679},[2487],{"type":42,"tag":446,"props":2488,"children":2489},{},[2490],{"type":48,"value":2491},"                components.append(lines[line_idx].strip())\n",{"type":42,"tag":446,"props":2493,"children":2494},{"class":448,"line":688},[2495],{"type":42,"tag":446,"props":2496,"children":2497},{"emptyLinePlaceholder":494},[2498],{"type":48,"value":497},{"type":42,"tag":446,"props":2500,"children":2501},{"class":448,"line":705},[2502],{"type":42,"tag":446,"props":2503,"children":2504},{},[2505],{"type":48,"value":2506},"    return hashlib.sha256(\"\".join(components).encode()).hexdigest()[:16]\n",{"type":42,"tag":162,"props":2508,"children":2510},{"id":2509},"_3-missing-or-incomplete-data",[2511],{"type":48,"value":2512},"3. Missing or Incomplete Data",{"type":42,"tag":51,"props":2514,"children":2515},{},[2516],{"type":48,"value":2517},"SARIF allows many optional fields. Always use defensive access:",{"type":42,"tag":149,"props":2519,"children":2521},{"className":825,"code":2520,"language":827,"meta":158,"style":158},"def safe_get_location(result: dict) -> tuple[str, int]:\n    \"\"\"Safely extract file and line from result.\"\"\"\n    try:\n        loc = result.get(\"locations\", [{}])[0]\n        phys = loc.get(\"physicalLocation\", {})\n        file_path = phys.get(\"artifactLocation\", {}).get(\"uri\", \"unknown\")\n        line = phys.get(\"region\", {}).get(\"startLine\", 0)\n        return file_path, line\n    except (IndexError, KeyError, TypeError):\n        return \"unknown\", 0\n",[2522],{"type":42,"tag":156,"props":2523,"children":2524},{"__ignoreMap":158},[2525,2533,2541,2549,2557,2565,2573,2581,2589,2597],{"type":42,"tag":446,"props":2526,"children":2527},{"class":448,"line":449},[2528],{"type":42,"tag":446,"props":2529,"children":2530},{},[2531],{"type":48,"value":2532},"def safe_get_location(result: dict) -> tuple[str, int]:\n",{"type":42,"tag":446,"props":2534,"children":2535},{"class":448,"line":459},[2536],{"type":42,"tag":446,"props":2537,"children":2538},{},[2539],{"type":48,"value":2540},"    \"\"\"Safely extract file and line from result.\"\"\"\n",{"type":42,"tag":446,"props":2542,"children":2543},{"class":448,"line":490},[2544],{"type":42,"tag":446,"props":2545,"children":2546},{},[2547],{"type":48,"value":2548},"    try:\n",{"type":42,"tag":446,"props":2550,"children":2551},{"class":448,"line":500},[2552],{"type":42,"tag":446,"props":2553,"children":2554},{},[2555],{"type":48,"value":2556},"        loc = result.get(\"locations\", [{}])[0]\n",{"type":42,"tag":446,"props":2558,"children":2559},{"class":448,"line":509},[2560],{"type":42,"tag":446,"props":2561,"children":2562},{},[2563],{"type":48,"value":2564},"        phys = loc.get(\"physicalLocation\", {})\n",{"type":42,"tag":446,"props":2566,"children":2567},{"class":448,"line":534},[2568],{"type":42,"tag":446,"props":2569,"children":2570},{},[2571],{"type":48,"value":2572},"        file_path = phys.get(\"artifactLocation\", {}).get(\"uri\", \"unknown\")\n",{"type":42,"tag":446,"props":2574,"children":2575},{"class":448,"line":542},[2576],{"type":42,"tag":446,"props":2577,"children":2578},{},[2579],{"type":48,"value":2580},"        line = phys.get(\"region\", {}).get(\"startLine\", 0)\n",{"type":42,"tag":446,"props":2582,"children":2583},{"class":448,"line":551},[2584],{"type":42,"tag":446,"props":2585,"children":2586},{},[2587],{"type":48,"value":2588},"        return file_path, line\n",{"type":42,"tag":446,"props":2590,"children":2591},{"class":448,"line":576},[2592],{"type":42,"tag":446,"props":2593,"children":2594},{},[2595],{"type":48,"value":2596},"    except (IndexError, KeyError, TypeError):\n",{"type":42,"tag":446,"props":2598,"children":2599},{"class":448,"line":584},[2600],{"type":42,"tag":446,"props":2601,"children":2602},{},[2603],{"type":48,"value":2604},"        return \"unknown\", 0\n",{"type":42,"tag":162,"props":2606,"children":2608},{"id":2607},"_4-large-file-performance",[2609],{"type":48,"value":2610},"4. Large File Performance",{"type":42,"tag":51,"props":2612,"children":2613},{},[2614],{"type":48,"value":2615},"For very large SARIF files (100MB+):",{"type":42,"tag":149,"props":2617,"children":2619},{"className":825,"code":2618,"language":827,"meta":158,"style":158},"import ijson  # pip install ijson\n\ndef stream_results(sarif_path: str):\n    \"\"\"Stream results without loading entire file.\"\"\"\n    with open(sarif_path, \"rb\") as f:\n        # Stream through results arrays\n        for result in ijson.items(f, \"runs.item.results.item\"):\n            yield result\n",[2620],{"type":42,"tag":156,"props":2621,"children":2622},{"__ignoreMap":158},[2623,2631,2638,2646,2654,2662,2670,2678],{"type":42,"tag":446,"props":2624,"children":2625},{"class":448,"line":449},[2626],{"type":42,"tag":446,"props":2627,"children":2628},{},[2629],{"type":48,"value":2630},"import ijson  # pip install ijson\n",{"type":42,"tag":446,"props":2632,"children":2633},{"class":448,"line":459},[2634],{"type":42,"tag":446,"props":2635,"children":2636},{"emptyLinePlaceholder":494},[2637],{"type":48,"value":497},{"type":42,"tag":446,"props":2639,"children":2640},{"class":448,"line":490},[2641],{"type":42,"tag":446,"props":2642,"children":2643},{},[2644],{"type":48,"value":2645},"def stream_results(sarif_path: str):\n",{"type":42,"tag":446,"props":2647,"children":2648},{"class":448,"line":500},[2649],{"type":42,"tag":446,"props":2650,"children":2651},{},[2652],{"type":48,"value":2653},"    \"\"\"Stream results without loading entire file.\"\"\"\n",{"type":42,"tag":446,"props":2655,"children":2656},{"class":448,"line":509},[2657],{"type":42,"tag":446,"props":2658,"children":2659},{},[2660],{"type":48,"value":2661},"    with open(sarif_path, \"rb\") as f:\n",{"type":42,"tag":446,"props":2663,"children":2664},{"class":448,"line":534},[2665],{"type":42,"tag":446,"props":2666,"children":2667},{},[2668],{"type":48,"value":2669},"        # Stream through results arrays\n",{"type":42,"tag":446,"props":2671,"children":2672},{"class":448,"line":542},[2673],{"type":42,"tag":446,"props":2674,"children":2675},{},[2676],{"type":48,"value":2677},"        for result in ijson.items(f, \"runs.item.results.item\"):\n",{"type":42,"tag":446,"props":2679,"children":2680},{"class":448,"line":551},[2681],{"type":42,"tag":446,"props":2682,"children":2683},{},[2684],{"type":48,"value":2685},"            yield result\n",{"type":42,"tag":162,"props":2687,"children":2689},{"id":2688},"_5-schema-validation",[2690],{"type":48,"value":2691},"5. Schema Validation",{"type":42,"tag":51,"props":2693,"children":2694},{},[2695],{"type":48,"value":2696},"Validate before processing to catch malformed files:",{"type":42,"tag":149,"props":2698,"children":2700},{"className":438,"code":2699,"language":440,"meta":158,"style":158},"# Using ajv-cli\nnpm install -g ajv-cli\najv validate -s sarif-schema-2.1.0.json -d results.sarif\n\n# Using Python jsonschema\npip install jsonschema\n",[2701],{"type":42,"tag":156,"props":2702,"children":2703},{"__ignoreMap":158},[2704,2712,2735,2767,2774,2782],{"type":42,"tag":446,"props":2705,"children":2706},{"class":448,"line":449},[2707],{"type":42,"tag":446,"props":2708,"children":2709},{"style":453},[2710],{"type":48,"value":2711},"# Using ajv-cli\n",{"type":42,"tag":446,"props":2713,"children":2714},{"class":448,"line":459},[2715,2720,2725,2730],{"type":42,"tag":446,"props":2716,"children":2717},{"style":463},[2718],{"type":48,"value":2719},"npm",{"type":42,"tag":446,"props":2721,"children":2722},{"style":474},[2723],{"type":48,"value":2724}," install",{"type":42,"tag":446,"props":2726,"children":2727},{"style":474},[2728],{"type":48,"value":2729}," -g",{"type":42,"tag":446,"props":2731,"children":2732},{"style":474},[2733],{"type":48,"value":2734}," ajv-cli\n",{"type":42,"tag":446,"props":2736,"children":2737},{"class":448,"line":490},[2738,2743,2748,2753,2758,2763],{"type":42,"tag":446,"props":2739,"children":2740},{"style":463},[2741],{"type":48,"value":2742},"ajv",{"type":42,"tag":446,"props":2744,"children":2745},{"style":474},[2746],{"type":48,"value":2747}," validate",{"type":42,"tag":446,"props":2749,"children":2750},{"style":474},[2751],{"type":48,"value":2752}," -s",{"type":42,"tag":446,"props":2754,"children":2755},{"style":474},[2756],{"type":48,"value":2757}," sarif-schema-2.1.0.json",{"type":42,"tag":446,"props":2759,"children":2760},{"style":474},[2761],{"type":48,"value":2762}," -d",{"type":42,"tag":446,"props":2764,"children":2765},{"style":474},[2766],{"type":48,"value":487},{"type":42,"tag":446,"props":2768,"children":2769},{"class":448,"line":500},[2770],{"type":42,"tag":446,"props":2771,"children":2772},{"emptyLinePlaceholder":494},[2773],{"type":48,"value":497},{"type":42,"tag":446,"props":2775,"children":2776},{"class":448,"line":509},[2777],{"type":42,"tag":446,"props":2778,"children":2779},{"style":453},[2780],{"type":48,"value":2781},"# Using Python jsonschema\n",{"type":42,"tag":446,"props":2783,"children":2784},{"class":448,"line":534},[2785,2790,2794],{"type":42,"tag":446,"props":2786,"children":2787},{"style":463},[2788],{"type":48,"value":2789},"pip",{"type":42,"tag":446,"props":2791,"children":2792},{"style":474},[2793],{"type":48,"value":2724},{"type":42,"tag":446,"props":2795,"children":2796},{"style":474},[2797],{"type":48,"value":2798}," jsonschema\n",{"type":42,"tag":149,"props":2800,"children":2802},{"className":825,"code":2801,"language":827,"meta":158,"style":158},"from jsonschema import validate, ValidationError\nimport json\n\ndef validate_sarif(sarif_path: str, schema_path: str) -> bool:\n    \"\"\"Validate SARIF file against schema.\"\"\"\n    with open(sarif_path) as f:\n        sarif = json.load(f)\n    with open(schema_path) as f:\n        schema = json.load(f)\n\n    try:\n        validate(sarif, schema)\n        return True\n    except ValidationError as e:\n        print(f\"Validation error: {e.message}\")\n        return False\n",[2803],{"type":42,"tag":156,"props":2804,"children":2805},{"__ignoreMap":158},[2806,2814,2821,2828,2836,2844,2851,2858,2866,2874,2881,2888,2896,2904,2912,2920],{"type":42,"tag":446,"props":2807,"children":2808},{"class":448,"line":449},[2809],{"type":42,"tag":446,"props":2810,"children":2811},{},[2812],{"type":48,"value":2813},"from jsonschema import validate, ValidationError\n",{"type":42,"tag":446,"props":2815,"children":2816},{"class":448,"line":459},[2817],{"type":42,"tag":446,"props":2818,"children":2819},{},[2820],{"type":48,"value":1400},{"type":42,"tag":446,"props":2822,"children":2823},{"class":448,"line":490},[2824],{"type":42,"tag":446,"props":2825,"children":2826},{"emptyLinePlaceholder":494},[2827],{"type":48,"value":497},{"type":42,"tag":446,"props":2829,"children":2830},{"class":448,"line":500},[2831],{"type":42,"tag":446,"props":2832,"children":2833},{},[2834],{"type":48,"value":2835},"def validate_sarif(sarif_path: str, schema_path: str) -> bool:\n",{"type":42,"tag":446,"props":2837,"children":2838},{"class":448,"line":509},[2839],{"type":42,"tag":446,"props":2840,"children":2841},{},[2842],{"type":48,"value":2843},"    \"\"\"Validate SARIF file against schema.\"\"\"\n",{"type":42,"tag":446,"props":2845,"children":2846},{"class":448,"line":534},[2847],{"type":42,"tag":446,"props":2848,"children":2849},{},[2850],{"type":48,"value":1937},{"type":42,"tag":446,"props":2852,"children":2853},{"class":448,"line":542},[2854],{"type":42,"tag":446,"props":2855,"children":2856},{},[2857],{"type":48,"value":1945},{"type":42,"tag":446,"props":2859,"children":2860},{"class":448,"line":551},[2861],{"type":42,"tag":446,"props":2862,"children":2863},{},[2864],{"type":48,"value":2865},"    with open(schema_path) as f:\n",{"type":42,"tag":446,"props":2867,"children":2868},{"class":448,"line":576},[2869],{"type":42,"tag":446,"props":2870,"children":2871},{},[2872],{"type":48,"value":2873},"        schema = json.load(f)\n",{"type":42,"tag":446,"props":2875,"children":2876},{"class":448,"line":584},[2877],{"type":42,"tag":446,"props":2878,"children":2879},{"emptyLinePlaceholder":494},[2880],{"type":48,"value":497},{"type":42,"tag":446,"props":2882,"children":2883},{"class":448,"line":593},[2884],{"type":42,"tag":446,"props":2885,"children":2886},{},[2887],{"type":48,"value":2548},{"type":42,"tag":446,"props":2889,"children":2890},{"class":448,"line":618},[2891],{"type":42,"tag":446,"props":2892,"children":2893},{},[2894],{"type":48,"value":2895},"        validate(sarif, schema)\n",{"type":42,"tag":446,"props":2897,"children":2898},{"class":448,"line":626},[2899],{"type":42,"tag":446,"props":2900,"children":2901},{},[2902],{"type":48,"value":2903},"        return True\n",{"type":42,"tag":446,"props":2905,"children":2906},{"class":448,"line":635},[2907],{"type":42,"tag":446,"props":2908,"children":2909},{},[2910],{"type":48,"value":2911},"    except ValidationError as e:\n",{"type":42,"tag":446,"props":2913,"children":2914},{"class":448,"line":652},[2915],{"type":42,"tag":446,"props":2916,"children":2917},{},[2918],{"type":48,"value":2919},"        print(f\"Validation error: {e.message}\")\n",{"type":42,"tag":446,"props":2921,"children":2922},{"class":448,"line":661},[2923],{"type":42,"tag":446,"props":2924,"children":2925},{},[2926],{"type":48,"value":2927},"        return False\n",{"type":42,"tag":57,"props":2929,"children":2931},{"id":2930},"cicd-integration-patterns",[2932],{"type":48,"value":2933},"CI\u002FCD Integration Patterns",{"type":42,"tag":162,"props":2935,"children":2937},{"id":2936},"github-actions",[2938],{"type":48,"value":2939},"GitHub Actions",{"type":42,"tag":149,"props":2941,"children":2945},{"className":2942,"code":2943,"language":2944,"meta":158,"style":158},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","- name: Upload SARIF\n  uses: github\u002Fcodeql-action\u002Fupload-sarif@v3\n  with:\n    sarif_file: results.sarif\n\n- name: Check for high severity\n  run: |\n    HIGH_COUNT=$(jq '[.runs[].results[] | select(.level == \"error\")] | length' results.sarif)\n    if [ \"$HIGH_COUNT\" -gt 0 ]; then\n      echo \"Found $HIGH_COUNT high severity issues\"\n      exit 1\n    fi\n","yaml",[2946],{"type":42,"tag":156,"props":2947,"children":2948},{"__ignoreMap":158},[2949,2973,2990,3003,3019,3026,3046,3064,3072,3080,3088,3096],{"type":42,"tag":446,"props":2950,"children":2951},{"class":448,"line":449},[2952,2957,2963,2968],{"type":42,"tag":446,"props":2953,"children":2954},{"style":468},[2955],{"type":48,"value":2956},"-",{"type":42,"tag":446,"props":2958,"children":2960},{"style":2959},"--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178",[2961],{"type":48,"value":2962}," name",{"type":42,"tag":446,"props":2964,"children":2965},{"style":468},[2966],{"type":48,"value":2967},":",{"type":42,"tag":446,"props":2969,"children":2970},{"style":474},[2971],{"type":48,"value":2972}," Upload SARIF\n",{"type":42,"tag":446,"props":2974,"children":2975},{"class":448,"line":459},[2976,2981,2985],{"type":42,"tag":446,"props":2977,"children":2978},{"style":2959},[2979],{"type":48,"value":2980},"  uses",{"type":42,"tag":446,"props":2982,"children":2983},{"style":468},[2984],{"type":48,"value":2967},{"type":42,"tag":446,"props":2986,"children":2987},{"style":474},[2988],{"type":48,"value":2989}," github\u002Fcodeql-action\u002Fupload-sarif@v3\n",{"type":42,"tag":446,"props":2991,"children":2992},{"class":448,"line":490},[2993,2998],{"type":42,"tag":446,"props":2994,"children":2995},{"style":2959},[2996],{"type":48,"value":2997},"  with",{"type":42,"tag":446,"props":2999,"children":3000},{"style":468},[3001],{"type":48,"value":3002},":\n",{"type":42,"tag":446,"props":3004,"children":3005},{"class":448,"line":500},[3006,3011,3015],{"type":42,"tag":446,"props":3007,"children":3008},{"style":2959},[3009],{"type":48,"value":3010},"    sarif_file",{"type":42,"tag":446,"props":3012,"children":3013},{"style":468},[3014],{"type":48,"value":2967},{"type":42,"tag":446,"props":3016,"children":3017},{"style":474},[3018],{"type":48,"value":487},{"type":42,"tag":446,"props":3020,"children":3021},{"class":448,"line":509},[3022],{"type":42,"tag":446,"props":3023,"children":3024},{"emptyLinePlaceholder":494},[3025],{"type":48,"value":497},{"type":42,"tag":446,"props":3027,"children":3028},{"class":448,"line":534},[3029,3033,3037,3041],{"type":42,"tag":446,"props":3030,"children":3031},{"style":468},[3032],{"type":48,"value":2956},{"type":42,"tag":446,"props":3034,"children":3035},{"style":2959},[3036],{"type":48,"value":2962},{"type":42,"tag":446,"props":3038,"children":3039},{"style":468},[3040],{"type":48,"value":2967},{"type":42,"tag":446,"props":3042,"children":3043},{"style":474},[3044],{"type":48,"value":3045}," Check for high severity\n",{"type":42,"tag":446,"props":3047,"children":3048},{"class":448,"line":542},[3049,3054,3058],{"type":42,"tag":446,"props":3050,"children":3051},{"style":2959},[3052],{"type":48,"value":3053},"  run",{"type":42,"tag":446,"props":3055,"children":3056},{"style":468},[3057],{"type":48,"value":2967},{"type":42,"tag":446,"props":3059,"children":3061},{"style":3060},"--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic",[3062],{"type":48,"value":3063}," |\n",{"type":42,"tag":446,"props":3065,"children":3066},{"class":448,"line":551},[3067],{"type":42,"tag":446,"props":3068,"children":3069},{"style":474},[3070],{"type":48,"value":3071},"    HIGH_COUNT=$(jq '[.runs[].results[] | select(.level == \"error\")] | length' results.sarif)\n",{"type":42,"tag":446,"props":3073,"children":3074},{"class":448,"line":576},[3075],{"type":42,"tag":446,"props":3076,"children":3077},{"style":474},[3078],{"type":48,"value":3079},"    if [ \"$HIGH_COUNT\" -gt 0 ]; then\n",{"type":42,"tag":446,"props":3081,"children":3082},{"class":448,"line":584},[3083],{"type":42,"tag":446,"props":3084,"children":3085},{"style":474},[3086],{"type":48,"value":3087},"      echo \"Found $HIGH_COUNT high severity issues\"\n",{"type":42,"tag":446,"props":3089,"children":3090},{"class":448,"line":593},[3091],{"type":42,"tag":446,"props":3092,"children":3093},{"style":474},[3094],{"type":48,"value":3095},"      exit 1\n",{"type":42,"tag":446,"props":3097,"children":3098},{"class":448,"line":618},[3099],{"type":42,"tag":446,"props":3100,"children":3101},{"style":474},[3102],{"type":48,"value":3103},"    fi\n",{"type":42,"tag":162,"props":3105,"children":3107},{"id":3106},"fail-on-new-issues",[3108],{"type":48,"value":3109},"Fail on New Issues",{"type":42,"tag":149,"props":3111,"children":3113},{"className":825,"code":3112,"language":827,"meta":158,"style":158},"from sarif import loader\n\ndef check_for_regressions(baseline: str, current: str) -> int:\n    \"\"\"Return count of new issues not in baseline.\"\"\"\n    baseline_data = loader.load_sarif_file(baseline)\n    current_data = loader.load_sarif_file(current)\n\n    baseline_fps = {get_fingerprint(r) for r in baseline_data.get_results()}\n    new_issues = [r for r in current_data.get_results()\n                  if get_fingerprint(r) not in baseline_fps]\n\n    return len(new_issues)\n",[3114],{"type":42,"tag":156,"props":3115,"children":3116},{"__ignoreMap":158},[3117,3124,3131,3139,3147,3155,3163,3170,3178,3186,3194,3201],{"type":42,"tag":446,"props":3118,"children":3119},{"class":448,"line":449},[3120],{"type":42,"tag":446,"props":3121,"children":3122},{},[3123],{"type":48,"value":1028},{"type":42,"tag":446,"props":3125,"children":3126},{"class":448,"line":459},[3127],{"type":42,"tag":446,"props":3128,"children":3129},{"emptyLinePlaceholder":494},[3130],{"type":48,"value":497},{"type":42,"tag":446,"props":3132,"children":3133},{"class":448,"line":490},[3134],{"type":42,"tag":446,"props":3135,"children":3136},{},[3137],{"type":48,"value":3138},"def check_for_regressions(baseline: str, current: str) -> int:\n",{"type":42,"tag":446,"props":3140,"children":3141},{"class":448,"line":500},[3142],{"type":42,"tag":446,"props":3143,"children":3144},{},[3145],{"type":48,"value":3146},"    \"\"\"Return count of new issues not in baseline.\"\"\"\n",{"type":42,"tag":446,"props":3148,"children":3149},{"class":448,"line":509},[3150],{"type":42,"tag":446,"props":3151,"children":3152},{},[3153],{"type":48,"value":3154},"    baseline_data = loader.load_sarif_file(baseline)\n",{"type":42,"tag":446,"props":3156,"children":3157},{"class":448,"line":534},[3158],{"type":42,"tag":446,"props":3159,"children":3160},{},[3161],{"type":48,"value":3162},"    current_data = loader.load_sarif_file(current)\n",{"type":42,"tag":446,"props":3164,"children":3165},{"class":448,"line":542},[3166],{"type":42,"tag":446,"props":3167,"children":3168},{"emptyLinePlaceholder":494},[3169],{"type":48,"value":497},{"type":42,"tag":446,"props":3171,"children":3172},{"class":448,"line":551},[3173],{"type":42,"tag":446,"props":3174,"children":3175},{},[3176],{"type":48,"value":3177},"    baseline_fps = {get_fingerprint(r) for r in baseline_data.get_results()}\n",{"type":42,"tag":446,"props":3179,"children":3180},{"class":448,"line":576},[3181],{"type":42,"tag":446,"props":3182,"children":3183},{},[3184],{"type":48,"value":3185},"    new_issues = [r for r in current_data.get_results()\n",{"type":42,"tag":446,"props":3187,"children":3188},{"class":448,"line":584},[3189],{"type":42,"tag":446,"props":3190,"children":3191},{},[3192],{"type":48,"value":3193},"                  if get_fingerprint(r) not in baseline_fps]\n",{"type":42,"tag":446,"props":3195,"children":3196},{"class":448,"line":593},[3197],{"type":42,"tag":446,"props":3198,"children":3199},{"emptyLinePlaceholder":494},[3200],{"type":48,"value":497},{"type":42,"tag":446,"props":3202,"children":3203},{"class":448,"line":618},[3204],{"type":42,"tag":446,"props":3205,"children":3206},{},[3207],{"type":48,"value":3208},"    return len(new_issues)\n",{"type":42,"tag":57,"props":3210,"children":3212},{"id":3211},"key-principles",[3213],{"type":48,"value":3214},"Key Principles",{"type":42,"tag":3216,"props":3217,"children":3218},"ol",{},[3219,3229,3239,3249,3259,3269],{"type":42,"tag":73,"props":3220,"children":3221},{},[3222,3227],{"type":42,"tag":180,"props":3223,"children":3224},{},[3225],{"type":48,"value":3226},"Validate first",{"type":48,"value":3228},": Check SARIF structure before processing",{"type":42,"tag":73,"props":3230,"children":3231},{},[3232,3237],{"type":42,"tag":180,"props":3233,"children":3234},{},[3235],{"type":48,"value":3236},"Handle optionals",{"type":48,"value":3238},": Many fields are optional; use defensive access",{"type":42,"tag":73,"props":3240,"children":3241},{},[3242,3247],{"type":42,"tag":180,"props":3243,"children":3244},{},[3245],{"type":48,"value":3246},"Normalize paths",{"type":48,"value":3248},": Tools report paths differently; normalize early",{"type":42,"tag":73,"props":3250,"children":3251},{},[3252,3257],{"type":42,"tag":180,"props":3253,"children":3254},{},[3255],{"type":48,"value":3256},"Fingerprint wisely",{"type":48,"value":3258},": Combine multiple strategies for stable deduplication",{"type":42,"tag":73,"props":3260,"children":3261},{},[3262,3267],{"type":42,"tag":180,"props":3263,"children":3264},{},[3265],{"type":48,"value":3266},"Stream large files",{"type":48,"value":3268},": Use ijson or similar for 100MB+ files",{"type":42,"tag":73,"props":3270,"children":3271},{},[3272,3277],{"type":42,"tag":180,"props":3273,"children":3274},{},[3275],{"type":48,"value":3276},"Aggregate thoughtfully",{"type":48,"value":3278},": Preserve tool metadata when combining files",{"type":42,"tag":57,"props":3280,"children":3282},{"id":3281},"skill-resources",[3283],{"type":48,"value":3284},"Skill Resources",{"type":42,"tag":51,"props":3286,"children":3287},{},[3288,3290,3297],{"type":48,"value":3289},"For ready-to-use query templates, see ",{"type":42,"tag":3291,"props":3292,"children":3294},"a",{"href":3293},"%7BbaseDir%7D\u002Fresources\u002Fjq-queries.md",[3295],{"type":48,"value":3296},"{baseDir}\u002Fresources\u002Fjq-queries.md",{"type":48,"value":2967},{"type":42,"tag":69,"props":3299,"children":3300},{},[3301,3306],{"type":42,"tag":73,"props":3302,"children":3303},{},[3304],{"type":48,"value":3305},"40+ jq queries for common SARIF operations",{"type":42,"tag":73,"props":3307,"children":3308},{},[3309],{"type":48,"value":3310},"Severity filtering, rule extraction, aggregation patterns",{"type":42,"tag":51,"props":3312,"children":3313},{},[3314,3316,3322],{"type":48,"value":3315},"For Python utilities, see ",{"type":42,"tag":3291,"props":3317,"children":3319},{"href":3318},"%7BbaseDir%7D\u002Fresources\u002Fsarif_helpers.py",[3320],{"type":48,"value":3321},"{baseDir}\u002Fresources\u002Fsarif_helpers.py",{"type":48,"value":2967},{"type":42,"tag":69,"props":3324,"children":3325},{},[3326,3337,3348],{"type":42,"tag":73,"props":3327,"children":3328},{},[3329,3335],{"type":42,"tag":156,"props":3330,"children":3332},{"className":3331},[],[3333],{"type":48,"value":3334},"normalize_path()",{"type":48,"value":3336}," - Handle tool-specific path formats",{"type":42,"tag":73,"props":3338,"children":3339},{},[3340,3346],{"type":42,"tag":156,"props":3341,"children":3343},{"className":3342},[],[3344],{"type":48,"value":3345},"compute_fingerprint()",{"type":48,"value":3347}," - Stable fingerprinting ignoring paths",{"type":42,"tag":73,"props":3349,"children":3350},{},[3351,3357],{"type":42,"tag":156,"props":3352,"children":3354},{"className":3353},[],[3355],{"type":48,"value":3356},"deduplicate_results()",{"type":48,"value":3358}," - Remove duplicates across runs",{"type":42,"tag":57,"props":3360,"children":3362},{"id":3361},"reference-links",[3363],{"type":48,"value":3364},"Reference Links",{"type":42,"tag":69,"props":3366,"children":3367},{},[3368,3379,3389,3399,3409,3419,3429],{"type":42,"tag":73,"props":3369,"children":3370},{},[3371],{"type":42,"tag":3291,"props":3372,"children":3376},{"href":3373,"rel":3374},"https:\u002F\u002Fdocs.oasis-open.org\u002Fsarif\u002Fsarif\u002Fv2.1.0\u002Fsarif-v2.1.0.html",[3375],"nofollow",[3377],{"type":48,"value":3378},"OASIS SARIF 2.1.0 Specification",{"type":42,"tag":73,"props":3380,"children":3381},{},[3382],{"type":42,"tag":3291,"props":3383,"children":3386},{"href":3384,"rel":3385},"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fsarif-tutorials",[3375],[3387],{"type":48,"value":3388},"Microsoft SARIF Tutorials",{"type":42,"tag":73,"props":3390,"children":3391},{},[3392],{"type":42,"tag":3291,"props":3393,"children":3396},{"href":3394,"rel":3395},"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fsarif-sdk",[3375],[3397],{"type":48,"value":3398},"SARIF SDK (.NET)",{"type":42,"tag":73,"props":3400,"children":3401},{},[3402],{"type":42,"tag":3291,"props":3403,"children":3406},{"href":3404,"rel":3405},"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fsarif-tools",[3375],[3407],{"type":48,"value":3408},"sarif-tools (Python)",{"type":42,"tag":73,"props":3410,"children":3411},{},[3412],{"type":42,"tag":3291,"props":3413,"children":3416},{"href":3414,"rel":3415},"https:\u002F\u002Fgithub.com\u002FKjeld-P\u002Fpysarif",[3375],[3417],{"type":48,"value":3418},"pysarif (Python)",{"type":42,"tag":73,"props":3420,"children":3421},{},[3422],{"type":42,"tag":3291,"props":3423,"children":3426},{"href":3424,"rel":3425},"https:\u002F\u002Fdocs.github.com\u002Fen\u002Fcode-security\u002Fcode-scanning\u002Fintegrating-with-code-scanning\u002Fsarif-support-for-code-scanning",[3375],[3427],{"type":48,"value":3428},"GitHub SARIF Support",{"type":42,"tag":73,"props":3430,"children":3431},{},[3432],{"type":42,"tag":3291,"props":3433,"children":3436},{"href":3434,"rel":3435},"https:\u002F\u002Fsarifweb.azurewebsites.net\u002F",[3375],[3437],{"type":48,"value":419},{"type":42,"tag":3439,"props":3440,"children":3441},"style",{},[3442],{"type":48,"value":3443},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":3445,"total":3536},[3446,3463,3473,3489,3502,3515,3526],{"slug":3447,"name":3447,"fn":3448,"description":3449,"org":3450,"tags":3451,"stars":23,"repoUrl":24,"updatedAt":3462},"address-sanitizer","detect memory errors during fuzzing","AddressSanitizer detects memory errors during fuzzing. Use when fuzzing C\u002FC++ code to find buffer overflows and use-after-free bugs.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3452,3455,3458,3459],{"name":3453,"slug":3454,"type":16},"C#","c",{"name":3456,"slug":3457,"type":16},"Debugging","debugging",{"name":14,"slug":15,"type":16},{"name":3460,"slug":3461,"type":16},"Testing","testing","2026-07-17T06:05:14.925095",{"slug":3464,"name":3464,"fn":3465,"description":3466,"org":3467,"tags":3468,"stars":23,"repoUrl":24,"updatedAt":3472},"aflpp","perform multi-core fuzzing of C\u002FC++ projects","AFL++ is a fork of AFL with better fuzzing performance and advanced features. Use for multi-core fuzzing of C\u002FC++ projects.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3469,3470,3471],{"name":3453,"slug":3454,"type":16},{"name":14,"slug":15,"type":16},{"name":3460,"slug":3461,"type":16},"2026-07-17T06:05:12.433192",{"slug":3474,"name":3474,"fn":3475,"description":3476,"org":3477,"tags":3478,"stars":23,"repoUrl":24,"updatedAt":3488},"agentic-actions-auditor","audit GitHub Actions for security vulnerabilities","Audits GitHub Actions workflows for security vulnerabilities in AI agent integrations including Claude Code Action, Gemini CLI, OpenAI Codex, and GitHub AI Inference. Detects attack vectors where attacker-controlled input reaches AI agents running in CI\u002FCD pipelines, including env var intermediary patterns, direct expression injection, dangerous sandbox configurations, and wildcard user allowlists. Use when reviewing workflow files that invoke AI coding agents, auditing CI\u002FCD pipeline security for prompt injection risks, or evaluating agentic action configurations.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3479,3482,3485,3486,3487],{"name":3480,"slug":3481,"type":16},"Agents","agents",{"name":3483,"slug":3484,"type":16},"CI\u002FCD","ci-cd",{"name":21,"slug":22,"type":16},{"name":2939,"slug":2936,"type":16},{"name":14,"slug":15,"type":16},"2026-07-18T05:47:48.564744",{"slug":3490,"name":3490,"fn":3491,"description":3492,"org":3493,"tags":3494,"stars":23,"repoUrl":24,"updatedAt":3501},"algorand-vulnerability-scanner","scan Algorand smart contracts for vulnerabilities","Scans Algorand smart contracts for 11 common vulnerabilities including rekeying attacks, unchecked transaction fees, missing field validations, and access control issues. Use when auditing Algorand projects (TEAL\u002FPyTeal).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3495,3496,3497,3498],{"name":18,"slug":19,"type":16},{"name":21,"slug":22,"type":16},{"name":14,"slug":15,"type":16},{"name":3499,"slug":3500,"type":16},"Smart Contracts","smart-contracts","2026-07-18T05:47:43.989063",{"slug":3503,"name":3503,"fn":3504,"description":3505,"org":3506,"tags":3507,"stars":23,"repoUrl":24,"updatedAt":3514},"ask-questions-if-underspecified","clarify requirements before implementation","Clarify requirements before implementing. Use when serious doubts arise.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3508,3511],{"name":3509,"slug":3510,"type":16},"Engineering","engineering",{"name":3512,"slug":3513,"type":16},"Productivity","productivity","2026-07-17T06:05:33.543262",{"slug":3516,"name":3516,"fn":3517,"description":3518,"org":3519,"tags":3520,"stars":23,"repoUrl":24,"updatedAt":3525},"atheris","fuzz Python code with Atheris","Atheris is a coverage-guided Python fuzzer based on libFuzzer. Use for fuzzing pure Python code and Python C extensions.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3521,3523,3524],{"name":3522,"slug":827,"type":16},"Python",{"name":14,"slug":15,"type":16},{"name":3460,"slug":3461,"type":16},"2026-07-17T06:05:14.575191",{"slug":3527,"name":3527,"fn":3528,"description":3529,"org":3530,"tags":3531,"stars":23,"repoUrl":24,"updatedAt":3535},"audit-augmentation","augment code graphs with audit findings","Augments Trailmark code graphs with external audit findings from SARIF static analysis results, weAudit annotation files, and version-gated Trailmark 0.4.x binary-analysis graph exports. Maps findings to graph nodes by file and line overlap, creates severity-based subgraphs, and enables cross-referencing findings with pre-analysis data (blast radius, taint, etc.). Use when projecting SARIF results onto a code graph, overlaying weAudit annotations, importing binary graph findings, cross-referencing Semgrep, CodeQL, or binary-analysis findings with call graph data, or visualizing audit findings in the context of code structure.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3532,3533,3534],{"name":18,"slug":19,"type":16},{"name":21,"slug":22,"type":16},{"name":14,"slug":15,"type":16},"2026-08-01T05:44:54.920542",77,{"items":3538,"total":3642},[3539,3546,3552,3560,3567,3572,3578,3584,3597,3608,3620,3631],{"slug":3447,"name":3447,"fn":3448,"description":3449,"org":3540,"tags":3541,"stars":23,"repoUrl":24,"updatedAt":3462},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3542,3543,3544,3545],{"name":3453,"slug":3454,"type":16},{"name":3456,"slug":3457,"type":16},{"name":14,"slug":15,"type":16},{"name":3460,"slug":3461,"type":16},{"slug":3464,"name":3464,"fn":3465,"description":3466,"org":3547,"tags":3548,"stars":23,"repoUrl":24,"updatedAt":3472},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3549,3550,3551],{"name":3453,"slug":3454,"type":16},{"name":14,"slug":15,"type":16},{"name":3460,"slug":3461,"type":16},{"slug":3474,"name":3474,"fn":3475,"description":3476,"org":3553,"tags":3554,"stars":23,"repoUrl":24,"updatedAt":3488},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3555,3556,3557,3558,3559],{"name":3480,"slug":3481,"type":16},{"name":3483,"slug":3484,"type":16},{"name":21,"slug":22,"type":16},{"name":2939,"slug":2936,"type":16},{"name":14,"slug":15,"type":16},{"slug":3490,"name":3490,"fn":3491,"description":3492,"org":3561,"tags":3562,"stars":23,"repoUrl":24,"updatedAt":3501},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3563,3564,3565,3566],{"name":18,"slug":19,"type":16},{"name":21,"slug":22,"type":16},{"name":14,"slug":15,"type":16},{"name":3499,"slug":3500,"type":16},{"slug":3503,"name":3503,"fn":3504,"description":3505,"org":3568,"tags":3569,"stars":23,"repoUrl":24,"updatedAt":3514},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3570,3571],{"name":3509,"slug":3510,"type":16},{"name":3512,"slug":3513,"type":16},{"slug":3516,"name":3516,"fn":3517,"description":3518,"org":3573,"tags":3574,"stars":23,"repoUrl":24,"updatedAt":3525},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3575,3576,3577],{"name":3522,"slug":827,"type":16},{"name":14,"slug":15,"type":16},{"name":3460,"slug":3461,"type":16},{"slug":3527,"name":3527,"fn":3528,"description":3529,"org":3579,"tags":3580,"stars":23,"repoUrl":24,"updatedAt":3535},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3581,3582,3583],{"name":18,"slug":19,"type":16},{"name":21,"slug":22,"type":16},{"name":14,"slug":15,"type":16},{"slug":3585,"name":3585,"fn":3586,"description":3587,"org":3588,"tags":3589,"stars":23,"repoUrl":24,"updatedAt":3596},"audit-context-building","build architectural context for code analysis","Enables ultra-granular, line-by-line code analysis to build deep architectural context before vulnerability or bug finding.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3590,3593,3594,3595],{"name":3591,"slug":3592,"type":16},"Architecture","architecture",{"name":18,"slug":19,"type":16},{"name":21,"slug":22,"type":16},{"name":3509,"slug":3510,"type":16},"2026-07-18T05:47:40.122449",{"slug":3598,"name":3598,"fn":3599,"description":3600,"org":3601,"tags":3602,"stars":23,"repoUrl":24,"updatedAt":3607},"audit-prep-assistant","prepare codebases for security audits","Prepares codebases for security review using Trail of Bits' checklist. Helps set review goals, runs static analysis tools, increases test coverage, removes dead code, ensures accessibility, and generates documentation (flowcharts, user stories, inline comments).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3603,3604,3605,3606],{"name":18,"slug":19,"type":16},{"name":21,"slug":22,"type":16},{"name":3509,"slug":3510,"type":16},{"name":14,"slug":15,"type":16},"2026-07-18T05:47:39.210985",{"slug":3609,"name":3609,"fn":3610,"description":3611,"org":3612,"tags":3613,"stars":23,"repoUrl":24,"updatedAt":3619},"burpsuite-project-parser","parse Burp Suite project files","Searches and explores Burp Suite project files (.burp) from the command line. Use when searching response headers or bodies with regex patterns, extracting security audit findings, dumping proxy history or site map data, or analyzing HTTP traffic captured in a Burp project.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3614,3615,3618],{"name":18,"slug":19,"type":16},{"name":3616,"slug":3617,"type":16},"CLI","cli",{"name":14,"slug":15,"type":16},"2026-07-17T06:05:33.198077",{"slug":3621,"name":3621,"fn":3622,"description":3623,"org":3624,"tags":3625,"stars":23,"repoUrl":24,"updatedAt":3630},"c-review","audit C and C++ code","Performs comprehensive C\u002FC++ security review for memory corruption, integer overflows, race conditions, and platform-specific vulnerabilities. Use when auditing native C\u002FC++ applications, reviewing daemons or services for memory safety, or hunting integer overflow \u002F use-after-free \u002F race conditions in userspace code.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3626,3627,3628,3629],{"name":18,"slug":19,"type":16},{"name":3453,"slug":3454,"type":16},{"name":21,"slug":22,"type":16},{"name":14,"slug":15,"type":16},"2026-07-17T06:05:11.333374",{"slug":3632,"name":3632,"fn":3633,"description":3634,"org":3635,"tags":3636,"stars":23,"repoUrl":24,"updatedAt":3641},"cairo-vulnerability-scanner","scan Cairo and StarkNet contracts for vulnerabilities","Scans Cairo\u002FStarkNet smart contracts for 6 critical vulnerabilities including felt252 arithmetic overflow, L1-L2 messaging issues, address conversion problems, and signature replay. Use when auditing StarkNet projects.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3637,3638,3639,3640],{"name":18,"slug":19,"type":16},{"name":21,"slug":22,"type":16},{"name":14,"slug":15,"type":16},{"name":3499,"slug":3500,"type":16},"2026-07-18T05:47:42.84568",111]