Commit Graph

  • 6e4563b2a0 chore: bump internal gitlink (backlog cockpit) Oliver Kieselbach 2026-09-04 03:31:47 +02:00
  • 435aef867c CLAUDE: public half keeps directives, knowledge map, hard rules and code quality; process, working files, OKF, customer-claims detail live in the private half Oliver Kieselbach 2026-09-04 03:15:26 +02:00
  • e76f312adb chore: bump internal gitlink (rules moved into the private instruction half) Oliver Kieselbach 2026-09-04 03:13:44 +02:00
  • eac470e781 chore: bump internal gitlink (lesson) Oliver Kieselbach 2026-09-04 03:12:51 +02:00
  • 685a86215b chore: bump skills and internal gitlinks (skills rework 20 -> 16, MCP-first) Oliver Kieselbach 2026-09-04 03:12:12 +02:00
  • 8a259241b5 chore: operator scripts leave the public tree (versioned in the private infra submodule); scripts/ keeps only Bootstrap and CustomerSetup Oliver Kieselbach 2026-09-04 02:42:56 +02:00
  • fd41dded3d chore: bump internal gitlink (private instructions: operations facts) Oliver Kieselbach 2026-09-04 02:23:47 +02:00
  • db020789e1 chore: private submodule docs/ becomes internal/ (knowledge + decisions + work files + private instructions) Oliver Kieselbach 2026-09-04 02:18:20 +02:00
  • 186c151046 fix(web): tenant filter resolves a typed domain name, session search matches tenant domain and ID Oliver Kieselbach 2026-09-03 18:20:43 +02:00
  • c4c253a03a feat(app-homing): name the add-on roles that block the self-service switch and hand the admin the fix Oliver Kieselbach 2026-09-03 17:08:03 +02:00
  • f833882e07 fix(ops): AgentEmergencyBreak ops event severity follows session state at break time Oliver Kieselbach 2026-09-03 16:03:47 +02:00
  • 0505e5d5bd feat(web): switchable color modes and legend on the geographic performance map Oliver Kieselbach 2026-09-03 15:56:07 +02:00
  • a4140c4e4c docs(readme): refresh screenshots, add social preview source Oliver Kieselbach 2026-09-03 14:50:19 +02:00
  • 0e928b5506 Merge branch 'main' of https://github.com/okieselbach/AutopilotMonitor Oliver Kieselbach 2026-09-03 14:29:28 +02:00
  • ce9efb6f6c docs(repo): AGPL-3.0 root license, MCP section in README, security policy and issue forms Oliver Kieselbach 2026-09-03 14:29:14 +02:00
  • 73961f67f2 build(deps-dev): bump @humanfs/node (#146) dependabot[bot] 2026-09-03 13:27:42 +02:00
  • 48fe68b87c build(deps): bump fast-uri in /src/McpServer/autopilot-monitor-mcp (#143) dependabot[bot] 2026-09-03 13:15:00 +02:00
  • f1820b2b43 build(deps): bump qs in /src/McpServer/autopilot-monitor-mcp (#144) dependabot[bot] 2026-09-03 13:14:46 +02:00
  • 78f810a326 build(deps): bump the mcp group (#145) dependabot[bot] 2026-09-03 13:14:29 +02:00
  • 9207b066db changed dependabot to batching Oliver Kieselbach 2026-09-03 13:06:38 +02:00
  • 0d41f50ace feat(web): answer-engine optimization — problem-framed FAQ on /about, entity graph JSON-LD, llms.txt, AI crawlers in robots Oliver Kieselbach 2026-09-03 00:41:06 +02:00
  • b294d2025d feat(plans): tenant-wide MCP usage-plan override + Community upgrade hint on quota 429 Oliver Kieselbach 2026-09-02 23:51:48 +02:00
  • 12a75ef13d feat(delegation): MCP quota follows the managed tenant, delegated tenant slots, self-service delegation Oliver Kieselbach 2026-09-02 21:37:36 +02:00
  • 5494fd1862 fix(web): re-home hop peeks the post-login deep link instead of re-saving it Oliver Kieselbach 2026-09-02 17:26:28 +02:00
  • 6c6d746532 feat(web): small "Docs" link in every portal page header Oliver Kieselbach 2026-09-02 16:34:37 +02:00
  • 7563ec9158 feat(web): DocsLink shows a one-word label on narrow screens Oliver Kieselbach 2026-09-02 16:18:45 +02:00
  • 4e1a3c6312 feat(auth): prepare the new app registration as the default sign-in app Oliver Kieselbach 2026-09-02 16:18:45 +02:00
  • 3eb1f39c4b feat(web): bulk actions capped at 100 sessions, deletes above 10 require typing DELETE Oliver Kieselbach 2026-09-02 15:26:14 +02:00
  • 7e82decc01 feat(plan): company name in the tenant contact profile, required with the contact address at the Pro trial entry point Oliver Kieselbach 2026-09-02 12:52:08 +02:00
  • af7850d786 Revert "fix(web): MCP Usage page tolerates a backend without the tenant quota fields" Oliver Kieselbach 2026-09-02 12:00:56 +02:00
  • 62c7a29b46 fix(web): MCP Usage page tolerates a backend without the tenant quota fields Oliver Kieselbach 2026-09-02 11:58:33 +02:00
  • 78a043db08 feat(web): shared SectionCardHeader with docs links in every settings section Oliver Kieselbach 2026-09-02 11:02:45 +02:00
  • ec3f0ce237 Merge branch 'main' of https://github.com/okieselbach/AutopilotMonitor Oliver Kieselbach 2026-09-02 10:35:01 +02:00
  • eb47dfb6ce feat(mcp): organization-wide MCP quota next to the per-user quota Oliver Kieselbach 2026-09-02 10:32:15 +02:00
  • 7a2465fecf build(deps-dev): bump postcss-selector-parser (#142) dependabot[bot] 2026-09-02 10:26:09 +02:00
  • 4b8b2a5a6d fix(mcp): AllMembers grants tenant members only, not every authenticated token; per-user overrides stay visible Oliver Kieselbach 2026-09-02 10:03:10 +02:00
  • 4521e248b3 test(agent): V2 Core scenario budgets become one 30 s deadlock guard Oliver Kieselbach 2026-09-02 04:30:41 +02:00
  • 4c32d66a63 ci: print failed-test messages in the .NET test steps Oliver Kieselbach 2026-09-02 04:22:04 +02:00
  • a635ac3bc6 feat(health): operator-only cards and URLs follow the Global-Admin view; stacked URL rows Oliver Kieselbach 2026-09-02 03:57:10 +02:00
  • d59286b8b3 chore(mcp): keep X-MCP-Pretty out of the repo's own client config Oliver Kieselbach 2026-09-02 03:33:51 +02:00
  • 05641d4076 feat(mcp): per-client pretty-printed results via the X-MCP-Pretty header Oliver Kieselbach 2026-09-02 03:30:50 +02:00
  • 7288220341 perf(mcp): smaller results and parallel type walks (perf audit P4) Oliver Kieselbach 2026-09-02 03:13:17 +02:00
  • ea7b850ef0 perf(mcp): lean event projection only on unfiltered reads; filtered reads stay complete Oliver Kieselbach 2026-09-02 02:59:38 +02:00
  • e85dd082d6 perf: read caches, cross-tenant fan-out and lean MCP results (2026-09 perf audit, P1) Oliver Kieselbach 2026-09-02 02:55:18 +02:00
  • 034aaa03c8 feat(web): dashboard Select mode for bulk session delete / device block Oliver Kieselbach 2026-09-02 01:42:11 +02:00
  • 9b6b03dac5 fix(backend): verdict radar carries legacy:{rule} into the sweep:{rule} baseline; derive ESP-message rows as esp_failure_fallback Oliver Kieselbach 2026-09-02 01:01:49 +02:00
  • fd6f359e1c update wording Oliver Kieselbach 2026-09-02 00:54:22 +02:00
  • e99a7d67c0 test(agent): ScenarioTests join the SerialThreading collection Oliver Kieselbach 2026-09-02 00:52:56 +02:00
  • 45a1620f6b chore: remove root build.ps1 Oliver Kieselbach 2026-09-02 00:20:07 +02:00
  • 1955efdae5 test: replace the operator's own tenant/device identities with synthetic values Oliver Kieselbach 2026-09-02 00:13:59 +02:00
  • a5c42de28c chore: move infra/ into a private submodule Oliver Kieselbach 2026-09-02 00:06:38 +02:00
  • 19c4354224 docs: README assets move to .github/assets, drop the architecture link Oliver Kieselbach 2026-09-02 00:02:40 +02:00
  • 8e439176fd chore: move docs/ into a private submodule, drop the change log Oliver Kieselbach 2026-09-01 23:53:59 +02:00
  • 73c0fbdd7e docs: device identity block binding, session-scoped blocks, progress lookup, validator budget Oliver Kieselbach 2026-09-01 23:37:43 +02:00
  • bffb76625c perf(backend): time budget for the Graph device validators Oliver Kieselbach 2026-09-01 23:37:42 +02:00
  • d8ef8b11b0 fix(backend): progress-portal lookup resolves exact serial/device name via index queries Oliver Kieselbach 2026-09-01 23:37:42 +02:00
  • fb49dc9ad2 fix(backend): bind device block/kill to the certificate identity and repair session-scoped auto-unblock Oliver Kieselbach 2026-09-01 23:37:41 +02:00
  • 27bf96f23f docs: demo session import concept (OKF) Oliver Kieselbach 2026-09-01 23:15:27 +02:00
  • 0f1f2e593f chore: bump .claude/commands (import-session skill) Oliver Kieselbach 2026-09-01 23:14:51 +02:00
  • 4b5ae562c7 fix(web): drop retired /docs, /roadmap and /changelog from sitemap lastmod generator Oliver Kieselbach 2026-09-01 22:55:54 +02:00
  • d6783fe327 feat: free-text note search on the annotation lists Oliver Kieselbach 2026-09-01 22:49:35 +02:00
  • 2fe1095758 perf(raw-events): budgeted, resumable cross-session scan; tenant from index rows; MCP pageSize override + timeout retry Oliver Kieselbach 2026-09-01 22:35:46 +02:00
  • 8a3b4136a6 feat(web): internal annotation lane follows the Global-Admin view Oliver Kieselbach 2026-09-01 20:43:58 +02:00
  • 7323ca72b2 feat(web): demo presentation mode for live product demos (#141) Oliver Kieselbach 2026-09-01 20:30:54 +02:00
  • 52ae6541a7 feat(agent): signal-pipeline health block in agent_metrics_snapshot v2.0.1446 Oliver Kieselbach 2026-09-01 18:26:22 +02:00
  • d763a885e2 Ops alert channels, ops-events server-side filters, and generated MCP tool vocabularies (#140) Oliver Kieselbach 2026-09-01 16:57:41 +02:00
  • eedca11214 build(deps): bump the web group (#139) dependabot[bot] 2026-09-01 15:53:05 +02:00
  • 73a6817713 build(deps): bump github/codeql-action from 4.37.3 to 4.37.9 (#138) dependabot[bot] 2026-09-01 15:52:26 +02:00
  • 0a0c323f73 fix(backend): CMTrace skew tripwire judges only the current ingest era Oliver Kieselbach 2026-09-01 13:53:59 +02:00
  • bac26def52 commands: bump to ad441d6 (ux-insights gegen die reale Telemetrie verifiziert) Oliver Kieselbach 2026-08-31 19:17:53 +02:00
  • 4c70ea8a0a feat(web): /buy — Marketplace card first, direct-purchase channel named Cleverbridge Oliver Kieselbach 2026-08-31 18:15:28 +02:00
  • eaef1601d0 feat(backend,mcp,web): typisierung follow-up — the deliberately-untyped remainder is typed end-to-end, and the raw table entities leave the wire (#137) Oliver Kieselbach 2026-08-31 15:22:16 +02:00
  • 51737174b2 feat(backend,mcp): typed API contract COMPLETED — every remaining top-level success body is a manifest-exported wire DTO, and the guard now catches the bypass shapes that let them escape. The 08-31 migration's ratchet only saw inline WriteAsJsonAsync(new {...}); four bypass classes had kept ~20 endpoint families untyped: (A) Shared classes without IApiResponse — GeographicMetricsResponse, FleetHealthMetrics, ImePatternHealthResponse, SlaMetricsResponse, PlatformUsageMetrics now implement it (wire byte-identical, interface is empty); (B) Functions-local named response families moved verbatim to Shared — PlatformAgentMetricsResponse/SessionAgentMetric/DeliveryLatencyMetrics/CrashRateMetrics/CrashExceptionSummary, AgentEfficiencyMetricsResponse family (CloneAsCacheHit relocated into the service; init→set for netstandard2.0), OffboardResponse; (C) anonymous/object builders converted to DTOs with old-vs-new wire-parity facts (TypedContractCompletionWireParityTests, 14 facts incl. every vanishing-key case): rule-stats x2 (RuleStatsResponse; uniqueRules is global-only via an optional slot at its exact wire position), verdict-calibration (builder renamed VerdictCalibrationResponseBuilder, PathRow/TrendWindow to Shared), metrics/app (MetricsMath), apps list/analytics/sessions x6 routes (AppsAnalyticsHelper; legacy + paged list share ONE DTO via optional paging keys), ime-versions lean projection (ImeVersionHistoryLeanEntry [WireContract]; bare-array envelope stays, typed as IReadOnlyList<object>), device-journeys + time-attribution fleet envelopes, feature-flags, diagnostics/paths, session restore, cascade-delete Enqueued arm (error arms stay anonymous by design), feedback eligibility/list; (D) Serialize+WriteString path — ListBackupsResponse under the backup surface's own serializer options; AppInsightsQuery's Serialize is an outgoing REQUEST body (not response debt, baselined as such). TypedResponseGuardTests gains three ratchets with mutation-proven red/green: WriteAsJsonAsync(<variable assigned an anonymous object>) (empty baseline), JsonSerializer.Serialize(new{ (error-tolerant; 1 documented request-body entry), and object/Task<object>-returning Build*/Compute*/*Payload/*Response methods (1 documented union entry); the local-WriteJson-wrapper class is closed structurally (generic T : IApiResponse — an interface-typed parameter would serialize zero properties, the generic keeps the concrete type). Manifest regenerated 208→298 exported types; MCP adopts the new types (get_rule_stats typed with the trend-drop as explicit output shaping, get_platform_metrics drops its hand-declared mirror for PlatformAgentMetricsResponse, verdict-calibration-shape derives its rows via extends Omit<generated wire row> so backend renames go tsc-red). Six backend shape-test files switched from default-options serialization to the production wire options via TestWire (ApiJsonOptions; the old default-options runs only matched while payloads were anonymous with lowercase members), and three assertions that pinned null-VALUED keys now pin key ABSENCE — production always omitted them (WhenWritingNull); the parity facts prove the wire itself is unchanged. Deliberately still untyped: auth/me, CheckMcpAccess dictionary, TenantAdmins/GlobalAdmins entity-row arrays and the object-slot payloads (RuleDryRun, HealthCheck, TenantConfigFieldSchema, ...) — the documented read-model follow-up. Suites: Backend 5323, DecisionCore 577, Agent 2805, SummaryDialog 44, MCP 617, Web tsc 0 + vitest 957, generator idempotent. Oliver Kieselbach 2026-08-31 04:32:51 +02:00
  • c04280cf68 feat(mcp): MCP server adopts the generated wire types — the codegen now writes a second copy of wire-types.generated.ts into McpServer/src/generated (MCP_HEADER, buildMcpWireTypesSource, same freshness contract), pinned by a new MCP vitest freshness test (repo-root discovery like event-types-drift, byte-equal to a fresh codegen run, mutation-proven red/green) and enforced in the CI shared-manifests-in-sync diff check; the hand-declared response shapes and structural inline casts in tools/*.ts now import the generated types on the INPUT side only (tool output contracts unchanged): sessions.ts (GetSessionResponse/GetSessionEventsResponse/GetRuleResultsResponse/GetSessionAnnotationsResponse/DiagnosticsDownloadTicketResponse; get_session_summary events are Partial<EnrollmentEvent>, RuleResult drops the dead ruleTitle??title fallback), admin.ts (usage union GetMcpUserUsage/GetGlobalMcpUsage/GetGlobalMcpUsageDaily, GeographicLocationSessionsLeanResponse, GetAllTenantConfigurationsResponse, QueryRawTableResponse, GetUnmatchedSoftwareResponse, SoftwareInventoryResponse; the client-side pagers pin their wire-critical records/summaries/inventory key names via satisfies keyof so a backend rename goes tsc-red instead of silently paging nothing), search.ts (GetSessionEventsResponse on both event readers, SearchSessionsResponse on the legacy fallback, RawEventsPage = Partial<Pick<QueryRawEventsResponse,...>>), rules.ts (DryRunAnalyzeRuleResponse envelope; the inner RuleDryRun trace stays a local reader because the wire type is deliberately unknown); the raw-session fallback in get_session_diagnostics/get_session_summary is removed — { success, session } IS the pinned contract, and the diag test now asserts a non-enveloped object surfaces as a loud tool error instead of being silently absorbed; rule-stats, global/metrics/platform and verdict-calibration keep local readers (backend still serializes anonymous objects there — no DTO exists to import); MCP suite 617 green, tsc green, generator idempotent Oliver Kieselbach 2026-08-31 03:19:47 +02:00
  • d055eed074 Typed API contract: every success response is a wire DTO, generated end-to-end into TypeScript (#136) Oliver Kieselbach 2026-08-31 02:30:39 +02:00
  • 2b6565b3cb fix(agent): IME matcher line budget charges thread CPU cycles, regex timeouts get one starvation-gated retry - the wall-clock budget and the 1s .NET regex timeout both measured guest-scheduler starvation as match cost on CPU-saturated Hyper-V VMs (session b9f1d134: PS-AGENT-OUTPUT timed out on a line it genuinely matches in 5.8us, factor ~170,000, with 2.5s of process-wide log silence and CPU pinned at 100% during the DeviceSetup script burst; six spurious budget breaks the same pass); MatchLine now charges the per-line budget in cycles the poll thread actually executed (QueryThreadCycleTime, probed once, Stopwatch wall-tick fallback preserving the old behaviour exactly), and a RegexMatchTimeoutException whose attempt consumed < 0.5s of match cost (starvation, not spinning) is retried exactly once - counted into the new regexTimeoutRetries (recovered, NOT skipped work, no degraded event) while an attempt at/over the gate is skipped without a second helping of CPU, so regexTimeouts now means "really burned CPU"; the gate is unreachable in the wall-clock fallback by construction (no retry there); retries surfaced through health snapshot, restart-safe persistence, ime_tracker_degraded, ime_pattern_hits and agent_metrics_snapshot (ime_regex_timeout_retries); known limit: hypervisor-level vCPU steal stays invisible to the guest kernel and still charges cycles - strict improvement over wall clock, not perfect steal accounting v2.0.1445 Oliver Kieselbach 2026-08-30 21:49:15 +02:00
  • 5f3ae2d06b fix(backend): SessionTenantLookup is claimed first-writer-wins at registration — the sessionId→tenantId row that every Global Admin/Reader cross-tenant session resolve trusts was an unconditional last-writer-wins upsert AFTER the session write keyed by the client-supplied sessionId, so any tenant's device (self-service onboarding) that knew a victim session GUID (portal deep links, Teams/Slack notifications, progress-portal URLs) could re-point the mapping at its own tenant and have every no-tenantId GA read and GA write (globaladmin annotation lane, restore fallback) silently served from a forged session; StoreSessionAsync now claims the row create-only BEFORE any Sessions/SessionsIndex write (409 → read owner: same tenant = re-registration, foreign tenant = SessionTenantConflictException → 409 session_owner_mismatch without disclosing the owner + SessionTenantConflict ops event, nothing written, not fail-soft), the legacy self-heal write is Add-only, ResolveSessionScopeAsync(requireGlobalAdmin) corroborates the resolved tenant against its Sessions row (silent, no operator confirmation), and scripts/Migration/Backfill-SessionTenantLookup.ps1 closed the pre-table window (13509 sessions, 8795 rows added, verify dry-run added=0); no tombstone on deletion by design v2.0.1444 Oliver Kieselbach 2026-08-30 20:34:50 +02:00
  • 83e15e49de fix(core,backend): every payload-derived string stored into DecisionState is bounded (FactStringBounds, 256) and the reducer-verification replay streams signals — RealmJoinFacts.WithPackageStarted/Completed left PackageId/Version/Scope unbounded and deduped via a linear ordinal compare over up to 200 rows, so a forged 5000-signal stream with ~400 KB near-identical package ids cost minutes of CPU per GET sessions/{id}/reducer-verification while ReducerVerifier held all decoded payloads (~GBs) live; keys are now bounded BEFORE the dedupe scan (forged near-duplicates collapse to one row), Scope normalised to machine/user, and the same bound applied to failed app ids, HelloOutcome, ImeMatchedPatternId, EspAdvisoryFailureCategory and RJ product version/channel incl. WithVersionOverride — never truncating real telemetry (registry key names ≤ 255, GUIDs, enum-like strings); the agent adapter bounds packageId/version at the source; ReducerVerifier replays decode-fold-discard (head-signal check before the journal so stubbed streams stay silent, mid-stream truncation and count-imbalance semantics unchanged, pinned by tests); ISignalRepository.QueryBySessionAsync carries a cumulative PayloadJson budget (SignalQueryLimits, 32 M chars) reflected in the truncated flag of sessions/{id}/signals and reducer-verification; no ingest cap — stored signals stay verbatim Oliver Kieselbach 2026-08-30 20:06:51 +02:00
  • 3c8ef02191 feat(agent): RealmJoin self-update is observed and reported as realmjoin_autoupdate_detected — realmjoin_detected read the version once from RealmJoin.exe, but the tray AppUpdater often replaces the MSI-shipped binary on its first start minutes after detection (gktatooine 946ccbd6: 4.21.4 -> 4.21.18, whole deployment ran on the new build while the timeline and RealmJoinFacts kept 4.21.4); the updater's only registry write is HKLM\...\Uninstall\RealmJoin (DisplayVersion/InstallDate), so RealmJoinWatcher arms a RegNotifyChangeKeyValue watcher there once Detected fired (appearance-watch on the Uninstall parent until the tray creates the entry), re-reads the binary version after a 2 s debounce (DisplayVersion fallback while the file is locked, channel from baseline) and emits the Info event {previousVersion,newVersion,releaseChannel} plus the typed RealmJoinAutoUpdateDetected signal, whose reducer overrides RealmJoinFacts.ProductVersion/ReleaseChannel (WithVersionOverride) and touches neither gate, deadline nor LastActivityUtc; unreadable baseline at detection is seeded silently; portal device card prefers the updated version ("auto-updated from X"); event registered in Constants/resource-catalog/shared-manifests with replay fixture and watcher/adapter/reducer tests Oliver Kieselbach 2026-08-30 20:02:39 +02:00
  • 251e82a45a fix(mcp): tool-call logging never records tenant-config secret values — withToolTelemetry/summarizeArgs gain a per-argument ArgPolicy ('keys' | 'drop') declared at the call site, update_tenant_config logs only the NAMES of args.fields (webhook/SAS URLs and custom headers previously went verbatim, up to 200 chars, into the container log stream under the IaC-default MCP_TOOL_LOGGING=true, readable by log consumers below Global Admin); values remain in the GA-gated backup snapshot and audit log. Backend: TenantConfigPatchService no longer returns the Newtonsoft ex.Message on a JSON type mismatch (it quotes the offending value, so a secret sent into a numeric field echoed into the tool response and log line) — only the field path is named. Tests for keys-only/drop rendering, log-line content on success and soft-error paths, and the backend error text Oliver Kieselbach 2026-08-30 19:58:23 +02:00
  • 01311898a8 fix(ci): no third-party action holds release/deploy credentials by mutable tag — build-agent creates releases with first-party gh release create (job holds contents/id-token/attestations:write), combine-rules pushes generated outputs with plain git (contents:write on main), deploy-web builds the static export in CI (setup-node 22, npm ci, next build — same path ci.yml tests) and uploads it with the exactly pinned SWA CLI 2.0.10 instead of Azure/static-web-apps-deploy (Oryx in-container build, deploy token as action input), attests portal-<sha>.tar.gz and drops the unusable per-PR preview environments; azure/login and Azure/functions-action are SHA-pinned with version comments for Dependabot Oliver Kieselbach 2026-08-30 18:21:19 +02:00
  • a5bca8a730 fix(web): CSV exports neutralize spreadsheet formula triggers in device-controlled cells (CWE-1236) — generateCsvExport/generateSessionCsvExport/generateRuleResultsCsvExport only doubled quotes, so an event Message, EventType, Source, DataJson, DeviceName, SerialNumber, FailureReason or rule explanation starting with =, +, -, @, tab or CR was evaluated as a formula (HYPERLINK/WEBSERVICE exfiltration, DDE) when a tenant admin opened the exported CSV or the operator opened the problem-report attachment; shared csvCell() now prefixes such cells with an apostrophe in addition to quoting, with regression tests for the DDE/HYPERLINK payloads and all three exporters Oliver Kieselbach 2026-08-30 17:54:40 +02:00
  • 87cb1f1cea fix(backend): mcp-usage/user/{oid} filters usage rows to the caller's tenant and never signals foreign existence — UsageCrossTenantGuard skipped rows with an empty TenantId (legacy rows without the column, tokens without a resolvable tid), so a self-service tenant admin of any tenant could read another tenant's user usage (UPN, endpoints/tool names, per-day counts) by oid; and the distinct 403 CrossTenantAccessDenied vs 200-empty made the route a cross-tenant user-existence oracle; FilterForCaller now returns only rows whose TenantId equals the caller's tid for non-global callers (empty TenantId or empty caller tid ⇒ nothing), the handler always answers 200 with the filtered set (foreign oid ≡ unknown oid), dropped rows are logged as an ops warning only; tests rewritten for the filter semantics Oliver Kieselbach 2026-08-30 17:38:16 +02:00
  • 44dd8161df fix(agent): ImeProcessWatcher verifies process identity before attaching and re-arms discovery after an exit — it attached to the first IntuneManagementExtension name match, so a standard user starting a renamed binary during AccountSetup could forge an ime_process_exited Warning and mute the real signal for the rest of the session (CWE-807), and even a legitimate IME service restart was never watched again; ImeProcessIdentity requires session 0 + image under the IME install root (QueryFullProcessImageName, cross-bitness), prefers the oldest trusted candidate, logs untrusted matches once per PID, and OnImeExited restarts the discovery timer (ReferenceEquals guard against duplicate events) v2.0.1443 Oliver Kieselbach 2026-08-30 17:07:18 +02:00
  • c84a1c23eb fix(backend): registration StartedAt is clamped to the event skew window (+24h / -168h around server time) in the shared register core — the device-supplied anchor drives the SessionsIndex RowKey, the retention filter, TotalToday and the supersede-predecessor comparison, so a far-future value pinned the row at the head of every list (incl. the Global Admin cross-tenant list), inflated today's counters forever and never became a retention candidate; both cert-auth and bootstrap entries flow through ProcessRegisterAsync, re-registration keeps the earlier stored anchor (min-merge) Oliver Kieselbach 2026-08-30 17:03:05 +02:00
  • 4a7eb2749d fix(backend): bootstrap revoke enforces code ownership — DELETE /api/bootstrap/sessions/{code} only scoped the caller's ?tenantId while the repository resolved the owning tenant from CodeLookup and revoked any tenant's code; RevokeBootstrapSessionAsync now takes the caller tenant and refuses a mismatch as not-found (no owner leak, foreign partition never read), with regression tests Oliver Kieselbach 2026-08-30 16:59:32 +02:00
  • a536553ee3 fix(web): IME Tracker card shows skip counters without the ime_tracker_degraded pointer — a tenant admin cannot act on it; a tooltip explains that pattern coverage may be incomplete and points to Report Session Oliver Kieselbach 2026-08-30 16:38:07 +02:00
  • ce202c6d47 fix(ops): SlaBreachNotification ops event prints Duration breaches in minutes — the P95 value carried the % unit of the rate breach types ("Duration 864.0% (target 60.0%)"); the unit now follows SlaBreachType like the customer-facing alert builder v2.0.1442 Oliver Kieselbach 2026-08-30 16:19:59 +02:00
  • 74902bf185 fix(agent): IME per-line matching budget counts Regex.Match time only — the wall-clock window also held the match handlers and scheduler stalls, and session 946ccbd6 (Hyper-V guest, 5–10 s process-wide stalls, 1 MB log, no line over 5 KB) broke it twice on genuine lines and silently skipped their remaining patterns; ime_pattern_hits is Debug severity (operator/backend signal, hidden from the default timeline — the IME Tracker card and the ingest fold read it regardless); the session page's IME Tracker card moves to the right column under Hardware Oliver Kieselbach 2026-08-30 16:00:28 +02:00
  • 4f021852ee updated command v2.0.1441 Oliver Kieselbach 2026-08-30 15:14:26 +02:00
  • 7a3516e570 feat(observability): IME pattern-drift loop — nothing the log tracker skips or stops matching stays silent: the agent keeps cumulative tracker health counters and a per-pattern match histogram (restart-safe in ImeTrackerState), reports ime_* keys in agent_metrics_snapshot (files tailed, backlog bytes, lines read, matches, oversized/timeouts/budget breaks/held tails/unanchored), raises a one-shot immediate-upload ime_tracker_degraded Warning on the first pass that skipped work, and emits ime_pattern_hits at termination (every enabled pattern, zeros included, IME version); the backend folds the histogram into the permanent per-IME-version ImePatternStats table (built-in IDs only, plausible version only, one partition read + one transactional batch) and evaluates drift without a hard-coded list — baseline = the version with the most reporting sessions (≥100), expected = ≥80 % hit rate there, drift = an expected pattern matched in none of ≥25 sessions on a newer version — raising ImePatternDriftSuspected once per version×pattern (ETag-stamped, dual-registered in the web ops-event list with a new guard test); GET metrics/ime-pattern-health (GlobalReadOrAdmin) + Global Admin → Metrics → IME Pattern Health matrix (drift/silent/low/few cells, open alerts, retired patterns) + session-page "IME Tracker" rows (latest snapshot by max sequence) + MCP get_ime_pattern_health (GA/Reader) Oliver Kieselbach 2026-08-30 15:12:38 +02:00
  • 6907a41a84 updated command Oliver Kieselbach 2026-08-30 14:40:27 +02:00
  • f4a5eac539 chore(rules): IME pattern-pack hygiene against the IME decompiles (1.97.107.0 / 1.104.102.0 / 1.105.103.0) — no wording drift in any shipped pattern; the 11 disabled legacy patterns whose literals last existed in IME 1.50 (fleet minimum 1.97) are deleted instead of left as re-enable traps (IME-DETECTED-OLD, IME-SET-CURRENT-1/2/3, IME-SKIP-APPLICABILITY-NOTMET, IME-SKIP-DEPENDENCY-DETECT, IME-SKIP-DETECTED, IME-SKIP-UNINSTALL-NOTDETECTED-2, IME-UPDATE-NAME-1/2/3); IME-DO-TIMEOUT-1/2 match the exact source literal "DO downloading is not finished within timeout, jobId = …, fileId = …" instead of two lookaheads; IME-DO-TEL documented as emitter-less from 1.104 on and kept only for the 1.101/1.103 fleet share Oliver Kieselbach 2026-08-30 14:23:14 +02:00
  • 09492e908c fix(security): IME log-pattern pack is anchored and linear (all 90 patterns start with ^, IME-APP-VERSION spells the report-delta JSON out instead of a chained [^}]*? scan) and the ImeLogTracker read loop is bounded — BoundedLineReader (32 MB per line, evidence: real 5.6 MB "Get policies" lines; exact byte bookmarks, no decoder read-ahead loss on cancel), 2 s per-line matching budget across all patterns, held-back unterminated EOF tails (≤1 MB, 1 s settle) instead of dropping them and raw-matching fragments, a complete entry after a dropped one is no longer swallowed as its close, one Warning per pass (oversizedLines/regexTimeouts/lineBudgetBreaks) instead of silent Debug timeouts; unanchored PS-SCRIPT-CONTEXT false positives on [Win32App] SideCarScript… lines (which overwrote the pending platform script's RunContext) are gone; --run-ime-matching assembles multiline entries like the tracker Oliver Kieselbach 2026-08-30 14:03:50 +02:00
  • f432353efa fix(security): startup self-update no longer trusts the on-disk remote-config.json cache — Program.LoadCachedSelfUpdateContext (raw deserialize, bypassed the read-side strip) is removed, the boot-time check runs with allowDowngrade:false and the version-v2.json manifest hash as its only integrity gate; LatestAgentSha256 (update-ZIP hash, ranked above the manifest hash in SelfUpdater) joins the CacheConfig/LoadCachedConfig strip lists so neither the startup nor the FromCache runtime path can take a planted ZIP hash or downgrade flag; verify+extract already share one exclusive handle (1daf631a) Oliver Kieselbach 2026-08-30 13:53:00 +02:00
  • 1daf631a02 fix(security): self-update verifies and extracts the ZIP through one exclusive FileShare.None handle — SHA-256 is computed on the same stream the ZipArchive reads, so the file can no longer be swapped between verification and extraction (TOCTOU); handle is released before cleanup, open failure aborts with integrity_mismatch marker v2.0.1440 Oliver Kieselbach 2026-08-30 13:03:50 +02:00
  • d0190f443d fix(ci): build-agent workflow_dispatch build_number can no longer inject PowerShell — the input and the agent blob SAS reach the version step only via env (BUILD_NUMBER_OVERRIDE, AGENT_SAS_TOKEN) instead of ${{ }} in the script body, and the override is validated against ^\d+$ before the [int] cast and the counter-blob CAS; counter semantics unchanged Oliver Kieselbach 2026-08-30 12:37:36 +02:00
  • e96f49799f fix(security): McpUsers whitelist rows are honoured only for the identity the UPN is bound to (AdminIdentityBindings tid+oid) — grant, UsagePlan override and Disabled kill-switch no longer follow a same-UPN token from another tenant or a recycled account; POST global/mcp-users binds first (resolver, 422 HomeTenantUnresolved, 409 conflict) and the settings UI shows the home-tenant picker on 422; unbound legacy rows are inert Oliver Kieselbach 2026-08-30 12:13:03 +02:00
  • 4864b2d21d fix(security): gather RuleStats resolve the device-claimed ruleId against the tenant's active gather catalog — title/category/severity come from the catalog rule, unknown or inactive IDs are dropped, and the cross-tenant global_* row is written only for IsBuiltIn/IsCommunity catalog rules (same design as the analyze path); a forged GatherRuleExecutor event can no longer inflate fleet-wide counters, respell a built-in rule's title or mint global rows for retired IDs Oliver Kieselbach 2026-08-30 12:13:00 +02:00
  • 56c37866d1 fix(security): ImeVersionHistory no longer takes a device-reported IME version as a platform fact — RecordImeVersionAsync rejects strings outside MSI ProductVersion bounds (no row/ops event/archive job), a session counts once (UpdateSessionImeAgentVersionAsync returns whether the version changed), a second tenant's sighting stamps CorroboratedAt, and the MemberRead view lists only fleet-confirmed versions (Archived, corroborated, or pre-2026-08-31) while Global scope still sees every row Oliver Kieselbach 2026-08-30 11:38:27 +02:00