All mutable state fields accessed from multiple threads (timer callbacks,
ImeLogTracker events, EspAndHelloTracker events, desktop detection,
background Task.Run) are now protected by a single _stateLock.
Key fixes:
- TryEmitEnrollmentComplete: atomic guard-check-and-claim prevents double
enrollment_complete emission (the most critical race condition)
- RecordSignal: List<string> mutations are now thread-safe
- NotifyDesktopArrived: atomic check-and-set prevents duplicate processing
- CollectDeviceInfo: _enrollmentType/_skipUserStatusPage writes are visible
to concurrent readers immediately after lock release
- OnHelloCompleted/OnWaitingForHelloSafetyTimeout: consistent multi-field
reads prevent wrong branch selection
- SummaryTimerCallback: _stateDirty flag and _stateData serialization are
synchronized to prevent corrupted state persistence
- EmitAppTrackingSummaryIfChanged: hash comparison is atomic to prevent
duplicate summary events
Lock discipline: all lock sections contain ONLY field reads/writes — no
_emitEvent, _logger, timer operations, or external callbacks. This
guarantees no deadlocks and microsecond-level contention.
https://claude.ai/code/session_01EWzmYXFmXUaPpeksi6WGKZ
When CorrelateAsync returned null (zero findings after correct version
filtering), the rescan code path fell through to returning the OLD
stored report instead of persisting the new result. Now properly
handles the null case by deleting the stale report and returning
a clean response.
Added DeleteVulnerabilityReportAsync to the repository interface
and implementations.
https://claude.ai/code/session_01Kv2aAEVqbeFSsyPv7gU7pF
The session ID is generated inside StreamableHTTPServerTransport during
handleRequest() when it processes the initialize method. The previous
code called transports.set() BEFORE handleRequest(), at which point
transport.sessionId was still undefined — so the session was never
stored in the map. Every subsequent request got "No valid session."
https://claude.ai/code/session_01S8iM5fK53kYa54tydpgsNq
The missing piece for dynamic client registration: Claude Code needs
/.well-known/oauth-protected-resource to discover the authorization
server after receiving a 401. Without this, the OAuth discovery chain
never starts and sessions can't be established.
Changes:
- Add GET /.well-known/oauth-protected-resource endpoint (RFC 9728)
- Add WWW-Authenticate header with resource_metadata URL to all 401s
- Update flow documentation in oauth.ts header comment
https://claude.ai/code/session_01S8iM5fK53kYa54tydpgsNq
Remove hardcoded clientId from .mcp.json so clients (Claude Code) can
dynamically register via POST /oauth/register instead of requiring a
pre-configured client ID. The MCP server generates a UUID per client
and stores it in-memory; the real Entra ID credentials stay server-side.
https://claude.ai/code/session_01S8iM5fK53kYa54tydpgsNq
The FilterByVersion wildcard logic was treating CVEs with specific
versions in their CPE URI as affecting ALL versions. Now extracts the
version from the CPE 2.3 URI and compares it against the installed
version, so only truly relevant CVEs are shown.
Also adds missing CPE seed mappings for common Intune/Autopilot
managed software: Intune Management Extension, Company Portal,
ODBC/OLE DB drivers, Remote Desktop, Azure CLI.
https://claude.ai/code/session_01Kv2aAEVqbeFSsyPv7gU7pF