Skip to content

feat(mcp): compliance_view MCP App (KLA-405)#31

Merged
jklaassenjc merged 2 commits into
mainfrom
juergen/kla-405-compliance-view
May 20, 2026
Merged

feat(mcp): compliance_view MCP App (KLA-405)#31
jklaassenjc merged 2 commits into
mainfrom
juergen/kla-405-compliance-view

Conversation

@jklaassenjc
Copy link
Copy Markdown
Collaborator

@jklaassenjc jklaassenjc commented May 20, 2026

Closes KLA-405. Audit-friendly compliance snapshot — sibling of KLA-404 device_view, same registerApp pattern.

Summary

  • New no-args tool compliance_view + ui:// resource ui://jc/compliance. Slotted into appSpecs alongside dashboard_view.
  • Four cards: MFA adoption, device encryption (FDE) with per-OS breakdown, password-age histogram, admin inventory.

Backend (internal/mcp/apps_compliance.go)

Three parallel V1 calls:

  1. /systemusers — drives MFA + password-age aggregation in a single scan. MFA scope deliberately excludes suspended and locked accounts so the percentage isn't polluted by inactive seats. Password buckets <30d / 30-60d / 60-90d / >90d with a separate no_data count for users missing password_date so the histogram math stays honest. Both RFC3339 and date-only (2026-01-01) parse paths supported.
  2. /systems — FDE coverage, segmented by OS, sorted by total desc → alphabetical. Returns an unencrypted drill-down list.
  3. /users — admin inventory (distinct V1 endpoint from /systemusers). One snapshot pulls every admin; orgs typically have single-digit admins.

Offender lists (without_mfa, unencrypted) capped at 200 with a *_total field so the UI can show "showing N of M." Per-fetch failures land in Warnings; only the all-failed case errors the tool.

UI (apps_html/compliance.html)

2x2 card grid; each metric leads with a big-number percentage + color-coded coverage bar (green ≥95 %, yellow ≥80 %, red below). Per-OS breakdown table inside the FDE card. Password card shows 4-column histogram. Admin card shows per-admin role, MFA badge, last login. Refresh button reuses jcApp.callTool with empty args.

Tests

  • aggregateUserCompliance — 7-user fixture covering active/suspended/locked exclusion, MFA via both totp_enabled and mfa.configured, every password bucket including no_data
  • aggregateDeviceCompliance — per-OS sort, unencrypted drill-down, unknown-OS fallback
  • aggregateAdmins — alphabetical sort, lastLogin pass-through
  • End-to-end with full httptest server (/systemusers + /systems + /users)
  • Registration + resource-serving assertions mirror device_view
  • TestMCP_ListTools_AllRegistered count bumped 198 → 199

Out of scope (deliberate, follow-up)

  • Per-method MFA breakdown (totp / webauthn / push). Needs the V2 per-user MFA detail endpoint; basic /systemusers only exposes totp_enabled and mfa.configured booleans.
  • Password-expiration-upcoming windows (7 / 30 days). Needs the org's password policy max-age value to compute reliably.
  • CSV / JSON export button. The operator can already pipe the tool's JSON response into jq; a dedicated export tool is a separate small ticket.

Test plan

  • go test ./... clean
  • go vet ./... clean
  • gofmt -l clean on touched files
  • make build && ./jc mcp tools | grep compliance_view finds the tool
  • Manual: render compliance_view from Claude Desktop on a real org and sanity-check the percentages against the existing compliance-report recipe output.

🤖 Generated with Claude Code


Note

Medium Risk
Adds a new MCP App tool that fans out to multiple JumpCloud V1 endpoints and aggregates org-wide compliance metrics; main risk is increased API load and correctness/edge cases in compliance calculations (MFA scope, password-date parsing/bucketing, FDE OS segmentation). Changes are isolated to the MCP app layer and covered by new unit/end-to-end tests, reducing regression risk.

Overview
Adds a new MCP App, compliance_view, with a ui://jc/compliance resource that renders an audit-focused 4-card compliance snapshot (MFA adoption, device encryption/FDE with per-OS breakdown and unencrypted drill-down, password-age histogram, and admin inventory).

Implements backend aggregation in fetchComplianceData via three parallel V1 list calls (/systemusers, /systems, /users), including bounded offender lists, warning-on-partial-failure behavior, and defensive password-date parsing/bucketing (including future dates routed to the >90d bucket).

Introduces the embedded apps_html/compliance.html UI and adds comprehensive tests for the aggregators, tool/resource registration, and an end-to-end httptest server; updates the tool registry test to expect one additional tool (198→199).

Reviewed by Cursor Bugbot for commit 29e644e. Bugbot is set up for automated code reviews on this repo. Configure here.

Audit-friendly compliance snapshot. Four cards: MFA adoption, device
encryption (FDE) with per-OS breakdown, password-age histogram, admin
inventory. Same pattern as KLA-403/404 — no-args tool, slotted into
appSpecs alongside dashboard_view.

Backend (internal/mcp/apps_compliance.go):
- Three parallel V1 calls: /systemusers (drives MFA + password age in
  a single scan), /systems (drives FDE), /users (admin inventory).
- MFA scope intentionally excludes suspended and locked accounts so
  the percentage isn't polluted by inactive seats.
- Password buckets: <30d / 30-60d / 60-90d / >90d, with a no_data
  count for users missing password_date so the histogram math stays
  honest. Both RFC3339 and date-only ("2026-01-01") parse paths
  supported — JumpCloud emits both shapes depending on tenant.
- FDE segmented by OS, sorted by total desc then alphabetical so the
  busiest platforms surface first.
- Offender lists (without_mfa, unencrypted) capped at 200 entries
  with a *_total field so the UI can show "showing N of M".
- Per-fetch failures land in Warnings; only the all-failed case
  returns a tool error.

UI (apps_html/compliance.html):
- 2x2 card grid with a single big number + color-coded coverage bar
  per metric (green >=95 %, yellow >=80 %, red below).
- Truncation notes when the per-card list is capped.
- Refresh button reuses jcApp.callTool with no args.

Tests:
- aggregateUserCompliance: 7-user fixture covering active/suspended/
  locked exclusion, MFA via both totp_enabled and mfa.configured,
  every password bucket including no_data.
- aggregateDeviceCompliance: per-OS sort, unencrypted drill-down,
  unknown-OS fallback.
- aggregateAdmins: alphabetical sort, lastLogin pass-through.
- End-to-end: full httptest server covering /systemusers + /systems +
  /users.
- Registration + resource-serving assertions mirror device_view.
- TestMCP_ListTools_AllRegistered count bumped 198 → 199.

Out of scope (deliberate, follow-up):
- Per-method MFA breakdown (totp / webauthn / push). Needs the V2
  per-user MFA detail endpoint; the basic /systemusers response
  only exposes totp_enabled and mfa.configured booleans.
- Password-expiration-upcoming windows (7 / 30 days). Needs the
  org's password policy max-age value to compute reliably.
- CSV / JSON export button. Currently the operator can already pipe
  the tool's JSON response into jq; a dedicated export tool is a
  separate small ticket.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
if (children) children.forEach(function(c){
if (c == null) return;
if (typeof c === "string") n.appendChild(document.createTextNode(c));
else if (c instanceof Node) n.appendChild(c);
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

Reviewed by Cursor Bugbot for commit 45a1285. Configure here.

Comment thread internal/mcp/apps_compliance.go
Comment thread internal/mcp/apps_html/compliance.html Outdated
…Bugbot KLA-405)

Two real findings from the post-45a1285 review pass.

1. Future password_date inflated the healthy bucket. `now.Sub(t)`
   produces a negative duration, which floors to a negative `days`,
   which satisfies `days < 30` and lands in `bucketCounts[0]` — the
   <30d "fresh" bucket. The complianceAgeLT* comment promised future
   dates would "defensively land in >90d," but the implementation
   didn't. Add an explicit `days < 0` route to bucket 3.

   Causes: clock skew, tenants that record expiration date instead
   of set date, API timestamp oddities. Auditors should see those
   anomalies in the suspicious bucket, not as healthy entries.

   New TestAggregateUserCompliance_FutureDateRoutesToOver90d pins
   the contract.

2. Warning + error banners never rendered. The CSS has
   `#warnings { display: none }` and `#error { display: none }`;
   setting `style.display = ""` only removes the inline override,
   leaving the stylesheet rule in effect. Must set
   `style.display = "block"` to actually show the elements (which
   is what dashboard.html does in the same spot). Partial-failure
   warnings and load-failure errors were silently swallowed.

   `#content`'s display:none is inline only (no stylesheet rule),
   so its `""` clear still works — noted in the code comment.

HTML-only + small Go change. Go tests pass; the HTML change is
visual-only and falls under the existing manual-verify acceptance.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jklaassenjc
Copy link
Copy Markdown
Collaborator Author

On the CodeQL alert (js/xss at apps_html/compliance.html:204) — dismissing as a false positive with false positive rationale.

The flagged line is else if (c instanceof Node) n.appendChild(c); inside the el() helper. CodeQL traces event.data (the postMessage listener in common.js:21) → eventually into el()appendChild(c). But the helper is safe by construction:

  • The only string accepted as a child wraps in document.createTextNode (safe — never parsed as HTML).
  • The only Node accepted as a child must have been built by the same el() function or by another DOM API in the file, all of which are themselves safe (only className and textContent are set; unknown attributes like href/onclick are dropped on the floor rather than forwarded to setAttribute).
  • No innerHTML, outerHTML, insertAdjacentHTML, document.write, eval, or new Function anywhere in this file.

The same helper ships unchanged in apps_html/dashboard.html, user.html, and device.html with no CodeQL alert. CodeQL flagged this file specifically because the additional render functions extend the analyzer's confidence on the existing flow — but the underlying contract hasn't changed.

Refactoring a safe-by-construction helper to silence a false positive would make the code harder to read for everyone with no security benefit; dismissal is the right disposition.

@jklaassenjc jklaassenjc merged commit 520d983 into main May 20, 2026
6 of 7 checks passed
@jklaassenjc jklaassenjc deleted the juergen/kla-405-compliance-view branch May 20, 2026 19:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Development

Successfully merging this pull request may close these issues.

4 participants