Skip to content

docs(security): publish public threat model - #74

Merged
mohanagy merged 2 commits into
developmentfrom
feat/issue-37-threat-model
Jul 14, 2026
Merged

docs(security): publish public threat model#74
mohanagy merged 2 commits into
developmentfrom
feat/issue-37-threat-model

Conversation

@mohanagy

@mohanagy mohanagy commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Summary

  • Publish a public threat model mapping [Security] Publish a threat model and complete an external security review #37 scope to implemented controls and explicit residual risks.
  • Link it from the README, security model, architecture, and security reporting policy.
  • Add a focused documentation contract for public links, all ten threat rows, mandatory non-loopback bearer authentication, and explicit non-goals.

Security impact

  • Corrects the public matrix to state that every explicitly enabled non-loopback HTTP binding requires a secret-backed bearer; no production behavior changed.
  • Documents containment, credential, routing, audit, identity, supply-chain, and availability limits without claiming external assurance.

Validation

  • Focused red/green:
    RUN v3.2.7 /private/tmp/miftah-issue37-threat-model

✓ tests/threat-model-docs-contract.test.ts (2 tests) 3ms

Test Files 1 passed (1)
Tests 2 passed (2)
Start at 19:20:29
Duration 162ms (transform 19ms, setup 0ms, collect 16ms, tests 3ms, environment 0ms, prepare 31ms) failed on the prior optional-auth wording, then passed after the correction.

@lubab/miftah@0.1.1 test
vitest run

RUN v3.2.7 /private/tmp/miftah-issue37-threat-model

✓ tests/mcp-wrapper.test.ts (88 tests) 41623ms
✓ Miftah MCP wrapper > scopes annotation trust to each named base upstream 386ms
✓ Miftah MCP wrapper > verifies requested named upstreams alone and all configured upstreams in sorted order 477ms
✓ Miftah MCP wrapper > invalidates passive identity status when a verified upstream profile restarts 426ms
✓ Miftah MCP wrapper > exposes management and upstream capabilities while routing calls by active profile 504ms
✓ Miftah MCP wrapper > routes a standard resource read through a canonical provider URI matcher 491ms
✓ Miftah MCP wrapper > uses the collector snapshot for matching redacted preview and audit evidence 547ms
✓ Miftah MCP wrapper > routes a client-visible multi-upstream GitHub tool through its static matcher binding 1049ms
✓ Miftah MCP wrapper > records bounded canonical matcher evidence for an ambiguous proxied operation without forwarding it 606ms
✓ Miftah MCP wrapper > uses the same canonical matcher evidence for preview and successful proxied operation audits 802ms
✓ Miftah MCP wrapper > does not treat a static matcher as the explicit rule required for destructive operations 652ms
✓ Miftah MCP wrapper > advertises and emits tool list changes after a profile switch 328ms
✓ Miftah MCP wrapper > propagates a cancelled tool call to the selected stdio upstream and records one terminal audit outcome 769ms
✓ Miftah MCP wrapper > propagates cancellation through initial tool discovery 763ms
✓ Miftah MCP wrapper > propagates cancellation through management tool discovery 817ms
✓ Miftah MCP wrapper > keeps shared tool discovery alive when one downstream caller cancels 818ms
✓ Miftah MCP wrapper > starts fresh tool discovery when a prior sole caller has cancelled 791ms
✓ Miftah MCP wrapper > fans shared tool discovery progress out to every downstream caller 446ms
✓ Miftah MCP wrapper > propagates cancellation through resource discovery 754ms
✓ Miftah MCP wrapper > aggregates concurrent upstream tool-discovery progress on one downstream token 493ms
✓ Miftah MCP wrapper > namespaces aggregated resource templates and routes instantiated reads to their origin upstream 351ms
✓ Miftah MCP wrapper > reports a stable unavailable error when no aggregated upstream implements resource templates 363ms
✓ Miftah MCP wrapper > proxies resource subscriptions to the selected upstream and namespaces update notifications 454ms
✓ Miftah MCP wrapper > forwards only updates for the subscribed resource 333ms
✓ Miftah MCP wrapper > does not forward an update from a subscription that fails before activation 331ms
✓ Miftah MCP wrapper > cleans up an upstream subscription that finishes after downstream cancellation 491ms
✓ Miftah MCP wrapper > releases a subscription after its unsubscribe request is cancelled 447ms
✓ Miftah MCP wrapper > bounds a pending subscription before switching profiles at capacity 604ms
✓ Miftah MCP wrapper > serializes cancelled subscription cleanup before a retry 1395ms
✓ Miftah MCP wrapper > fans one upstream resource update out to every matching static and template subscription 561ms
✓ Miftah MCP wrapper > re-establishes a resource subscription after its upstream restarts 369ms
✓ Miftah MCP wrapper > clears a direct resource subscription when its upstream lifecycle ends 423ms
✓ Miftah MCP wrapper > serializes overlapping resource subscription transitions before releasing idle capacity 398ms
✓ Miftah MCP wrapper > joins an in-flight unsubscribe before profile cleanup 484ms
✓ Miftah MCP wrapper > does not retain a subscription invalidated during its upstream handshake 462ms
✓ Miftah MCP wrapper > does not establish a captured-profile subscription after a profile switch 336ms
✓ Miftah MCP wrapper > keeps a resource subscription alive beyond the upstream idle timeout 348ms
✓ Miftah MCP wrapper > drops an old-profile resource subscription before a delayed update can be forwarded 512ms
✓ Miftah MCP wrapper > clears a routed third-profile subscription when the active profile changes 628ms
✓ Miftah MCP wrapper > bounds resource subscription cleanup while switching profiles 1733ms
✓ Miftah MCP wrapper > releases subscription-capability probes before serving the active profile at capacity 397ms
✓ Miftah MCP wrapper > forwards upstream tools, resources, and prompts list-change notifications 393ms
✓ Miftah MCP wrapper > forwards a tool list change from a session first used for discovery 486ms
✓ Miftah MCP wrapper > refreshes the initial tool snapshot after a concurrent upstream list change 383ms
✓ Miftah MCP wrapper > does not reject a cold tool call when an idle upstream emits a list change 318ms
✓ Miftah MCP wrapper > clears a deferred tool invalidation when its list request is cancelled 461ms
✓ Miftah MCP wrapper > refreshes the advertised tool schema after a profile switch 494ms
✓ Miftah MCP wrapper > retries tool discovery when the active profile changes during listing 587ms
✓ Miftah MCP wrapper > rejects routing to a profile with a different advertised tool schema 517ms
✓ Miftah MCP wrapper > fails strict discovery when configured profiles expose different tool schemas 417ms
✓ Miftah MCP wrapper > invalidates strict discovery when a non-active profile becomes unavailable 527ms
✓ Miftah MCP wrapper > resolves unregistered miftah-prefixed names through the tool registry 346ms
✓ Miftah MCP wrapper > shares controlled discovery between a list request and a cold call 365ms
✓ Miftah MCP wrapper > invalidates a profile tool snapshot after an explicit restart 433ms
✓ Miftah MCP wrapper > linearizes a cold tool call before a concurrent profile switch 523ms
✓ Miftah MCP wrapper > blocks denied resource reads before forwarding them upstream 351ms
✓ Miftah MCP wrapper > collects runtime roots once and routes repeated proxied calls from the cached context 612ms
✓ Miftah MCP wrapper > routes proxied calls with empty roots when the initialized notification is dropped 457ms
✓ Miftah MCP wrapper > keeps a relative runtime config distinct from a later project marker 603ms
✓ Miftah MCP wrapper > keeps fallback routing usable without roots and preserves direct-server empty context 728ms
✓ Miftah MCP wrapper > falls back after a failed runtime roots request without retrying per operation 462ms
✓ Miftah MCP wrapper > refreshes advertised roots once and ignores unadvertised roots changes 1773ms
✓ Miftah MCP wrapper > refreshes again when roots change during an in-flight roots request 604ms
✓ Miftah MCP wrapper > uses exactly one routing context snapshot per proxied operation 659ms
✓ Miftah MCP wrapper > returns unknown context profile errors without forwarding the proxied call 474ms
✓ Miftah MCP wrapper > restores a workspace-scoped active profile and exposes safe selection metadata 647ms
✓ tests/package-contract.test.ts (16 tests) 18525ms
✓ packed artifact contract > keeps the test worker responsive while an npm subprocess is running 561ms
✓ packed artifact contract > includes captured output when an npm command exits unsuccessfully 328ms
✓ packed artifact contract > contains required runtime, documentation, and example files from a real dry run 388ms
✓ packed artifact contract > runs the checked package command against the real npm pack output 627ms
✓ packed artifact contract > loads the installed entry point and runs the installed binary from a real tarball 10309ms
✓ tests/multi-upstream.test.ts (28 tests) 11966ms
✓ multi-upstream wrapper > keeps healthy aggregate capabilities available when a bundled upstream cannot start 390ms
✓ multi-upstream wrapper > reports redacted capability discovery failures as degraded upstream health 376ms
✓ multi-upstream wrapper > fails strict tool discovery with a complete unavailable-upstream diagnostic 358ms
✓ multi-upstream wrapper > fails strict resource discovery with a complete unavailable-upstream diagnostic 312ms
✓ multi-upstream wrapper > fails strict prompt discovery with a complete unavailable-upstream diagnostic 347ms
✓ multi-upstream wrapper > aggregates namespaced resources and routes each read to its originating upstream 405ms
✓ multi-upstream wrapper > removes stale routes when a bundled upstream fails resource discovery 367ms
✓ multi-upstream wrapper > removes stale routes when a bundled upstream fails prompt discovery 463ms
✓ multi-upstream wrapper > notifies clients when partial resource and prompt discovery recovers 626ms
✓ multi-upstream wrapper > rejects ambiguous namespaced resource and prompt collisions atomically 408ms
✓ multi-upstream wrapper > preserves independent upstream pagination through opaque aggregate cursors 399ms
✓ multi-upstream wrapper > notifies clients to re-list aggregated resources and prompts after a profile change 896ms
✓ multi-upstream wrapper > redacts configured and URI-embedded credentials before publishing a namespaced resource URI 353ms
✓ multi-upstream wrapper > uses controlled discovery to route cold namespaced reads and prompt gets exactly 359ms
✓ multi-upstream wrapper > routes a namespaced tool call before tools are listed 366ms
✓ multi-upstream wrapper > restarts every upstream in a profile bundle and invalidates its tool snapshot 718ms
✓ multi-upstream wrapper > recovers healthy tool discovery after a partial multi-upstream restart failure 841ms
✓ multi-upstream wrapper > refreshes the tool registry after an upstream crashes and later recovers 1375ms
✓ multi-upstream wrapper > waits for every bundled restart to settle before returning a restart failure 705ms
✓ multi-upstream wrapper > lists namespaced upstream tools through the deterministic registry 364ms
✓ multi-upstream wrapper > namespaces and routes tools from multiple upstream servers 377ms
✓ tests/upstream-manager.test.ts (26 tests) 7721ms
✓ upstream process manager > shuts down an idle profile and starts a fresh process on its next use 331ms
✓ upstream process manager > keeps a replacement session healthy and reserved while an idle predecessor is still closing 1571ms
✓ upstream process manager > does not idle-shutdown a profile while an upstream request is in flight 336ms
✓ upstream process manager > does not restart a crashed process unless automatic recovery is configured 423ms
✓ upstream process manager > automatically recovers a crashed profile after a bounded backoff 336ms
✓ upstream process manager > stops automatic recovery when the configured restart budget is exhausted 806ms
✓ upstream process manager > records a failed restart teardown before starting a replacement session 333ms
✓ upstream process manager > cancels pending automatic recovery when the manager shuts down 427ms
✓ tests/operation-pipeline.test.ts (20 tests) 7389ms
✓ operation pipeline > verifies named upstream writes against their exact override or inherited identity 410ms
✓ operation pipeline > rejects ambiguous resource and prompt routes before forwarding them upstream 396ms
✓ operation pipeline > does not forward destructive calls when project context profile hints conflict 371ms
✓ operation pipeline > routes resource reads and prompt retrieval to the selected profile 391ms
✓ operation pipeline > keeps in-flight resource and prompt operations bound to their captured profile 604ms
✓ operation pipeline > completes in-flight aggregate reads and prompts after a profile switch 817ms
✓ operation pipeline > retries cold aggregate route discovery against the captured profile after a switch 1073ms
✓ operation pipeline > redacts credential-bearing unknown resource URIs in diagnostics 467ms
✓ tests/approval-fallback.test.ts (23 tests) 4413ms
✓ approval fallback > audits a native approval that expires while its form is open 305ms
✓ tests/audit-outcomes.test.ts (15 tests) 5119ms
✓ audit outcomes > records one terminal operation event for list, management, and unknown-tool requests 537ms
✓ audit outcomes > records wrapper and lazy upstream lifecycle outcomes 753ms
✓ audit outcomes > redacts secret-bearing discovery metadata before it reaches MCP clients 448ms
✓ audit outcomes > redacts discovery failures from client errors, health, and audit output 420ms
✓ audit outcomes > keeps MCP results available and exposes audit health when fail-open writes fail 338ms
✓ audit outcomes > records upstream crash and automatic recovery outcomes 469ms
✓ audit outcomes > records a static profile lock as wrapper startup metadata 338ms
✓ tests/identity-manager.test.ts (23 tests) 4588ms
✓ identity verifier > forwards cancellation through identity probe discovery 705ms
✓ identity verifier > keeps a shared identity probe alive when one request cancels 632ms
✓ tests/doctor.test.ts (22 tests) 4594ms
✓ doctor readiness runner > accepts a relative executable resolved from its configured working directory 1129ms
✓ doctor readiness runner > reports profile tool schema differences as warnings or strict errors 499ms
✓ tests/remote-transport.test.ts (11 tests) 4088ms
✓ remote upstream transports > forces local cleanup when a Streamable HTTP session DELETE hangs 3812ms
✓ tests/http-server.test.ts (15 tests) 4405ms
✓ Miftah Streamable HTTP server > bounds oversized JSON requests before they can reserve a session 366ms
✓ Miftah Streamable HTTP server > keeps profile selection isolated across concurrent HTTP client sessions 1011ms
✓ Miftah Streamable HTTP server > reconnects an interrupted SSE stream to the existing session without creating another runtime 353ms
✓ Miftah Streamable HTTP server > expires idle sessions and releases their retained upstream transport 1042ms
✓ Miftah Streamable HTTP server > keeps capacity reserved until a deleted session runtime has finished closing 568ms
✓ Miftah Streamable HTTP server > reserves capacity during concurrent initialization and releases it after DELETE 596ms
✓ tests/audit-integrity.test.ts (20 tests) 2866ms
✓ audit journal integrity > compacts integrity metadata when retention removes every rotated archive 971ms
✓ tests/secret-providers.test.ts (89 tests | 11 skipped) 2613ms
✓ external secret providers > resolves default keychain and 1Password commands from absolute PATH entries 876ms
✓ tests/audit-log-reader.test.ts (35 tests) 2078ms
✓ logs command integration > follows a managed rotation that completes before its next poll 557ms
✓ tests/profile-lease-pipeline.test.ts (6 tests) 1574ms
✓ profile leases in the operation pipeline > requires an explicit unexpired selection before forwarding a lease-protected risk 310ms
✓ profile leases in the operation pipeline > does not let a routed profile borrow the active profile's lease 486ms
✓ tests/audit.test.ts (24 tests) 1936ms
✓ audit logger > serializes concurrent logger instances through the journal lock at a rotation boundary 1582ms
✓ tests/plugin-api.test.ts (13 tests) 1220ms
✓ tests/plugin-routing-server.test.ts (1 test) 790ms
✓ plugin routing server integration > uses configured routing plugins for real proxied MCP tool calls 789ms
✓ tests/profile-lock-mcp.test.ts (3 tests) 978ms
✓ MCP profile locks > exposes opt-in connection-bound lock controls and safe profile state 387ms
✓ MCP profile locks > keeps lock controls discoverable but rejects calls while the opt-in is disabled 353ms
✓ tests/profile-runtime-isolation.test.ts (23 tests) 1056ms
✓ profile runtime isolation > atomically rematerializes mapped files on restart and never deletes source files or runtime state 305ms
✓ tests/routing-context.test.ts (21 tests) 960ms
✓ tests/executable-resolver.test.ts (4 tests) 1280ms
✓ secret executable resolution > resolves explicit relative paths and case-insensitive quoted PATH entries 543ms
✓ tests/config-preflight.test.ts (4 tests) 579ms
✓ tests/profile-transition-audit-barrier.test.ts (1 test) 484ms
✓ profile transition audit barrier > does not expose a new profile lease to concurrent calls before its required audit transition commits 483ms
✓ tests/public-api.test.ts (4 tests) 293ms
✓ tests/secrets.test.ts (20 tests) 131ms
✓ tests/plugin-routing-pipeline.test.ts (1 test) 116ms
✓ tests/config-migration.test.ts (21 tests) 153ms
✓ tests/audit-verify.test.ts (2 tests) 107ms
✓ tests/fake-upstream-fixture.test.ts (1 test) 109ms
✓ tests/audit-export.test.ts (6 tests) 96ms
✓ tests/profile-state.test.ts (10 tests) 75ms
✓ tests/negative-paths.test.ts (5 tests) 65ms
✓ tests/init-command.test.ts (10 tests) 23ms
✓ tests/managed-audit-follow.test.ts (1 test) 22ms
✓ tests/config-runtime-parity.test.ts (62 tests) 18ms
✓ tests/config.test.ts (56 tests) 14ms
✓ tests/runtime-config-resolution.test.ts (4 tests) 16ms
✓ tests/config-schema-contract.test.ts (8 tests) 13ms
✓ tests/config-diagnostics.test.ts (29 tests) 14ms
✓ tests/approval-store.test.ts (11 tests) 13ms
✓ tests/audit-journal-path-safety.test.ts (1 test) 13ms
✓ tests/preset-catalog.test.ts (31 tests) 9ms
✓ tests/config-public-contract.test.ts (4 tests) 10ms
✓ tests/identity-docs-contract.test.ts (2 tests) 8ms
✓ tests/doctor-report.test.ts (10 tests) 8ms
✓ tests/resource-prompt-registry.test.ts (13 tests) 9ms
✓ tests/routing-policy.test.ts (24 tests) 8ms
✓ tests/windows-config-acl.test.ts (6 tests) 9ms
✓ tests/windows-config-migration-acl-failure.test.ts (1 test) 9ms
✓ tests/release-config.test.ts (18 tests) 8ms
✓ tests/tool-registry.test.ts (1 test) 8ms
✓ tests/config-loader.test.ts (4 tests) 9ms
✓ tests/logs-writer.test.ts (4 tests) 5ms
✓ tests/preset-docs-contract.test.ts (1 test) 6ms
✓ tests/secret-provider-availability.test.ts (8 tests | 2 skipped) 6ms
✓ tests/client-snippets.test.ts (30 tests) 4ms
✓ tests/provider-routing-matchers.test.ts (14 tests) 5ms
✓ tests/release-version.test.ts (4 tests) 4ms
✓ tests/profile-manager.test.ts (10 tests) 4ms
✓ tests/cli-parse.test.ts (10 tests) 4ms
✓ tests/threat-model-docs-contract.test.ts (2 tests) 3ms
✓ tests/progress-preserving-transport.test.ts (7 tests) 3ms
✓ tests/tooling-config.test.ts (4 tests) 3ms
✓ tests/presets.test.ts (4 tests) 2ms
✓ tests/secret-provider-docs-contract.test.ts (3 tests) 2ms
✓ tests/profile-state-docs-contract.test.ts (1 test) 3ms
✓ tests/risk-classification-docs-contract.test.ts (1 test) 2ms
✓ tests/approval-docs-contract.test.ts (1 test) 2ms
✓ tests/cli-exit-codes.test.ts (3 tests) 2ms
✓ tests/provider-routing-matchers-docs-contract.test.ts (1 test) 2ms
✓ tests/profile-runtime-isolation-docs-contract.test.ts (2 tests) 8ms
✓ tests/windows-secret-command-contract.test.ts (2 tests) 5ms
✓ tests/profile-leases-docs-contract.test.ts (1 test) 3ms
✓ tests/routing-context-docs-contract.test.ts (1 test) 6ms
↓ tests/windows-config-migration-acl.test.ts (9 tests | 9 skipped)

Test Files 75 passed | 1 skipped (76)
Tests 998 passed | 22 skipped (1020)
Start at 19:20:30
Duration 149.20s (transform 988ms, setup 0ms, collect 4.19s, tests 134.31s, environment 6ms, prepare 2.29s) — passed.

@lubab/miftah@0.1.1 test:core
vitest run tests/config.test.ts tests/config-loader.test.ts tests/config-diagnostics.test.ts tests/config-migration.test.ts tests/config-public-contract.test.ts tests/config-runtime-parity.test.ts tests/config-schema-contract.test.ts tests/executable-resolver.test.ts tests/http-server.test.ts tests/presets.test.ts tests/profile-manager.test.ts tests/provider-routing-matchers.test.ts tests/provider-routing-matchers-docs-contract.test.ts tests/routing-context.test.ts tests/routing-policy.test.ts tests/secret-provider-availability.test.ts tests/secret-providers.test.ts tests/secrets.test.ts tests/tooling-config.test.ts tests/windows-config-acl.test.ts tests/windows-config-migration-acl-failure.test.ts tests/windows-config-migration-acl.test.ts

RUN v3.2.7 /private/tmp/miftah-issue37-threat-model

✓ tests/http-server.test.ts (15 tests) 3728ms
✓ Miftah Streamable HTTP server > keeps profile selection isolated across concurrent HTTP client sessions 588ms
✓ Miftah Streamable HTTP server > expires idle sessions and releases their retained upstream transport 1040ms
✓ Miftah Streamable HTTP server > keeps capacity reserved until a deleted session runtime has finished closing 556ms
✓ Miftah Streamable HTTP server > reserves capacity during concurrent initialization and releases it after DELETE 491ms
✓ tests/secret-providers.test.ts (89 tests | 11 skipped) 3874ms
✓ external secret providers > resolves default keychain and 1Password commands from absolute PATH entries 1786ms
✓ tests/executable-resolver.test.ts (4 tests) 885ms
✓ tests/routing-context.test.ts (21 tests) 923ms
✓ tests/config-migration.test.ts (21 tests) 126ms
✓ tests/secrets.test.ts (20 tests) 135ms
✓ tests/config-runtime-parity.test.ts (62 tests) 21ms
✓ tests/config.test.ts (56 tests) 16ms
✓ tests/config-diagnostics.test.ts (29 tests) 12ms
✓ tests/config-schema-contract.test.ts (8 tests) 14ms
✓ tests/config-public-contract.test.ts (4 tests) 10ms
✓ tests/windows-config-acl.test.ts (6 tests) 9ms
✓ tests/windows-config-migration-acl-failure.test.ts (1 test) 7ms
✓ tests/config-loader.test.ts (4 tests) 9ms
✓ tests/routing-policy.test.ts (24 tests) 8ms
✓ tests/secret-provider-availability.test.ts (8 tests | 2 skipped) 6ms
✓ tests/provider-routing-matchers.test.ts (14 tests) 5ms
✓ tests/profile-manager.test.ts (10 tests) 5ms
✓ tests/tooling-config.test.ts (4 tests) 3ms
✓ tests/presets.test.ts (4 tests) 3ms
✓ tests/provider-routing-matchers-docs-contract.test.ts (1 test) 3ms
↓ tests/windows-config-migration-acl.test.ts (9 tests | 9 skipped)

Test Files 21 passed | 1 skipped (22)
Tests 392 passed | 22 skipped (414)
Start at 19:23:00
Duration 14.76s (transform 480ms, setup 0ms, collect 1.02s, tests 9.80s, environment 2ms, prepare 714ms) — 392 passed, 22 expected platform skips.

@lubab/miftah@0.1.1 test:coverage
vitest run --coverage

RUN v3.2.7 /private/tmp/miftah-issue37-threat-model
Coverage enabled with v8

✓ tests/mcp-wrapper.test.ts (88 tests) 38238ms
✓ Miftah MCP wrapper > uses explicitly trusted tool annotations and records risk provenance 315ms
✓ Miftah MCP wrapper > scopes annotation trust to each named base upstream 393ms
✓ Miftah MCP wrapper > verifies requested named upstreams alone and all configured upstreams in sorted order 521ms
✓ Miftah MCP wrapper > invalidates passive identity status when a verified upstream profile restarts 455ms
✓ Miftah MCP wrapper > exposes management and upstream capabilities while routing calls by active profile 503ms
✓ Miftah MCP wrapper > routes a standard resource read through a canonical provider URI matcher 486ms
✓ Miftah MCP wrapper > uses the collector snapshot for matching redacted preview and audit evidence 515ms
✓ Miftah MCP wrapper > routes a client-visible multi-upstream GitHub tool through its static matcher binding 757ms
✓ Miftah MCP wrapper > records bounded canonical matcher evidence for an ambiguous proxied operation without forwarding it 623ms
✓ Miftah MCP wrapper > uses the same canonical matcher evidence for preview and successful proxied operation audits 766ms
✓ Miftah MCP wrapper > does not treat a static matcher as the explicit rule required for destructive operations 685ms
✓ Miftah MCP wrapper > propagates a cancelled tool call to the selected stdio upstream and records one terminal audit outcome 742ms
✓ Miftah MCP wrapper > propagates cancellation through initial tool discovery 748ms
✓ Miftah MCP wrapper > propagates cancellation through management tool discovery 748ms
✓ Miftah MCP wrapper > keeps shared tool discovery alive when one downstream caller cancels 827ms
✓ Miftah MCP wrapper > starts fresh tool discovery when a prior sole caller has cancelled 785ms
✓ Miftah MCP wrapper > fans shared tool discovery progress out to every downstream caller 474ms
✓ Miftah MCP wrapper > propagates cancellation through resource discovery 757ms
✓ Miftah MCP wrapper > aggregates concurrent upstream tool-discovery progress on one downstream token 473ms
✓ Miftah MCP wrapper > namespaces aggregated resource templates and routes instantiated reads to their origin upstream 370ms
✓ Miftah MCP wrapper > reports a stable unavailable error when no aggregated upstream implements resource templates 416ms
✓ Miftah MCP wrapper > proxies resource subscriptions to the selected upstream and namespaces update notifications 419ms
✓ Miftah MCP wrapper > forwards only updates for the subscribed resource 319ms
✓ Miftah MCP wrapper > does not forward an update from a subscription that fails before activation 323ms
✓ Miftah MCP wrapper > cleans up an upstream subscription that finishes after downstream cancellation 468ms
✓ Miftah MCP wrapper > releases a subscription after its unsubscribe request is cancelled 452ms
✓ Miftah MCP wrapper > bounds a pending subscription before switching profiles at capacity 802ms
✓ Miftah MCP wrapper > serializes cancelled subscription cleanup before a retry 1395ms
✓ Miftah MCP wrapper > fans one upstream resource update out to every matching static and template subscription 570ms
✓ Miftah MCP wrapper > re-establishes a resource subscription after its upstream restarts 359ms
✓ Miftah MCP wrapper > clears a direct resource subscription when its upstream lifecycle ends 373ms
✓ Miftah MCP wrapper > serializes overlapping resource subscription transitions before releasing idle capacity 398ms
✓ Miftah MCP wrapper > joins an in-flight unsubscribe before profile cleanup 458ms
✓ Miftah MCP wrapper > does not retain a subscription invalidated during its upstream handshake 517ms
✓ Miftah MCP wrapper > does not establish a captured-profile subscription after a profile switch 368ms
✓ Miftah MCP wrapper > keeps a resource subscription alive beyond the upstream idle timeout 348ms
✓ Miftah MCP wrapper > drops an old-profile resource subscription before a delayed update can be forwarded 519ms
✓ Miftah MCP wrapper > clears a routed third-profile subscription when the active profile changes 664ms
✓ Miftah MCP wrapper > bounds resource subscription cleanup while switching profiles 547ms
✓ Miftah MCP wrapper > releases subscription-capability probes before serving the active profile at capacity 360ms
✓ Miftah MCP wrapper > forwards upstream tools, resources, and prompts list-change notifications 307ms
✓ Miftah MCP wrapper > forwards a tool list change from a session first used for discovery 321ms
✓ Miftah MCP wrapper > does not reject a cold tool call when an idle upstream emits a list change 499ms
✓ Miftah MCP wrapper > clears a deferred tool invalidation when its list request is cancelled 507ms
✓ Miftah MCP wrapper > refreshes the advertised tool schema after a profile switch 517ms
✓ Miftah MCP wrapper > retries tool discovery when the active profile changes during listing 629ms
✓ Miftah MCP wrapper > rejects routing to a profile with a different advertised tool schema 555ms
✓ Miftah MCP wrapper > fails strict discovery when configured profiles expose different tool schemas 387ms
✓ Miftah MCP wrapper > invalidates strict discovery when a non-active profile becomes unavailable 479ms
✓ Miftah MCP wrapper > shares controlled discovery between a list request and a cold call 335ms
✓ Miftah MCP wrapper > invalidates a profile tool snapshot after an explicit restart 419ms
✓ Miftah MCP wrapper > linearizes a cold tool call before a concurrent profile switch 498ms
✓ Miftah MCP wrapper > collects runtime roots once and routes repeated proxied calls from the cached context 562ms
✓ Miftah MCP wrapper > routes proxied calls with empty roots when the initialized notification is dropped 406ms
✓ Miftah MCP wrapper > keeps a relative runtime config distinct from a later project marker 545ms
✓ Miftah MCP wrapper > keeps fallback routing usable without roots and preserves direct-server empty context 661ms
✓ Miftah MCP wrapper > falls back after a failed runtime roots request without retrying per operation 410ms
✓ Miftah MCP wrapper > refreshes advertised roots once and ignores unadvertised roots changes 1192ms
✓ Miftah MCP wrapper > refreshes again when roots change during an in-flight roots request 459ms
✓ Miftah MCP wrapper > uses exactly one routing context snapshot per proxied operation 541ms
✓ Miftah MCP wrapper > returns unknown context profile errors without forwarding the proxied call 389ms
✓ Miftah MCP wrapper > restores a workspace-scoped active profile and exposes safe selection metadata 526ms
✓ tests/package-contract.test.ts (16 tests) 19164ms
✓ packed artifact contract > keeps the test worker responsive while an npm subprocess is running 550ms
✓ packed artifact contract > includes captured output when an npm command exits unsuccessfully 325ms
✓ packed artifact contract > contains required runtime, documentation, and example files from a real dry run 391ms
✓ packed artifact contract > runs the checked package command against the real npm pack output 610ms
✓ packed artifact contract > loads the installed entry point and runs the installed binary from a real tarball 11370ms
✓ tests/multi-upstream.test.ts (28 tests) 11490ms
✓ multi-upstream wrapper > keeps healthy aggregate capabilities available when a bundled upstream cannot start 438ms
✓ multi-upstream wrapper > reports redacted capability discovery failures as degraded upstream health 423ms
✓ multi-upstream wrapper > fails strict tool discovery with a complete unavailable-upstream diagnostic 302ms
✓ multi-upstream wrapper > aggregates namespaced resources and routes each read to its originating upstream 360ms
✓ multi-upstream wrapper > removes stale routes when a bundled upstream fails resource discovery 377ms
✓ multi-upstream wrapper > removes stale routes when a bundled upstream fails prompt discovery 540ms
✓ multi-upstream wrapper > notifies clients when partial resource and prompt discovery recovers 590ms
✓ multi-upstream wrapper > rejects ambiguous namespaced resource and prompt collisions atomically 342ms
✓ multi-upstream wrapper > preserves independent upstream pagination through opaque aggregate cursors 363ms
✓ multi-upstream wrapper > notifies clients to re-list aggregated resources and prompts after a profile change 837ms
✓ multi-upstream wrapper > redacts configured and URI-embedded credentials before publishing a namespaced resource URI 377ms
✓ multi-upstream wrapper > uses controlled discovery to route cold namespaced reads and prompt gets exactly 407ms
✓ multi-upstream wrapper > routes a namespaced tool call before tools are listed 357ms
✓ multi-upstream wrapper > restarts every upstream in a profile bundle and invalidates its tool snapshot 642ms
✓ multi-upstream wrapper > recovers healthy tool discovery after a partial multi-upstream restart failure 840ms
✓ multi-upstream wrapper > refreshes the tool registry after an upstream crashes and later recovers 1154ms
✓ multi-upstream wrapper > waits for every bundled restart to settle before returning a restart failure 709ms
✓ multi-upstream wrapper > lists namespaced upstream tools through the deterministic registry 356ms
✓ multi-upstream wrapper > namespaces and routes tools from multiple upstream servers 381ms
✓ tests/upstream-manager.test.ts (26 tests) 7539ms
✓ upstream process manager > shuts down an idle profile and starts a fresh process on its next use 323ms
✓ upstream process manager > keeps a replacement session healthy and reserved while an idle predecessor is still closing 1578ms
✓ upstream process manager > does not restart a crashed process unless automatic recovery is configured 428ms
✓ upstream process manager > automatically recovers a crashed profile after a bounded backoff 328ms
✓ upstream process manager > stops automatic recovery when the configured restart budget is exhausted 794ms
✓ upstream process manager > records a failed restart teardown before starting a replacement session 330ms
✓ upstream process manager > cancels pending automatic recovery when the manager shuts down 431ms
✓ tests/operation-pipeline.test.ts (20 tests) 7838ms
✓ operation pipeline > verifies named upstream writes against their exact override or inherited identity 409ms
✓ operation pipeline > rejects ambiguous resource and prompt routes before forwarding them upstream 529ms
✓ operation pipeline > does not forward destructive calls when project context profile hints conflict 380ms
✓ operation pipeline > routes resource reads and prompt retrieval to the selected profile 369ms
✓ operation pipeline > keeps in-flight resource and prompt operations bound to their captured profile 575ms
✓ operation pipeline > completes in-flight aggregate reads and prompts after a profile switch 810ms
✓ operation pipeline > retries cold aggregate route discovery against the captured profile after a switch 1312ms
✓ operation pipeline > checks aggregate resource and prompt policy before discovering upstream routes 340ms
✓ operation pipeline > redacts credential-bearing resource URIs before writing audit metadata 306ms
✓ operation pipeline > redacts credential-bearing unknown resource URIs in diagnostics 375ms
✓ operation pipeline > redacts sensitive URI components from direct resource and prompt metadata 303ms
✓ tests/audit-outcomes.test.ts (15 tests) 4320ms
✓ audit outcomes > records one terminal operation event for list, management, and unknown-tool requests 541ms
✓ audit outcomes > keeps MCP results available and exposes audit health when fail-open writes fail 444ms
✓ audit outcomes > records upstream crash and automatic recovery outcomes 516ms
✓ tests/doctor.test.ts (22 tests) 4201ms
✓ doctor readiness runner > accepts a relative executable resolved from its configured working directory 805ms
✓ doctor readiness runner > reports profile tool schema differences as warnings or strict errors 479ms
✓ tests/identity-manager.test.ts (23 tests) 4299ms
✓ identity verifier > forwards cancellation through identity probe discovery 683ms
✓ identity verifier > keeps a shared identity probe alive when one request cancels 628ms
✓ tests/approval-fallback.test.ts (23 tests) 4678ms
✓ tests/remote-transport.test.ts (11 tests) 4124ms
✓ remote upstream transports > forces local cleanup when a Streamable HTTP session DELETE hangs 3821ms
✓ tests/secret-providers.test.ts (89 tests | 11 skipped) 2559ms
✓ external secret providers > resolves default keychain and 1Password commands from absolute PATH entries 754ms
✓ tests/http-server.test.ts (15 tests) 3738ms
✓ Miftah Streamable HTTP server > bounds oversized JSON requests before they can reserve a session 311ms
✓ Miftah Streamable HTTP server > keeps profile selection isolated across concurrent HTTP client sessions 505ms
✓ Miftah Streamable HTTP server > reconnects an interrupted SSE stream to the existing session without creating another runtime 333ms
✓ Miftah Streamable HTTP server > expires idle sessions and releases their retained upstream transport 1033ms
✓ Miftah Streamable HTTP server > keeps capacity reserved until a deleted session runtime has finished closing 538ms
✓ Miftah Streamable HTTP server > reserves capacity during concurrent initialization and releases it after DELETE 519ms
✓ tests/audit-integrity.test.ts (20 tests) 3012ms
✓ audit journal integrity > compacts integrity metadata when retention removes every rotated archive 1065ms
✓ tests/audit-log-reader.test.ts (35 tests) 2198ms
✓ logs command integration > follows a managed rotation that completes before its next poll 562ms
✓ tests/audit.test.ts (24 tests) 1834ms
✓ audit logger > serializes concurrent logger instances through the journal lock at a rotation boundary 1502ms
✓ tests/profile-lease-pipeline.test.ts (6 tests) 1534ms
✓ profile leases in the operation pipeline > does not let a routed profile borrow the active profile's lease 474ms
✓ tests/plugin-api.test.ts (13 tests) 1221ms
✓ tests/profile-runtime-isolation.test.ts (23 tests) 961ms
✓ tests/profile-lock-mcp.test.ts (3 tests) 989ms
✓ MCP profile locks > exposes opt-in connection-bound lock controls and safe profile state 381ms
✓ MCP profile locks > keeps lock controls discoverable but rejects calls while the opt-in is disabled 369ms
✓ tests/routing-context.test.ts (21 tests) 962ms
✓ tests/executable-resolver.test.ts (4 tests) 870ms
✓ tests/plugin-routing-server.test.ts (1 test) 705ms
✓ plugin routing server integration > uses configured routing plugins for real proxied MCP tool calls 705ms
✓ tests/config-preflight.test.ts (4 tests) 554ms
✓ tests/profile-transition-audit-barrier.test.ts (1 test) 519ms
✓ profile transition audit barrier > does not expose a new profile lease to concurrent calls before its required audit transition commits 518ms
✓ tests/public-api.test.ts (4 tests) 255ms
✓ tests/secrets.test.ts (20 tests) 135ms
✓ tests/config-migration.test.ts (21 tests) 141ms
✓ tests/plugin-routing-pipeline.test.ts (1 test) 115ms
✓ tests/fake-upstream-fixture.test.ts (1 test) 108ms
✓ tests/audit-verify.test.ts (2 tests) 105ms
✓ tests/audit-export.test.ts (6 tests) 106ms
✓ tests/profile-state.test.ts (10 tests) 77ms
✓ tests/negative-paths.test.ts (5 tests) 69ms
✓ tests/init-command.test.ts (10 tests) 22ms
✓ tests/managed-audit-follow.test.ts (1 test) 23ms
✓ tests/config-runtime-parity.test.ts (62 tests) 19ms
✓ tests/runtime-config-resolution.test.ts (4 tests) 15ms
✓ tests/config.test.ts (56 tests) 18ms
✓ tests/config-schema-contract.test.ts (8 tests) 16ms
✓ tests/approval-store.test.ts (11 tests) 15ms
✓ tests/audit-journal-path-safety.test.ts (1 test) 16ms
✓ tests/config-diagnostics.test.ts (29 tests) 12ms
✓ tests/config-public-contract.test.ts (4 tests) 12ms
✓ tests/preset-catalog.test.ts (31 tests) 13ms
✓ tests/windows-config-acl.test.ts (6 tests) 10ms
✓ tests/config-loader.test.ts (4 tests) 8ms
✓ tests/resource-prompt-registry.test.ts (13 tests) 8ms
✓ tests/release-config.test.ts (18 tests) 8ms
✓ tests/profile-runtime-isolation-docs-contract.test.ts (2 tests) 4ms
✓ tests/identity-docs-contract.test.ts (2 tests) 7ms
✓ tests/routing-policy.test.ts (24 tests) 7ms
✓ tests/doctor-report.test.ts (10 tests) 11ms
✓ tests/tool-registry.test.ts (1 test) 9ms
✓ tests/windows-config-migration-acl-failure.test.ts (1 test) 8ms
✓ tests/secret-provider-availability.test.ts (8 tests | 2 skipped) 7ms
✓ tests/preset-docs-contract.test.ts (1 test) 6ms
✓ tests/routing-context-docs-contract.test.ts (1 test) 2ms
✓ tests/profile-manager.test.ts (10 tests) 5ms
✓ tests/logs-writer.test.ts (4 tests) 6ms
✓ tests/provider-routing-matchers.test.ts (14 tests) 5ms
✓ tests/windows-secret-command-contract.test.ts (2 tests) 2ms
✓ tests/client-snippets.test.ts (30 tests) 5ms
✓ tests/release-version.test.ts (4 tests) 5ms
✓ tests/cli-parse.test.ts (10 tests) 4ms
✓ tests/profile-state-docs-contract.test.ts (1 test) 3ms
✓ tests/provider-routing-matchers-docs-contract.test.ts (1 test) 3ms
✓ tests/presets.test.ts (4 tests) 3ms
✓ tests/tooling-config.test.ts (4 tests) 4ms
✓ tests/threat-model-docs-contract.test.ts (2 tests) 4ms
✓ tests/progress-preserving-transport.test.ts (7 tests) 4ms
✓ tests/profile-leases-docs-contract.test.ts (1 test) 6ms
✓ tests/secret-provider-docs-contract.test.ts (3 tests) 3ms
✓ tests/risk-classification-docs-contract.test.ts (1 test) 2ms
✓ tests/approval-docs-contract.test.ts (1 test) 2ms
✓ tests/cli-exit-codes.test.ts (3 tests) 2ms
↓ tests/windows-config-migration-acl.test.ts (9 tests | 9 skipped)

Test Files 75 passed | 1 skipped (76)
Tests 998 passed | 22 skipped (1020)
Start at 19:23:15
Duration 146.13s (transform 1.02s, setup 0ms, collect 4.24s, tests 129.00s, environment 8ms, prepare 2.91s)

% Coverage report from v8
-------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-------------------|---------|----------|---------|---------|-------------------
All files | 95.46 | 92.04 | 98.27 | 95.46 |
config | 95.76 | 93.33 | 100 | 95.76 |
diagnostics.ts | 96.77 | 93.93 | 100 | 96.77 | 18-19
env-expand.ts | 100 | 100 | 100 | 100 |
...son-schema.ts | 98.96 | 93.75 | 100 | 98.96 | 9-10
load-config.ts | 94.4 | 86.27 | 100 | 94.4 | 98-104
...ate-config.ts | 98.09 | 96 | 100 | 98.09 | 107-108
path-resolve.ts | 100 | 75 | 100 | 100 | 5
presets.ts | 96.73 | 94.56 | 100 | 96.73 | ...51-252,262-263
schema.ts | 94.99 | 94.48 | 100 | 94.99 | ...-819,1117-1125
types.ts | 0 | 0 | 0 | 0 |
...ate-config.ts | 90.38 | 86.66 | 100 | 90.38 | 22,59-60,62-63
versions.ts | 100 | 100 | 100 | 100 |
mcp/server | 97.9 | 93.64 | 98.92 | 97.9 |
...n-pipeline.ts | 100 | 96.9 | 100 | 100 | 105,217,273
...t-registry.ts | 97.45 | 92.1 | 98.14 | 97.45 | ...78,693,762-763
tool-registry.ts | 97.47 | 94.49 | 100 | 97.47 | 194-198
secrets | 94.56 | 90.49 | 96.33 | 94.56 |
...-providers.ts | 100 | 90 | 100 | 100 | 56,77,84
...e-resolver.ts | 100 | 89.79 | 100 | 100 | 21,25,60-61
...-providers.ts | 95.12 | 92 | 94.73 | 95.12 | ...83-284,294-295
...-reference.ts | 97.89 | 95.91 | 100 | 97.89 | 99-100
redact.ts | 96.13 | 94.73 | 100 | 96.13 | ...63-264,276-280
...ess-runner.ts | 86.97 | 81.69 | 94.11 | 86.97 | ...67,180-182,239
...ailability.ts | 94.93 | 81.57 | 100 | 94.93 | 85-86,100-101
...t-provider.ts | 0 | 0 | 0 | 0 |
...t-resolver.ts | 93.98 | 91.89 | 85.71 | 93.98 | ...08,214,224-226
upstream | 92.98 | 90.06 | 98.52 | 92.98 |
remote-error.ts | 91.66 | 80 | 100 | 91.66 | 41-42,50-51
...ss-manager.ts | 93.06 | 90.7 | 98.38 | 93.06 | ...22-823,884-885
-------------------|---------|----------|---------|---------|-------------------,

@lubab/miftah@0.1.1 lint
eslint .,
@lubab/miftah@0.1.1 typecheck
tsc --noEmit,
@lubab/miftah@0.1.1 build
tsup

CLI Building entry: {"cli/main":"src/cli/main.ts","index":"src/index.ts","plugin-api":"src/plugins/plugin-api.ts","plugin-host":"src/plugin-host.mjs"}
CLI Using tsconfig: tsconfig.json
CLI tsup v8.5.1
CLI Using tsup config: /private/tmp/miftah-issue37-threat-model/tsup.config.ts
CLI Target: es2022
CLI Cleaning output folder
ESM Build start
ESM dist/index.js 440.00 B
ESM dist/chunk-FY43IYAP.js 170.00 B
ESM dist/plugin-api.js 165.00 B
ESM dist/plugin-host.js 4.85 KB
ESM dist/cli/main.js 136.40 KB
ESM dist/chunk-R67JK7EH.js 552.47 KB
ESM dist/index.js.map 71.00 B
ESM dist/chunk-FY43IYAP.js.map 2.61 KB
ESM dist/plugin-host.js.map 8.60 KB
ESM dist/plugin-api.js.map 71.00 B
ESM dist/cli/main.js.map 252.91 KB
ESM dist/chunk-R67JK7EH.js.map 1.03 MB
ESM ⚡️ Build success in 38ms
DTS Build start
DTS ⚡️ Build success in 4515ms
DTS dist/cli/main.d.ts 73.00 B
DTS dist/index.d.ts 17.86 KB
DTS dist/plugin-host.d.ts 4.83 KB
DTS dist/plugin-api.d.ts 2.72 KB,

@lubab/miftah@0.1.1 smoke:cli
node dist/cli/main.js schema

{
"type": "object",
"properties": {
"version": {
"type": "string",
"enum": [
"1",
"2"
]
},
"name": {
"type": "string",
"minLength": 1
},
"description": {
"type": "string"
},
"defaultProfile": {
"type": "string",
"minLength": 1
},
"upstream": {
"type": "object",
"properties": {
"transport": {
"type": "string",
"enum": [
"stdio",
"http",
"sse",
"streamable-http"
]
},
"command": {
"type": "string",
"minLength": 1
},
"args": {
"type": "array",
"items": {
"type": "string"
}
},
"env": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"cwd": {
"type": "string"
},
"url": {
"type": "string",
"format": "uri"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"trustToolAnnotations": {
"type": "boolean"
}
},
"required": [
"transport"
],
"additionalProperties": false
},
"upstreams": {
"type": "object",
"additionalProperties": {
"$ref": "#/properties/upstream"
}
},
"profiles": {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
},
"env": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"args": {
"type": "array",
"items": {
"type": "string"
}
},
"cwd": {
"type": "string"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"policy": {
"type": "string"
},
"identity": {
"type": "object",
"properties": {
"expected": {
"type": "object",
"properties": {
"provider": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"login": {
"$ref": "#/properties/profiles/additionalProperties/properties/identity/properties/expected/properties/provider"
},
"organization": {
"$ref": "#/properties/profiles/additionalProperties/properties/identity/properties/expected/properties/provider"
},
"host": {
"$ref": "#/properties/profiles/additionalProperties/properties/identity/properties/expected/properties/provider"
}
},
"additionalProperties": false
},
"probe": {
"type": "object",
"properties": {
"tool": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"resultFormat": {
"type": "string",
"enum": [
"text",
"json"
]
},
"provider": {
"$ref": "#/properties/profiles/additionalProperties/properties/identity/properties/expected/properties/provider"
}
},
"required": [
"tool",
"resultFormat"
],
"additionalProperties": false
},
"maxAgeMs": {
"type": "integer",
"exclusiveMinimum": true,
"minimum": 0,
"maximum": 86400000
},
"requiredForRisk": {
"type": "array",
"items": {
"type": "string",
"enum": [
"write",
"destructive"
]
},
"minItems": 1
}
},
"required": [
"expected",
"probe",
"maxAgeMs"
],
"additionalProperties": false
},
"lease": {
"type": "object",
"properties": {
"ttlMs": {
"type": "integer",
"minimum": 1000,
"maximum": 3600000
},
"requiredForRisk": {
"type": "array",
"items": {
"type": "string",
"enum": [
"write",
"destructive"
]
},
"minItems": 1,
"maxItems": 2,
"uniqueItems": true
}
},
"required": [
"ttlMs",
"requiredForRisk"
],
"additionalProperties": false
},
"isolation": {
"type": "object",
"properties": {
"files": {
"type": "array",
"items": {
"type": "object",
"properties": {
"source": {
"type": "string",
"minLength": 1,
"maxLength": 512,
"pattern": "^(?![A-Za-z]:)(?![\\/])(?!.(?:^|[\\/])(?:\.{1,2})(?:[\\/]|$))[^\\\\/\\u0000]+(?:[\\/][^\\\\/\\u0000]+)$"
},
"destination": {
"$ref": "#/properties/profiles/additionalProperties/properties/isolation/properties/files/items/properties/source"
},
"environment": {
"type": "string",
"pattern": "^[A-Za-z_][A-Za-z0-9_]{0,127}$"
}
},
"required": [
"source",
"destination"
],
"additionalProperties": false
},
"maxItems": 32,
"uniqueItems": true
},
"containerVolumes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"source": {
"type": "string",
"minLength": 1,
"maxLength": 512,
"pattern": "^(?![A-Za-z]:)(?![\\/])(?!.(?:^|[\\/])(?:\.{1,2})(?:[\\/]|$))[^\\\\/,\\u0000]+(?:[\\/][^\\\\/,\\u0000]+)$"
},
"destination": {
"type": "string",
"minLength": 1,
"maxLength": 512,
"pattern": "^\/(?!.(?:^|\/)(?:\.{1,2})(?:\/|$))[^\\\\/,\\u0000]+(?:\/[^\\\\/,\\u0000]+)$"
},
"readOnly": {
"type": "boolean"
},
"environment": {
"$ref": "#/properties/profiles/additionalProperties/properties/isolation/properties/files/items/properties/environment"
}
},
"required": [
"source",
"destination"
],
"additionalProperties": false
},
"maxItems": 32,
"uniqueItems": true
}
},
"additionalProperties": false
},
"routing": {
"type": "object",
"properties": {
"match": {
"type": "object",
"properties": {
"github": {
"type": "object",
"properties": {
"repositories": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[a-z0-9][a-z0-9_.-]{0,99}\/[a-z0-9][a-z0-9_.-]{0,99}$"
},
"minItems": 1,
"maxItems": 32,
"uniqueItems": true
},
"organizations": {
"type": "array",
"items": {
"type": "string",
"pattern": "^a-z0-9$"
},
"minItems": 1,
"maxItems": 32,
"uniqueItems": true
}
},
"additionalProperties": false,
"minProperties": 1
},
"sentry": {
"type": "object",
"properties": {
"organizations": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[a-z0-9][a-z0-9_-]{0,127}$"
},
"minItems": 1,
"maxItems": 32,
"uniqueItems": true
},
"projects": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[a-z0-9][a-z0-9_-]{0,127}\/[a-z0-9][a-z0-9_-]{0,127}$"
},
"minItems": 1,
"maxItems": 32,
"uniqueItems": true
},
"environments": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[A-Za-z0-9][A-Za-z0-9.-]{0,127}$"
},
"minItems": 1,
"maxItems": 32,
"uniqueItems": true
}
},
"additionalProperties": false,
"minProperties": 1
},
"jira": {
"type": "object",
"properties": {
"sites": {
"type": "array",
"items": {
"type": "string",
"maxLength": 256,
"pattern": "^https:\/\/(?!.:443$)(?:a-z0-9?)(?:\.(?:a-z0-9?))(?::(?:[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?$"
},
"minItems": 1,
"maxItems": 32,
"uniqueItems": true
},
"projects": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9
]{0,9}$"
},
"minItems": 1,
"maxItems": 32,
"uniqueItems": true
}
},
"additionalProperties": false,
"minProperties": 1
},
"linear": {
"type": "object",
"properties": {
"workspaces": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[a-z0-9][a-z0-9-]{0,127}$"
},
"minItems": 1,
"maxItems": 32,
"uniqueItems": true
},
"teams": {
"type": "array",
"items": {
"$ref": "#/properties/profiles/additionalProperties/properties/routing/properties/match/properties/linear/properties/workspaces/items"
},
"minItems": 1,
"maxItems": 32,
"uniqueItems": true
}
},
"additionalProperties": false,
"minProperties": 1
},
"posthog": {
"type": "object",
"properties": {
"hosts": {
"type": "array",
"items": {
"$ref": "#/properties/profiles/additionalProperties/properties/routing/properties/match/properties/jira/properties/sites/items",
"pattern": "^https:\/\/(?!.:443$)(?:a-z0-9?)(?:\.(?:a-z0-9?))(?::(?:[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?$"
},
"minItems": 1,
"maxItems": 32,
"uniqueItems": true
},
"projects": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[1-9][0-9]{0,17}$"
},
"minItems": 1,
"maxItems": 32,
"uniqueItems": true
}
},
"additionalProperties": false,
"minProperties": 1
}
},
"additionalProperties": false,
"minProperties": 1
}
},
"required": [
"match"
],
"additionalProperties": false
},
"upstreams": {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"args": {
"type": "array",
"items": {
"type": "string"
}
},
"env": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"cwd": {
"type": "string"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"identity": {
"$ref": "#/properties/profiles/additionalProperties/properties/identity"
},
"isolation": {
"$ref": "#/properties/profiles/additionalProperties/properties/isolation"
}
},
"additionalProperties": false
}
}
},
"additionalProperties": false
}
},
"routing": {
"type": "object",
"properties": {
"mode": {
"type": "string",
"const": "hybrid"
},
"fallback": {
"type": "string",
"enum": [
"default",
"activeProfile",
"ask",
"block"
]
},
"rules": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"when": {
"type": "object",
"additionalProperties": {}
},
"profile": {
"type": "string",
"minLength": 1
}
},
"required": [
"when",
"profile"
],
"additionalProperties": false
}
}
},
"additionalProperties": false
},
"policies": {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"allow": {
"type": "array",
"items": {
"type": "string",
"enum": [
"read",
"write",
"destructive"
]
}
},
"allowRisk": {
"type": "array",
"items": {
"type": "string",
"enum": [
"read",
"write",
"destructive"
]
}
},
"deny": {
"type": "array",
"items": {
"type": "string"
}
},
"denyRisk": {
"type": "array",
"items": {
"type": "string",
"enum": [
"read",
"write",
"destructive"
]
}
},
"requireConfirmation": {
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false
}
},
"security": {
"type": "object",
"properties": {
"allowPlaintextSecrets": {
"type": "boolean"
},
"redactSecrets": {
"type": "boolean",
"const": true
},
"allowProfileSwitchingFromMcp": {
"type": "boolean"
},
"requireProfileSwitchConfirmation": {
"type": "boolean"
},
"allowProfileLockingFromMcp": {
"type": "boolean"
},
"requireExplicitProfileForDestructive": {
"type": "boolean"
},
"requireExplicitSelectionForDestructive": {
"type": "boolean"
},
"lockToProfile": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": false
},
"process": {
"type": "object",
"properties": {
"startupTimeoutMs": {
"type": "integer",
"exclusiveMinimum": true,
"minimum": 0
},
"shutdownTimeoutMs": {
"type": "integer",
"exclusiveMinimum": true,
"minimum": 0
},
"idleTimeoutMs": {
"type": "integer",
"exclusiveMinimum": true,
"minimum": 0
},
"restartOnCrash": {
"type": "boolean"
},
"maxRestarts": {
"type": "integer",
"minimum": 0
},
"maxConcurrentProfiles": {
"type": "integer",
"exclusiveMinimum": true,
"minimum": 0
}
},
"additionalProperties": false
},
"audit": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"path": {
"type": "string",
"minLength": 1
},
"format": {
"type": "string",
"const": "jsonl"
},
"includeArguments": {
"type": "boolean"
},
"redact": {
"type": "boolean",
"const": true
},
"failureMode": {
"type": "string",
"enum": [
"fail-open",
"fail-closed"
]
},
"rotation": {
"type": "object",
"properties": {
"maxBytes": {
"type": "integer",
"exclusiveMinimum": true,
"minimum": 0,
"maximum": 2147483647
},
"maxAgeMs": {
"type": "integer",
"exclusiveMinimum": true,
"minimum": 0,
"maximum": 31536000000
},
"retainFiles": {
"type": "integer",
"minimum": 0,
"maximum": 2000
}
},
"required": [
"retainFiles"
],
"additionalProperties": false,
"anyOf": [
{
"required": [
"maxBytes"
]
},
{
"required": [
"maxAgeMs"
]
}
]
},
"integrity": {
"type": "object",
"properties": {
"algorithm": {
"type": "string",
"const": "sha256-chain"
}
},
"required": [
"algorithm"
],
"additionalProperties": false
}
},
"additionalProperties": false
},
"tooling": {
"type": "object",
"properties": {
"collisionStrategy": {
"type": "string",
"enum": [
"prefix-upstream",
"fail"
]
},
"toolDiscoveryMode": {
"type": "string",
"enum": [
"permissive",
"strict"
]
},
"toolRiskOverrides": {
"type": "object",
"additionalProperties": {
"type": "string",
"enum": [
"read",
"write",
"destructive"
]
}
},
"unknownToolRisk": {
"type": "string",
"enum": [
"write",
"destructive"
]
}
},
"additionalProperties": false
},
"secrets": {
"type": "object",
"properties": {
"envFiles": {
"type": "array",
"items": {
"type": "string"
}
},
"allowPlaintextSecrets": {
"type": "boolean"
},
"providerTimeoutMs": {
"type": "integer",
"minimum": 100,
"maximum": 120000
}
},
"additionalProperties": false
},
"plugins": {
"type": "object",
"properties": {
"allowlist": {
"type": "array",
"items": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string",
"pattern": "^[a-z][a-z0-9-]{0,63}$"
},
"kind": {
"type": "string",
"const": "secret-provider"
},
"path": {
"type": "string",
"pattern": "^\.\/(?:[A-Za-z0-9][A-Za-z0-9.-]\/)[A-Za-z0-9][A-Za-z0-9.-]*\.mjs$"
}
},
"required": [
"id",
"kind",
"path"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"id": {
"$ref": "#/properties/plugins/properties/allowlist/items/anyOf/0/properties/id"
},
"kind": {
"type": "string",
"const": "routing-matcher"
},
"path": {
"$ref": "#/properties/plugins/properties/allowlist/items/anyOf/0/properties/path"
},
"bindings": {
"type": "object",
"additionalProperties": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"propertyNames": {
"pattern": "^[a-z0-9][a-z0-9._-]{0,127}$"
}
}
},
"required": [
"id",
"kind",
"path",
"bindings"
],
"additionalProperties": false
}
]
},
"minItems": 1,
"maxItems": 32
},
"timeoutMs": {
"type": "integer",
"minimum": 100,
"maximum": 60000
}
},
"required": [
"allowlist"
],
"additionalProperties": false
},
"state": {
"type": "object",
"properties": {
"persistActiveProfile": {
"type": "boolean"
},
"scope": {
"type": "string",
"enum": [
"process",
"session",
"workspace",
"global"
]
}
},
"additionalProperties": false
},
"server": {
"type": "object",
"properties": {
"http": {
"type": "object",
"properties": {
"host": {
"type": "string",
"minLength": 1,
"maxLength": 253
},
"port": {
"type": "integer",
"minimum": 0,
"maximum": 65535
},
"allowNonLoopback": {
"type": "boolean",
"const": true
},
"authToken": {
"type": "string",
"minLength": 1,
"maxLength": 4096
},
"allowedHosts": {
"type": "array",
"items": {
"$ref": "#/properties/server/properties/http/properties/host"
},
"minItems": 1,
"maxItems": 64
},
"allowedOrigins": {
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 2048
},
"maxItems": 64
},
"maxSessions": {
"type": "integer",
"minimum": 1,
"maximum": 256
},
"sessionIdleTimeoutMs": {
"type": "integer",
"minimum": 1000,
"maximum": 86400000
},
"maxRequestBytes": {
"type": "integer",
"minimum": 1024,
"maximum": 10485760
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
},
"required": [
"version",
"name",
"defaultProfile",
"profiles"
],
"additionalProperties": false,
"$schema": "https://json-schema.org/draft/2019-09/schema#",
"allOf": [
{
"oneOf": [
{
"required": [
"upstream"
]
},
{
"required": [
"upstreams"
]
}
]
},
{
"if": {
"required": [
"state"
],
"properties": {
"state": {
"required": [
"persistActiveProfile"
],
"properties": {
"persistActiveProfile": {
"const": true
}
}
}
}
},
"then": {
"properties": {
"state": {
"required": [
"scope"
],
"properties": {
"scope": {
"enum": [
"workspace",
"global"
]
}
}
}
}
}
},
{
"if": {
"required": [
"state"
],
"properties": {
"state": {
"required": [
"scope"
],
"properties": {
"scope": {
"enum": [
"workspace",
"global"
]
}
}
}
}
},
"then": {
"properties": {
"state": {
"required": [
"persistActiveProfile"
],
"properties": {
"persistActiveProfile": {
"const": true
}
}
}
}
}
},
{
"if": {
"required": [
"audit"
],
"properties": {
"audit": {
"anyOf": [
{
"required": [
"rotation"
]
},
{
"required": [
"integrity"
]
}
]
}
}
},
"then": {
"properties": {
"audit": {
"required": [
"path"
],
"properties": {
"enabled": {
"not": {
"const": false
}
}
}
}
}
}
},
{
"if": {
"required": [
"version"
],
"properties": {
"version": {
"const": "2"
}
}
},
"then": {
"allOf": [
{
"not": {
"required": [
"security"
],
"properties": {
"security": {
"required": [
"allowPlaintextSecrets"
]
}
}
}
},
{
"not": {
"required": [
"security"
],
"properties": {
"security": {
"required": [
"redactSecrets"
]
}
}
}
},
{
"not": {
"required": [
"audit"
],
"properties": {
"audit": {
"required": [
"redact"
]
}
}
}
},
{
"not": {
"required": [
"upstream"
],
"properties": {
"upstream": {
"required": [
"transport"
],
"properties": {
"transport": {
"const": "http"
}
}
}
}
}
},
{
"properties": {
"upstreams": {
"additionalProperties": {
"not": {
"required": [
"transport"
],
"properties": {
"transport": {
"const": "http"
}
}
}
}
}
}
}
]
}
}
],
"title": "Miftah configuration"
},

@lubab/miftah@0.1.1 check:pack
node scripts/check-pack.mjs

Package contract verified (41 files)., and

@lubab/miftah@0.1.1 test:package
vitest run tests/package-contract.test.ts

RUN v3.2.7 /private/tmp/miftah-issue37-threat-model

✓ tests/package-contract.test.ts (16 tests) 18728ms
✓ packed artifact contract > keeps the test worker responsive while an npm subprocess is running 513ms
✓ packed artifact contract > includes captured output when an npm command exits unsuccessfully 327ms
✓ packed artifact contract > contains required runtime, documentation, and example files from a real dry run 393ms
✓ packed artifact contract > runs the checked package command against the real npm pack output 610ms
✓ packed artifact contract > loads the installed entry point and runs the installed binary from a real tarball 10885ms

Test Files 1 passed (1)
Tests 16 passed (16)
Start at 19:25:55
Duration 18.91s (transform 50ms, setup 0ms, collect 49ms, tests 18.73s, environment 0ms, prepare 29ms) — passed.

Issue status

This delivers the documentation portion of #37. The independent external review remains uncommissioned and is explicitly recorded as a release-blocking follow-up; this PR does not claim external assurance or close the issue.

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Threat Model Documentation

Layer / File(s) Summary
Threat model foundations
docs/threat-model.md
Defines scope, methodology, protected assets, actors, trust assumptions, and system boundaries.
Controls and operational boundaries
docs/threat-model.md
Documents threats, controls, residual risks, security defaults, non-goals, operator responsibilities, review status, and maintenance policy.
Documentation links and contract validation
README.md, SECURITY.md, docs/architecture.md, docs/security.md, tests/threat-model-docs-contract.test.ts
Adds threat-model cross-links and tests required headings, links, table structure, boundary statements, residual-risk phrases, and threat entries.

Estimated code review effort: 2 (Simple) | ~10 minutes

Poem

A rabbit found a model deep in docs,
With guarded paths and sturdy locks.
Threats now hop in ordered rows,
While tests keep links where each one goes.
The burrow’s map is clear and bright—
Safe little carrots sleep tonight.


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (1 error)

Check name Status Explanation Resolution
Docstring Coverage ❌ Error Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely summarizes the main change: publishing a public threat model under security docs.
Description check ✅ Passed The description follows the required Summary, Security impact, and Validation sections and includes concrete test commands and outcomes.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-37-threat-model

Comment @coderabbitai help to get the list of available commands.

@mohanagy

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/threat-model.md`:
- Line 63: Update the MCP client to local ingress row in the threat-model matrix
to state that secret-backed bearer authentication is mandatory for every
explicitly enabled non-loopback binding, replacing the current “optional”
wording while preserving the other listed controls.

In `@tests/threat-model-docs-contract.test.ts`:
- Around line 68-79: Update the threat list in the contract test’s iteration to
include “**Credential-runtime or container handoff exposure**” and “**Misleading
upstream identity or response data**”. Keep the existing assertions unchanged so
every documented threat row in threatModel is required by the test.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3b65f6d4-3980-4d16-8ecf-60b36b79833b

📥 Commits

Reviewing files that changed from the base of the PR and between c03564f and 5a6eec8.

📒 Files selected for processing (6)
  • README.md
  • SECURITY.md
  • docs/architecture.md
  • docs/security.md
  • docs/threat-model.md
  • tests/threat-model-docs-contract.test.ts

Comment thread docs/threat-model.md Outdated
Comment thread tests/threat-model-docs-contract.test.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant