Skip to content

PAM: Path B actuator, secure-desktop dialog, console redesign, and remote-session privacy fixes#2588

Merged
ToddHebebrand merged 16 commits into
mainfrom
ToddHebebrand/PAM-Testing
Jul 18, 2026
Merged

PAM: Path B actuator, secure-desktop dialog, console redesign, and remote-session privacy fixes#2588
ToddHebebrand merged 16 commits into
mainfrom
ToddHebebrand/PAM-Testing

Conversation

@ToddHebebrand

Copy link
Copy Markdown
Collaborator

Rolls up the PAM testing branch: the Path B token-launch actuator, the secure-desktop dialog work, a full redesign of the PAM web console and the Windows-side elevation/session UI, and four real bugs found while live-testing remote sessions against an enrolled Windows device.

Agent — elevation UX

  • Path B token-launch elevation actuator (AP / Path B: prototype CreateProcessAsUser token-mixing successor #1157) and secure-desktop dialog placement: the PAM prompt renders on the Winlogon secure desktop, stays in the target session, and cleans up on panic.
  • Custom Win32 elevation dialog replacing the bare MessageBoxW: shield icon, "x.exe is requesting elevation" headline, field grid (program / signer / user / command line), live expiry countdown with progress bar, Deny-default/Approve buttons. Raw user32/gdi32 only (secure-desktop safe), with a hard fallback to the original MessageBox so a prompt can never be swallowed. The prompt now auto-denies when the broker timeout lapses instead of lingering forever.
  • Session banner polish: Segoe UI, rounded pill sized to its label, live green dot, elapsed-session clock, DPI scaling, and WS_EX_TRANSPARENT so it never swallows clicks.

Web — PAM console

  • Redesigned all five /pam tabs on shared primitives (pam/ui.tsx): house table chrome, skeleton loading, proper empty states, unified buttons/badges/pager/switch.
  • All PAM modals gained visible titles; the rule form is sectioned (Match criteria / Time window), compacted ~40%, and uses a pinned action footer so Cancel/Save are always on screen.
  • New End-user privacy & consent section in the Remote Access configuration-policy tab, exposing the five consent settings that were fully wired server-side but had no UI (prompt mode, consent-unavailable behavior, session-end notice, active-session indicator, technician identity level). Locale strings for en/de/es/fr/pt.

Bugs found live-testing and fixed

  1. Viewer-token WS sessions never showed the connect notice or banner: desktopWs.ts built its start_desktop payload without the prompt block — only the REST route attached it. Prompt construction is now a shared buildRemoteSessionPromptPayload helper used by both paths (system DB context so the context-less WS handler can't silently read 0 rows under FORCE RLS), with a regression test on the WS payload.
  2. Every end-user prompt said "A technician": the API nested identity under prompt.technicianDisplay, but the agent and assist app deserialize flat technicianName/technicianEmail/orgName. The nested object was read by nothing. The API now ships the flat contract.
  3. Windows toasts never executed once: powershell -Command <script> -xml <value> appends trailing argv to the command text (it does not bind script params), so the toast script was a parse error on every invocation. The XML now travels via an environment variable, the script is try/catch-wrapped, and stderr is captured into the helper log.
  4. Toasts from an unregistered AUMID are silently dropped on Server SKUs / recent Win11: the helper now registers a per-user Breeze.Agent AUMID (branded "Breeze") before showing toasts.

Verification

  • Live-tested end to end against an enrolled Windows Server 2022 device: custom elevation dialog on the secure desktop, session banner with identity + elapsed clock, connect/disconnect toasts, and the policy UI round-trip.
  • Web: PAM suites (95), i18n keyUsage/parity, no-silent-mutations, astro check clean. API: remote-session + desktopWs suites, tsc --noEmit clean. Agent: go test -race on userhelper/sessionbroker/heartbeat, go vet and cross-compile for windows/amd64+arm64.

🤖 Generated with Claude Code

Todd Hebebrand and others added 14 commits July 15, 2026 22:52
AP-forward successor to the SendInput/consent.exe actuator (Path A): suppress
consent.exe, then launch the target elevated AS ~breeze_elev via
LogonUser → CreateProcessAsUser (runas model). Ships dark behind
pam_actuator_strategy: token_launch (defaults to sendinput).

Core:
- pam_actuator_strategy config; pamactuator.Strategy/NewWithStrategy;
  tokenLaunchActuator (suppress-then-launch orchestration + testable seams);
  heartbeat strategy selection; target path/cmdline plumbing into
  actuate_elevation; guaranteed-demote-on-failure.
- winTokenLauncher.Launch raw Win32 layer, hardware-validated: UAC split-token
  → linked elevated token; CREATE_NEW_CONSOLE; two-stage SYSTEM-into-session
  helper for desktop-DACL/paint (MaybeRunSessionLaunchHelper).

- MaybeRunSessionLaunchHelper() called first in agent Main(); non-windows no-op
  stub so cross-platform builds compile.

elevation_requests.subject_username already NOT NULL):
- Request.SubjectUsername + reserved SubjectSessionID; pamTarget/actuatePayload
  carry it; RunPamFlow passes ev.SubjectUsername; actuateElevation.ts echoes the
  stored subject_username. Resolver: explicit session (reserved) → username→live
  session (WTSEnumerateSessions/WTSQuerySessionInformation) → console fallback.
  Precedence in the CI-tested platform-neutral session_resolve.go.

Validated on hardware (Win11 dell70601 + Server VM): Case A (console, elevated,
paints) and Case E (RDP — resolver auto-selected the requesting user's session).

Diagnostic scaffolding (diag_windows.go, cmd/pamlaunchtest) is retained for
validation and stripped pre-PR.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Align the /pam console with the app-wide design system: shared ui.tsx
primitives (house table chrome, skeleton loading, empty states, unified
buttons, status/risk badges, pager, switch), visible dialog headers on all
PAM modals, sectioned rule form (Match criteria / Time window), and
consistent filter/toolbar styling. All data-testids, roles, exports, and
i18n keys preserved; two new pamPamRuleModal.sections keys added across
all five locales.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The rule form was a 90vh scroll region with Cancel/Create below the fold
and no visible scrollbar, so the modal read as cut off. Widen it to 2xl,
cap at 85vh, and pin the action footer (rule + signer-group modals) so
only the field area scrolls.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the bare MessageBoxW elevation prompt with a purpose-built Win32
window: shield icon + program headline, aligned field grid (now including
the command line), a live expiry countdown with progress bar, and
Deny-default/Approve buttons. Raw user32/gdi32 only, so it remains legal
on the Winlogon secure desktop; any window-creation failure falls back to
the original MessageBox so an elevation prompt can never be swallowed.
The prompt now also self-dismisses (deny, not user-dismissed) when the
request's broker timeout lapses instead of lingering forever.

Shared content helpers (headline, field list, fallback body, countdown
format) move to an untagged file with unit tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The form still ran ~1050px tall. Shrink it to ~665px: negate toggles
move inline with their field labels, Description shares a row with
Scope, the Enabled checkbox and Preview button move into the pinned
footer (the preview panel renders only once results exist), and the
modal uses denser inputs and rhythm.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The viewer-token desktop WS offer handler (desktopWs.ts) built its
start_desktop payload without the prompt block, so sessions started
through the web viewer never showed the end-user 'technician connected'
notice or the on-screen session indicator banner — despite the policy
defaulting both on. Extract the prompt construction from the REST offer
route into a shared buildRemoteSessionPromptPayload helper (lookups run
in a system DB context so the context-less WS path can't silently read
0 rows under FORCE RLS) and attach it on both paths.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The API shipped technician identity nested under prompt.technicianDisplay,
but every consumer (agent ipc.DesktopPrompt, assist app desktop.rs /
ConsentDialog.tsx) deserializes flat technicianName/technicianEmail/orgName
— so the banner and consent dialogs always fell back to 'A technician'.
Ship the flat contract.

Banner overhaul (Windows user-helper): Segoe UI text, live green dot,
elapsed-session clock (1s timer), DPI-scaled pill sized to its label with
fully rounded ends, and WS_EX_TRANSPARENT so the pill never swallows
clicks. The show seam now carries the session start for the clock.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Windows toasts were raised under an unregistered AppUserModelID, which
Server SKUs and recent Win11 builds accept and silently never display —
the session connect/end notices never appeared. Register a per-user
'Breeze.Agent' AUMID (HKCU, branded 'Breeze') before showing toasts.

The remote_access policy's five consent fields (session prompt mode,
consent-unavailable behavior, session-end notice, active-session
indicator, technician identity level) were fully wired server-side but
had no UI. Add an 'End-user privacy & consent' section to the
RemoteAccessTab, with locale strings for all five languages.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
powershell -Command <script> <args> appends the extra argv tokens to the
command TEXT instead of binding script params, so the toast script was a
parse error on every invocation and no notification ever showed. Pass the
toast XML via an environment variable, wrap the script in try/catch, and
log the captured stderr so future failures are visible.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 16, 2026

Copy link
Copy Markdown

Deploying breeze with  Cloudflare Pages  Cloudflare Pages

Latest commit: fab68b7
Status: ✅  Deploy successful!
Preview URL: https://47b357de.breeze-9te.pages.dev
Branch Preview URL: https://toddhebebrand-pam-testing.breeze-9te.pages.dev

View logs

…ting-3

# Conflicts:
#	agent/internal/heartbeat/pam_flow.go
#	agent/internal/pamactuator/actuator.go
#	agent/internal/sessionbroker/pam_approval_test.go
#	agent/internal/userhelper/pam_dialog_windows.go
#	docs/testing/FEATURE_TEST_LOG.md
- gofmt: re-align bareUsername test map after a longer key was added
  (session_resolve_test.go)
- unused: move pamDialogTitle const from the cross-platform
  pam_dialog_content.go into the windows-only pam_dialog_windows.go — it
  is referenced only by the Win32 dialog + MessageBox renderings, so on
  the linux lint host it read as unused.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ToddHebebrand
ToddHebebrand merged commit d2f0a3d into main Jul 18, 2026
50 checks passed
@ToddHebebrand
ToddHebebrand deleted the ToddHebebrand/PAM-Testing branch July 18, 2026 04:17
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