3295 Commits

Author SHA1 Message Date
Oliver Kieselbach 351c8d4b3a bump pointer: internal (activation race fix live, D-378)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-22 21:01:23 +02:00
Oliver Kieselbach e704aa7359 Activation seats the onboarding admin before it activates the tenant; the first-user seat belongs to that member alone (D-378)
Security finding (CWE-367): the activation wrote the approved row first and the OnboardedBy
admin row a few storage round-trips later. In between the tenant was activated with no
members, and auth/me seated any roleless caller of that moment as Admin; every member of the
customer's Entra tenant could poll auth/me across the auto-approve moment.

- TenantApprovalService: uncached pre-check, then the OnboardedBy Admin row (conditional
  insert, best-effort, fresh config read), then the activation row (still the arbiter), then
  the welcome mail. The two tables cannot share a batch, so "before" is the guarantee.
- AuthFunction.BuildAuthResult: with a promotable OnboardedBy the first-user auto-admin goes
  to that UPN only; other roleless callers stay roleless. Without one (rows from before the
  field existed) the first sign-in still becomes the admin. The grant logs at Warning.
- Both first-admin writers use IAdminRepository.TryAddTenantMemberAsync (AddEntityAsync,
  409 = left untouched) instead of an upsert; TenantAdminsService.TryAddTenantAdminAsync
  replaces AddTenantAdminAsync. PreviewWhitelistService.IsApprovedFreshAsync is the pre-check.
- Dead after this: IsTenantAdminAsync on the service, the repository interface and the table
  repository, plus its test.
- Tests pin the write order, the designated-member guard (unit and auth/me end to end) and the
  conditional write; each part was mutated back to the old code and failed its tests.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-22 20:47:53 +02:00
Oliver Kieselbach 622a2437a0 bump pointer: internal (plan note on the customer wording correction, D-377)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-22 19:01:17 +02:00
Oliver Kieselbach 5ceab7e611 bump pointer: internal (bootstrap security findings released, D-377)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-22 18:52:47 +02:00
Oliver Kieselbach 8a7cd5ed98 bump pointer: internal (MSI field check and lab name both MSI paths)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 18:51:18 +02:00
Oliver Kieselbach 15804e2817 bump pointer: internal (bootstrap MSI agent start report closed)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 18:20:39 +02:00
Oliver Kieselbach 194958d3cd bump pointer: internal (bootstrap security findings built, D-377)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
v2.0.1492
2026-10-22 18:07:54 +02:00
Oliver Kieselbach 40323ca57e Bootstrap hardening after a security review, and the MSI channel installs again (D-377)
Findings of a Claude security scan against b4af8c7, each measured first, plus
the MSI channel incident a parallel session found.

- Installer 2.8 extracts the agent ZIP with its own containment check
  (Expand-AgentPayload): every entry is resolved against the payload folder
  before the first write, a rooted or escaping name refuses the package. The
  inbox Expand-Archive of Windows PowerShell 5.1 already refuses such entries
  (measured), so the finding was not exploitable as reported; the guarantee is
  now the script's own, pinned by tests, and Expand-Archive is banned.
- Installer 2.8 also moves PowerShell's own location out of the agent folder
  (Set-Location): on the MSI channel the location pointed at the deleted Msi
  folder and the agent start failed with error 267 since 2.7 went live. When
  the agent program never started, the Agent folder is removed again so a later
  run can install; the stderr line is flattened to one line.
- Run folders younger than an hour stay (a loader may still run there); the
  loader's log is taken only from a run folder that is ours.
- Loader 1.4 (minimum installer 2.8) checks before every download attempt that
  its run folder is still a real folder of ours, opens the download for reading
  before the signature and identity checks and holds it until the run ends, so
  what it checked is what runs and nothing can replace or delete it meanwhile.
- Agent: the protected folder security names Administrators as owner; a folder
  with another owner or a reparse point is not taken as protected, reparse points
  are refused, the base is created protected, install mode protects base and
  Agent and refuses to install when it cannot; every start re-checks both.
- Bootstrap MSI 1.3.0 installs its loader under Program Files, where a standard
  user cannot create the folder first.
- Tests: containment table, hour rule, held handle, owner and junction tests
  (ElevatedFact for the owner assignment), MSI location contract; harness
  scenarios for an escaping package (installer and OOBE script), the hour rule,
  the Program Files layout and an agent that does not start, with the install
  stand-in starting a real program from PowerShell's location. OOBE golden
  regenerated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-22 18:07:51 +02:00
Oliver Kieselbach 1b4a87d467 bump pointer: internal (onboarding role fix live, D-376)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 16:53:00 +02:00
Oliver Kieselbach a43a4e474a New tenants' first admin keeps the Admin role right after activation (D-376)
After auto-activation, a third of new tenants' first admins were answered by
auth/me without a role and sent from the activation page to /progress. The
auto-approve worker promotes the onboarding member on another instance, so the
portal's caches never hear of it: auth/me combined a cached "no row" (refreshed
1.5 s after every activation poll by feedback/status) with a fresh member list,
and neither the role nor the first-user auto-admin applied.

- TenantAdminsService caches only an enabled membership; a missing or disabled
  row is read on every call. IsTenantAdminAsync reads through the same cache.
- auth/me takes the caller's row and "the tenant has any row" from one uncached
  partition read (GetMembershipSnapshotAsync); disabled and application rows
  still count as members.
- PreviewWhitelistService caches only an activation; "not activated" is read on
  every call, so the activation page leaves at the first poll after approval.
  IsApprovedFreshAsync is folded into IsApprovedAsync.
- Removed the dead ClearCacheForTenant, GetMemberRoleAsync and
  CanManageBootstrapAsync.

Tests reproduce the sequence over the real caches (auth/me and the first
dashboard request); each part fails against the old caching.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 16:11:10 +02:00
Oliver Kieselbach 82dafc5e8f bump pointer: internal (bootstrap loader 1.3 / installer 2.7 released, D-375)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 15:33:56 +02:00
Oliver Kieselbach b4af8c77be Bootstrap integration tests read the publish script's output as text
GitHub Actions runs powershell steps with $ErrorActionPreference = 'stop', and
under Windows PowerShell 5.1 a stderr line of a native process redirected with
2>&1 then ends the test: the three publish refusals failed in CI although the
publish refused as it should. The publish dry runs now start pwsh through
System.Diagnostics.Process like every other scenario. Local run under 'stop':
199 of 199.

[skip actions]: published by a dispatch of publish-scripts.yml, channel dev first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 13:59:45 +02:00
Oliver Kieselbach 0bfab326e5 bump pointer: internal (bootstrap loader 1.3 / installer 2.7 built, D-375)
[skip actions]: the scripts go out by a dispatch of publish-scripts.yml, channel dev first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 13:35:10 +02:00
Oliver Kieselbach 3a898b5071 Bootstrap leaves nothing behind: loader 1.3 and installer 2.7 (D-375)
- Loader 1.3 downloads into a protected run folder of its own
  (%ProgramData%\AutopilotMonitor-Loader-<8 hex>), runs the installer only when it
  is signed by the pinned publisher and declares the installer role and version
  2.7 or later, removes the folder after a successful run and never touches the
  agent folder. Unbound arguments now reach the installer by name; the array
  splat bound them by position.
- Installer 2.7 refuses without elevation and decides before it writes: guards
  read HKLM through the 64-bit view (a 32-bit host never saw the deployment
  marker), guard 5 is covered by the agent check. An agent's folder stays as it
  is, a skip without agent removes the folder, an install starts from an emptied,
  protected folder and stages the ZIP in a run folder. It moves the MSI's working
  directory out, writes stderr only on failure and removes the copy older loaders
  left at the fixed path.
- Help texts of both scripts state what customers need; .CHANGELOG, which made
  Get-Help ignore the help, gives way to the Platform Changelog.
- OOBE go-script: the installer's shared helpers, its own elevation check
  (#Requires is ignored under irm | iex), a run folder.
- Publish: version read line-anchored, the loader's own check on the exact
  publish bytes, installers before loaders, -Channel dev (dispatch input).
- Bootstrap MSI 1.2.0; Test-ShouldBootstrapAgent reads the 64-bit view;
  Uninstall removes loader run folders and keeps the Deployed marker.
- Tests: Pester unit suites, an integration harness running the real scripts
  (loader 1.2 and the MSI 1.0 runner frozen in TestData), BootstrapContractTests;
  the gate runs on windows-2022 and windows-2025, once more as SYSTEM, and against
  a real standard user.

[skip actions]: published by a dispatch of publish-scripts.yml, channel dev first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 13:34:54 +02:00
Oliver Kieselbach 9377671349 Customers metrics: each new customer links to its tenant in Tenant Management
The signup list under the chart now links every domain to
/admin/tenants/management?tenantId=…, the deep link the ops events
"View tenant" button already uses, so the list opens narrowed to that tenant.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 10:31:27 +02:00
Oliver Kieselbach 902560692b bump pointer: internal (agent v2.0.1490 docs, web and MCP deployed, D-374)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 10:20:44 +02:00
Oliver Kieselbach 3946eddcea bump pointer: .claude/commands (ux-insights expected values)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 10:18:30 +02:00
Oliver Kieselbach 48e27fb2d6 bump pointer: internal (Mark as Succeeded released as agent v2.0.1490, D-374)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 09:29:17 +02:00
Oliver Kieselbach 41b33895c5 bump pointer: internal (Mark as Succeeded ends a running agent, D-374)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v2.0.1490
2026-10-22 08:53:18 +02:00
Oliver Kieselbach 157273726d Mark as Succeeded ends a running agent, and the agent acts on an admin mark once (D-374)
The backend repeats AdminAction on every telemetry response while the session
row carries it. The agent handled each echo anew: a new admin_marked_session
with an immediate upload, whose response carried the next echo. With a running
agent on a Mark-Succeeded session that looped at the device rate limit until
the agent ended; Mark-Failed ended the agent and stayed bounded.

- ServerControlPlane: AdminActionLatch admits the first AdminAction of a
  process; every later one only logs at Debug.
- AdminAction=Succeeded runs the agent's own termination pipeline with outcome
  Succeeded, like Failed: what the portal's confirmation dialog and the docs
  promise, and what the restart path already did.
- HandleAdminActionTests: Succeeded terminates; repeated echoes, a later
  different outcome and concurrent callers are handled once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 08:51:49 +02:00
Oliver Kieselbach 0d994773d8 bump pointer: internal, .claude/commands (entry channel released)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 18:30:14 +02:00
Oliver Kieselbach 7dd9f81c31 bump pointer: internal, .claude/commands (entry channel, D-373)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 18:23:50 +02:00
Oliver Kieselbach 7d07cb7805 Marketing clicks and page views on www carry the channel their page load came in through (D-373)
On the public host a telemetry initializer adds the referrer's host and
the utm_source, utm_medium and utm_campaign tags of the entry URL to every
page view and custom event. Both come from the document itself
(document.referrer and the navigation entry), so nothing is stored and no
identifier is added; metrics, dependencies and the portal stay as they are.

The hero and final get-started buttons and the three Pro plan links now
navigate client-side, so landing, plans, get-started and the sign-in click
stay one page load. A guard fails on a plain <a> to /get-started or /plans.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 18:23:03 +02:00
Oliver Kieselbach 485a5b4604 bump pointer: internal (TPM-PSS precision released)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 13:08:15 +02:00
Oliver Kieselbach 2109133aee bump pointer: internal, .claude/commands (TPM-PSS TLS probe, D-372)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v2.0.1489
2026-10-21 12:23:07 +02:00
Oliver Kieselbach 7ac7a5e8ba TPM-PSS distress carries a TLS probe: protocol, whether the certificate went out, who answered (D-372)
tls= was empty on every report: .NET Framework's SecureChannelFailure
carries no inner Win32 code. After a terminal SecureChannelFailure the
agent now opens one direct SslStream connection to the backend host with
the client certificate (system-default protocols, normal server
validation, never a proxy) and sends one GET of the agent config with the
agent's headers - the platform asks for the certificate only once it has
seen the path. The message carries tlsp=<protocol>,<m|->,<answer>:
b<status> only when the probe's own correlation id comes back (shared
ResponseOriginClassifier), cc<status> for the platform's "Client
Certificate Required" page, st/x/io otherwise; or unreachable,
hs(<SSPI code>), hs(srvcert), timeout, error. A server certificate not
issued by Microsoft appends ,i=<issuer CN> (TLS inspection).

The probe runs beside the TPM and Schannel reads, all three within 10 s
(4 s per network step), on the failure path only. The Schannel window now
ends when the failure is detected (ReadBetween), so the probe's own
handshake never shows up in sch=. A connect that fails after its timeout
is observed (an unobserved task exception would write crash artefacts);
an answer that began is kept when the server does not close.

Live, .NET Framework 4.8 against production without a certificate:
tls12,-,cc403.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 12:22:56 +02:00
Oliver Kieselbach 014b3e1e83 bump pointer: .claude/commands (platform-health catalog notes)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 12:18:26 +02:00
Oliver Kieselbach b948d32c9a bump pointer: internal, .claude/commands (TPM-PSS precision, D-372)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 11:26:43 +02:00
Oliver Kieselbach 40d3922fe0 TPM-PSS distress says where the device stood when the failure began: ESP phase, OOBE flag, boot; a later success compares reboot and certificate (D-372)
The first reports with context all carried OobeState=completed: Windows'
OOBE flag flips at the desktop, was read at send time after minutes of
retries, and marks the later successful starts too, so it separated
nothing.

Agent: EspPhaseSnapshot reads the ESP registry once with the tracker's
own parser (device_preparation / device_setup / account_setup while
running, <category>_failed, *_done, esp_done, none, unavailable).
DistressDeviceContext (OOBE flag, ESP phase, boot from the uptime
counter) is captured before a registration's first attempt and travels
with the TPM-PSS report; other reports sample it at send time. The
reporter takes a DistressDetail (TPM version, context). sch= is always
set: the events, none, unreadable or timeout. The failed-run record keeps
the ESP phase and OOBE flag, and beside it - never on the wire - the boot
time and the client-certificate thumbprint; the next successful start's
agent_started reports priorRegistrationLastEspPhase, LastOobeState,
RebootedSince (boots more than 2 min apart) and CertChanged, and every
start reports espPhaseAtAgentStart.

Shared/backend: DistressReport.EspPhase (kept as [a-z_] up to 32) and
DeviceBootUtc (kept within 30 days before and 5 minutes after the report),
stored, logged and on the custom event; the TPM panel item carries the ESP
phase. The GA distress detail names the OOBE flag as such and shows ESP
phase and device boot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 11:26:41 +02:00
Oliver Kieselbach 37bd4c73f5 chore(deps-dev): bump vite to 8.3.4 in web and MCP
Closes Dependabot alerts #123 and #124 (GHSA-698v-4r3x-3vcv, launch-editor
escapeCmdArgs() omits % escaping on Windows). Lockfile-only: vite is a
transitive dev dependency of vitest, whose range already allows 8.3.x.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 21:21:07 +02:00
dependabot[bot] 9f4c1efe26 chore(deps): bump hono (#190)
Bumps the mcp-security group with 1 update in the /src/McpServer/autopilot-monitor-mcp directory: [hono](https://github.com/honojs/hono).


Updates `hono` from 4.13.7 to 4.13.13
- [Release notes](https://github.com/honojs/hono/releases)
- [Commits](https://github.com/honojs/hono/compare/v4.13.7...v4.13.13)

---
updated-dependencies:
- dependency-name: hono
  dependency-version: 4.13.13
  dependency-type: indirect
  dependency-group: mcp-security
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-20 21:11:26 +02:00
Oliver Kieselbach 13a34fb7f1 bump pointer: internal (CI credential isolation released)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 18:44:57 +02:00
Oliver Kieselbach 60a41e9778 CI: signing jobs run in the release environment
The code-signing identity trusts only environment:release now, so build-agent
release, build-msi sign-loader and release, and publish-scripts publish run in it.
Every job that federates to Azure carries an environment; the ref is no longer a
trusted subject. Help texts of the storage and endpoint helpers follow: the local
build publishes nothing since D-354.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 18:30:02 +02:00
Oliver Kieselbach 9aa5e46e22 bump pointer: internal (TPM-PSS distress released, D-370)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 17:36:32 +02:00
Oliver Kieselbach 3ba4a10031 bump pointer: internal, .claude/commands (TPM-PSS distress, D-370)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v2.0.1487
2026-10-20 16:56:56 +02:00
Oliver Kieselbach fd0ae98868 TPM-PSS distress says when and where, the tenant panel shows devices that connected later (D-370)
A TpmPssUnsupported report classifies the registration start that failed,
not the device: devices with older TPM firmware fail during setup and
connect after it, on 24H2, 25H2 and 26H2 alike.

Agent: every distress report carries the OS build and the Windows setup
state at send time; the TPM-PSS report adds the TPM manufacturer and
firmware and a language-neutral message with the NCrypt codes of the PSS
signs, the native code of the failed handshake and up to two Schannel
System-log events since the registration started (numeric properties
only). The TPM and System-log reads run in parallel for at most 5 s. The
send is awaited, since the run exits right after it. An attempt that got
an answer resets the last exception, so the probe never classifies an
earlier attempt. The run's last error carries the native code. Win32_Tpm
is read by one shared TpmInfoReader (tpm_status unchanged).

Shared/backend: one body cap for both sides (DistressReportMaxBytes 2048);
the agent shortens its message to stay within it. The endpoint keeps OS
build and setup state only in their format, stores the three fields and
adds them to the log and the custom event. GET audit/tpm-pss-unsupported
returns TPM version, OS build, setup state and connectedAt: the last
upload of a certificate-authenticated session of the serial after the
device's last report (bootstrap sessions excluded, six serials per
SessionsIndex query). The bell says the device could not connect and may
connect after setup and a restart.

Web: the Incompatible TPM panel shows TPM and Status columns, uses the
generated types and no longer says "never" or names a Windows version; the
GA distress detail shows the new fields, Cloud PC and the certificate.

Tests: probe message, Schannel tokens, native codes, payload cap, awaited
send, context bound, last-exception reset, connectedAt rule, filter
comparison limit, repository round trip, panel render. A device serial
from production data in a test fixture is replaced.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 16:56:43 +02:00
Oliver Kieselbach 8101aa1e50 bump pointer: internal (push encryption claim in the docs)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 15:02:55 +02:00
Oliver Kieselbach cf3647da49 bump pointer: internal (docs descriptions within GitBook's 200-character limit)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 14:34:40 +02:00
Oliver Kieselbach cafb1e9a33 bump pointer: internal (push customer release live, D-368, D-369)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 14:28:33 +02:00
Oliver Kieselbach d38265071d bump pointer: internal, .claude/commands (push customer release built, D-368, D-369)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 14:14:12 +02:00
Oliver Kieselbach 40d88ef1e0 Push notifications for every tenant: one Push channel per scope that gates every send, app-role members equal, alerts name the tenant (D-368, D-369)
- Tenant Admins add the Push channel; Telegram and e-mail stay Global-Admin-only
- One Push channel per scope on the tenant and ops paths; the dispatcher fans Push out once per send
- The channel also gates session-watch completions and the device test; paired devices stay listed while it is off
- Members whose role comes from an Entra app role pair and receive like table members; a paused device resumes at the owner's next sign-in
- SLA and failure-streak alerts name the tenant; the id moves to a Tenant ID fact
- The legacy single webhook accepts URL webhook providers only
- Portal: Conditional Access hint in the channel editor, neutral role texts
- Landing: push section with the Notify me when done animation and the hero announcement; SignalR is "live updates"
- Privacy, about and FAQ describe push notifications

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 14:13:58 +02:00
Oliver Kieselbach 14d1266b89 bump pointer: internal (target prefetch removal verified in production, D-366)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 10:48:43 +02:00
Oliver Kieselbach 0730373e9b bump pointer: internal (target prefetch removed, D-366)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 10:31:17 +02:00
Oliver Kieselbach 03f4470db6 The sign-in return no longer prefetches the target route (D-366)
Measured in production with 300 ms latency, router.prefetch loaded only the route tree while auth/me was in flight, and the replace still fetched the route payload after auth/me: no gain on the critical path, one to three extra requests per sign-in return.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 10:31:16 +02:00
Oliver Kieselbach 697077c7e5 bump pointer: internal (sign-in return and preflight cache released, D-366, D-367)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 10:18:35 +02:00
dependabot[bot] befc101d8f chore(deps-dev): bump knip (#187)
Bumps the web group in /src/Web/autopilot-monitor-web with 1 update: [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip).


Updates `knip` from 6.40.0 to 6.41.0
- [Release notes](https://github.com/webpro-nl/knip/releases)
- [Commits](https://github.com/webpro-nl/knip/commits/knip@6.41.0/packages/knip)

---
updated-dependencies:
- dependency-name: knip
  dependency-version: 6.41.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: web
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-20 10:03:08 +02:00
dependabot[bot] 7250baca02 chore(deps-dev): bump jscpd (#189)
Bumps the quality-tooling group in /tools/quality with 1 update: [jscpd](https://github.com/kucherenko/jscpd/tree/HEAD/rust/jscpd).


Updates `jscpd` from 5.4.0 to 5.4.1
- [Release notes](https://github.com/kucherenko/jscpd/releases)
- [Commits](https://github.com/kucherenko/jscpd/commits/v5.4.1/rust/jscpd)

---
updated-dependencies:
- dependency-name: jscpd
  dependency-version: 5.4.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: quality-tooling
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-20 10:02:53 +02:00
dependabot[bot] fa1809d910 chore(deps): bump the mcp group (#188)
Bumps the mcp group in /src/McpServer/autopilot-monitor-mcp with 4 updates: [express](https://github.com/expressjs/express), [zod](https://github.com/colinhacks/zod), [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).


Updates `express` from 5.2.1 to 5.3.0
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/v5.2.1...v5.3.0)

Updates `zod` from 4.6.5 to 4.6.7
- [Release notes](https://github.com/colinhacks/zod/releases)
- [Commits](https://github.com/colinhacks/zod/compare/v4.6.5...v4.6.7)

Updates `knip` from 6.40.0 to 6.41.0
- [Release notes](https://github.com/webpro-nl/knip/releases)
- [Commits](https://github.com/webpro-nl/knip/commits/knip@6.41.0/packages/knip)

Updates `typescript-eslint` from 8.71.1 to 8.71.2
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.71.2/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: express
  dependency-version: 5.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: mcp
- dependency-name: zod
  dependency-version: 4.6.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: mcp
- dependency-name: knip
  dependency-version: 6.41.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: mcp
- dependency-name: typescript-eslint
  dependency-version: 8.71.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: mcp
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-20 10:02:41 +02:00
Oliver Kieselbach 9a718539fd bump pointer: internal, .claude/commands (sign-in return and preflight cache, D-366, D-367)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 02:46:15 +02:00
Oliver Kieselbach a1ba1fd1ab Sign-in returns no longer show the public landing, and API preflights are cached for two hours (D-366, D-367)
- MSAL's in-flight redirect state stays in its tab. storeAuthStateInCookie mirrored it into origin-wide cookies that MSAL reads first, so two tabs signing in at once overwrote each other and the first one failed with state_mismatch, stuck on the landing. A guard test pins the cache config.
- AuthGate keeps the auth-pending overlay for as long as the browser is signed in, so it lifts only once the target route has committed; it used to lift when loading ended and exposed the landing for the whole client navigation. The target's route payload is prefetched while auth/me is in flight.
- A sign-in return that carried a code but ends without an account starts one fresh sign-in from "/", keeping the saved deep link; any further failure in the tab hands over to ProtectedRoute's sign-in card on the deep link, which waits for a click. The auth response is read at module load (the router writes its hydration-time URL back), the tab's temporary MSAL keys are purged before a retry and before the card's sign-in, and loading holds while a redirect leaves the page. New events: auth_redirect_recovered, auth_redirect_recovery_exhausted, auth_redirect_failed (error codes only).
- host.json adds Access-Control-Max-Age: 7200 through the http customHeaders. The Functions host answers every preflight itself and its CORS policy carries no max-age; its custom-headers middleware runs outside the CORS middleware, so the header lands on the preflight too. A guard test pins the entry to exactly that header. The CORS startup log goes: it read settings this host does not use, at a level that never reached the telemetry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 02:46:03 +02:00