| name | generate-team-report |
| description | Use when the user asks to generate a shareable findings folder, executive summary, or team-specific security audit report for one or more products from the findings directory. |
| user-invocable | true |
Generate Team Security Report
Creates a self-contained, shareable folder containing all relevant component-level
security audit findings for a given set of products, plus an executive summary HTML
dashboard suitable for coordination meetings.
When to Use
- User asks to "generate a report for [product/team]"
- User asks to "create a shareable folder of findings for [product]"
- User asks for an "executive summary" of findings for specific products
- User asks to "package findings" for coordination with a team
Inputs
$ARGUMENTS is one or more product names, team names, or operator names.
The findings live in the analysis-results sibling repo at:
../analysis-results/findings/<product>/<component>/<component>-security-audit.md
Map user-provided names to directory names under findings/. Common mappings:
| User says | Directories to scan |
|---|
| RHACM, ACM, Advanced Cluster Management | rhacm/, advanced-cluster-management/ |
| MCE, Multicluster Engine | multicluster-engine/, mce/, klusterlet-product/, placement-operator/ |
| Gatekeeper | gatekeeper/, gatekeeper-operator-product/ |
| GlobalHub | globalhub/, multicluster-global-hub-operator-rh/ |
| VolSync | volsync/, volsync-product/ |
| OpenShift GitOps, ArgoCD | openshift-gitops-operator/ |
| OpenShift Pipelines, Tekton | openshift-pipelines-operator-rh/ |
| OpenShift AI, RHODS | rhods-operator/ |
| Service Mesh v2 | servicemeshoperator/ |
| Service Mesh v3 | servicemeshoperator3/ |
| Kiali OSSM | kiali-ossm/ |
| MCP Gateway | mcp-gateway/ |
If the user names a product not in this table, search the findings/ directory
for matching folder names (case-insensitive, partial match).
When a product has mirror directories (e.g. rhacm/ ↔ advanced-cluster-management/),
do not assume they are identical — mirror trees drift as different pipeline
stages (verify-remediation, validation backfill, branch re-audits) land on one
side only. Pick the canonical tree by git recency
(git log -1 --format=%ad -- <dir>), preferring the side that carries
remediation-verification artifacts, then supplement it with files from the other
tree whose basename does not exist in the canonical copy (this is how
release-branch re-audits like <comp>__release-5.1-* usually arrive). For
same-basename files that differ in content, the canonical (fresher) copy wins.
Deduplicate by (product_group, component_basename) key.
Procedure
Step 1 — Locate the Analysis Results Repository
The findings data lives in the sibling repository. Determine its path:
RESULTS_DIR="$(cd "$(dirname "$0")/../analysis-results" 2>/dev/null && pwd)" \
|| RESULTS_DIR="$(find .. -maxdepth 1 -name 'analysis-results' -type d | head -1)"
If running from the analysis-results repo directly, RESULTS_DIR is the
current repo root.
Step 2 — Identify Source Directories
- Search
$RESULTS_DIR/findings/ for all directories matching the requested products.
- List all report artefacts recursively under those directories — every file
matching one of these suffixes:
-security-audit.md, -security-audit.json, -triage.json, -triage.md,
-threat-model.md. The .md audit report drives the executive summary; the
JSON/triage/threat-model siblings are copied through so downstream tooling
(notably the validate-findings skill) can ingest structured data.
- EXCLUDE top-level flat
.md files directly under findings/ — these are
legacy experimental reports scheduled for cleanup.
Step 3 — Extract Severity Data
Severity counts must be disposition-aware wherever the artifacts allow it —
raw audit reports overstate because they still contain findings later refuted or
resolved. Per component, use the best available source, in this precedence:
<component>-findings-current.json (disposition ledger view, harness
≥ 0.27.0). This is the trusted source: its dispositions come from
live-validation events, /verify-remediation reports, and countersigned
human decisions. Per finding: skip disposition.validity == "false_positive"; route validation_status == "hardening" to the Step 3a
backlog; count disposition.resolution == "resolved" separately
(remediation progress, not open exposure); count the rest by the severity
field. disposition.validity == "confirmed" findings are execution-proven —
badge them in the outputs.
<component>-security-audit.{json,md} raw audit counts, labeled
"unverified" in the outputs. Even when a <component>-triage.json
exists, do not count from its verdicts: the early /triage runs
over-marked false positives (unreachable repos, hardening issues misfiled as
FP), and counting "true positives only" silently trusts exactly those bad FP
verdicts — a wrong FP hides a real vulnerability. Triage verdict: "hardening" entries are still trustworthy affirmative classifications and
still feed the Step 3a backlog; when a raw-audit finding can be matched to a
triage hardening verdict (by source finding id, else file+title), move it
out of the vulnerability counts into the backlog.
Only count from unconfirmed triage TP/FP verdicts if the user explicitly asks;
label those components "unconfirmed triage" wherever their numbers appear.
⚠️ Triage schema trap if you do: severity holds the label
(critical/high/…) while severity_label holds the CVSS vector string
with the score in parentheses, e.g. CVSS:3.1/AV:N/... (8.2) — parse the score
with re.search(r'\((\d+\.\d+)\)', severity_label), never feed
severity_label to the severity bucketer.
Report the excluded false-positive and resolved totals in the KPI tiles/README
so readers see which counts are validated and which are raw audit output.
Branch variants: artifacts whose slug contains __ (e.g.
console__release-2.14, search-v2-api__release-5.1-*) are release-branch
re-audits of the same code. Copy them into the package and list them as a
coverage note, but exclude them from severity counts — otherwise one
component is counted once per audited branch.
The audit-md fallback can use the extraction script at
scripts/extract_findings.py relative to this skill's directory:
python3 <skill_dir>/scripts/extract_findings.py \
"$RESULTS_DIR/findings/dir1" "$RESULTS_DIR/findings/dir2" \
--product-map "dir1=ProductName1,dir2=ProductName2" \
> /tmp/findings_data.json
If the script is unavailable, extract inline. For each audit file, scan for lines
containing CVSS and parse score + severity:
cleaned = re.sub(r'v3\.\d+', 'vXX', line)
scores = [float(s) for s in re.findall(r'(?<![/v])(\d+\.\d+)', cleaned)
if 1.0 <= float(s) <= 10.0]
score = max(scores)
sev = re.search(r'\b(Critical|High|Medium|Low|Informational)\b', line, re.IGNORECASE)
Also extract the finding ID and title from the preceding ### FIND-NNN — Title
header. Finding IDs may also use FND-NNN, KC-NN, or OCM-NN prefixes in
some legacy reports.
Step 3a — Extract the Hardening Backlog
When a component carries triage or ledger artifacts, split hardening
findings out of the vulnerability counts (harness ≥ 0.27.0 taxonomy —
see docs/disposition-ledger.md §7):
- From
<component>-triage.json: findings with verdict: "hardening".
- From
<component>-findings-current.json: findings with
validation_status: "hardening" (this wins when both exist).
Collect per component: count, categories, titles + locations, and the
owner hint. These are accurate defense-in-depth/benchmark gaps with no
demonstrated exploit path — engineering hygiene and compliance work,
never presented as vulnerabilities, but never omitted: absent hardening
degrades posture and amplifies co-located vulnerabilities. In the
executive-summary HTML and README (Steps 5-6), render a dedicated
"Hardening Backlog" section AFTER the severity tables: per-component
counts with category breakdown and a one-line framing ("posture debt —
λ-weighted in the portfolio risk index; file as regular engineering work
via /file-security-defect --hardening, not as security defects").
Step 3b — Lift Attack Scenarios from Threat Models
When a component's <component>-threat-model.md contains a
## 9. Attack scenarios section (harness ≥ 0.33.0 emissions), lift it
verbatim into the team package's executive summary and README under an
"Attack scenarios" heading, attributed to the source threat model. These
narratives are written to the schema's bar — present tense, jargon-free,
readable by a non-technical VP — and are the single most effective
persuasion artifact in a team package; never paraphrase them into table
rows. Older threat models without section 9 get no such section (do not
synthesize one from the threat table).
Step 4 — Create Output Folder
Create a folder at the repo root (or user-specified location) named after the
products: <product-slug>-findings/
For single-product reports:
<slug>-findings/
├── Executive-Summary-<Product>.html
├── README.md
├── <ComponentA>/
│ ├── <ComponentA>-security-audit.md
│ ├── <ComponentA>-security-audit.json
│ ├── <ComponentA>-triage.json
│ ├── <ComponentA>-triage.md
│ └── <ComponentA>-threat-model.md # if present
└── <ComponentB>/
└── … (same set)
For multi-product reports, group by product:
<slug>-findings/
├── Executive-Summary-<Products>.html
├── README.md
├── ProductA/
│ ├── componentX/
│ └── componentY/
└── ProductB/
└── componentZ/
Copy the component directories (not symlink) so the folder is fully
self-contained and shareable via email/drive/slack. Use cp -RL so any
symlinked artifacts are dereferenced into real files. After copying, delete
pipeline scratch state from the package — it is internal checkpoint data, not
part of the deliverable:
find <slug>-findings -type d \( -name '.triage-state' -o -name '.threat-model-state' \) -exec rm -rf {} +
find <slug>-findings -name '.DS_Store' -delete
Step 5 — Generate Executive Summary HTML
The HTML dashboard must follow this exact structure and styling. Use the
reference template at references/executive-summary-template.html relative to
this skill's directory for the complete CSS. The key elements are:
1. Document Structure
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{Product} — Security Audit Executive Summary</title>
<style></style>
</head>
<body>
<div class="banner">...</div>
<header>...</header>
<div class="grid">
</div>
<footer>...</footer>
</body>
</html>
2. CSS Variables (required)
:root {
--crit: #cc0000; --high: #ec7a08; --med: #f0ab00;
--low: #3f9c35; --info: #73bcf7;
--rh-red: #ee0000; --ink: #151515; --muted: #6a6e73;
--bg: #f5f5f5; --card: #ffffff;
}
3. Required Sections
| Section | Grid span | Key class | Content |
|---|
| Banner | full width | .banner | RED HAT INTERNAL — EMBARGOED. warning |
| Header | full width | header | Product name(s), component count, frameworks, date, HCMSEC-3528 |
| KPI tiles | 4 × span-3 | .kpi + .crit or .high | Critical count, High count, Total findings, Repos audited |
| Severity bar | span-12 | .sevbar | Proportional colored bar with .c/.h/.m/.l/.i segments + .legend |
| Themes | span-7 | .themes | Grouped vulnerability themes with Critical/High .pill counts |
| Component chart | span-5 | .hbar | Horizontal stacked bars per component/product, C+H count |
| Top findings table | span-8 | table | Columns: Component, Finding, Impact, CVSS. Sort by CVSS desc. Show all Critical + top High. |
| Actions | span-4 | .actions | Ordered list with .tag.now (≤7d), .tag.d30 (≤30d), .tag.d90 (≤90d) time-horizon tags |
| Footer | full width | footer | Source files, methodology note |
4. Key CSS Classes
| Class | Purpose |
|---|
.grid | 12-column CSS grid, 16px gap |
.card | White card with border-radius 6px, shadow |
.kpi .num | 40px bold number |
.sevbar > div | Flex segments with severity background colors |
.hbar .row.tight | Grid: 220px label, 1fr track, 50px value |
.hbar .fill .c / .h | Stacked bar segments (critical red, high orange) |
.pill.c / .pill.h | Inline pill badges for theme counts |
.tag.now / .d30 / .d90 | Priority time-horizon labels |
td.cvss | Bold red CVSS score |
td.comp | Bold component name |
td.imp | Muted impact description |
For multi-product reports, add a td.prod column (font-size 11px, muted) before
the Component column.
5. Theme Identification
Group Critical + High findings into recurring themes. Common themes observed:
- Multi-cluster trust collapse (hub↔spoke CSR/impersonate escalation)
- Over-privileged operator RBAC (secrets/rbac/exec cluster-wide)
- Confused-deputy SSRF → credential exfiltration
- Arbitrary container-image override → privileged execution
- Supply-chain integrity (unpinned refs, mutable tags, curl|sh)
- Auth bypass / hardcoded credentials / secret leakage
- Unauthenticated admin APIs / dashboards
- Code / config injection → RCE
- Approval-gate bypass (mutable thresholds)
- Multi-tenant boundary bypass (cross-namespace, cross-project)
Only include themes that have ≥1 Critical or High finding in the current data.
6. Action Priorities
- ≤7d (
tag.now): Critical findings — unauth RCE/admin, credential
exfiltration, CSR bypass, system:authenticated over-grants
- ≤30d (
tag.d30): High findings requiring code changes — RBAC reduction,
confused-deputy fixes, auth hardening, approval-gate immutability
- ≤90d (
tag.d90): Systemic hardening — supply-chain pinning, cipher
upgrades, defense-in-depth, container hardening
Step 6 — Generate README.md
Include:
RED HAT INTERNAL — EMBARGOED classification
- Date and tracking reference (
HCMSEC-3528)
- Summary table: Component × Severity (Critical/High/Medium/Low/Info/Total)
- Folder structure tree (abbreviated)
- Top Critical findings with one-line impact descriptions
- Source directory references
Step 7 — Redact Secrets in Output
Scan every file in the output folder for secrets and partially redact them
in-place. The original files in analysis-results/ are never modified.
For each match, keep the first 5 characters and replace the remainder
with ...REDACTED (e.g., AKIAI...REDACTED, xoxb-...REDACTED).
Patterns to detect
| Category | Regex | Notes |
|---|
| AWS access keys | AKIA[0-9A-Z]{12,} | |
| GitHub tokens | gh[posru]_[a-zA-Z0-9]{30,} | ghp_, gho_, ghs_, ghr_, ghu_ |
| GitHub fine-grained PATs | github_pat_[a-zA-Z0-9_]{20,} | |
| GitLab tokens | glpat-[a-zA-Z0-9\-_]{20,} | |
| Slack tokens | xox[bpsa]-[a-zA-Z0-9\-]{10,} | xoxb-, xoxp-, xoxs-, xapp- |
| OpenAI / LLM API keys | sk-[a-zA-Z0-9]{20,} | |
| Google API keys | AIza[0-9A-Za-z_\-]{35} | |
| Bearer tokens | Bearer\s+[a-zA-Z0-9._\-]{20,} | |
| JWT tokens | eyJ[a-zA-Z0-9_\-]{10,}\.[a-zA-Z0-9_\-]{10,}\.[a-zA-Z0-9_\-]{10,} | |
| PEM private key blocks | -----BEGIN .*PRIVATE KEY----- through -----END .*PRIVATE KEY----- | See special handling below |
| Password assignments | (password|passwd)\s*[:=]\s*['"][^'"]{8,}['"] | Redact the quoted value only |
| Secret assignments | secret\s*[:=]\s*['"][^'"]{8,}['"] | Redact the quoted value only |
| Token assignments | token\s*[:=]\s*['"][^'"]{8,}['"] | Redact the quoted value only |
| API key assignments | api[_-]?key\s*[:=]\s*['"][^'"]{8,}['"] | Redact the quoted value only |
PEM private key handling
Keep the -----BEGIN / -----END markers. Replace all lines between them
with a single line:
-----BEGIN PRIVATE KEY-----
[REDACTED — PEM key material removed]
-----END PRIVATE KEY-----
This applies to all PEM private key variants (PRIVATE KEY,
RSA PRIVATE KEY, EC PRIVATE KEY, ENCRYPTED PRIVATE KEY, etc.).
Exclusions — do NOT redact
- Git commit SHAs — 40-character hex strings that appear after
commit,
@, sha/, ref, or inside a git/GitHub URL. These are not secrets.
- CVSS scores and version numbers.
- Strings shorter than 8 characters — too short to be a meaningful secret.
- Pattern names used as examples — lines that describe patterns rather than
contain live values (e.g.,
No AKIA…, scan for ghp_). Use judgment: if
the match is inside prose describing what to look for rather than a real
credential, skip it.
Procedure
- Use
grep -rn -P with each pattern above against the output folder to
identify matches.
- For each match, apply the partial redaction using
sed -i or the Edit
tool.
- After all redactions, report a summary: number of redactions, which files
were modified, and a sample of redacted stubs.
Step 8 — Verify
- Confirm file counts match expectations
- Report total size with
du -sh
- Summarize output to user
Output Format
Always present a final summary to the user with:
- Output folder path and total size
- Component × Severity table
- Key critical/high findings highlighted
Notes
- Report files follow an 8-section structure: Executive Summary, OWASP ASVS,
K8s Top 10, CIS/STIG, Supply Chain, Detailed Analysis, Severity Summary,
Remediation Roadmap.
- Finding IDs are sequential within each report (
FIND-001, FIND-002, …).
- When mirror directories exist (same component shipped by multiple products),
the reports often diverge — pick the canonical copy by git recency (see
Inputs) and supplement with unique-basename files from the mirror.
- Some components have 0 CVSS-scored findings (stub reports from failed audits
or org-URL manifest entries). These should still be included in the folder for
completeness but will show 0 in the severity table.