| name | executive-summary-findings |
| description | Use when the user asks for an executive summary, leadership roll-up, or cross-portfolio dashboard of security-audit findings — e.g. "summarise all findings", "how many criticals do we have", "credential leaks across the campaign", "findings by language", or "rebuild the executive summary". Aggregates every *security-audit.{json,md} report under analysis-results/ into a one-page Markdown brief and a self-contained HTML dashboard. |
Executive Summary — Findings
Roll up every completed security-audit report into a leadership-ready summary: severity totals, top Critical/High findings, credential & secret leaks, common CWE risk themes, and a per-language breakdown. Outputs both a Markdown one-pager and an interactive HTML dashboard.
Input
$ARGUMENTS is optional and may be any combination of:
| Token | Meaning |
|---|
| (none) | Standard run — scan findings/ and oss-findings/, write both outputs. |
open | Open the resulting HTML dashboard in the default browser when done. |
summary | After building, print the severity table and top-5 themes/criticals to the conversation. |
by-segment | Add a per-input-segment breakdown (openshift / operator-catalog / services), mapping each repo against the hybrid-platforms-inputs inventories. The ansible input is excluded by default (--exclude-segment to change). Pass --by-segment to the script. |
include-triage | For repos without a findings-current disposition ledger, count from unconfirmed /triage verdicts (true positives only) instead of the raw audit. OFF by default — early /triage runs over-marked false positives (unreachable repos, hardening misfiled as FP), so counting TP-only silently trusts those bad FP exclusions. When enabled, every affected figure is labeled "unconfirmed triage" in both outputs (KPI card + warning note with FP-excluded and undetermined counts). Pass --include-triage to the script. |
exclude-branch-variants | Skip __-slugged release-branch re-audits when the same product tree also carries the base-slug audit (the within-product duplicate case, e.g. rhacm/search-v2-api holding main + release-5.1 audits). Branch-only audits — per-release payload products like openshift-4.14-payload, where the branch audit is the only audit — always count: release branches are tracked per-branch by design. The excluded-report count is stated in both outputs. Pass --exclude-branch-variants to the script. |
<path> | Override the analysis-results root (directory containing findings/ and oss-findings/). |
Procedure
Step 1 — Locate the analysis-results root
The harness needs the directory that contains findings/ and oss-findings/.
Resolution order:
- A path passed in
$ARGUMENTS.
$AUDIT_RESULTS_ROOT environment variable.
../analysis-results relative to the ai-security-harness checkout (the normal sibling layout).
- Walk upward from
$PWD looking for a directory that contains findings/_manifest/audit-manifest.csv.
If none resolve, ask the user for the path.
Step 2 — Run the harness script
The skill ships build_executive_summary.py alongside this file. Invoke it with Bash:
python3 "$SKILL_DIR/executive-summary-findings/build_executive_summary.py" \
--results-root <resolved-root> \
[--open] [--include-triage] [--exclude-branch-variants] \
[--by-segment [--inputs-root <hybrid-platforms-inputs>] \
[--exclude-segment SEG]...]
What the script does:
- Walks
<results-root>/findings/ and <results-root>/oss-findings/ for every *security-audit.json (preferred) or *security-audit.md (fallback when no JSON sibling exists). The _manifest/ directory is skipped. When a sibling *-findings-current.json (cumulative report from the track-findings skill) exists, it is used instead of the audit by default: human-confirmed false positives are excluded from every figure, severity counts are recomputed from the surviving findings, and per-finding resolution status feeds a "Remediation Status" section (Markdown) / KPIs + cards (HTML): closure rate across dispositioned repos, a "Findings remediated" count broken down by severity, and a per-month remediation trend (stacked resolved / partially-resolved bar chart, bucketed by each finding's last disposition event date).
- Loads
<results-root>/findings/_manifest/gh-languages-cache.jsonl (produced by /loc-dashboard) to resolve each repository's primary language; falls back to metadata.additional.resource_type or the report's own language row.
- Aggregates:
- Per-severity totals (Critical / High / Medium / Low / Informational)
- A "unique repos" KPI alongside the report count: reports deduplicated
by canonical repository URL (identical across mirror products and branch
re-audits), falling back to the component slug with any
__<branch>
suffix stripped — so "8,391 reports across 3,586 unique repositories"
reads correctly even though totals remain per-report occurrences
- Top Critical & High findings, deduplicated by
(repo, title) and ranked by CVSS
- Credential / secret-leak findings — matched on CWE-798/259/321/522/540/256/312/313/260/547/1392 or a title heuristic (hard-coded, committed secret, API key,
.env, kubeconfig, etc.)
- Common risk themes — CWE → bucket clustering (Injection, Path Traversal, Secrets, Crypto, AuthN/Z, SSRF, TLS, RBAC, Supply-Chain, XSS, DoS, …)
- Per-language severity matrix and repo counts
- With
--by-segment: a per-input-segment severity matrix. Every *-repos.csv under hybrid-platforms-inputs/{openshift,operator-catalog,services} (the ansible input and any --exclude-segment skipped; owners*.csv ignored) is read into a canonical-URL → segment map; each report's metadata.repository resolves to its first matching segment in priority order openshift → operator-catalog → services (totals stay additive; the multi-segment overlap count is reported), and repos absent from the inventories — including all of oss-findings/ — land in unmapped. Renders as a Markdown table and an HTML stacked-bar card + table. --inputs-root defaults to $HP_INPUTS_ROOT or the results root's sibling hybrid-platforms-inputs/.
- Stamps the harness version (
VERSION + short git SHA) into both outputs.
- Writes:
<results-root>/Executive-summary-findings.md — leadership one-pager
<results-root>/Executive-summary-findings.html — self-contained dashboard (Chart.js via CDN: severity doughnut, theme bar, language stacked-bar, Critical/High table, credential-leak table, full per-language table)
Step 3 — Report back
Always tell the user:
- The two output paths (relative to the results root).
- Headline numbers: repos audited, total findings, Critical count, High count, credential-leak count, repos with ≥1 Critical.
- Top 3 risk themes.
If summary was requested, also print the full severity table and the top-10 Critical/High table to the conversation.
Outputs
| File | Purpose |
|---|
Executive-summary-findings.md | Markdown one-pager: severity table, top Critical/High, credential leaks, risk themes, per-language matrix. Suitable for pasting into a doc or Slack. |
Executive-summary-findings.html | Self-contained dashboard with KPI cards, charts, and sortable tables. Suitable for sharing with leadership or attaching to a coordination meeting. |
Both files are written to the top of the analysis-results/ directory and are overwritten on every run.
Operational notes
- No network calls. The script reads only local report files and the existing
gh-languages-cache.jsonl. If the language cache is missing or stale, run /loc-dashboard first (or accept Unknown language buckets).
- JSON preferred, MD fallback. A
*security-audit.md is parsed only when its .json sibling is absent. The MD parser is heuristic — it extracts the metadata table, the severity-count row/table, ### FIND-… headers, inline | Severity | rows, and CWE-\d+ references.
- Triage verdicts are never a default counting source. The trusted precedence is
findings-current.json (ledger: live-validation, verify-remediation, countersigned decisions) → raw audit. --include-triage is the explicit opt-in, and its figures are always labeled. Schema trap if touching triage data: severity is the label, severity_label is the CVSS vector string (score in parentheses).
- Dedup. Critical/High and credential-leak tables deduplicate by
(short-repo, title[:80]) so mirror/symlink products that audit the same repo at the same ref don't double-count in the headline tables. Raw occurrence counts are still shown alongside the unique counts.
- Idempotent. Safe to wire into
finalize_batch.sh or a /loop after each audit batch lands.
- Tuning. Edit the constants at the top of
build_executive_summary.py:
CREDENTIAL_CWES / CREDENTIAL_TITLE_RX — what counts as a secret leak
THEME_MAP — CWE → risk-theme bucket
_LANG_CANON — language-name normalisation
Failure modes
| Symptom | Likely cause | Fix |
|---|
error: could not locate analysis-results root | Wrong working directory / non-standard layout | Pass the path explicitly as $ARGUMENTS or set $AUDIT_RESULTS_ROOT. |
parsed 0 reports | No *security-audit.{json,md} under findings/ yet | Run audits first; this skill summarises completed reports only. |
Most repos show language Unknown | gh-languages-cache.jsonl missing or stale | Run /loc-dashboard to (re)populate the cache, then re-run this skill. |
| HTML charts blank when opened offline | Chart.js loaded from cdn.jsdelivr.net | Open while online, or vendor chart.umd.min.js and update the <script src>. |