Skip to content

feat(security): wire Dependabot alert posture into portfolio risk model#27

Merged
saagpatel merged 2 commits into
mainfrom
feat/security-posture-radar
May 31, 2026
Merged

feat(security): wire Dependabot alert posture into portfolio risk model#27
saagpatel merged 2 commits into
mainfrom
feat/security-posture-radar

Conversation

@saagpatel
Copy link
Copy Markdown
Owner

The radar — a security dimension for the portfolio OS

The 6-factor risk model was 100% context/path/docs — zero security signal. A portfolio-wide Dependabot sweep (61 PRs, ~290 alerts) ran with no instrument tracking it. This wires GHAS Dependabot posture into the truth layer.

What changed

  • Schema 0.4.0 → 0.5.0 — new frozen SecurityFields on every PortfolioTruthProject (Dependabot critical/high/medium/low, code-scanning, secret-scanning, alerts_available). alerts_available distinguishes scanned-clean from never-looked.
  • 7th risk factor active-high-severity-alerts — open high alerts add one factor toward the existing 3+ elevation bar; an open critical alert force-elevates on its own (mirrors the existing weak-context+investigate short-circuit precedent). A lone unpatched critical CVE can't hide in a clean repo.
  • Opt-in overlay--portfolio-truth-include-security reads the latest output/ghas-alerts-<user>-*.json (already name-keyed). Mirrors the release_count_by_name pattern: the truth pipeline itself stays network-free / offline-testable; the CLI precomputes and injects.
  • Weekly digest — new ## Security Posture section, critical-first, distinguishing scanned-clean / unscanned / open-alert states.

Safety

  • Fully inert unless fed — every security default is zero/false, so with the flag off, the new factor never fires and all existing risk tiers are unchanged.
  • Active-gated — like every other factor, the security factor and force-elevate only apply to active/recent repos; alerts on stale/deferred repos don't fire it.

Tests

27 new tests: risk-tier math + force-elevate + active-gate + deferred short-circuit, GHAS→SecurityFields mapping + opt-in overlay end-to-end, digest states, CLI loader. 2140 pass, ruff clean. Smoke-tested against the real 126-repo GHAS file.

Reviewed via python-reviewer — all four invariants (opt-in inertness, active-gate, alerts_available semantics, critical force-elevate) confirmed; no CRITICAL/HIGH findings.

Schema 0.4.0 -> 0.5.0: new SecurityFields (Dependabot / code-scanning /
secret-scanning counts) on every PortfolioTruthProject. New risk factor
active-high-severity-alerts — open high alerts add one factor toward the
3+ elevation threshold; an open critical alert force-elevates on its own,
so a lone unpatched critical CVE cannot hide in a clean repo.

Opt-in via --portfolio-truth-include-security, overlaying the latest
output/ghas-alerts-<user>-*.json (mirrors the release_count overlay; the
truth pipeline itself stays network-free / offline-testable). Weekly
digest gains a ## Security Posture section distinguishing scanned-clean
from unscanned repos. Fully inert unless fed: defaults keep the factor
dormant and all existing risk tiers unchanged.

27 new tests across risk math, GHAS mapping, opt-in, force-elevate,
deferred short-circuit, digest states, and the CLI loader. 2140 pass.
Copy link
Copy Markdown

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

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: 4ec9f4fad6

ℹ️ 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 src/cli.py
Comment on lines +624 to +625
"--portfolio-truth-include-security",
action="store_true",
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 Expose the security overlay flag on the report subcommand

This only registers --portfolio-truth-include-security on the legacy top-level parser, but the documented/normal portfolio truth flow is audit report <user> --portfolio-truth and _build_report_subparser() only adds --portfolio-truth there. In that subcommand context, audit report user --portfolio-truth --portfolio-truth-include-security is rejected by argparse before _run_portfolio_truth_mode() can load the GHAS overlay, so users following the current CLI shape cannot enable the new security risk factor.

Useful? React with 👍 / 👎.

The security overlay is keyed by GitHub repo name, but local dir display
names often differ ("Signal & Noise" vs "signal-noise"), so 40 repos with
open alerts were silently missed. Extract `_select_security_entry`: match
on the repo name from repo_full_name first, fall back to display_name.

Live impact: overlay match rate 113 -> 153 of 161 local projects; e.g.
Signal & Noise (9 high), Devil's Advocate (6), Interruption Resume Studio
(3) now correctly join. 4 join-precedence tests added.
@saagpatel saagpatel merged commit 1e9a8a7 into main May 31, 2026
3 checks passed
@saagpatel saagpatel deleted the feat/security-posture-radar branch May 31, 2026 14:02
saagpatel added a commit that referenced this pull request May 31, 2026
…es (#28)

* feat(security): surface Dependabot posture in portfolio render surfaces

The radar's truth-layer security dimension (RiskFields.security_risk,
SecurityFields Dependabot counts, the active-high-severity-alerts factor)
was wired into the risk model and weekly digest in #27, but the two
human-facing render surfaces — PORTFOLIO-AUDIT-REPORT.md and
project-registry.md — did not surface it. This adds that, mirroring the
digest's Security Posture treatment:

- Portfolio report: a Coverage Summary line + a dedicated '## Security
  Posture' section (TOC entry included) with the same three states as the
  digest — per-repo open high/critical (critical-first, capped at 5),
  'all N scanned clear', or 'overlay not run'.
- Registry: a pipe-free per-repo security flag in the Notes column (fires
  only for scanned repos with open high/critical) plus four aggregate rows
  in the Portfolio Summary table.

Shared _security_overview / _security_attention_items helpers mirror the
digest's aggregation on the in-memory snapshot. The Notes flag is pipe-free
and the summary rows are digit-valued, so the registry still round-trips
through parse_registry unchanged; both markdown validators stay green.

5 new tests cover all three report states, the registry flag + round-trip,
and the unscanned case.

* test(security): guard Security Posture section + cover cap/sort and registry clean path

Addresses code-review findings on the render surfaces:
- validate_portfolio_report_markdown now requires the '## Security Posture'
  header, so the section can't silently vanish in a future refactor (every
  other section header is already guarded).
- New unit test pins _security_attention_items' cap-at-5 and critical-desc /
  high-desc / name-asc sort — the one behavior unique to the attention list.
- Extends the scanned-clear test to assert the registry's per-repo flag is
  absent for a medium-only repo while it still counts as scanned.
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.

1 participant