Skip to content

Audit fixes: the remaining 9 HIGH findings (parsers, analysis, wiring) - #26

Open
Krishcalin wants to merge 3 commits into
mainfrom
audit-remaining-highs
Open

Krishcalin wants to merge 3 commits into
mainfrom
audit-remaining-highs

Conversation

@Krishcalin

Copy link
Copy Markdown
Owner

Audit fixes — the remaining 9 HIGH findings

Closes the last of the 23 highs from the 2026-09-30 adversarial audit (docs/audit/2026-09-30-adversarial-audit.md). Base main; independent of #22–#25. 9 fixes in three commits, each with the regression test the audit noted was missing.

Parser fidelity (fb116f5) — 4

  • Check Point NAT direction: a hide/source NAT carries the built-in "Original" object as its translated destination; counting that as a destination translation misread every source NAT as publishing a host (fabricated exposure). Strip "Original" before deciding direction.
  • NSX mixed group: a group with static members AND a tag condition was typed a plain group, so its static members looked like the complete set. Any dynamic condition now types it dynamic-group (externally resolved).
  • Junos deactivate: | display set renders deactivations as their own deactivate <path> lines (not inactive:), which were ignored — deactivated services/policies reported live. Now collected and applied to the subtree.
  • Junos AAA secret: secret "$9$…" matched no redaction rule and reached provenance excerpts. Added a rule anchored on Junos's quoted form (no clash with FortiOS set secret ENC).

Analysis correctness (51f0925) — 3

  • EOL product shadowing: EOL records were filtered by vendor only, so FortiAnalyzer's supported 7.0 could clear FortiOS's end-of-support 7.0. _best_cycle now restricts to the device's product (normalised containment: asa↔cisco_asa, FortiOS↔fortios).
  • Discovery fingerprint conflict: a string naming two vendors returned one and flagged no conflict. It now yields no vendor (ambiguous), while a single-vendor string still takes its most specific platform.
  • AAA fabricated finding: a never-collected device (already not-evaluated) fell through to the unregistered-device append — a HIGH from absent data. Guarded.

Wiring (643692b) — 2

  • KEV never notified for medium/low: scan() filtered findings to critical/high before KEV promotion, so a medium/low KEV CVE never raised KEV_MATCHED. It now fetches VULN-with-CVE findings so promotion can reach them; non-KEV low ones are dropped, not notified.
  • Retention never ran: JobType.RETENTION had an executor but no creator, and the generic create() needs a device scope. Added create_retention (device-less), a schedule branch, and ensure_system_schedules() seeding a daily retention schedule at scheduler startup. Artefact retention (FR-ADM-01) and the abandoned-SSO-state sweep (FR-AUTH-04, an unauthenticated unbounded-growth table) now run.

Tests

Green across every touched suite: junos (49), checkpoint+cloud/sdn (148), eol (26), discovery-fingerprint+aaa (91), triggers+notifications (24), schedules+retention (34).

With this, all 23 HIGH audit findings are resolved (across #23, #24, #25, and this PR).

🤖 Generated with Claude Code

Krishcalin and others added 3 commits September 30, 2026 07:55
…dit HIGH x4)

- Check Point NAT direction (checkpoint/mgmt.py): a hide/source-NAT rule carries the
  built-in "Original" object as its translated destination, and a non-empty
  translated-destination was read as a destination translation — so every source NAT was
  misclassified as publishing an internal host, a fabricated exposure (FR-FW-04). Strip
  "Original" before deciding direction.
- NSX mixed group typing (vmware/nsx.py): a group with static members AND a tag condition
  was typed a plain 'group', so its static members looked like the complete set and a rule
  using it silently excluded everything the tag matches. Any dynamic condition now types it
  'dynamic-group' (externally resolved); the static members remain as a partial view.
- Junos deactivation (juniper/junos.py): `| display set` — the profile's primary form —
  renders a deactivated statement as its own `deactivate <path>` line, not an `inactive:`
  prefix, and those were ignored, so deactivated services/policies/interfaces reported as
  live. to_set_statements now collects `deactivate` paths and marks any set under one
  inactive (subtree included).
- Junos AAA secret (core/redaction.py): `secret "$9$..."` matched no redaction rule, so
  the RADIUS/TACACS shared secret reached the AAA-server provenance excerpt and flowed into
  findings/reports. Added a rule anchored on Junos's quoted-secret form, which does not
  half-fire on FortiOS `set secret ENC <unquoted>`.

Each adds the regression test the audit noted was missing. Green: junos (49),
checkpoint+cloud/sdn (148), silent-emptiness/parsers/aaa (378 across the touched suites).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…absent data (audit HIGH x3)

- EOL product shadowing (vuln/eol.py): _eol_records filters only by vendor, so a vendor's
  several products versioned in lockstep (FortiOS 7.0 end-of-support, FortiAnalyzer 7.0
  supported) both reached _best_cycle, which matched purely on the numeric cycle and let
  one product's lifecycle clear a real end-of-support finding on another. _best_cycle now
  restricts to records whose product aligns with the device's platform (containment either
  way, so `asa` matches `cisco_asa` and `FortiOS` matches `fortios`), falling back to all
  records when nothing aligns.
- Discovery fingerprint vendor conflict (discovery/fingerprint.py): read_text let a later
  platform-bearing pattern overwrite an earlier vendor-only match, so a string naming two
  vendors returned one, discarded the other, and flagged NO conflict — and fingerprint()
  built a confident verdict from an ambiguous signal. A multi-vendor string now yields no
  vendor; a single-vendor string still takes its most specific platform.
- AAA fabricated finding (aaa_correlation.py): a device in inventory but never collected
  (already counted as not-evaluated) fell through to the unregistered-device append, so a
  device nothing was collected from was reported as a HIGH local-only/unregistered finding.
  Guarded: an uncollected device is skipped from registration analysis.

Each adds the regression test the audit noted was missing. Green: eol (26), discovery
fingerprint + aaa correlation + eol (91 across the touched suites).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…t HIGH x2)

- KEV never notified for medium/low findings (integrations/triggers.py): scan() fetched
  new findings filtered to critical/high BEFORE the KEV promotion loop, so a finding for a
  CISA-KEV CVE scored medium/low (CVSS < 7, common) was excluded and EventKind.KEV_MATCHED
  was never emitted — the "being exploited right now" alert the KEV path exists to raise.
  The query now also fetches every VULN finding with a CVE so KEV promotion can reach it;
  non-KEV low-severity findings are dropped in the loop, not notified.
- Retention never ran (services/jobs.py, services/schedules.py, workers/scheduler.py):
  JobType.RETENTION had an executor (_run_retention) but no creator, and the generic
  create() requires a device scope, so a retention job could not be made. Added
  JobService.create_retention (device-less, like create_notify), a RETENTION branch in
  ScheduleService._fire_one, and ScheduleService.ensure_system_schedules() which seeds a
  daily retention schedule (idempotent), called once at scheduler startup. Artefact
  retention (FR-ADM-01) and the abandoned-SSO-state sweep (FR-AUTH-04, an unauthenticated
  table that otherwise grows without bound) now run automatically.

Adds test_triggers.py (KEV medium promoted to a critical KEV event; non-KEV medium not
notified) and schedule tests (a due retention schedule fires a device-less job; the seed
is idempotent). Green: triggers+notifications (24), schedules+retention (34).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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