BREAKING CHANGE: the {NNN} in CA-{SCANNER}-{NNN} identifies the check that
produced the finding. It used to be the finding's position in that scanner's
output for that run, which made it unstable across CONFIGURATIONS, not just
across releases as STATE framed it. Measured on two fixtures: "No custom
subagents" was CA-GAP-007 on minimal-project and CA-GAP-004 on healthy-project.
A user who fixed an unrelated earlier gap silently renumbered every later one,
so a .config-audit-ignore pin retargeted to a neighbouring finding with no
version change at all.
Second measured arm: README already documented the opposite scheme. It and the
scanner headers describe ~20 numbers as check codes (CA-SKL-003 = oversized
body, CA-PLH-015 = folder shadowing, CA-TOK-006 = schema deferral), and the
counter could only produce those in the all-fire case -- source-order positions
are 4, 3 and 8. The documentation described the scheme; the implementation was
what was wrong. Every published number is preserved by construction and pinned
exhaustively in tests/lib/finding-codes.test.mjs.
scanners/lib/finding-codes.mjs is the single authority. Every finding() call
passes a `code`; an undeclared or missing one THROWS. No counter fallback --
that would reproduce D1's findGapId -> 'unknown' silent degradation and let a
half-converted scanner ship IDs that look valid. findingCounter/resetCounter
are deleted outright, not left as no-ops. Retirement is now a mechanism:
RETIRED_CODES tombstones a withdrawn key so its number is never reissued,
seeded with GAP t3_8 -- the D1 removal that opened this chunk.
IDs are consequently NOT unique per finding: one check failing in three files
emits three findings sharing an ID. That inverts which consumer is correct, so
every f.id/findingId site was classified before the change. diff-engine and
most of fix-engine already keyed on scanner+title+file (drift was never lying);
fix-engine's verification did not, and keyed on the ID alone -- fixing one of
two sibling instances marked both fixed, and the untouched one, still present
in the re-scan, was reported as a REGRESSION. Red test first, then keyed on
(findingId, file), which both planFixes and applyFixes already carry.
plugin-health's crossIds Set was measured and is a clean negative: cross
findings are allFindings.slice(crossPluginStart) and codes 18/19 are emitted
only in that tail, so the partition holds by construction.
unknownSuppressions() reports a pin that names no declared check, in the
--output-file payload (ux-rules rule 2 -- a stderr-only warning is invisible to
the commands) and only when one exists, so a clean config is byte-identical.
That is what makes the break safe: a stale pin goes loud instead of dying quiet.
Frozen tests/snapshots/v5.0.0/ untouched on disk. IDs are masked out of that
comparison (mask-finding-ids.mjs) rather than re-derived -- re-deriving
positional IDs would assert the retired scheme against itself, and #58's
isGapEntry off-by-one is the measured example of that misfiring. The dead
re-derivation is removed from strip-retired-gap.mjs. default-output snapshots
re-approved after confirming the diff is IDs and nothing else.
Guards, each seen red against its own defect: a missing code (scanner errors
out mid-sweep), an orphan declaration, a resurrected retired key, and a
documented ID naming no check. The sweep asserts the union across all 16
scanners, never per scanner -- a per-scanner assertion goes green on a partial
conversion.
Fasit written before implementation: docs/mbug28-id-semantics-fasit.local.md,
including one correction made before running (CML has 12 checks over 13 call
sites -- the anchored and calibrated char-budget arms are one check, which a
repeated-title sweep found and my call-site count had missed).
Suite 1535 -> 1573, 0 failing.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MyqCQKK2ornJ1jFWwqx17E
616 lines
21 KiB
JSON
616 lines
21 KiB
JSON
{
|
|
"kind": "json",
|
|
"payload": {
|
|
"meta": {
|
|
"target": "<TARGET>",
|
|
"timestamp": "<TIMESTAMP>",
|
|
"version": "2.2.0",
|
|
"tool": "config-audit"
|
|
},
|
|
"scanners": [
|
|
{
|
|
"scanner": "CML",
|
|
"status": "ok",
|
|
"files_scanned": 1,
|
|
"duration_ms": 0,
|
|
"findings": [
|
|
{
|
|
"id": "CA-CML-007",
|
|
"scanner": "CML",
|
|
"severity": "low",
|
|
"title": "Your instructions file is missing common sections",
|
|
"description": "Sections like Project Overview, Commands, and Conventions help Claude apply your guidance consistently across tasks.",
|
|
"file": "/Users/ktg/repos/ktg-plugin-marketplace/config-audit/tests/fixtures/marketplace-medium/CLAUDE.md",
|
|
"line": null,
|
|
"evidence": "Present sections: Marketplace Medium, Plugins, Commands, Conventions",
|
|
"category": null,
|
|
"recommendation": "Add the missing sections noted in the details.",
|
|
"autoFixable": false,
|
|
"userImpactCategory": "Configuration mistake",
|
|
"userActionLanguage": "Optional cleanup",
|
|
"relevanceContext": "test-fixture-no-impact"
|
|
}
|
|
],
|
|
"counts": {
|
|
"critical": 0,
|
|
"high": 0,
|
|
"medium": 0,
|
|
"low": 1,
|
|
"info": 0
|
|
}
|
|
},
|
|
{
|
|
"scanner": "SET",
|
|
"status": "ok",
|
|
"files_scanned": 1,
|
|
"duration_ms": 0,
|
|
"findings": [],
|
|
"counts": {
|
|
"critical": 0,
|
|
"high": 0,
|
|
"medium": 0,
|
|
"low": 0,
|
|
"info": 0
|
|
}
|
|
},
|
|
{
|
|
"scanner": "HKV",
|
|
"status": "ok",
|
|
"files_scanned": 1,
|
|
"duration_ms": 0,
|
|
"findings": [],
|
|
"counts": {
|
|
"critical": 0,
|
|
"high": 0,
|
|
"medium": 0,
|
|
"low": 0,
|
|
"info": 0
|
|
}
|
|
},
|
|
{
|
|
"scanner": "RUL",
|
|
"status": "skipped",
|
|
"files_scanned": 0,
|
|
"duration_ms": 0,
|
|
"findings": [],
|
|
"counts": {
|
|
"critical": 0,
|
|
"high": 0,
|
|
"medium": 0,
|
|
"low": 0,
|
|
"info": 0
|
|
}
|
|
},
|
|
{
|
|
"scanner": "MCP",
|
|
"status": "ok",
|
|
"files_scanned": 1,
|
|
"duration_ms": 0,
|
|
"findings": [],
|
|
"counts": {
|
|
"critical": 0,
|
|
"high": 0,
|
|
"medium": 0,
|
|
"low": 0,
|
|
"info": 0
|
|
}
|
|
},
|
|
{
|
|
"scanner": "IMP",
|
|
"status": "ok",
|
|
"files_scanned": 1,
|
|
"duration_ms": 0,
|
|
"findings": [],
|
|
"counts": {
|
|
"critical": 0,
|
|
"high": 0,
|
|
"medium": 0,
|
|
"low": 0,
|
|
"info": 0
|
|
}
|
|
},
|
|
{
|
|
"scanner": "CNF",
|
|
"status": "ok",
|
|
"files_scanned": 2,
|
|
"duration_ms": 0,
|
|
"findings": [],
|
|
"counts": {
|
|
"critical": 0,
|
|
"high": 0,
|
|
"medium": 0,
|
|
"low": 0,
|
|
"info": 0
|
|
}
|
|
},
|
|
{
|
|
"scanner": "GAP",
|
|
"status": "ok",
|
|
"files_scanned": 4,
|
|
"duration_ms": 0,
|
|
"findings": [
|
|
{
|
|
"id": "CA-GAP-004",
|
|
"scanner": "GAP",
|
|
"severity": "medium",
|
|
"title": "You haven't added any custom shortcuts yet",
|
|
"description": "Custom skills give you `/your-shortcut` invocations for tasks you do often.",
|
|
"file": null,
|
|
"line": null,
|
|
"evidence": null,
|
|
"category": "t1",
|
|
"recommendation": "Create a skill in `.claude/skills/` for a workflow you find yourself repeating.",
|
|
"autoFixable": false,
|
|
"userImpactCategory": "Missed opportunity",
|
|
"userActionLanguage": "Fix when convenient",
|
|
"relevanceContext": "affects-everyone"
|
|
},
|
|
{
|
|
"id": "CA-GAP-006",
|
|
"scanner": "GAP",
|
|
"severity": "low",
|
|
"title": "You only have settings at one level",
|
|
"description": "Settings can live at user, project, or local-only scope. Using more than one lets you keep personal preferences separate from team-shared ones.",
|
|
"file": null,
|
|
"line": null,
|
|
"evidence": null,
|
|
"category": "t2",
|
|
"recommendation": "Consider moving team-wide settings to project scope and keeping personal ones at user or local scope.",
|
|
"autoFixable": false,
|
|
"userImpactCategory": "Missed opportunity",
|
|
"userActionLanguage": "Optional cleanup",
|
|
"relevanceContext": "affects-everyone"
|
|
},
|
|
{
|
|
"id": "CA-GAP-008",
|
|
"scanner": "GAP",
|
|
"severity": "low",
|
|
"title": "You haven't set up path-scoped rules yet",
|
|
"description": "Path-scoped rules only load when you're working with files that match — keeps each conversation focused.",
|
|
"file": null,
|
|
"line": null,
|
|
"evidence": null,
|
|
"category": "t2",
|
|
"recommendation": "Add scoping to your rules so they only load for the files they apply to.",
|
|
"autoFixable": false,
|
|
"userImpactCategory": "Missed opportunity",
|
|
"userActionLanguage": "Optional cleanup",
|
|
"relevanceContext": "affects-everyone"
|
|
},
|
|
{
|
|
"id": "CA-GAP-010",
|
|
"scanner": "GAP",
|
|
"severity": "low",
|
|
"title": "Your automations all listen to similar events",
|
|
"description": "Listening to a wider range of events (before-tool, after-tool, session-start, etc.) lets you catch more workflow opportunities.",
|
|
"file": null,
|
|
"line": null,
|
|
"evidence": null,
|
|
"category": "t2",
|
|
"recommendation": "Look at the events your current automations skip and consider adding one or two.",
|
|
"autoFixable": false,
|
|
"userImpactCategory": "Missed opportunity",
|
|
"userActionLanguage": "Optional cleanup",
|
|
"relevanceContext": "affects-everyone"
|
|
},
|
|
{
|
|
"id": "CA-GAP-011",
|
|
"scanner": "GAP",
|
|
"severity": "low",
|
|
"title": "You haven't set up any specialized helper agents yet",
|
|
"description": "Subagents handle parallel work in separate contexts (research, code review, testing) without crowding your main conversation.",
|
|
"file": null,
|
|
"line": null,
|
|
"evidence": null,
|
|
"category": "t2",
|
|
"recommendation": "Create a subagent in `.claude/agents/` for a task you delegate often.",
|
|
"autoFixable": false,
|
|
"userImpactCategory": "Missed opportunity",
|
|
"userActionLanguage": "Optional cleanup",
|
|
"relevanceContext": "affects-everyone"
|
|
},
|
|
{
|
|
"id": "CA-GAP-012",
|
|
"scanner": "GAP",
|
|
"severity": "low",
|
|
"title": "You haven't pinned a model preference",
|
|
"description": "Setting a default model lets you choose between speed and depth of reasoning for your work.",
|
|
"file": null,
|
|
"line": null,
|
|
"evidence": null,
|
|
"category": "t2",
|
|
"recommendation": "Add a `model` setting in your settings file.",
|
|
"autoFixable": false,
|
|
"userImpactCategory": "Missed opportunity",
|
|
"userActionLanguage": "Optional cleanup",
|
|
"relevanceContext": "affects-everyone"
|
|
},
|
|
{
|
|
"id": "CA-GAP-013",
|
|
"scanner": "GAP",
|
|
"severity": "info",
|
|
"title": "You haven't set up a status line yet",
|
|
"description": "A status line shows live context (token usage, current branch, time) at the bottom of your terminal.",
|
|
"file": null,
|
|
"line": null,
|
|
"evidence": null,
|
|
"category": "t3",
|
|
"recommendation": "Add a `statusLine` setting if you want this information at a glance.",
|
|
"autoFixable": false,
|
|
"userImpactCategory": "Missed opportunity",
|
|
"userActionLanguage": "FYI",
|
|
"relevanceContext": "affects-everyone"
|
|
},
|
|
{
|
|
"id": "CA-GAP-014",
|
|
"scanner": "GAP",
|
|
"severity": "info",
|
|
"title": "You haven't set up any custom keybindings",
|
|
"description": "Custom keybindings let you trigger your most-used skills with a keystroke.",
|
|
"file": null,
|
|
"line": null,
|
|
"evidence": null,
|
|
"category": "t3",
|
|
"recommendation": "Add bindings in your settings for skills you run often.",
|
|
"autoFixable": false,
|
|
"userImpactCategory": "Missed opportunity",
|
|
"userActionLanguage": "FYI",
|
|
"relevanceContext": "affects-everyone"
|
|
},
|
|
{
|
|
"id": "CA-GAP-015",
|
|
"scanner": "GAP",
|
|
"severity": "info",
|
|
"title": "You're using the default output style",
|
|
"description": "Output styles let you change how Claude formats responses (concise, verbose, bullet-heavy, etc.).",
|
|
"file": null,
|
|
"line": null,
|
|
"evidence": null,
|
|
"category": "t3",
|
|
"recommendation": "Try a different `outputStyle` setting if you have a strong preference.",
|
|
"autoFixable": false,
|
|
"userImpactCategory": "Missed opportunity",
|
|
"userActionLanguage": "FYI",
|
|
"relevanceContext": "affects-everyone"
|
|
},
|
|
{
|
|
"id": "CA-GAP-016",
|
|
"scanner": "GAP",
|
|
"severity": "info",
|
|
"title": "You haven't set up parallel worktree support",
|
|
"description": "Worktrees let Claude work on a branch in an isolated copy of the repo without disturbing your main checkout.",
|
|
"file": null,
|
|
"line": null,
|
|
"evidence": null,
|
|
"category": "t3",
|
|
"recommendation": "Enable worktrees if you regularly work on multiple branches at once.",
|
|
"autoFixable": false,
|
|
"userImpactCategory": "Missed opportunity",
|
|
"userActionLanguage": "FYI",
|
|
"relevanceContext": "affects-everyone"
|
|
},
|
|
{
|
|
"id": "CA-GAP-017",
|
|
"scanner": "GAP",
|
|
"severity": "info",
|
|
"title": "Your skills don't use the richer settings block",
|
|
"description": "Adding richer settings at the top of a skill lets you control when it loads, what tools it uses, and more.",
|
|
"file": null,
|
|
"line": null,
|
|
"evidence": null,
|
|
"category": "t3",
|
|
"recommendation": "Add fields like `model`, `tools`, or `description` to your skill files where useful.",
|
|
"autoFixable": false,
|
|
"userImpactCategory": "Missed opportunity",
|
|
"userActionLanguage": "FYI",
|
|
"relevanceContext": "affects-everyone"
|
|
},
|
|
{
|
|
"id": "CA-GAP-018",
|
|
"scanner": "GAP",
|
|
"severity": "info",
|
|
"title": "You haven't set up subagent isolation yet",
|
|
"description": "Isolated subagents run in their own copy of the repo so they can't accidentally disturb your main work.",
|
|
"file": null,
|
|
"line": null,
|
|
"evidence": null,
|
|
"category": "t3",
|
|
"recommendation": "Add `isolation: worktree` to subagents that do destructive or experimental work.",
|
|
"autoFixable": false,
|
|
"userImpactCategory": "Missed opportunity",
|
|
"userActionLanguage": "FYI",
|
|
"relevanceContext": "affects-everyone"
|
|
},
|
|
{
|
|
"id": "CA-GAP-019",
|
|
"scanner": "GAP",
|
|
"severity": "info",
|
|
"title": "Your skills don't include live context",
|
|
"description": "Dynamic context lets a skill see fresh information (file contents, command output) at the moment it runs, not at the time it was written.",
|
|
"file": null,
|
|
"line": null,
|
|
"evidence": null,
|
|
"category": "t3",
|
|
"recommendation": "Use the dynamic-context block in skills that need up-to-date information.",
|
|
"autoFixable": false,
|
|
"userImpactCategory": "Missed opportunity",
|
|
"userActionLanguage": "FYI",
|
|
"relevanceContext": "affects-everyone"
|
|
},
|
|
{
|
|
"id": "CA-GAP-021",
|
|
"scanner": "GAP",
|
|
"severity": "info",
|
|
"title": "You haven't built a custom plugin yet",
|
|
"description": "Plugins let you bundle skills, automations, and connected services that you want available across many projects.",
|
|
"file": null,
|
|
"line": null,
|
|
"evidence": null,
|
|
"category": "t4",
|
|
"recommendation": "If you have workflows you repeat across projects, consider packaging them as a plugin.",
|
|
"autoFixable": false,
|
|
"userImpactCategory": "Missed opportunity",
|
|
"userActionLanguage": "FYI",
|
|
"relevanceContext": "affects-everyone"
|
|
},
|
|
{
|
|
"id": "CA-GAP-023",
|
|
"scanner": "GAP",
|
|
"severity": "info",
|
|
"title": "Your project has no settings managed by your organization",
|
|
"description": "Managed settings let your organization apply rules everyone has to follow.",
|
|
"file": null,
|
|
"line": null,
|
|
"evidence": null,
|
|
"category": "t4",
|
|
"recommendation": "If you work in a team setting, consider whether managed settings would help.",
|
|
"autoFixable": false,
|
|
"userImpactCategory": "Missed opportunity",
|
|
"userActionLanguage": "FYI",
|
|
"relevanceContext": "affects-everyone"
|
|
},
|
|
{
|
|
"id": "CA-GAP-024",
|
|
"scanner": "GAP",
|
|
"severity": "info",
|
|
"title": "You haven't connected Claude to your editor's language servers",
|
|
"description": "Language-server connections let Claude see types, error messages, and definitions the same way your editor does.",
|
|
"file": null,
|
|
"line": null,
|
|
"evidence": null,
|
|
"category": "t4",
|
|
"recommendation": "Set up LSP integration if you work in a typed language.",
|
|
"autoFixable": false,
|
|
"userImpactCategory": "Missed opportunity",
|
|
"userActionLanguage": "FYI",
|
|
"relevanceContext": "affects-everyone"
|
|
}
|
|
],
|
|
"counts": {
|
|
"critical": 0,
|
|
"high": 0,
|
|
"medium": 1,
|
|
"low": 5,
|
|
"info": 10
|
|
}
|
|
},
|
|
{
|
|
"scanner": "TOK",
|
|
"status": "ok",
|
|
"files_scanned": 2,
|
|
"duration_ms": 0,
|
|
"findings": [
|
|
{
|
|
"id": "CA-TOK-005",
|
|
"scanner": "TOK",
|
|
"severity": "low",
|
|
"title": "A connected service exposes many tools, all loading on every turn",
|
|
"description": "Each tool a connected service exposes adds its description to every turn. Services with many tools eat space fast.",
|
|
"file": ".mcp.json",
|
|
"line": null,
|
|
"evidence": "tool_count=unknown; server=\"memory\"; source=\".mcp.json\" — severity reflects estimated tokens/turn based on structural heuristic; not measured against runtime telemetry",
|
|
"category": "token-efficiency",
|
|
"recommendation": "Limit which tools the service exposes (often via a `tools` allow-list), or disconnect services you rarely use.",
|
|
"autoFixable": false,
|
|
"userImpactCategory": "Wasted tokens",
|
|
"userActionLanguage": "Optional cleanup",
|
|
"relevanceContext": "affects-everyone"
|
|
}
|
|
],
|
|
"counts": {
|
|
"critical": 0,
|
|
"high": 0,
|
|
"medium": 0,
|
|
"low": 1,
|
|
"info": 0
|
|
},
|
|
"hotspots": [
|
|
{
|
|
"source": "mcp:memory (.mcp.json)",
|
|
"estimated_tokens": 500,
|
|
"rank": 1,
|
|
"loadPattern": "always",
|
|
"survivesCompaction": "yes",
|
|
"derivationConfidence": "inferred",
|
|
"recommendations": [
|
|
"Review whether this source needs to load on every turn."
|
|
]
|
|
},
|
|
{
|
|
"source": "CLAUDE.md",
|
|
"estimated_tokens": 116,
|
|
"rank": 2,
|
|
"loadPattern": "always",
|
|
"survivesCompaction": "yes",
|
|
"derivationConfidence": "confirmed",
|
|
"recommendations": [
|
|
"Move volatile top-of-file content to the bottom or extract to an @import-ed file.",
|
|
"Split overlong CLAUDE.md into focused @imports (≤200 lines each)."
|
|
],
|
|
"path": "/Users/ktg/repos/ktg-plugin-marketplace/config-audit/tests/fixtures/marketplace-medium/CLAUDE.md"
|
|
},
|
|
{
|
|
"source": "hooks/hooks.json",
|
|
"estimated_tokens": 81,
|
|
"rank": 3,
|
|
"loadPattern": "external",
|
|
"survivesCompaction": "n/a",
|
|
"derivationConfidence": "inferred",
|
|
"recommendations": [
|
|
"Deduplicate overlapping entries — each duplicate inflates the per-turn schema payload.",
|
|
"Move rarely-used permissions to a project-local override."
|
|
],
|
|
"path": "/Users/ktg/repos/ktg-plugin-marketplace/config-audit/tests/fixtures/marketplace-medium/hooks/hooks.json"
|
|
},
|
|
{
|
|
"source": ".claude/settings.json",
|
|
"estimated_tokens": 59,
|
|
"rank": 4,
|
|
"loadPattern": "external",
|
|
"survivesCompaction": "n/a",
|
|
"derivationConfidence": "inferred",
|
|
"recommendations": [
|
|
"Deduplicate overlapping entries — each duplicate inflates the per-turn schema payload.",
|
|
"Move rarely-used permissions to a project-local override."
|
|
],
|
|
"path": "/Users/ktg/repos/ktg-plugin-marketplace/config-audit/tests/fixtures/marketplace-medium/.claude/settings.json"
|
|
},
|
|
{
|
|
"source": ".mcp.json",
|
|
"estimated_tokens": 45,
|
|
"rank": 5,
|
|
"loadPattern": "external",
|
|
"survivesCompaction": "n/a",
|
|
"derivationConfidence": "inferred",
|
|
"recommendations": [
|
|
"Deduplicate overlapping entries — each duplicate inflates the per-turn schema payload.",
|
|
"Move rarely-used permissions to a project-local override."
|
|
],
|
|
"path": "/Users/ktg/repos/ktg-plugin-marketplace/config-audit/tests/fixtures/marketplace-medium/.mcp.json"
|
|
}
|
|
],
|
|
"total_estimated_tokens": 801,
|
|
"activeConfig": {
|
|
"claudeMdEstimatedTokens": "<ANCESTOR_DERIVED>",
|
|
"mcpServerCount": 1,
|
|
"pluginCount": 0,
|
|
"skillCount": 0
|
|
}
|
|
},
|
|
{
|
|
"scanner": "CPS",
|
|
"status": "ok",
|
|
"files_scanned": 1,
|
|
"duration_ms": 0,
|
|
"findings": [],
|
|
"counts": {
|
|
"critical": 0,
|
|
"high": 0,
|
|
"medium": 0,
|
|
"low": 0,
|
|
"info": 0
|
|
}
|
|
},
|
|
{
|
|
"scanner": "DIS",
|
|
"status": "ok",
|
|
"files_scanned": 1,
|
|
"duration_ms": 0,
|
|
"findings": [],
|
|
"counts": {
|
|
"critical": 0,
|
|
"high": 0,
|
|
"medium": 0,
|
|
"low": 0,
|
|
"info": 0
|
|
}
|
|
},
|
|
{
|
|
"scanner": "COL",
|
|
"status": "ok",
|
|
"files_scanned": 0,
|
|
"duration_ms": 0,
|
|
"findings": [],
|
|
"counts": {
|
|
"critical": 0,
|
|
"high": 0,
|
|
"medium": 0,
|
|
"low": 0,
|
|
"info": 0
|
|
}
|
|
},
|
|
{
|
|
"scanner": "SKL",
|
|
"status": "ok",
|
|
"files_scanned": 0,
|
|
"duration_ms": 0,
|
|
"findings": [],
|
|
"counts": {
|
|
"critical": 0,
|
|
"high": 0,
|
|
"medium": 0,
|
|
"low": 0,
|
|
"info": 0
|
|
}
|
|
},
|
|
{
|
|
"scanner": "AGT",
|
|
"status": "ok",
|
|
"files_scanned": 0,
|
|
"duration_ms": 0,
|
|
"findings": [],
|
|
"counts": {
|
|
"critical": 0,
|
|
"high": 0,
|
|
"medium": 0,
|
|
"low": 0,
|
|
"info": 0
|
|
}
|
|
},
|
|
{
|
|
"scanner": "OST",
|
|
"status": "ok",
|
|
"files_scanned": 0,
|
|
"duration_ms": 0,
|
|
"findings": [],
|
|
"counts": {
|
|
"critical": 0,
|
|
"high": 0,
|
|
"medium": 0,
|
|
"low": 0,
|
|
"info": 0
|
|
}
|
|
},
|
|
{
|
|
"scanner": "OPT",
|
|
"status": "ok",
|
|
"files_scanned": 1,
|
|
"duration_ms": 0,
|
|
"findings": [],
|
|
"counts": {
|
|
"critical": 0,
|
|
"high": 0,
|
|
"medium": 0,
|
|
"low": 0,
|
|
"info": 0
|
|
}
|
|
}
|
|
],
|
|
"aggregate": {
|
|
"total_findings": 18,
|
|
"counts": {
|
|
"critical": 0,
|
|
"high": 0,
|
|
"medium": 1,
|
|
"low": 7,
|
|
"info": 10
|
|
},
|
|
"risk_score": 11,
|
|
"risk_band": "Medium",
|
|
"verdict": "PASS",
|
|
"scanners_ok": 15,
|
|
"scanners_error": 0,
|
|
"scanners_skipped": 1
|
|
}
|
|
}
|
|
}
|