Skip to content

feat: dashboard interactivity + Own.NET Auditor roadmap + SARIF exporter (phase 1) - #7

Merged
PhysShell merged 7 commits into
mainfrom
claude/sts-runtime-analysis-2mo4z9
Jun 26, 2026
Merged

feat: dashboard interactivity + Own.NET Auditor roadmap + SARIF exporter (phase 1)#7
PhysShell merged 7 commits into
mainfrom
claude/sts-runtime-analysis-2mo4z9

Conversation

@PhysShell

@PhysShell PhysShell commented Jun 26, 2026

Copy link
Copy Markdown
Owner

Что внутри

Три связанных трека, поверх смерженного #6.

1. Интерактивность дашборда (viz/build_dashboard.py)

Переработка статичных графиков в интерактивный вид — те четыре фичи, что обсуждались:

  • Фильтр по tool/category — чипы; все «живые» графики (категории, source, tool, rules, тиры) и таблица пересчитываются на клиенте. Находки встроены со стринговым интернингом (пути/правила/тулы/категории → int-индексы), поэтому фильтрация по 72k мгновенная, файл ~6.8MB.
  • Drill-down из модуля — клик по плитке treemap фильтрует на находки модуля (самый длинный сегмент-префикс) → таблица с file:line, правилом, бейджем тира, категорией, тулом.
  • Диаграмма fix-тиров (T1–T4)T1 auto / T2 review / T3 unfixable / T4 bespoke; тиры из настоящего fixarm.tiers.tier_of, чтобы дашборд и fix-arm не разъезжались. Фильтр-зависимая.
  • Тренд по прогонам — каждая сборка дописывает датированный снимок (total + по тирам) в viz/history.jsonl; график рисует ряд (пока одна точка + подпись).

Проверено headless (Chromium): 8 графиков, ноль ошибок; фильтр own-check → 380 (все T4), drill-down Broker → 8 986 находок.

2. Стратегические доки (docs/)

  • docs/audit-data-leverage.md — разбор «72k находок, какую ещё пользу извлечь»: переформулировка FP ≠ true-but-wontfix, почему FP-rate неизмерим без ground truth и трюк «размечать по правилу, а не случайно», каталог метрик, ранжированные побочные продукты. С реальными числами текущего прогона.
  • docs/own-net-auditor.md — дорожная карта «сделать из Own.NET аудитор уровня NDepend + Joern + WPF-инспектор», приземлённая на существующий OwnAudit: таблица «вижн → что уже есть → реальный гэп», три предупреждения (не переписывать семантику Roslyn; граф оправдывать архитектурой; runtime — Windows-only трек), фазовый план.

3. SARIF-экспортер (report/) — Фаза 1 дорожной карты

Чистый трансформ sts_audit/findings.json → SARIF 2.1.0 для GitHub code scanning, без .NET, тестируется в CI.

  • один run на тул (own-check / CodeQL / Infer# / Roslyn), дедуп правил в драйвере;
  • severity по категории (leaks → error, correctness/arch → warning, style → note);
  • line-independent partialFingerprints — GitHub коррелирует алерт между коммитами даже если строка съехала;
  • passthrough suppression, тир+категория в properties; тиры из fixarm.tiers;
  • --min-level / --max-results — экспорт по severity, а не вываливание 72k (полный лог 47MB > gzip-лимита GitHub; --min-level warning → 6 115 результатов / ~4MB);
  • CLI пишет ownnet-audit.sarif + metrics.json + report.md.

Test plan

PYTHONPATH=fix python3 fix/tests/test_ai_fix.py        # 9/9
PYTHONPATH=fix python3 fix/tests/test_own_fix.py       # 25/25
PYTHONPATH=fix python3 fix/tests/test_orchestrate.py   # 7/7
PYTHONPATH=.  python3 report/tests/test_sarif.py       # 9/9 (и под -O)
python3 viz/build_dashboard.py                         # -> viz/sts-dashboard.html
PYTHONPATH=.  python3 -m report.cli                    # -> report/out/{sarif,metrics,report}

Генерируемые артефакты (viz/sts-dashboard.html, report/out/) в .gitignore; viz/history.jsonl коммитится как трендовая база.

🤖 Generated with Claude Code


Generated by Claude Code

Summary by CodeRabbit

  • New Features
    • Added a command-line export that converts audit findings into GitHub Code Scanning SARIF plus a human-readable report.
    • Reworked the interactive dashboard with chip-based filtering/drilldown, tier-aware reporting, and updated embedded history.
  • Documentation
    • Added an audit-data leverage guide and a Russian roadmap for an incremental architecture-auditing workflow.
  • Bug Fixes
    • Prevented generated report artifacts from appearing as untracked version-control changes.
  • Tests
    • Added comprehensive tests covering SARIF output, suppression/min-severity/max-results behavior, and CLI exports.

claude added 4 commits June 25, 2026 23:28
…s-run trend

Reworks the dashboard from static server-rendered charts into an interactive view:

- Filter chips for tool and category; every live chart (category, source, tool,
  rules, tiers) and the findings table re-aggregate client-side. Per-finding rows
  are embedded with string interning (paths/rules/tools/cats -> int indices) so
  filtering over 72k findings stays cheap and the file stays ~6.8MB.
- Drill-down: click a module treemap tile to filter to its findings (longest
  segment-aligned module prefix), shown in a sortable-looking findings table with
  file:line, rule, tier badge, category, tool.
- Fix-tier chart (T1 auto / T2 review / T3 unfixable / T4 bespoke), tiers taken
  from the real fixarm.tiers.tier_of so the dashboard and the fix arm never drift.
- Trend across runs: each build appends a dated snapshot (total + per-tier) to
  viz/history.jsonl; the trend chart plots the series (one point until the next
  run, with a note).

Verified headless: 8 charts, no console errors; own-check filter -> 380 (all T4),
Broker drill-down -> 8,986 findings, tier totals T1 3935 / T2 58403 / T3 9851 /
T4 380.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LLHz4cevQyXHKZnjzCqhoa
…roducts)

Strategy note from the "we have 72k findings — what other value can we extract"
discussion. Covers: the FP-vs-true-but-wontfix reframe, why FP rate is unmeasurable
without ground truth and the per-rule-homogeneity trick that makes labeling cheap,
a metric catalog (free-now / needs-labeling / needs-trend), and ranked byproduct
options (rule-verdict sheet, churn hotspots, codemod mining; AI corpus parked,
public dataset advised against). Grounded in real numbers from the current run.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LLHz4cevQyXHKZnjzCqhoa
…eline

Turns the "make Own.NET an auditor like NDepend + Joern + WPF inspector" vision
into a phased plan over the EXISTING OwnAudit instead of a greenfield CLI. Maps
vision -> what already exists (findings.json, the audit orchestrator, fix-arm
diff_findings, history.jsonl, the dashboard) -> the real gap (architecture graph
+ SARIF/baseline output). Three guardrails: don't rebuild Roslyn's semantic
layer (graph = projection over symbols), justify the graph by cross-cutting
architecture not re-derived metrics, sequence the runtime track as Windows-only.
Phases: SARIF -> baseline/diff -> architecture-pass -> drift report -> runtime
correlation; DSL and the 40-metric zoo deferred.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LLHz4cevQyXHKZnjzCqhoa
…r phase 1)

First slice of the auditor roadmap: a pure transform over the existing
sts_audit/findings.json into SARIF — no .NET, testable in CI.

- report/sarif.py: one run per tool (own-check/CodeQL/Infer#/Roslyn), deduped
  rule set per driver, category->level mapping (leaks=error, correctness/arch=
  warning, style=note), startLine clamped, line-independent partialFingerprints
  so GitHub correlates an alert across edits, suppression passthrough, tier+
  category in result properties. Reuses fixarm.tiers.tier_of as the single
  source of truth for T1..T4.
- min_level / max_results_per_run so you export by severity instead of dumping
  72k alerts: the full log is 47MB (over GitHub's gzip limit), --min-level
  warning brings it to 6,115 results / ~4MB.
- report/cli.py: writes ownnet-audit.sarif + metrics.json + report.md.
- 9 tests (shape, rule dedup+index, level mapping, region clamp, fingerprint
  stability/line-independence, suppression, min_level, max_results, tier), green
  under python3 and python3 -O. report/out/ gitignored as a build artifact.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LLHz4cevQyXHKZnjzCqhoa
@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e3fe87dd-1649-4901-a318-090834c14b42

📥 Commits

Reviewing files that changed from the base of the PR and between 16f41f3 and 32ad096.

📒 Files selected for processing (2)
  • viz/build_dashboard.py
  • viz/history.jsonl
✅ Files skipped from review due to trivial changes (1)
  • viz/history.jsonl
🚧 Files skipped from review as they are similar to previous changes (1)
  • viz/build_dashboard.py

📝 Walkthrough

Walkthrough

Adds two planning documents, a SARIF export and CLI report generator, and a filterable dashboard with compact embedded data and history snapshots.

Changes

Planning documents

Layer / File(s) Summary
Audit-data leverage analysis
docs/audit-data-leverage.md
Defines aggregate-only constraints, FP-vs-wontfix framing, free metrics, FP-rate estimation approaches, and ranked secondary outputs for STS findings.
Own.NET Auditor roadmap
docs/own-net-auditor.md
Describes the phased Own.NET Auditor plan, existing gaps, constraints, target finding model, and metric readiness split.

SARIF export pipeline

Layer / File(s) Summary
SARIF export core
report/sarif.py
Builds SARIF 2.1.0 runs from findings, maps categories to levels, computes fingerprints, clamps locations, and applies suppression and truncation handling.
CLI outputs and ignore rules
report/cli.py, .gitignore
Parses CLI options, computes summary metrics, renders markdown, writes the SARIF and report outputs, and ignores generated report/out* artifacts.
SARIF exporter tests
report/tests/test_sarif.py
Checks SARIF shape, rule deduping, severity mapping, line clamping, fingerprint stability, suppression passthrough, filtering, truncation, tier properties, CLI validation, and end-to-end artifact output.

Interactive dashboard

Layer / File(s) Summary
Compact data model
viz/build_dashboard.py
Adds tier wiring, OWN-shape aggregation, module parsing, interning, compact row and dimension outputs, and history snapshot generation.
Filter controls and state
viz/build_dashboard.py
Adds chip filters, reset behavior, module drilldown state, and client-side row filtering and aggregation helpers.
Drilldown rendering and history
viz/build_dashboard.py, viz/history.jsonl
Redraws the treemap, tier chart, table, and trend view from filtered rows and appends the new history snapshot.

Sequence Diagram(s)

SARIF export flow

sequenceDiagram
  participant Main as report.cli.main
  participant Sarif as report.sarif.to_sarif
  participant Metrics as report.cli._metrics
  participant Render as report.cli._report_md

  Main->>Sarif: convert findings.json with min_level/max_results_per_run
  Sarif-->>Main: SARIF log
  Main->>Metrics: aggregate totals by tool, category, level, and tier
  Metrics-->>Main: metrics dict
  Main->>Render: render markdown summary tables
  Render-->>Main: report.md text
  Main->>Main: write ownnet-audit.sarif, metrics.json, report.md
Loading

Dashboard filter/update flow

sequenceDiagram
  participant Collect as collect()
  participant Update as update()
  participant Filtered as filteredRows()
  participant Treemap as drawTreemap()
  participant Tiers as drawTiers()
  participant Table as drawTable()
  participant Trend as drawTrend()

  Collect-->>Update: D.rows and D.history
  Update->>Filtered: select rows by chips and module drilldown
  Filtered-->>Update: filtered rows
  Update->>Treemap: redraw filtered treemap
  Update->>Tiers: redraw tier bars from filtered rows
  Update->>Table: render filtered findings table
  Update->>Trend: redraw trend from D.history
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • PhysShell/Own.NET#43: Shares the SARIF export path and output format used by this PR’s new report/sarif.py and CLI artifact generation.
  • PhysShell/OwnAudit#6: Extends the same dashboard generator path with compact rows, filters, drilldown rendering, and history updates.

Poem

🐇 I hopped through reports, both new and bright,
and packed the findings into tidy light.
Chips and charts now guide my way,
while history keeps the hops in play.
Hooray for burrows neat today!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 24.39% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the PR’s three main tracks: dashboard interactivity, roadmap docs, and the phase-1 SARIF exporter.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/sts-runtime-analysis-2mo4z9

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 392b59cca0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread report/cli.py Outdated
Comment on lines +55 to +58
ap.add_argument("--min-level", choices=("note", "warning", "error"), default=None,
help="drop results below this SARIF level (GitHub-friendly export)")
ap.add_argument("--max-results", type=int, default=None,
help="cap results per run (highest severity kept first)")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Default to a GitHub-safe SARIF size

With the current sts_audit/findings.json, the documented default python3 -m report.cli leaves both filters unset and emits 62,338 Roslyn results in one run; GitHub's SARIF support caps uploads at 25,000 results per run (https://docs.github.com/en/enterprise-cloud@latest/code-security/reference/code-scanning/sarif-files/sarif-support#file-compatibility), so the default ownnet-audit.sarif advertised as GitHub-ready is rejected unless callers know to pass --min-level/--max-results. Please make the default output GitHub-safe or fail before writing an unuploadable artifact.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@codex Fixed in 799e018. --max-results now defaults to GitHub's per-run limit (GITHUB_MAX_RESULTS_PER_RUN = 25000), so the default python3 -m report.cli is uploadable out of the box — Roslyn's 62,338 results cap to 25,000 (highest severity first), and the drop is reported on stderr and in metrics.json's export block. --max-results 0 opts back into the full (non-GitHub-safe) set. Test: test_default_cap_is_github_limit + the CLI integration test.


Generated by Claude Code

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread report/sarif.py
def _fingerprint(f) -> str:
"""Stable, line-INDEPENDENT id (rule + path + normalized message) so an alert is the
same across commits even if the line moved."""
basis = "\n".join([f.get("rule") or "", f.get("path") or "", _norm(f.get("message"))])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep fingerprints unique per occurrence

On files that contain multiple instances of the same rule with the same message at different lines, this basis gives every instance the same fingerprint (the current data has thousands of such groups, including repeated OWN001 hits in a single XAML code-behind). Since SARIF consumers use partialFingerprints as result identity, these distinct alerts can be merged or correlated to the wrong occurrence; use a per-occurrence signal such as a source-line hash/duplicate counter instead of only rule+path+message.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@codex Fixed in 799e018. Added a per-occurrence disambiguator: within a run I keep a counter of identical base fingerprints, so the first hit keeps the stable line-independent base and each subsequent identical rule+path+message gets base/1, base/2, … — distinct alert identities, while the first occurrence still survives edits. Test test_duplicate_findings_get_distinct_fingerprints (two OWN001 at different lines in one file → 2 distinct fingerprints, first == base).


Generated by Claude Code

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (2)
docs/audit-data-leverage.md (1)

35-46: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Label the plain-text fences.

These blocks are not code, so the unlabeled fences trip markdownlint (MD040). Mark them as text for clarity and to keep the doc lint-clean.

Proposed fix
-```
+```text
 1. INPC020                       5607   7.7%   (INotifyPropertyChanged correctness)
 2. MA0006                        5198   7.2%   (Meziantou)
 3. MA0003                        3991   5.5%
 4. INPC003                       3472   4.8%
 5. WPF0041                       3351   4.6%   (WpfAnalyzers)
 6. MA0011                        3194   4.4%
 7. INPC017                       2595   3.6%
 8. INPC013                       2529   3.5%
 9. cs/useless-assignment-to-local 1954  2.7%   (CodeQL dead store)
10. INPC002                       1567   2.2%
-```
+```

-```
+```text
general-quality    32801  45.2%   <- главный кандидат на wontfix / severity-тюнинг
inpc-correctness   17122  23.6%
architecture       15301  21.1%
wpf-freezable       3902   5.4%
idisposable-leak    2440   3.4%
uncategorized        670   0.9%
subscription-leak    326   0.4%
region-escape          7   0.0%
-```
+```

-```
+```text
T1 (auto)       3935   5.4%
T2 (review)    58403  80.5%
T3 (unfixable)  9851  13.6%
T4 (bespoke)     380   0.5%
-```
+```

Also applies to: 49-58, 61-66

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/audit-data-leverage.md` around lines 35 - 46, The markdown fence blocks
in the audit data tables are plain text, so they should be labeled with text to
satisfy markdownlint MD040. Update the fenced blocks in the documented tables
near the referenced sections by using text-labeled fences consistently,
including the blocks containing the category counts and T1/T2/T3/T4 breakdown,
so the doc remains lint-clean.

Source: Linters/SAST tools

docs/own-net-auditor.md (1)

31-39: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Tag the illustrative fences as text.

These are prose snippets/diagrams, not code blocks. The unlabeled fences trigger markdownlint (MD040) and are slightly harder to read without a language hint.

Proposed fix
-```
+```text
 Own.NET Auditor =
   semantic code graph
   + architecture rules
   + WPF/runtime-specific probes
   + baseline/diff
   + SARIF/GitHub reports
   + AI explanation layer
-```
+```

-```
+```text
 PR `#1234` increased coupling in Broker.Documents by 18%.
 New dependencies: Broker.Documents -> DevExpress.Xpf.Grid, -> System.Data.SqlClient
 New cycle: Broker.Documents -> Broker.Services -> Broker.Documents
 Risk: High — domain-ish module now depends on UI and SQL infrastructure.
-```
+```

-```
+```text
 static:  subscribes to DocumentStore.Changed, no matching unsubscribe
 runtime: 132 retained instances, held by static DocumentStore.Changed delegate
 => MEM-WPF-014: event leak confirmed, ~84 MB retained after closing window 10×
    confidence: high
-```
+```

Also applies to: 73-77, 121-126, 130-135

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/own-net-auditor.md` around lines 31 - 39, The markdown examples in the
docs are plain prose/diagrams, not code, so the unlabeled fenced blocks should
be tagged as text to satisfy markdownlint MD040. Update each relevant fence in
the documentation snippet to use the text language hint, including the
illustrative blocks around the Own.NET Auditor summary, the dependency/cycle
example, and the memory leak probe example, so the content remains readable
without being treated as code.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@report/cli.py`:
- Around line 64-70: The CLI currently writes metrics and the markdown report
from the unfiltered findings while SARIF is generated from the filtered/exported
subset. Update the logic in cli.py around to_sarif() and _metrics() so
metrics.json and report.md are derived from the same filtered result set, or
explicitly include both raw and exported counts in the metrics/report output.
Use the existing args.min_level and args.max_results flow to keep all emitted
artifacts consistent.

In `@report/sarif.py`:
- Around line 74-79: Update to_sarif to validate its inputs before building the
log: reject any min_level value not recognized by _LEVEL_RANK instead of
defaulting to no filtering, and reject negative max_results_per_run values in
the to_sarif flow. Use the existing to_sarif function and _LEVEL_RANK lookup as
the entry point for this validation, and keep the capped-results and
dropped-count logic unchanged for valid inputs.

In `@viz/build_dashboard.py`:
- Line 78: Ruff is flagging the non-ASCII multiplication character used in the
docstring and matching user-facing text. Update the wording in the affected
`build_dashboard.py` strings to use plain ASCII terms instead of `×`, and make
the same replacement anywhere else the same phrase appears so the
`build_dashboard` copy stays lint-clean.
- Line 391: The dashboard rendering in the module row builder is injecting
audit-derived module names directly into innerHTML, which can allow HTML
injection. Update the code in the module-label rendering path (the b.innerHTML
assignment and the related path/rule/category/tool rendering logic referenced by
this audit comment) to escape or sanitize all audit-derived strings before
inserting them into innerHTML, or switch to text-safe DOM APIs for the dynamic
parts. Make sure the same treatment is applied to the other affected render
blocks noted in the comment so every path, rule, category, tool, and module name
is handled consistently.
- Around line 164-198: The history update logic in _update_history currently
deduplicates by date, which causes multiple same-day dashboard builds to
overwrite earlier audit points; change it so each snapshot is appended as a
separate entry instead of filtering out entries with the same snapshot["date"].
Keep the existing load-and-sort flow around HISTORY, but remove the same-date
replacement behavior so every run is preserved in the dated series used by the
trend chart.

---

Nitpick comments:
In `@docs/audit-data-leverage.md`:
- Around line 35-46: The markdown fence blocks in the audit data tables are
plain text, so they should be labeled with text to satisfy markdownlint MD040.
Update the fenced blocks in the documented tables near the referenced sections
by using text-labeled fences consistently, including the blocks containing the
category counts and T1/T2/T3/T4 breakdown, so the doc remains lint-clean.

In `@docs/own-net-auditor.md`:
- Around line 31-39: The markdown examples in the docs are plain prose/diagrams,
not code, so the unlabeled fenced blocks should be tagged as text to satisfy
markdownlint MD040. Update each relevant fence in the documentation snippet to
use the text language hint, including the illustrative blocks around the Own.NET
Auditor summary, the dependency/cycle example, and the memory leak probe
example, so the content remains readable without being treated as code.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 14cf3e6d-71e9-4d83-a34b-dc9df1cf2b81

📥 Commits

Reviewing files that changed from the base of the PR and between 45faaf9 and 392b59c.

📒 Files selected for processing (10)
  • .gitignore
  • docs/audit-data-leverage.md
  • docs/own-net-auditor.md
  • report/__init__.py
  • report/cli.py
  • report/sarif.py
  • report/tests/__init__.py
  • report/tests/test_sarif.py
  • viz/build_dashboard.py
  • viz/history.jsonl

Comment thread report/cli.py Outdated
Comment thread report/sarif.py
Comment thread viz/build_dashboard.py Outdated
Comment thread viz/build_dashboard.py Outdated
Comment thread viz/build_dashboard.py Outdated
… lint)

SARIF (report/):
- default export caps each run at GitHub's 25,000-results limit so the artifact
  is uploadable by default; --max-results 0 opts back into the full set, and the
  drop is reported (Codex P1).
- partialFingerprints disambiguate per occurrence (base/N) so identical
  rule+path+message hits in one file don't collapse to one alert, while the first
  occurrence keeps the stable line-independent base (Codex P2).
- to_sarif validates min_level / max_results_per_run instead of silently
  disabling the filter or off-by-one dropping (CodeRabbit).
- metrics.json/report.md now carry an "export" block (min_level, cap, emitted,
  dropped) so the three artifacts never disagree about what was exported.

Dashboard (viz/):
- escape audit-derived strings (path/rule/category/tool/module) before innerHTML
  in the table and module chip — no HTML injection from a crafted name (CodeRabbit).
- history keyed by UTC timestamp; distinct audits (even same-day) are preserved,
  only a no-op rebuild with identical counts is collapsed (CodeRabbit).
- drop the ambiguous U+00D7 in docstring/copy; [*mod_names, ...] over concat (Ruff).

Docs: label the plain-text fences as ```text (markdownlint MD040).

Tests: 13/13 SARIF (added arg-validation, per-occurrence fingerprint, default cap,
CLI export-consistency), green under -O. Dashboard re-verified headless: 8 charts,
esc() neutralizes <img onerror>, drill-down intact, no console errors. SARIF default
run caps Roslyn 62,338 -> 25,000.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LLHz4cevQyXHKZnjzCqhoa

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
viz/build_dashboard.py (2)

313-319: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Escape %DATA% for the script context too.

esc() protects later innerHTML writes, but audit-derived strings are already inside the <script> at const D = %DATA%;. A path/rule/module containing </script> can break out before esc() ever runs. Serialize the payload with <, >, and & escaped, e.g. \u003c, where %DATA% is substituted.

🛡️ Proposed direction
+def _json_for_script(data: dict) -> str:
+    return (json.dumps(data)
+            .replace("&", "\\u0026")
+            .replace("<", "\\u003c")
+            .replace(">", "\\u003e"))

Use _json_for_script(data) instead of raw json.dumps(data) when replacing %DATA%.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@viz/build_dashboard.py` around lines 313 - 319, The dashboard template
currently inserts raw `%DATA%` into the `<script>` block, so `esc()` only
protects later DOM writes and does not prevent script-breaking payloads. Update
the `%DATA%` substitution in `build_dashboard.py` to use a script-safe JSON
serializer such as `_json_for_script(data)` instead of plain `json.dumps(data)`,
so characters like `<`, `>`, and `&` are escaped before reaching `const D =
%DATA%;`.

426-435: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Make the treemap reflect active filters.

update() recomputes the category/tool/rule/source/tier/table views from filteredRows(), but drawTreemap() always renders global D.modules. After selecting a tool/category chip, the module sizes and hover counts remain unfiltered, which makes the dashboard state misleading.

Also applies to: 493-512

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@viz/build_dashboard.py` around lines 426 - 435, The treemap is still using
the global D.modules data instead of the currently active filtered dataset, so
it does not match the rest of the dashboard. Update drawTreemap() to build
labels, values, colors, customdata, and hover text from the same
filteredRows()/derived filtered aggregation that update() uses for the other
views, so selecting tool/category chips changes the treemap module sizes and
counts consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@report/cli.py`:
- Around line 62-71: Reject negative values for the --max-results argument
during CLI parsing in cli.py so invalid input fails as a normal usage error
instead of reaching to_sarif() and crashing. Update the argument handling around
ap.add_argument and args.max_results to validate that the value is either 0 or a
positive integer before computing cap, and report the error through argparse’s
standard validation path in the same flow that builds sarif via to_sarif().

In `@report/tests/test_sarif.py`:
- Around line 16-20: The test module import root is set too shallow in the
direct-execution setup, so `from report import cli` can resolve against the
wrong package path. Update the `HERE`/`ROOT` bootstrap in `test_sarif.py` so the
inserted path points to the project root that contains the `report` package, and
keep the `report` import unchanged so the test can run correctly under the bare
Python runner.

In `@viz/build_dashboard.py`:
- Around line 198-201: The trend-building logic in the snapshot append block is
deduping runs by aggregate totals and tier counts, which can merge distinct
audit runs. Update the series handling so every audit run is appended in this
path, or replace the equality check with a true run/input identity in the code
that builds the dashboard series, using the snapshot/series logic around the
unchanged check and series.append.

---

Outside diff comments:
In `@viz/build_dashboard.py`:
- Around line 313-319: The dashboard template currently inserts raw `%DATA%`
into the `<script>` block, so `esc()` only protects later DOM writes and does
not prevent script-breaking payloads. Update the `%DATA%` substitution in
`build_dashboard.py` to use a script-safe JSON serializer such as
`_json_for_script(data)` instead of plain `json.dumps(data)`, so characters like
`<`, `>`, and `&` are escaped before reaching `const D = %DATA%;`.
- Around line 426-435: The treemap is still using the global D.modules data
instead of the currently active filtered dataset, so it does not match the rest
of the dashboard. Update drawTreemap() to build labels, values, colors,
customdata, and hover text from the same filteredRows()/derived filtered
aggregation that update() uses for the other views, so selecting tool/category
chips changes the treemap module sizes and counts consistently.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a141cc35-9b84-4ac7-8a8f-31c6789f354c

📥 Commits

Reviewing files that changed from the base of the PR and between 392b59c and 799e018.

📒 Files selected for processing (7)
  • docs/audit-data-leverage.md
  • docs/own-net-auditor.md
  • report/cli.py
  • report/sarif.py
  • report/tests/test_sarif.py
  • viz/build_dashboard.py
  • viz/history.jsonl
✅ Files skipped from review due to trivial changes (2)
  • viz/history.jsonl
  • docs/own-net-auditor.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • docs/audit-data-leverage.md
  • report/sarif.py

Comment thread report/cli.py Outdated
Comment thread report/tests/test_sarif.py
Comment thread viz/build_dashboard.py Outdated
…validation

- viz: _json_for_script escapes <, >, & to \uXXXX before %DATA% lands in the
  <script> block, so an audit-derived </script> (etc.) can't break out of the
  script context. Verified it neutralizes </script> and JSON round-trips (CR).
- viz: history dedup now keys on the findings.json content digest, not aggregate
  counts — distinct audits are always appended (even with coincidentally equal
  total/tiers), only a byte-identical re-render is collapsed (CR).
- report/cli: --max-results is validated by argparse (_non_negative_int), so a
  negative value is a usage error, not a to_sarif() traceback (CR). Test added.

Not changed: the test_sarif.py ROOT bootstrap — ROOT already resolves to the
repo root (dirname(dirname(report/tests))), verified by running the suite
standalone from /tmp with no PYTHONPATH (14/14). The suggested extra dirname
would point above the repo and break the import.

Tests 14/14 (normal + -O); dashboard re-verified headless (8 charts, drill-down,
no console errors); SARIF default export unchanged (Roslyn 62,338 -> 25,000).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LLHz4cevQyXHKZnjzCqhoa

Copy link
Copy Markdown
Owner Author

@coderabbitai re: the two outside-diff findings from the last review:

  1. %DATA% script-context escaping (build_dashboard.py:313) — fixed in 16f41f3. Added _json_for_script(data) which escapes <, >, & to < / > / & before the payload is substituted into const D = %DATA%;. So an audit-derived </script> (or <!--, <script) can't break out of the script block, and the result is still valid JSON to JSON.parse. Verified it neutralizes </script> and round-trips.

  2. Treemap not filter-aware (build_dashboard.py:426) — holding this one for a maintainer decision rather than auto-applying. The treemap is sized by pain (a precomputed severity×agreement index from health-report.md, not recomputable from a tool/category-filtered subset of findings), and it doubles as the drill-down control. Making it filter-aware means either changing its metric to filtered counts or only updating hover counts — a real product choice, not a mechanical fix. Will follow up once decided.


Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@PhysShell Thanks for the detailed follow-up.

  1. The %DATA% escaping change sounds good to me. Using _json_for_script(data) to neutralize <, >, and & before embedding the payload into the script block is the right mitigation for audit-derived strings like </script>.

  2. Your treemap explanation also makes sense. If tile sizing is driven by the precomputed pain metric from health-report.md and the treemap is also the drill-down control, then making it “filter-aware” would change product semantics rather than fix a clear bug. I’m good to leave that to maintainer/product direction for this PR.

(ᵔᴥᵔ)


✏️ Learnings added
Learnt from: PhysShell
URL: https://github.com/PhysShell/OwnAudit/pull/7

Timestamp: 2026-06-26T04:09:10.762Z
Learning: In `viz/build_dashboard.py`, the treemap is intentionally sized by the precomputed `pain` metric derived from `health-report.md`, and it also serves as the module drill-down control. Whether it should react to tool/category filters is a product decision, not an automatic correctness fix, because `pain` is not recomputable from a filtered subset of findings.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
viz/build_dashboard.py (1)

179-180: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Handle zero-finding audits before dividing.

If findings.json contains an empty findings array, collect() crashes here with ZeroDivisionError instead of producing an empty dashboard.

Proposed fix
-        "fixable_pct": round(100 * sum(c for s, c in by_source.items()
-                                       if sources[s]["codefix"]) / len(findings)),
+        "fixable_pct": (round(100 * sum(c for s, c in by_source.items()
+                                        if sources[s]["codefix"]) / len(findings))
+                        if findings else 0),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@viz/build_dashboard.py` around lines 179 - 180, The collect() summary
calculation in build_dashboard.py divides by len(findings) without guarding
against an empty findings list, which can raise ZeroDivisionError. Update the
fixable_pct computation to handle zero findings safely by returning 0 (or an
equivalent empty-dashboard value) when findings is empty, and keep the existing
aggregation logic otherwise.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@viz/build_dashboard.py`:
- Line 125: The trend snapshot identity in build_dashboard.py is still using
SHA-1 via the digest assignment, which triggers the security lint and leaves
collision risk. Update the digest generation in the snapshot identity path to
use SHA-256 instead, keeping the existing truncation behavior if needed, and
ensure the change is applied where the raw snapshot string is hashed for the run
identity.

---

Outside diff comments:
In `@viz/build_dashboard.py`:
- Around line 179-180: The collect() summary calculation in build_dashboard.py
divides by len(findings) without guarding against an empty findings list, which
can raise ZeroDivisionError. Update the fixable_pct computation to handle zero
findings safely by returning 0 (or an equivalent empty-dashboard value) when
findings is empty, and keep the existing aggregation logic otherwise.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: de4533d0-74da-40ed-9c24-6a5b60ad7330

📥 Commits

Reviewing files that changed from the base of the PR and between 799e018 and 16f41f3.

📒 Files selected for processing (4)
  • report/cli.py
  • report/tests/test_sarif.py
  • viz/build_dashboard.py
  • viz/history.jsonl
✅ Files skipped from review due to trivial changes (1)
  • viz/history.jsonl
🚧 Files skipped from review as they are similar to previous changes (2)
  • report/cli.py
  • report/tests/test_sarif.py

Comment thread viz/build_dashboard.py Outdated
- viz: trend run identity uses sha256 (was sha1) — clears Ruff S324 and drops
  the collision edge case; digest stays 16 hex chars (CR).
- viz: fixable_pct guards against an empty findings array (returns 0 instead of
  ZeroDivisionError), so a zero-finding audit produces an empty dashboard (CR).

Verified: 14/14 SARIF tests; empty-findings collect() -> fixable_pct 0, no crash;
dashboard rebuild clean (1 run, sha256 digest).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LLHz4cevQyXHKZnjzCqhoa

Copy link
Copy Markdown
Owner Author

@coderabbitai re: the outside-diff finding on viz/build_dashboard.py:179-180 (ZeroDivisionError on empty findings) — fixed in 32ad096. fixable_pct now returns 0 when findings is empty instead of dividing by zero, so a zero-finding audit produces an empty dashboard rather than crashing. Verified: collect() on {"findings": []}fixable_pct == 0, no exception.


Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@PhysShell Thanks — that addresses the concern. Returning 0 for empty findings is the right fallback here, and your verification for the zero-finding case sounds good.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants