mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-24 12:42:38 +00:00
40323ca57ee43f9b203e45bf7f12d0fb5422d69a
15 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
224eb668a1 |
Web: helpers live in lib/, generated files in lib/generated/; utils/ is gone
The web app kept helpers in two root folders (lib/ and utils/) and named route-local helper folders either utils/ or lib/. Shared helpers now live in lib/, route-only helpers in a lib/ next to their route (app/dashboard/lib, app/sessions/lib). Generated wire types, manifests, guardrails, error codes and the lastmod file move to lib/generated/, like the MCP server's src/generated/. The session-search parity cases sit next to sessionSearchQuery.ts. Pure move, no behaviour change. Imports, the generators (combine.js, generate-shared-manifest-types.js, sync-error-codes.js, generate-lastmod.js), the CI drift paths, the two backend tests and the MCP freshness test that read these files, and the guard tests' registry path and scan list follow the new locations. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
04fae4fe86 |
Web Push channel: alerts to paired phones without a third-party messenger (#177)
* bump pointer: internal (ingest-session-row-reads RELEASED, measurement) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Web Push channel: alerts to paired phones without a third-party messenger (D-327 to D-330) Provider WebhookProviderType.Push = 50 in both channel scopes. It carries no destination; NotificationChannelDispatcher takes a NotificationScope from every caller and the push transport resolves the scope's paired devices at send time (table-backed Admin/Operator, GlobalAdmin with matching identity binding; Viewer and Global Reader never). The receiver never signs in: a signed-in Admin/Operator creates a one-shot pairing code (11 Crockford chars, hashed, 10 min, ETag one-shot, five-failure burn), the phone redeems it anonymously, the portal session confirms. The phone's only credential is the device token in X-Push-Device-Token, good for status, re-subscribe and unpair of its own row (10 calls per minute per device; the anonymous pairing routes 10 per minute per IP). Payloads are RFC 8291 per device (new BCL-only library src/Push with the RFC vector pinned byte for byte) with a VAPID key ring and the kid on every device row; the projection reads an allow-list only (never Sections, DataJson, Failure Reason, Last Failure or the ops message), serial masked, 2 KB. 404/410 mark a device Stale instead of deleting it and the receiver re-arms it on its next open (the same PUT lifts a delivery-failures pause); devices pause when their owner stops signing in (PushOwners stamp in auth/me) through one pause path and resume by themselves; an owner without a stamp is paused, never deleted outright; role loss revokes with a wipe push and deletes the row (there is no Revoked state). PushDevices and PushPairingGrants are credential-bearing: raw-table deny-list, no backup, redacted dependency rows. Receiver app under /push/ (module service worker, Declarative Web Push shape, local history, pairing and status pages, manifest + icons), portal UI (Push provider in the channel editor, pairing dialog with QR code, device lists for tenant and platform scope, "Notify me when done" on the session page — the watch row records the watcher's own scopes, so a Global Admin watching a customer session is notified on the platform-paired phone), 22 catalogued routes, ops events PushDeliveryFailed and PushEndpointRefused (deduplicated per instance), maintenance sweep inside the existing run, settings template. The tenant-scope provider stays Global-Admin-only until the customer release (same ValidateModel gate as Telegram). Backend 7371, library 125, MCP 997, web 2061 tests. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * bump pointer: internal, .claude/commands, infra (push-relay: plan, D-327 to D-330, markers, VAPID generator) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * bump pointer: internal (push-relay: commit step ticked) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Drop sprintf-js from both lockfiles (Dependabot #114, #117) GHSA-hp3w-g68c-fv3c (sprintf-js <= 1.1.3, denial of service through unbounded precision specifiers) has no patched release, so both chains are cut instead. MCP (#117): sprintf-js arrived via @huggingface/transformers -> onnxruntime-node -> global-agent 3 -> roarr. global-agent is only loaded by onnxruntime-node's CUDA install script, which the image never runs (npm ci --ignore-scripts). The override global-agent ^4.1.3 drops roarr; 4.x keeps the bootstrap export that script calls, and onnxruntime-node 1.30 depends on the same range. Rules tooling (#114): ajv-cli 5.0.0 (no release since 2023) pulled js-yaml 3 -> argparse 1 -> sprintf-js. rules/scripts/validate.js now checks the four rule sets against their schemas with ajv (draft 2020-12) and ajv-formats directly, and combine-rules.yml runs it in one step instead of four ajv-cli loops. The lockfile shrinks from 26 to 6 packages; the fast-json-patch override and the legacy-peer-deps .npmrc (only there for ajv-cli's ts-node peer) are gone. Verified: validate.js accepts all 149 rule files plus guardrails.json exactly as ajv-cli did and rejects a corrupted copy (missing field, broken JSON, U+2028 in a guardrail value) with exit 1; clean npm ci + combine.js leave the generated outputs unchanged; MCP build and 997 tests green; zizmor clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * bump pointer: internal, .claude/commands (validate.js replaces ajv-cli: B-gzv, rule-builder fallback) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Bump sharp, proxy-addr and source-map-js past fresh advisories (MCP, Web) - sharp 0.35.4 -> 0.35.5 (GHSA-wq5f-xc86-pv6w, librsvg): the existing sharp override in both projects now floors at ^0.35.5 so the fixed build cannot be undercut by a parent range. - proxy-addr 2.0.7 -> 2.0.8 (GHSA-jqcg-44mw-7w3h, via express; MCP only) and source-map-js 1.2.1 -> 1.2.2 (GHSA-68fv-2mgg-jv7q, via postcss) are lockfile-only bumps inside the ranges their parents declare. No other lockfile entry moved. npm audit in the MCP server is clean. Verified: MCP build and 997 tests green; Web tsc clean, 2061 tests green, next build (static export) succeeds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * bump pointer: internal (push-relay: CORS verified, cert-exclusion prerequisite, EU app facts) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Web: Next.js 16.3.6 -> 16.3.8 (six advisories) 16.3.8 closes GHSA-3w37-wq28-93x7, GHSA-4jqv-mc3x-m676, GHSA-39w2-rjm5-chcv, GHSA-f87g-xv8r-7p7x, GHSA-mcj8-r9mp-w47p and GHSA-cjq9-62q9-8jv4 (affected 16.0.0 - 16.3.7). Under output:"export" the cache, ISR and image-optimization paths never run on the SWA; the dev-server disclosure is local-only. The patch is taken anyway. next, eslint-config-next and @next/bundle-analyzer stay aligned and floor at ^16.3.8; the lockfile is pinned to 16.3.8, not the 16.4.0 minor the caret would resolve to. What npm audit still reports (braces, micromatch, chokidar, fast-glob, postcss-selector-parser) comes through Tailwind 3 and eslint-config-next, build-time only; braces has no fix, the rest needs Tailwind 4 (backlog). Verified: tsc clean, eslint 0 findings, 2061 tests green, next build (16.3.8, 102 static pages) succeeds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * bump pointer: internal (B-dzs lockfile merge note, B-hxz braces chain) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
d8ab70b166 |
chore(rules): override fast-json-patch to 3.1.1 (Dependabot #103)
ajv-cli 5.0.0 is the latest release and still declares fast-json-patch ^2.0.0, so neither Dependabot nor npm audit fix can move past CVE-2021-4279. An npm override forces 3.1.1 into the lockfile; the 3.x API is backward compatible and the validate command used in combine-rules.yml never touches the package. Verified: npm ci from the lockfile plus all four ajv validate steps pass locally. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
1b27babd17 |
ci: zizmor gate for the workflow files plus a clean baseline (D-268)
zizmor (static analysis for GitHub Actions) now runs twice: locally before a
workflow commit (hard rule in .claude/CLAUDE.md) and as the `zizmor` job next to
the pin gate in action-pins.yml (zizmorcore/zizmor-action pinned by SHA, zizmor
1.30.1 pinned, SARIF into Code scanning, severity low and above). Like the pin
gate it reports and does not block. zizmor.yml holds the single reasoned ignore.
Baseline fixes so the gate starts green:
- persist-credentials: false on every checkout except combine-rules.yml, the one
job that pushes with the checkout credentials (ignored in zizmor.yml, with reason)
- publish-scripts.yml: id-token: write moves from the workflow to the publish job;
indexnow-notify.yml: permissions: {}
- deploy-mcp.yml: env values referenced as shell variables inside bash steps
instead of template expansion
- pester.yml / publish-scripts.yml: reusable workflow via the self-repository form
$/ (GitHub changelog 2026-07-30); the pin gate treats $/ like ./
- combine-rules.yml: ajv-cli installed with npm ci from rules/scripts/package-lock.json
(no ts-node peer, no lifecycle scripts) instead of an ad-hoc global install;
Dependabot watches /rules/scripts
- azure/login pin comment corrected to the tag the SHA actually is (v3.0.2; v3
moved on to v3.1.0), no runtime change
- Dependabot: 7-day cooldown on every ecosystem (security updates are exempt)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
||
|
|
12e66299e6 |
feat(rules): allow-list folders under the signed-in user's profile (D-228, D-229)
Gather rules and admin-configured diagnostics paths could never reach a folder below a user profile. The %LOGGED_ON_USER_PROFILE% token lifts the C:\Users hard block for AppData\Local and AppData\Roaming, but lifting is not admitting: the path then fell into filePrefixes, which is matched literally against the already-expanded path, and the profile name is not known when that list is authored. The exception only ever worked in unrestricted mode, and no agent test passed a non-null userProfilePath. D-228 introduces userProfileFilePrefixes, holding entries relative to the profile root (AppData\Local\RealmJoin). Both guards resolve them against the detected profile and match segment-bounded; entries that are rooted, contain "..", or sit outside AppData\Local / AppData\Roaming are dropped at load, so configuration can only narrow the hard block's exception, never widen it. The allowlist is anchored to the detected profile, so a rule cannot reach another user's AppData. The AppData subdirectory list existed in both guard classes with the GatherRuleGuards copy unused; it is now one list and DiagnosticsPathGuards delegates. filePrefixes additionally gains C:\ProgramData\RealmJoin\choco\logs. D-229 fixes the pre-flight validators, which must only ever reject what the agent rejects: - the portal returned "allowed" on the lifted hard block without consulting the allowlist, so a rule the agent refuses passed the enable gate and then failed silently on the device with a security_warning; - the MCP validator offered logparser the diagnosticsPathPrefixes list that no collector consults (four paths reported allowed that the agent blocks); - the MCP validator did not expand environment variables at all, reporting %ProgramData% targets as blocked although the device reads them. Measured before changing the portal: no rule in any tenant used the token, so nothing that was working gets disabled. Verified: solution build 0 warnings, agent 2897 tests, web 1257 + tsc + eslint 0, MCP 779, guardrails.json validates against its schema, rules/dist regenerates without drift. The six new agent tests were green against the unchanged guard first, which is what proves the exception was inert. Takes effect on devices only after an agent release: guardrails.json is an EmbeddedResource in the agent assembly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
0dcf0eb503 |
Observation coverage in get_session_summary, absence audit with coverage lint, diagnostics packaging counters (D-213)
MCP: coverage block (window, IME tracker, collectors, upload, diagnostics, gaps) built from the health events the summary already fetches (session-coverage.ts, data.<key> slices); validate_rule warns on a required not_exists of an IME-log-derived event without the ime_tracker_degraded precondition; authoring guide 'absence'; instructions and prompts point at coverage.gaps. Rules: guardrails.json imeLogDerivedEventTypes (schema + combine). Backend: catalog invariant test — absence rules carry the coverage gate or a documented exception (ANALYZE-ID-004). Agent: diagnostics_uploaded/_upload_failed carry packaging counters (truncated, includedFiles, skippedFiles, skippedByReason, problemsByKind) — counts only, never paths. Also: shared-manifests.json regenerated (was stale at HEAD; McpAccessPolicy doc string) + codegen. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
0515523e49 |
fix(security): guardrails.json values are emitted into generated TS through a complete encoder (JSON.stringify + U+2028/29) and rejected up-front if they contain control or line-terminator characters; new guardrails schema validated in combine-rules before any bot commit
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
81c14bb3ed |
fix(rules): close the rule-ID namespace gaps around the shared global keyspace
Custom rules are tenant-partitioned and functionally collision-free, but three
surfaces still keyed on the bare non-unique ruleId string:
- RuleStats global aggregate (global_{ruleId}) summed same-ID custom rules of
unrelated tenants (title/severity last-writer-wins). The global row is now
catalog-only: analyze gates on IsBuiltIn/IsCommunity, gather gates on the
reserved-namespace pattern (agent events carry no flag; custom rules can
never occupy the reserved pattern).
- The 'never ship a built-in in the CUSTOM namespace' commitment from
RuleIdPolicy was documented but unenforced: combine.js now fails on
duplicate or non-reserved built-in IDs, BuiltInRuleCatalogPolicyTests pins
the same invariants on the embedded catalog, and the GitHub reseed rejects
non-reserved IDs before writing to the global partition.
- DeleteRuleStatesForRuleIdAcrossTenantsAsync (cross-partition delete by bare
RowKey) now refuses IDs outside the reserved namespace with failed=-1 so
callers abort the follow-up global catalog delete.
Verified: prod global partitions (45 analyze + 11 gather) are fully inside the
reserved namespace, so the guards touch no existing data. New OKF doc
docs/rules/rule-id-namespace.md captures the identity/collision model.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
06626a4095 |
web: evaluateOn in the analyze-rule editor + interim-trigger badge
The evaluateOn feature was API/MCP-only — portal users could not see or set evaluation triggers. The rule editor gains an Evaluation Triggers section (enrollment end / WhiteGlove seal checkboxes + on_event type list) with client-side grammar + hard-block validation sourced from the baked guardrails mirror (combine.js now emits BLOCKED_INTERIM_TRIGGER_EVENT_TYPES for the web); rule cards show an 'Interim: …' badge for interim-enabled rules (incl. the built-in ANALYZE-ID-004). Create/edit payloads round-trip evaluateOn; the template-copy path already deep-clones it server-side. Suites: web 762 + tsc + lint clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
2f8ad82b0a |
gather-rules: mirror hard blocks in portal/MCP, stamp author on PUT-upsert; agent: bootstrap retry tests
Codex follow-ups to
|
||
|
|
3c1229a301 |
MCP rule authoring: deterministic context, validate_rule, and session dry-run (#124)
* feat(backend): dry-run endpoint for draft analyze rules (rule authoring)
POST /api/rules/analyze/dryrun evaluates a DRAFT analyze rule against one
session's events and returns a full diagnostic trace: per-condition
matched/evidence (incl. per-eventType occurrence counts), per-factor
confidence breakdown, verdict, and the would-be MatchedConditions map for
{{token}} interpolation preview. Persists nothing: no RuleResult row, no
session-status change, no notifications.
- RuleEngine.DryRun.cs: DryRunRuleAsync / DryRunEvaluate share the exact
evaluators (EvaluatePrecondition/EvaluateCondition/EvaluateConfidenceFactor)
with the production path but evaluate ALL conditions instead of breaking on
the first failed required one; parity with EvaluateRule is pinned by tests.
- DryRunAnalyzeRuleFunction: TenantAdminOrGlobalReader (read-in-effect POST),
GA/GlobalReader cross-tenant via session tenant resolution, 1 MB body cap,
structural draft validation (unknown source/operator, correlation without
joinField, invalid regex, non-evaluable confidenceFactor DSL) with
actionable error messages.
- EndpointAccessPolicyCatalog: register the new route.
- RuleEngineDryRunTests: trace semantics, production parity matrix,
side-effect freedom (VerifyNoOtherCalls), draft validation matrix.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(mcp): rule-authoring resources — guide, schemas, guardrails
Deterministic authoring context for customer AIs, replacing chunked
search_docs retrieval for rule creation: get_resource (and the MCP-protocol
resources) now serve
- rule_authoring_guide: hand-written, complete authoring knowledge (collector
types, triggers, condition sources, operators, confidence model incl. the
exact factor-condition DSL shapes, {{token}} interpolation order, guardrail
matching semantics, pitfalls, validate->dry-run workflow)
- rule_schemas: the JSON Schemas (2020-12) for gather + analyze rules
- rule_guardrails: the on-device collection allowlists
Schemas + guardrails are generated into rule-authoring.generated.ts by a new
combine.js emitter (same pattern as the web guardrails mirror; rules/ stays
the single source of truth and CI guardrails-in-sync guards the file).
rule-authoring-drift.test.ts additionally pins generated == repo source,
guide enums == schema enums, and the get_resource size cap.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(mcp): validate_rule + test_analyze_rule tools and author-rule prompt
Closes the authoring loop for customer AIs: draft -> validate -> dry-run
against a real session -> iterate -> create in the portal.
- validate_rule (MCP-local, no backend call): ajv 2020-12 validation against
the baked schemas, guardrail pre-flight mirroring the agent matching
semantics (hive-stripped segment-bounded registry/file prefixes,
whitespace-bounded WMI, exact commands, /-bounded event log channels, hard
blocks), and semantic lint for schema-valid-but-dead rules (unreachable
threshold, non-evaluable factor DSL, unknown eventTypes, unanchored
not_regex allowlists, unresolvable {{tokens}}, trigger cross-fields,
outputEventType conventions/collisions).
- test_analyze_rule: calls the new POST /api/rules/analyze/dryrun and adds
the {{token}}-interpolated explanation/remediation preview via the
existing interpolate-rule-template.
- author-rule MCP prompt guiding scenario -> resources -> draft -> validate
-> dry-run.
- Both tools are visible to every role (backend enforces
TenantAdminOrGlobalReader on the dryrun route); catalog snapshot updated.
- deps: ajv + ajv-formats promoted to direct dependencies.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(okf): rule authoring surface concept doc
docs/rules/rule-authoring-surface.md: the AI-assisted authoring stack
(deterministic MCP resources via combine.js baking, MCP-local validate_rule
layers, the dry-run trace twin with its parity + side-effect invariants, and
what is deliberately absent); registered in index.md, logged 2026-08-05.
Customer docs (rules/ai-assisted-rule-authoring.md + SUMMARY + FAQ entry) are
committed in the autopilotmonitor-docs repository separately.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
3f9576d4a9 |
fix(agent): enforce the gather-rule allowlist in every collector
LogParserCollector and EventLogCollector called no guard at all, while File/Json/Xml did — and rules/gather-rules.md already promised customers that log-parser paths were allowlisted. A tenant admin could author a logparser rule against C:\Users via POST /api/rules/gather and have up to 1000 lines uploaded as telemetry, bypassing the hard C:\Users block the same page guarantees; eventlog could read any channel, Security included. The backend validates no collectorType, target, or parameters, so the agent is the only place this can be enforced. Both now guard. logparser cannot check rule.Target directly (GetFullPath throws on a wildcard), so it checks the directory for wildcard targets and re-checks each resolved path. Event log channels get an allowlist matched on a "/" boundary. Hard-blocked channels live in a C# constant rather than guardrails.json: a parse error drops the allowlists to empty, which is fail-closed, but would have lifted a block sourced from the same file. Also fixes an inert timeout in CommandCollector: ReadToEnd() blocks until the pipe closes, so WaitForExit(30000) was only reached once the process had already exited and a hung command pinned its worker indefinitely. Adds the first real CI gate. No workflow ran tests on a pull request — the deploy workflows build without testing and build-agent.yml covered only DecisionCore.Tests on manual dispatch — while trust/security-faq.md already told customers automated tests run in CI. ci.yml now runs all six suites on every PR and merge, plus a job that fails on generated-guardrail drift, alongside CodeQL and Dependabot. OfficeBinaryWatcherTests is marked SerialThreading; its bounded re-probe races the shared ThreadPool and failed once under full-suite load. Verified: 3101 backend, 1911 agent V2, 474 decision core, 44 dialog, 572 web, 447 MCP. The timeout fix is verified by runtime, not assertion — a Start-Sleep 120 command now returns after ~30s. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
ecd8036dc0 | updated guardrails | ||
|
|
280a413dac | added centralized guardrails for easier maintenance, added some more gather rules | ||
|
|
1f796bf6e3 |
feat: externalize rules to GitHub + API consolidation + preview mode
## Rules as GitHub single source of truth - Add 75 individual JSON rule files (2 gather, 18 analyze, 55 IME patterns) under rules/gather/, rules/analyze/, rules/ime-log-patterns/ - Add JSON Schemas for all three rule types with VS Code IntelliSense support - Add CI combine script (rules/scripts/combine.js) + GitHub Action that validates schemas with ajv-cli and generates rules/dist/ combined files - Convert BuiltIn*.cs from hardcoded C# objects to embedded JSON resource loaders - Add GitHubRuleRepository service to fetch dist/ files from raw.githubusercontent.com - Add ReseedFromGitHubFunction: POST /api/rules/reseed-from-github?type=all|gather|analyze|ime ## IME Log Patterns - Extend ImeLogPattern model with Description and IsBuiltIn properties - Add ImeLogPatterns table + API constants - Add TableStorageService CRUD methods for IME log patterns - Add ImeLogPatternService (CRUD, merge, auto-seed from embedded JSON) - Add ImeLogPatternsFunction: GET/PUT /api/rules/ime-log-patterns (Phase 1: read-only for tenants, Galactic Admin can enable/disable/edit globally) - Add IME Log Patterns management page (/ime-log-patterns) - Wire GetAgentConfigFunction to use ImeLogPatternService instead of static class ## API route consolidation - All rule endpoints now under /api/rules/* for single certificate exclusion path: /api/rules/gather, /api/rules/analyze, /api/rules/ime-log-patterns, /api/rules/reseed-from-github ## Export buttons - Add per-rule and export-all JSON download buttons to gather-rules, analyze-rules, and ime-log-patterns pages ## Admin configuration - Replace old individual reseed buttons with unified "Fetch & Reseed from GitHub" (calls new GitHub endpoint) plus compact individual-type reseed buttons - Add CDN cache hint: GitHub raw.githubusercontent.com caches up to 5 minutes ## Preview mode - ?preview=1 URL param seeds localStorage.previewMode (persists across navigation) - ?preview=0 clears it; toggle in Navbar settings dropdown (indigo, eye icon) - IME Log Patterns nav link gated behind galacticAdminMode OR previewMode - Preview badge shown on nav link when active for normal users ## Tests - Add xUnit test project (net8.0) with 5 tests for embedded resource loading, known IME actions validation, and valid categories check Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |