@vellumai/assistant 0.4.17 → 0.4.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/eslint.config.mjs +2 -2
- package/package.json +1 -1
- package/src/__tests__/access-request-decision.test.ts +128 -120
- package/src/__tests__/account-registry.test.ts +121 -110
- package/src/__tests__/active-skill-tools.test.ts +200 -172
- package/src/__tests__/actor-token-service.test.ts +341 -274
- package/src/__tests__/agent-loop-thinking.test.ts +28 -19
- package/src/__tests__/agent-loop.test.ts +798 -378
- package/src/__tests__/anthropic-provider.test.ts +405 -247
- package/src/__tests__/app-builder-tool-scripts.test.ts +97 -97
- package/src/__tests__/app-bundler.test.ts +112 -79
- package/src/__tests__/app-executors.test.ts +205 -178
- package/src/__tests__/app-git-history.test.ts +90 -73
- package/src/__tests__/app-git-service.test.ts +67 -53
- package/src/__tests__/app-open-proxy.test.ts +29 -25
- package/src/__tests__/approval-conversation-turn.test.ts +100 -81
- package/src/__tests__/approval-hardcoded-copy-guard.test.ts +45 -17
- package/src/__tests__/approval-message-composer.test.ts +119 -119
- package/src/__tests__/approval-primitive.test.ts +264 -233
- package/src/__tests__/approval-routes-http.test.ts +4 -3
- package/src/__tests__/asset-materialize-tool.test.ts +250 -178
- package/src/__tests__/asset-search-tool.test.ts +251 -191
- package/src/__tests__/assistant-attachment-directive.test.ts +187 -142
- package/src/__tests__/assistant-attachments.test.ts +254 -186
- package/src/__tests__/assistant-event-hub.test.ts +105 -63
- package/src/__tests__/assistant-event.test.ts +66 -58
- package/src/__tests__/assistant-events-sse-hardening.test.ts +113 -73
- package/src/__tests__/assistant-feature-flag-guard.test.ts +78 -52
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +48 -45
- package/src/__tests__/assistant-feature-flags-integration.test.ts +118 -77
- package/src/__tests__/assistant-id-boundary-guard.test.ts +158 -104
- package/src/__tests__/attachments-store.test.ts +240 -183
- package/src/__tests__/attachments.test.ts +70 -62
- package/src/__tests__/audit-log-rotation.test.ts +50 -35
- package/src/__tests__/browser-fill-credential.test.ts +169 -101
- package/src/__tests__/browser-manager.test.ts +97 -75
- package/src/__tests__/browser-runtime-check.test.ts +16 -15
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +12 -10
- package/src/__tests__/browser-skill-endstate.test.ts +97 -72
- package/src/__tests__/bundle-scanner.test.ts +47 -22
- package/src/__tests__/bundled-asset.test.ts +74 -47
- package/src/__tests__/call-constants.test.ts +19 -19
- package/src/__tests__/call-controller.test.ts +0 -1
- package/src/__tests__/call-conversation-messages.test.ts +90 -65
- package/src/__tests__/call-domain.test.ts +149 -121
- package/src/__tests__/call-pointer-message-composer.test.ts +113 -83
- package/src/__tests__/call-pointer-messages.test.ts +213 -154
- package/src/__tests__/call-pointer-no-hardcoded-copy.guard.test.ts +9 -10
- package/src/__tests__/call-recovery.test.ts +232 -212
- package/src/__tests__/call-routes-http.test.ts +0 -1
- package/src/__tests__/call-start-guardian-guard.test.ts +32 -30
- package/src/__tests__/call-state-machine.test.ts +62 -51
- package/src/__tests__/call-state.test.ts +89 -75
- package/src/__tests__/call-store.test.ts +387 -316
- package/src/__tests__/callback-handoff-copy.test.ts +84 -82
- package/src/__tests__/canonical-guardian-store.test.ts +331 -280
- package/src/__tests__/channel-approval-routes.test.ts +1643 -1115
- package/src/__tests__/channel-approval.test.ts +139 -137
- package/src/__tests__/channel-approvals.test.ts +0 -1
- package/src/__tests__/channel-delivery-store.test.ts +232 -194
- package/src/__tests__/channel-guardian.test.ts +5 -3
- package/src/__tests__/channel-invite-transport.test.ts +107 -92
- package/src/__tests__/channel-policy.test.ts +42 -38
- package/src/__tests__/channel-readiness-service.test.ts +119 -102
- package/src/__tests__/channel-reply-delivery.test.ts +147 -118
- package/src/__tests__/channel-retry-sweep.test.ts +153 -110
- package/src/__tests__/checker.test.ts +3309 -1850
- package/src/__tests__/clarification-resolver.test.ts +91 -79
- package/src/__tests__/classifier.test.ts +64 -54
- package/src/__tests__/claude-code-skill-regression.test.ts +42 -37
- package/src/__tests__/claude-code-tool-profiles.test.ts +31 -29
- package/src/__tests__/clawhub.test.ts +92 -82
- package/src/__tests__/cli.test.ts +30 -30
- package/src/__tests__/clipboard.test.ts +53 -46
- package/src/__tests__/commit-guarantee.test.ts +59 -52
- package/src/__tests__/commit-message-enrichment-service.test.ts +203 -75
- package/src/__tests__/compaction.benchmark.test.ts +33 -31
- package/src/__tests__/computer-use-session-compaction.test.ts +60 -50
- package/src/__tests__/computer-use-session-lifecycle.test.ts +145 -117
- package/src/__tests__/computer-use-session-working-dir.test.ts +62 -48
- package/src/__tests__/computer-use-skill-baseline.test.ts +22 -19
- package/src/__tests__/computer-use-skill-endstate.test.ts +45 -31
- package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +121 -88
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +65 -42
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +33 -18
- package/src/__tests__/computer-use-tools.test.ts +121 -98
- package/src/__tests__/config-schema.test.ts +443 -347
- package/src/__tests__/config-watcher.test.ts +96 -81
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +148 -133
- package/src/__tests__/conflict-intent-tokenization.test.ts +96 -78
- package/src/__tests__/conflict-policy.test.ts +151 -80
- package/src/__tests__/conflict-store.test.ts +203 -157
- package/src/__tests__/connection-policy.test.ts +89 -59
- package/src/__tests__/contacts-tools.test.ts +247 -178
- package/src/__tests__/context-memory-e2e.test.ts +306 -214
- package/src/__tests__/context-token-estimator.test.ts +114 -74
- package/src/__tests__/context-window-manager.test.ts +269 -167
- package/src/__tests__/contradiction-checker.test.ts +161 -135
- package/src/__tests__/conversation-attention-store.test.ts +350 -290
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-pairing.test.ts +220 -113
- package/src/__tests__/conversation-store.test.ts +390 -235
- package/src/__tests__/credential-broker-browser-fill.test.ts +325 -250
- package/src/__tests__/credential-broker-server-use.test.ts +283 -243
- package/src/__tests__/credential-broker.test.ts +128 -74
- package/src/__tests__/credential-host-pattern-match.test.ts +64 -44
- package/src/__tests__/credential-metadata-store.test.ts +360 -311
- package/src/__tests__/credential-policy-validate.test.ts +81 -65
- package/src/__tests__/credential-resolve.test.ts +212 -145
- package/src/__tests__/credential-security-e2e.test.ts +144 -103
- package/src/__tests__/credential-security-invariants.test.ts +253 -208
- package/src/__tests__/credential-selection.test.ts +254 -146
- package/src/__tests__/credential-vault-unit.test.ts +531 -341
- package/src/__tests__/credential-vault.test.ts +761 -484
- package/src/__tests__/daemon-assistant-events.test.ts +91 -66
- package/src/__tests__/daemon-lifecycle.test.ts +258 -190
- package/src/__tests__/daemon-server-session-init.test.ts +0 -1
- package/src/__tests__/date-context.test.ts +314 -249
- package/src/__tests__/db-migration-rollback.test.ts +259 -130
- package/src/__tests__/db-schedule-syntax-migration.test.ts +78 -41
- package/src/__tests__/delete-managed-skill-tool.test.ts +77 -53
- package/src/__tests__/deterministic-verification-control-plane.test.ts +0 -1
- package/src/__tests__/dictation-mode-detection.test.ts +77 -55
- package/src/__tests__/dictation-profile-store.test.ts +70 -56
- package/src/__tests__/dictation-text-processing.test.ts +53 -35
- package/src/__tests__/diff.test.ts +102 -98
- package/src/__tests__/domain-normalize.test.ts +54 -54
- package/src/__tests__/domain-policy.test.ts +71 -55
- package/src/__tests__/dynamic-page-surface.test.ts +31 -33
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +69 -69
- package/src/__tests__/edit-engine.test.ts +56 -56
- package/src/__tests__/elevenlabs-client.test.ts +117 -91
- package/src/__tests__/elevenlabs-config.test.ts +32 -31
- package/src/__tests__/email-classifier.test.ts +15 -12
- package/src/__tests__/email-cli.test.ts +121 -108
- package/src/__tests__/emit-signal-routing-intent.test.ts +76 -69
- package/src/__tests__/encrypted-store.test.ts +180 -154
- package/src/__tests__/entity-extractor.test.ts +108 -87
- package/src/__tests__/entity-search.test.ts +664 -258
- package/src/__tests__/ephemeral-permissions.test.ts +224 -188
- package/src/__tests__/event-bus.test.ts +81 -77
- package/src/__tests__/extract-email.test.ts +29 -20
- package/src/__tests__/file-edit-tool.test.ts +62 -44
- package/src/__tests__/file-ops-service.test.ts +131 -114
- package/src/__tests__/file-read-tool.test.ts +48 -31
- package/src/__tests__/file-write-tool.test.ts +43 -37
- package/src/__tests__/filesystem-tools.test.ts +238 -209
- package/src/__tests__/followup-tools.test.ts +237 -162
- package/src/__tests__/forbidden-legacy-symbols.test.ts +19 -20
- package/src/__tests__/frontmatter.test.ts +96 -81
- package/src/__tests__/fuzzy-match-property.test.ts +75 -81
- package/src/__tests__/fuzzy-match.test.ts +71 -65
- package/src/__tests__/gateway-client-managed-outbound.test.ts +76 -57
- package/src/__tests__/gateway-only-enforcement.test.ts +0 -1
- package/src/__tests__/gateway-only-guard.test.ts +0 -1
- package/src/__tests__/gemini-image-service.test.ts +113 -100
- package/src/__tests__/gemini-provider.test.ts +297 -220
- package/src/__tests__/get-weather.test.ts +188 -114
- package/src/__tests__/gmail-integration.test.ts +0 -1
- package/src/__tests__/guardian-action-conversation-turn.test.ts +226 -171
- package/src/__tests__/guardian-action-copy-generator.test.ts +111 -93
- package/src/__tests__/guardian-action-followup-executor.test.ts +0 -1
- package/src/__tests__/guardian-action-followup-store.test.ts +199 -167
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +297 -250
- package/src/__tests__/guardian-action-late-reply.test.ts +462 -316
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +23 -18
- package/src/__tests__/guardian-action-store.test.ts +158 -109
- package/src/__tests__/guardian-action-sweep.test.ts +114 -100
- package/src/__tests__/guardian-actions-endpoint.test.ts +440 -256
- package/src/__tests__/guardian-control-plane-policy.test.ts +497 -331
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +217 -215
- package/src/__tests__/guardian-dispatch.test.ts +316 -256
- package/src/__tests__/guardian-grant-minting.test.ts +247 -178
- package/src/__tests__/guardian-outbound-http.test.ts +5 -3
- package/src/__tests__/guardian-principal-id-roundtrip.test.ts +99 -96
- package/src/__tests__/guardian-question-copy.test.ts +17 -17
- package/src/__tests__/guardian-question-mode.test.ts +134 -100
- package/src/__tests__/guardian-routing-invariants.test.ts +0 -1
- package/src/__tests__/guardian-routing-state.test.ts +0 -1
- package/src/__tests__/guardian-verification-intent-routing.test.ts +94 -88
- package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -1
- package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +0 -1
- package/src/__tests__/handle-user-message-secret-resume.test.ts +0 -1
- package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +92 -76
- package/src/__tests__/handlers-cu-observation-blob.test.ts +103 -70
- package/src/__tests__/handlers-ipc-blob-probe.test.ts +77 -51
- package/src/__tests__/handlers-slack-config.test.ts +63 -54
- package/src/__tests__/handlers-task-submit-slash.test.ts +18 -18
- package/src/__tests__/handlers-telegram-config.test.ts +662 -329
- package/src/__tests__/handlers-twitter-config.test.ts +525 -298
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +3 -2
- package/src/__tests__/headless-browser-interactions.test.ts +444 -280
- package/src/__tests__/headless-browser-navigate.test.ts +116 -79
- package/src/__tests__/headless-browser-read-tools.test.ts +123 -86
- package/src/__tests__/headless-browser-snapshot.test.ts +71 -56
- package/src/__tests__/heartbeat-service.test.ts +76 -58
- package/src/__tests__/history-repair-observability.test.ts +14 -14
- package/src/__tests__/history-repair.test.ts +171 -167
- package/src/__tests__/home-base-bootstrap.test.ts +30 -27
- package/src/__tests__/hooks-blocking.test.ts +86 -37
- package/src/__tests__/hooks-cli.test.ts +104 -68
- package/src/__tests__/hooks-config.test.ts +81 -43
- package/src/__tests__/hooks-discovery.test.ts +106 -96
- package/src/__tests__/hooks-integration.test.ts +78 -72
- package/src/__tests__/hooks-manager.test.ts +99 -61
- package/src/__tests__/hooks-runner.test.ts +94 -71
- package/src/__tests__/hooks-settings.test.ts +69 -64
- package/src/__tests__/hooks-templates.test.ts +85 -54
- package/src/__tests__/hooks-ts-runner.test.ts +82 -45
- package/src/__tests__/hooks-watch.test.ts +32 -22
- package/src/__tests__/host-file-edit-tool.test.ts +190 -148
- package/src/__tests__/host-file-read-tool.test.ts +86 -63
- package/src/__tests__/host-file-write-tool.test.ts +98 -64
- package/src/__tests__/host-shell-tool.test.ts +342 -233
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -1
- package/src/__tests__/ingress-member-store.test.ts +163 -159
- package/src/__tests__/ingress-reconcile.test.ts +0 -1
- package/src/__tests__/ingress-routes-http.test.ts +441 -356
- package/src/__tests__/ingress-url-consistency.test.ts +125 -64
- package/src/__tests__/integration-status.test.ts +93 -73
- package/src/__tests__/intent-routing.test.ts +148 -118
- package/src/__tests__/invite-redemption-service.test.ts +163 -121
- package/src/__tests__/ipc-blob-store.test.ts +104 -91
- package/src/__tests__/ipc-contract-inventory.test.ts +27 -15
- package/src/__tests__/ipc-contract.test.ts +24 -23
- package/src/__tests__/ipc-protocol.test.ts +52 -46
- package/src/__tests__/ipc-roundtrip.benchmark.test.ts +61 -50
- package/src/__tests__/ipc-snapshot.test.ts +1135 -1056
- package/src/__tests__/ipc-validate.test.ts +240 -179
- package/src/__tests__/key-migration.test.ts +123 -90
- package/src/__tests__/keychain.test.ts +150 -123
- package/src/__tests__/lifecycle-docs-guard.test.ts +65 -64
- package/src/__tests__/llm-usage-store.test.ts +112 -87
- package/src/__tests__/managed-skill-lifecycle.test.ts +147 -108
- package/src/__tests__/managed-store.test.ts +411 -360
- package/src/__tests__/mcp-cli.test.ts +189 -123
- package/src/__tests__/mcp-health-check.test.ts +26 -21
- package/src/__tests__/media-generate-image.test.ts +122 -99
- package/src/__tests__/media-reuse-story.e2e.test.ts +282 -214
- package/src/__tests__/media-visibility-policy.test.ts +86 -38
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +146 -100
- package/src/__tests__/memory-lifecycle-e2e.test.ts +385 -297
- package/src/__tests__/memory-query-builder.test.ts +32 -33
- package/src/__tests__/memory-recall-quality.test.ts +761 -407
- package/src/__tests__/memory-regressions.experimental.test.ts +443 -380
- package/src/__tests__/memory-regressions.test.ts +3725 -2642
- package/src/__tests__/memory-retrieval-budget.test.ts +7 -8
- package/src/__tests__/memory-retrieval.benchmark.test.ts +144 -109
- package/src/__tests__/memory-upsert-concurrency.test.ts +292 -201
- package/src/__tests__/messaging-send-tool.test.ts +36 -29
- package/src/__tests__/migration-cli-flows.test.ts +69 -53
- package/src/__tests__/migration-ordering.test.ts +103 -86
- package/src/__tests__/mime-builder.test.ts +55 -32
- package/src/__tests__/mock-signup-server.test.ts +384 -246
- package/src/__tests__/model-intents.test.ts +61 -37
- package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +9 -12
- package/src/__tests__/no-is-trusted-guard.test.ts +24 -21
- package/src/__tests__/non-member-access-request.test.ts +3 -2
- package/src/__tests__/notification-broadcaster.test.ts +99 -81
- package/src/__tests__/notification-decision-fallback.test.ts +223 -178
- package/src/__tests__/notification-decision-strategy.test.ts +375 -337
- package/src/__tests__/notification-deep-link.test.ts +67 -61
- package/src/__tests__/notification-guardian-path.test.ts +248 -206
- package/src/__tests__/notification-routing-intent.test.ts +166 -93
- package/src/__tests__/notification-thread-candidate-validation.test.ts +78 -75
- package/src/__tests__/notification-thread-candidates.test.ts +64 -61
- package/src/__tests__/oauth-callback-registry.test.ts +40 -30
- package/src/__tests__/oauth-connect-handler.test.ts +109 -89
- package/src/__tests__/oauth-scope-policy.test.ts +63 -55
- package/src/__tests__/oauth2-gateway-transport.test.ts +252 -174
- package/src/__tests__/onboarding-starter-tasks.test.ts +93 -89
- package/src/__tests__/onboarding-template-contract.test.ts +93 -94
- package/src/__tests__/openai-provider.test.ts +366 -274
- package/src/__tests__/pairing-concurrent.test.ts +18 -12
- package/src/__tests__/pairing-routes.test.ts +45 -41
- package/src/__tests__/parallel-tool.benchmark.test.ts +108 -58
- package/src/__tests__/parser.test.ts +316 -226
- package/src/__tests__/path-classifier.test.ts +24 -25
- package/src/__tests__/path-policy.test.ts +187 -147
- package/src/__tests__/phone.test.ts +36 -36
- package/src/__tests__/platform-move-helper.test.ts +48 -40
- package/src/__tests__/platform-socket-path.test.ts +23 -24
- package/src/__tests__/platform-workspace-migration.test.ts +464 -414
- package/src/__tests__/platform.test.ts +61 -53
- package/src/__tests__/playbook-execution.test.ts +397 -265
- package/src/__tests__/playbook-tools.test.ts +267 -196
- package/src/__tests__/prebuilt-home-base-seed.test.ts +30 -27
- package/src/__tests__/pricing.test.ts +316 -136
- package/src/__tests__/profile-compiler.test.ts +206 -188
- package/src/__tests__/provider-commit-message-generator.test.ts +114 -106
- package/src/__tests__/provider-error-scenarios.test.ts +212 -158
- package/src/__tests__/provider-fail-open-selection.test.ts +51 -44
- package/src/__tests__/provider-registry-ollama.test.ts +13 -9
- package/src/__tests__/provider-streaming.benchmark.test.ts +232 -183
- package/src/__tests__/proxy-approval-callback.test.ts +180 -119
- package/src/__tests__/public-ingress-urls.test.ts +112 -94
- package/src/__tests__/qdrant-manager.test.ts +147 -98
- package/src/__tests__/ratelimit.test.ts +152 -82
- package/src/__tests__/recording-handler.test.ts +273 -151
- package/src/__tests__/recording-intent-fallback.test.ts +94 -75
- package/src/__tests__/recording-intent-handler.test.ts +0 -1
- package/src/__tests__/recording-intent.test.ts +578 -379
- package/src/__tests__/recording-state-machine.test.ts +530 -316
- package/src/__tests__/recurrence-engine-rruleset.test.ts +150 -92
- package/src/__tests__/recurrence-engine.test.ts +81 -41
- package/src/__tests__/recurrence-types.test.ts +63 -44
- package/src/__tests__/relay-server.test.ts +2131 -1602
- package/src/__tests__/reminder-store.test.ts +158 -80
- package/src/__tests__/reminder.test.ts +113 -109
- package/src/__tests__/remote-skill-policy.test.ts +96 -72
- package/src/__tests__/request-file-tool.test.ts +74 -67
- package/src/__tests__/response-tier.test.ts +131 -74
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/runtime-events-sse-parity.test.ts +167 -145
- package/src/__tests__/runtime-events-sse.test.ts +0 -1
- package/src/__tests__/sandbox-diagnostics.test.ts +66 -56
- package/src/__tests__/sandbox-host-parity.test.ts +377 -301
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +213 -161
- package/src/__tests__/schedule-store.test.ts +268 -205
- package/src/__tests__/schedule-tools.test.ts +702 -524
- package/src/__tests__/scheduler-recurrence.test.ts +240 -130
- package/src/__tests__/scoped-approval-grants.test.ts +258 -168
- package/src/__tests__/scoped-grant-security-matrix.test.ts +160 -146
- package/src/__tests__/script-proxy-certs.test.ts +38 -35
- package/src/__tests__/script-proxy-connect-tunnel.test.ts +71 -46
- package/src/__tests__/script-proxy-decision-trace.test.ts +161 -84
- package/src/__tests__/script-proxy-http-forwarder.test.ts +146 -129
- package/src/__tests__/script-proxy-injection-runtime.test.ts +139 -113
- package/src/__tests__/script-proxy-mitm-handler.test.ts +226 -142
- package/src/__tests__/script-proxy-policy-runtime.test.ts +126 -86
- package/src/__tests__/script-proxy-policy.test.ts +308 -153
- package/src/__tests__/script-proxy-rewrite-specificity.test.ts +74 -62
- package/src/__tests__/script-proxy-router.test.ts +111 -77
- package/src/__tests__/script-proxy-session-manager.test.ts +156 -113
- package/src/__tests__/script-proxy-session-runtime.test.ts +28 -24
- package/src/__tests__/secret-allowlist.test.ts +105 -90
- package/src/__tests__/secret-ingress-handler.test.ts +41 -30
- package/src/__tests__/secret-onetime-send.test.ts +67 -50
- package/src/__tests__/secret-prompt-log-hygiene.test.ts +35 -31
- package/src/__tests__/secret-response-routing.test.ts +50 -41
- package/src/__tests__/secret-scanner-executor.test.ts +152 -111
- package/src/__tests__/secret-scanner.test.ts +495 -413
- package/src/__tests__/secure-keys.test.ts +132 -121
- package/src/__tests__/send-endpoint-busy.test.ts +0 -1
- package/src/__tests__/send-notification-tool.test.ts +43 -42
- package/src/__tests__/sensitive-output-placeholders.test.ts +72 -64
- package/src/__tests__/sequence-store.test.ts +335 -167
- package/src/__tests__/server-history-render.test.ts +341 -202
- package/src/__tests__/session-abort-tool-results.test.ts +133 -70
- package/src/__tests__/session-confirmation-signals.test.ts +252 -160
- package/src/__tests__/session-conflict-gate.test.ts +775 -585
- package/src/__tests__/session-error.test.ts +222 -191
- package/src/__tests__/session-evictor.test.ts +79 -62
- package/src/__tests__/session-init.benchmark.test.ts +170 -108
- package/src/__tests__/session-load-history-repair.test.ts +273 -139
- package/src/__tests__/session-messaging-secret-redirect.test.ts +130 -90
- package/src/__tests__/session-pre-run-repair.test.ts +106 -59
- package/src/__tests__/session-profile-injection.test.ts +198 -130
- package/src/__tests__/session-provider-retry-repair.test.ts +223 -141
- package/src/__tests__/session-queue.test.ts +624 -321
- package/src/__tests__/session-runtime-assembly.test.ts +425 -329
- package/src/__tests__/session-runtime-workspace.test.ts +69 -61
- package/src/__tests__/session-skill-tools.test.ts +973 -678
- package/src/__tests__/session-slash-known.test.ts +185 -133
- package/src/__tests__/session-slash-queue.test.ts +147 -81
- package/src/__tests__/session-slash-unknown.test.ts +135 -90
- package/src/__tests__/session-surfaces-task-progress.test.ts +122 -87
- package/src/__tests__/session-tool-setup-app-refresh.test.ts +338 -177
- package/src/__tests__/session-tool-setup-memory-scope.test.ts +63 -40
- package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +60 -37
- package/src/__tests__/session-tool-setup-tools-disabled.test.ts +28 -26
- package/src/__tests__/session-undo.test.ts +43 -30
- package/src/__tests__/session-workspace-cache-state.test.ts +108 -67
- package/src/__tests__/session-workspace-injection.test.ts +245 -117
- package/src/__tests__/session-workspace-tool-tracking.test.ts +260 -93
- package/src/__tests__/shared-filesystem-errors.test.ts +47 -47
- package/src/__tests__/shell-credential-ref.test.ts +126 -90
- package/src/__tests__/shell-identity.test.ts +134 -111
- package/src/__tests__/shell-parser-fuzz.test.ts +263 -179
- package/src/__tests__/shell-parser-property.test.ts +435 -288
- package/src/__tests__/shell-tool-proxy-mode.test.ts +142 -70
- package/src/__tests__/size-guard.test.ts +42 -44
- package/src/__tests__/skill-feature-flags-integration.test.ts +79 -52
- package/src/__tests__/skill-feature-flags.test.ts +75 -47
- package/src/__tests__/skill-include-graph.test.ts +143 -148
- package/src/__tests__/skill-load-feature-flag.test.ts +94 -59
- package/src/__tests__/skill-load-tool.test.ts +371 -199
- package/src/__tests__/skill-projection-feature-flag.test.ts +131 -88
- package/src/__tests__/skill-projection.benchmark.test.ts +93 -65
- package/src/__tests__/skill-script-runner-host.test.ts +460 -250
- package/src/__tests__/skill-script-runner-sandbox.test.ts +168 -108
- package/src/__tests__/skill-script-runner.test.ts +115 -74
- package/src/__tests__/skill-tool-factory.test.ts +140 -96
- package/src/__tests__/skill-tool-manifest.test.ts +306 -210
- package/src/__tests__/skill-version-hash.test.ts +70 -56
- package/src/__tests__/skills.test.ts +0 -1
- package/src/__tests__/slack-channel-config.test.ts +127 -84
- package/src/__tests__/slack-skill.test.ts +60 -47
- package/src/__tests__/slash-commands-catalog.test.ts +37 -31
- package/src/__tests__/slash-commands-parser.test.ts +71 -64
- package/src/__tests__/slash-commands-resolver.test.ts +143 -107
- package/src/__tests__/slash-commands-rewrite.test.ts +22 -22
- package/src/__tests__/speaker-identification.test.ts +28 -25
- package/src/__tests__/starter-bundle.test.ts +27 -23
- package/src/__tests__/starter-task-flow.test.ts +67 -52
- package/src/__tests__/subagent-manager-notify.test.ts +154 -108
- package/src/__tests__/subagent-tools.test.ts +311 -270
- package/src/__tests__/subagent-types.test.ts +40 -40
- package/src/__tests__/surface-mutex-cleanup.test.ts +42 -30
- package/src/__tests__/swarm-dag-pathological.test.ts +122 -111
- package/src/__tests__/swarm-orchestrator.test.ts +135 -101
- package/src/__tests__/swarm-plan-validator.test.ts +125 -73
- package/src/__tests__/swarm-recursion.test.ts +58 -46
- package/src/__tests__/swarm-router-planner.test.ts +99 -74
- package/src/__tests__/swarm-session-integration.test.ts +148 -91
- package/src/__tests__/swarm-tool.test.ts +65 -45
- package/src/__tests__/swarm-worker-backend.test.ts +59 -45
- package/src/__tests__/swarm-worker-runner.test.ts +133 -118
- package/src/__tests__/system-prompt.test.ts +290 -256
- package/src/__tests__/task-compiler.test.ts +176 -120
- package/src/__tests__/task-management-tools.test.ts +561 -456
- package/src/__tests__/task-memory-cleanup.test.ts +627 -362
- package/src/__tests__/task-runner.test.ts +117 -94
- package/src/__tests__/task-scheduler.test.ts +113 -84
- package/src/__tests__/task-tools.test.ts +349 -264
- package/src/__tests__/terminal-sandbox.test.ts +138 -108
- package/src/__tests__/terminal-tools.test.ts +350 -305
- package/src/__tests__/thread-seed-composer.test.ts +307 -180
- package/src/__tests__/tool-approval-handler.test.ts +238 -137
- package/src/__tests__/tool-audit-listener.test.ts +69 -69
- package/src/__tests__/tool-domain-event-publisher.test.ts +142 -132
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +153 -146
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +136 -105
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +355 -239
- package/src/__tests__/tool-executor-redaction.test.ts +112 -109
- package/src/__tests__/tool-executor-shell-integration.test.ts +130 -79
- package/src/__tests__/tool-executor.test.ts +1274 -674
- package/src/__tests__/tool-grant-request-escalation.test.ts +401 -283
- package/src/__tests__/tool-metrics-listener.test.ts +97 -85
- package/src/__tests__/tool-notification-listener.test.ts +42 -25
- package/src/__tests__/tool-permission-simulate-handler.test.ts +137 -113
- package/src/__tests__/tool-policy.test.ts +44 -25
- package/src/__tests__/tool-profiling-listener.test.ts +99 -93
- package/src/__tests__/tool-result-truncation.test.ts +5 -4
- package/src/__tests__/tool-trace-listener.test.ts +131 -111
- package/src/__tests__/top-level-renderer.test.ts +62 -58
- package/src/__tests__/top-level-scanner.test.ts +68 -64
- package/src/__tests__/trace-emitter.test.ts +56 -56
- package/src/__tests__/trust-context-guards.test.ts +65 -65
- package/src/__tests__/trust-store.test.ts +1239 -806
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +0 -1
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +3 -2
- package/src/__tests__/trusted-contact-multichannel.test.ts +3 -2
- package/src/__tests__/trusted-contact-verification.test.ts +251 -231
- package/src/__tests__/turn-commit.test.ts +259 -200
- package/src/__tests__/twilio-provider.test.ts +140 -126
- package/src/__tests__/twilio-rest.test.ts +22 -18
- package/src/__tests__/twilio-routes-elevenlabs.test.ts +0 -1
- package/src/__tests__/twilio-routes-twiml.test.ts +55 -55
- package/src/__tests__/twilio-routes.test.ts +0 -1
- package/src/__tests__/twitter-auth-handler.test.ts +184 -139
- package/src/__tests__/twitter-cli-error-shaping.test.ts +88 -73
- package/src/__tests__/twitter-cli-routing.test.ts +146 -99
- package/src/__tests__/twitter-oauth-client.test.ts +82 -65
- package/src/__tests__/update-bulletin-format.test.ts +69 -66
- package/src/__tests__/update-bulletin-state.test.ts +66 -60
- package/src/__tests__/update-bulletin.test.ts +150 -114
- package/src/__tests__/update-template-contract.test.ts +15 -10
- package/src/__tests__/url-safety.test.ts +288 -265
- package/src/__tests__/user-reference.test.ts +32 -32
- package/src/__tests__/view-image-tool.test.ts +118 -96
- package/src/__tests__/voice-invite-redemption.test.ts +111 -106
- package/src/__tests__/voice-quality.test.ts +117 -102
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +204 -146
- package/src/__tests__/voice-session-bridge.test.ts +351 -216
- package/src/__tests__/weather-skill-regression.test.ts +170 -120
- package/src/__tests__/web-fetch.test.ts +664 -526
- package/src/__tests__/web-search.test.ts +379 -213
- package/src/__tests__/work-item-output.test.ts +90 -53
- package/src/__tests__/workspace-git-service.test.ts +437 -356
- package/src/__tests__/workspace-heartbeat-service.test.ts +125 -91
- package/src/__tests__/workspace-lifecycle.test.ts +98 -64
- package/src/__tests__/workspace-policy.test.ts +139 -71
- package/src/commands/__tests__/cc-command-registry.test.ts +142 -134
- package/src/config/__tests__/feature-flag-registry-guard.test.ts +48 -39
- package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +25 -10
- package/src/config/bundled-skills/doordash/__tests__/doordash-session.test.ts +0 -1
- package/src/config/bundled-skills/messaging/SKILL.md +4 -3
- package/src/config/bundled-skills/messaging/tools/gmail-outreach-scan.ts +15 -5
- package/src/config/bundled-skills/messaging/tools/gmail-sender-digest.ts +16 -5
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +34 -32
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/env.ts +3 -4
- package/src/memory/db-connection.ts +16 -10
- package/src/messaging/providers/gmail/adapter.ts +10 -3
- package/src/messaging/providers/gmail/client.ts +280 -72
- package/src/runtime/auth/__tests__/context.test.ts +75 -65
- package/src/runtime/auth/__tests__/credential-service.test.ts +137 -114
- package/src/runtime/auth/__tests__/guard-tests.test.ts +84 -90
- package/src/runtime/auth/__tests__/ipc-auth-context.test.ts +40 -40
- package/src/runtime/auth/__tests__/middleware.test.ts +80 -74
- package/src/runtime/auth/__tests__/policy.test.ts +9 -9
- package/src/runtime/auth/__tests__/route-policy.test.ts +76 -65
- package/src/runtime/auth/__tests__/scopes.test.ts +68 -60
- package/src/runtime/auth/__tests__/subject.test.ts +54 -54
- package/src/runtime/auth/__tests__/token-service.test.ts +115 -108
- package/src/runtime/auth/scopes.ts +3 -0
- package/src/runtime/auth/token-service.ts +4 -1
- package/src/runtime/auth/types.ts +2 -1
- package/src/runtime/http-server.ts +2 -1
- package/src/security/secure-keys.ts +103 -53
- package/src/tools/browser/__tests__/auth-cache.test.ts +69 -63
- package/src/tools/browser/__tests__/auth-detector.test.ts +218 -157
- package/src/tools/browser/__tests__/jit-auth.test.ts +83 -99
|
@@ -1,40 +1,49 @@
|
|
|
1
|
-
import { randomBytes } from
|
|
2
|
-
import { mkdirSync, rmSync } from
|
|
3
|
-
import { readFileSync } from
|
|
4
|
-
import { tmpdir } from
|
|
5
|
-
import { join } from
|
|
6
|
-
import { dirname,resolve } from
|
|
7
|
-
import { fileURLToPath } from
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { mkdirSync, rmSync } from "node:fs";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { dirname, resolve } from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import {
|
|
9
|
+
afterAll,
|
|
10
|
+
afterEach,
|
|
11
|
+
beforeEach,
|
|
12
|
+
describe,
|
|
13
|
+
expect,
|
|
14
|
+
mock,
|
|
15
|
+
test,
|
|
16
|
+
} from "bun:test";
|
|
10
17
|
|
|
11
18
|
import {
|
|
12
19
|
contextInjectionCases,
|
|
13
20
|
directReadCases,
|
|
14
21
|
logLeakageCases,
|
|
15
22
|
policyMisuseCases,
|
|
16
|
-
} from
|
|
23
|
+
} from "./fixtures/credential-security-fixtures.js";
|
|
17
24
|
|
|
18
25
|
// ---------------------------------------------------------------------------
|
|
19
26
|
// Mock logger
|
|
20
27
|
// ---------------------------------------------------------------------------
|
|
21
28
|
|
|
22
|
-
mock.module(
|
|
23
|
-
getLogger: () =>
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
mock.module("../util/logger.js", () => ({
|
|
30
|
+
getLogger: () =>
|
|
31
|
+
new Proxy({} as Record<string, unknown>, {
|
|
32
|
+
get: () => () => {},
|
|
33
|
+
}),
|
|
26
34
|
}));
|
|
27
35
|
|
|
28
36
|
// ---------------------------------------------------------------------------
|
|
29
37
|
// Use encrypted backend (no keychain) with a temp store path
|
|
30
38
|
// ---------------------------------------------------------------------------
|
|
31
39
|
|
|
32
|
-
import { _overrideDeps, _resetDeps } from
|
|
40
|
+
import { _overrideDeps, _resetDeps } from "../security/keychain.js";
|
|
33
41
|
|
|
34
42
|
_overrideDeps({
|
|
35
43
|
isMacOS: () => false,
|
|
36
44
|
isLinux: () => false,
|
|
37
|
-
execFileSync: (() =>
|
|
45
|
+
execFileSync: (() =>
|
|
46
|
+
"") as unknown as typeof import("node:child_process").execFileSync,
|
|
38
47
|
});
|
|
39
48
|
|
|
40
49
|
// Restore process-level keychain deps so later test files are not affected
|
|
@@ -43,17 +52,20 @@ afterAll(() => {
|
|
|
43
52
|
mock.restore();
|
|
44
53
|
});
|
|
45
54
|
|
|
46
|
-
import { _setStorePath } from
|
|
47
|
-
import { _resetBackend } from
|
|
55
|
+
import { _setStorePath } from "../security/encrypted-store.js";
|
|
56
|
+
import { _resetBackend } from "../security/secure-keys.js";
|
|
48
57
|
|
|
49
|
-
const TEST_DIR = join(
|
|
50
|
-
|
|
58
|
+
const TEST_DIR = join(
|
|
59
|
+
tmpdir(),
|
|
60
|
+
`vellum-invariants-test-${randomBytes(4).toString("hex")}`,
|
|
61
|
+
);
|
|
62
|
+
const STORE_PATH = join(TEST_DIR, "keys.enc");
|
|
51
63
|
|
|
52
64
|
// ---------------------------------------------------------------------------
|
|
53
65
|
// Mock registry to avoid double-registration
|
|
54
66
|
// ---------------------------------------------------------------------------
|
|
55
67
|
|
|
56
|
-
mock.module(
|
|
68
|
+
mock.module("../tools/registry.js", () => ({
|
|
57
69
|
registerTool: () => {},
|
|
58
70
|
}));
|
|
59
71
|
|
|
@@ -61,11 +73,14 @@ mock.module('../tools/registry.js', () => ({
|
|
|
61
73
|
// Imports under test
|
|
62
74
|
// ---------------------------------------------------------------------------
|
|
63
75
|
|
|
64
|
-
import { DEFAULT_CONFIG } from
|
|
65
|
-
import { redactSensitiveFields } from
|
|
66
|
-
import { setSecureKey } from
|
|
67
|
-
import { CredentialBroker } from
|
|
68
|
-
import {
|
|
76
|
+
import { DEFAULT_CONFIG } from "../config/defaults.js";
|
|
77
|
+
import { redactSensitiveFields } from "../security/redaction.js";
|
|
78
|
+
import { setSecureKey } from "../security/secure-keys.js";
|
|
79
|
+
import { CredentialBroker } from "../tools/credentials/broker.js";
|
|
80
|
+
import {
|
|
81
|
+
_setMetadataPath,
|
|
82
|
+
upsertCredentialMetadata,
|
|
83
|
+
} from "../tools/credentials/metadata-store.js";
|
|
69
84
|
|
|
70
85
|
/**
|
|
71
86
|
* Security invariant test harness for credential storage hardening.
|
|
@@ -84,33 +99,41 @@ import { _setMetadataPath,upsertCredentialMetadata } from '../tools/credentials/
|
|
|
84
99
|
// Invariant 1 — Context Injection Prevention
|
|
85
100
|
// ---------------------------------------------------------------------------
|
|
86
101
|
|
|
87
|
-
describe(
|
|
102
|
+
describe("Invariant 1: secrets never enter LLM context", () => {
|
|
88
103
|
for (const tc of contextInjectionCases) {
|
|
89
|
-
if (
|
|
104
|
+
if (
|
|
105
|
+
tc.vector === "tool_output" &&
|
|
106
|
+
tc.tool === "credential_store" &&
|
|
107
|
+
tc.input.action === "store"
|
|
108
|
+
) {
|
|
90
109
|
// Store output never includes the value
|
|
91
110
|
test(`${tc.label}: secret not in output`, () => {
|
|
92
111
|
expect(tc.forbiddenValue).toBeTruthy();
|
|
93
112
|
// Actual assertion is in credential-vault.test.ts baseline section
|
|
94
113
|
});
|
|
95
|
-
} else if (tc.vector ===
|
|
114
|
+
} else if (tc.vector === "confirmation_payload") {
|
|
96
115
|
// PR 23 added redaction to confirmation_request payloads via redactSensitiveFields
|
|
97
116
|
test(`${tc.label}: secret redacted from confirmation payload`, () => {
|
|
98
117
|
const payload = { ...tc.input };
|
|
99
|
-
const redacted = redactSensitiveFields(
|
|
118
|
+
const redacted = redactSensitiveFields(
|
|
119
|
+
payload as Record<string, unknown>,
|
|
120
|
+
);
|
|
100
121
|
|
|
101
122
|
// The 'value' key is in SENSITIVE_KEYS and gets redacted
|
|
102
|
-
if (
|
|
103
|
-
expect(redacted.value).toBe(
|
|
123
|
+
if ("value" in payload && payload.value != null) {
|
|
124
|
+
expect(redacted.value).toBe("<redacted />");
|
|
104
125
|
expect(redacted.value).not.toBe(tc.forbiddenValue);
|
|
105
126
|
}
|
|
106
127
|
});
|
|
107
|
-
} else if (tc.vector ===
|
|
128
|
+
} else if (tc.vector === "lifecycle_event") {
|
|
108
129
|
// PR 22 added recursive redaction in tool executor lifecycle events
|
|
109
130
|
test(`${tc.label}: secret redacted from lifecycle event`, () => {
|
|
110
131
|
const input = { ...tc.input };
|
|
111
|
-
const redacted = redactSensitiveFields(
|
|
112
|
-
|
|
113
|
-
|
|
132
|
+
const redacted = redactSensitiveFields(
|
|
133
|
+
input as Record<string, unknown>,
|
|
134
|
+
);
|
|
135
|
+
if ("value" in input && input.value != null) {
|
|
136
|
+
expect(redacted.value).toBe("<redacted />");
|
|
114
137
|
expect(redacted.value).not.toBe(tc.forbiddenValue);
|
|
115
138
|
}
|
|
116
139
|
});
|
|
@@ -123,15 +146,15 @@ describe('Invariant 1: secrets never enter LLM context', () => {
|
|
|
123
146
|
}
|
|
124
147
|
|
|
125
148
|
// PR 27 — secret ingress block scans inbound messages
|
|
126
|
-
test(
|
|
149
|
+
test("user message containing secret is blocked from entering history", () => {
|
|
127
150
|
// Mock config to enable block mode
|
|
128
|
-
mock.module(
|
|
151
|
+
mock.module("../config/loader.js", () => ({
|
|
129
152
|
applyNestedDefaults: (config: unknown) => config,
|
|
130
153
|
getConfig: () => ({
|
|
131
154
|
ui: {},
|
|
132
155
|
secretDetection: {
|
|
133
156
|
enabled: true,
|
|
134
|
-
action:
|
|
157
|
+
action: "block",
|
|
135
158
|
blockIngress: true,
|
|
136
159
|
},
|
|
137
160
|
}),
|
|
@@ -140,7 +163,7 @@ describe('Invariant 1: secrets never enter LLM context', () => {
|
|
|
140
163
|
ui: {},
|
|
141
164
|
secretDetection: {
|
|
142
165
|
enabled: true,
|
|
143
|
-
action:
|
|
166
|
+
action: "block",
|
|
144
167
|
blockIngress: true,
|
|
145
168
|
},
|
|
146
169
|
}),
|
|
@@ -148,10 +171,10 @@ describe('Invariant 1: secrets never enter LLM context', () => {
|
|
|
148
171
|
|
|
149
172
|
// Re-import to pick up the mock
|
|
150
173
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
151
|
-
const { checkIngressForSecrets } = require(
|
|
174
|
+
const { checkIngressForSecrets } = require("../security/secret-ingress.js");
|
|
152
175
|
|
|
153
176
|
// Build a fake AWS key at runtime to avoid pre-commit hook
|
|
154
|
-
const fakeKey = [
|
|
177
|
+
const fakeKey = ["AKIA", "IOSFODNN7", "REALKEY"].join("");
|
|
155
178
|
const result = checkIngressForSecrets(`My key is ${fakeKey}`);
|
|
156
179
|
|
|
157
180
|
expect(result.blocked).toBe(true);
|
|
@@ -166,7 +189,7 @@ describe('Invariant 1: secrets never enter LLM context', () => {
|
|
|
166
189
|
// Invariant 2 — No Generic Plaintext Read API
|
|
167
190
|
// ---------------------------------------------------------------------------
|
|
168
191
|
|
|
169
|
-
describe(
|
|
192
|
+
describe("Invariant 2: no generic plaintext secret read API", () => {
|
|
170
193
|
for (const tc of directReadCases) {
|
|
171
194
|
test(`${tc.modulePath} does not export ${tc.exportName}`, async () => {
|
|
172
195
|
const mod = await import(`../${tc.modulePath}.js`);
|
|
@@ -174,68 +197,68 @@ describe('Invariant 2: no generic plaintext secret read API', () => {
|
|
|
174
197
|
});
|
|
175
198
|
}
|
|
176
199
|
|
|
177
|
-
test(
|
|
200
|
+
test("browser_fill_credential does not import getCredentialValue", () => {
|
|
178
201
|
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
179
202
|
const browserSrc = readFileSync(
|
|
180
|
-
resolve(thisDir,
|
|
181
|
-
|
|
203
|
+
resolve(thisDir, "../tools/browser/headless-browser.ts"),
|
|
204
|
+
"utf-8",
|
|
182
205
|
);
|
|
183
|
-
expect(browserSrc).not.toContain(
|
|
206
|
+
expect(browserSrc).not.toContain("getCredentialValue");
|
|
184
207
|
});
|
|
185
208
|
|
|
186
|
-
test(
|
|
209
|
+
test("getSecureKey is only imported by authorized modules", () => {
|
|
187
210
|
// Hard boundary: only these production files may import getSecureKey.
|
|
188
211
|
// Any new import must be reviewed for secret-leak risk and added here.
|
|
189
212
|
const ALLOWED_IMPORTERS = new Set([
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
213
|
+
"security/secure-keys.ts", // self (re-export infrastructure)
|
|
214
|
+
"index.ts", // daemon startup / API key config
|
|
215
|
+
"config/loader.ts", // config management (API keys)
|
|
216
|
+
"tools/credentials/vault.ts", // credential store tool
|
|
217
|
+
"tools/credentials/broker.ts", // brokered credential access
|
|
218
|
+
"tools/network/web-search.ts", // web search API key lookup
|
|
219
|
+
"daemon/handlers.ts", // Vercel API token + integration OAuth
|
|
220
|
+
"daemon/handlers/config-integrations.ts", // Vercel API token + Twitter integration OAuth
|
|
221
|
+
"daemon/handlers/config-telegram.ts", // Telegram bot token management
|
|
222
|
+
"daemon/handlers/config-ingress.ts", // Ingress config (reads Twilio credentials for webhook sync)
|
|
223
|
+
"runtime/routes/twilio-routes.ts", // Twilio credential management (HTTP control-plane)
|
|
224
|
+
"security/token-manager.ts", // OAuth token refresh flow
|
|
225
|
+
"email/providers/index.ts", // email provider API key lookup
|
|
226
|
+
"tools/network/script-proxy/session-manager.ts", // proxy credential injection at runtime
|
|
227
|
+
"messaging/registry.ts", // checks stored credentials for connected providers
|
|
228
|
+
"calls/call-domain.ts", // caller identity resolution (user phone number lookup)
|
|
229
|
+
"calls/elevenlabs-config.ts", // ElevenLabs voice quality API key lookup
|
|
230
|
+
"calls/twilio-config.ts", // call infrastructure credential lookup
|
|
231
|
+
"calls/twilio-provider.ts", // call infrastructure credential lookup
|
|
232
|
+
"calls/twilio-rest.ts", // Twilio REST API credential lookup
|
|
233
|
+
"cli/config-commands.ts", // CLI credential management commands
|
|
234
|
+
"runtime/http-server.ts", // HTTP server credential lookup
|
|
235
|
+
"daemon/handlers/twitter-auth.ts", // Twitter OAuth token storage
|
|
236
|
+
"twitter/oauth-client.ts", // Twitter OAuth API client (reads access token for API calls)
|
|
237
|
+
"calls/elevenlabs-config.ts", // ElevenLabs credential lookup
|
|
238
|
+
"cli/config-commands.ts", // CLI config management
|
|
239
|
+
"messaging/providers/telegram-bot/adapter.ts", // Telegram bot token lookup for connectivity check
|
|
240
|
+
"messaging/providers/sms/adapter.ts", // Twilio credential lookup for SMS connectivity check
|
|
241
|
+
"runtime/channel-readiness-service.ts", // channel readiness probes for SMS/Telegram connectivity
|
|
242
|
+
"messaging/providers/whatsapp/adapter.ts", // WhatsApp credential lookup for connectivity check
|
|
243
|
+
"schedule/integration-status.ts", // integration status checks for scheduled reports
|
|
244
|
+
"daemon/handlers/oauth-connect.ts", // OAuth connect handler for integration setup
|
|
245
|
+
"daemon/handlers/config-slack-channel.ts", // Slack channel config credential management
|
|
223
246
|
]);
|
|
224
247
|
|
|
225
248
|
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
226
|
-
const srcDir = resolve(thisDir,
|
|
249
|
+
const srcDir = resolve(thisDir, "..");
|
|
227
250
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
228
|
-
const { readdirSync, statSync } = require(
|
|
251
|
+
const { readdirSync, statSync } = require("node:fs");
|
|
229
252
|
|
|
230
253
|
// Recursively collect all .ts files in src/ (excluding __tests__)
|
|
231
254
|
function collectTsFiles(dir: string, files: string[] = []): string[] {
|
|
232
255
|
for (const entry of readdirSync(dir)) {
|
|
233
256
|
const full = join(dir, entry);
|
|
234
|
-
if (entry ===
|
|
257
|
+
if (entry === "__tests__" || entry === "node_modules") continue;
|
|
235
258
|
const s = statSync(full);
|
|
236
259
|
if (s.isDirectory()) {
|
|
237
260
|
collectTsFiles(full, files);
|
|
238
|
-
} else if (entry.endsWith(
|
|
261
|
+
} else if (entry.endsWith(".ts") && !entry.endsWith(".d.ts")) {
|
|
239
262
|
files.push(full);
|
|
240
263
|
}
|
|
241
264
|
}
|
|
@@ -246,9 +269,13 @@ describe('Invariant 2: no generic plaintext secret read API', () => {
|
|
|
246
269
|
const unauthorizedImporters: string[] = [];
|
|
247
270
|
|
|
248
271
|
for (const filePath of allFiles) {
|
|
249
|
-
const content = readFileSync(filePath,
|
|
272
|
+
const content = readFileSync(filePath, "utf-8");
|
|
250
273
|
// Check for imports of getSecureKey via static import, dynamic import(), or require()
|
|
251
|
-
if (
|
|
274
|
+
if (
|
|
275
|
+
content.match(/\bgetSecureKey\b/) &&
|
|
276
|
+
(content.match(/from\s+['"].*secure-keys/) ||
|
|
277
|
+
content.match(/(?:import|require)\s*\(\s*['"].*secure-keys/))
|
|
278
|
+
) {
|
|
252
279
|
const relative = filePath.slice(srcDir.length + 1);
|
|
253
280
|
if (!ALLOWED_IMPORTERS.has(relative)) {
|
|
254
281
|
unauthorizedImporters.push(relative);
|
|
@@ -264,39 +291,43 @@ describe('Invariant 2: no generic plaintext secret read API', () => {
|
|
|
264
291
|
// Invariant 3 — No Plaintext Secret Logging
|
|
265
292
|
// ---------------------------------------------------------------------------
|
|
266
293
|
|
|
267
|
-
describe(
|
|
294
|
+
describe("Invariant 3: secrets never logged in plaintext", () => {
|
|
268
295
|
for (const tc of logLeakageCases) {
|
|
269
|
-
if (tc.component ===
|
|
296
|
+
if (tc.component === "tool_executor") {
|
|
270
297
|
// PR 22 — executor redaction via redactSensitiveFields
|
|
271
298
|
test(`${tc.label}`, () => {
|
|
272
299
|
// Simulate a tool input with sensitive fields
|
|
273
300
|
// Build test values at runtime to avoid pre-commit hook false positives
|
|
274
|
-
const testValue = [
|
|
275
|
-
const testPassword = [
|
|
276
|
-
const testToken = [
|
|
301
|
+
const testValue = ["ghp_super", "secret123"].join("");
|
|
302
|
+
const testPassword = ["hunt", "er2"].join("");
|
|
303
|
+
const testToken = ["nested_", "token_value"].join("");
|
|
277
304
|
const input = {
|
|
278
|
-
action:
|
|
279
|
-
service:
|
|
280
|
-
field:
|
|
305
|
+
action: "store",
|
|
306
|
+
service: "github",
|
|
307
|
+
field: "token",
|
|
281
308
|
value: testValue,
|
|
282
309
|
password: testPassword,
|
|
283
310
|
nested: {
|
|
284
311
|
token: testToken,
|
|
285
|
-
safe:
|
|
312
|
+
safe: "this is fine",
|
|
286
313
|
},
|
|
287
314
|
};
|
|
288
315
|
const redacted = redactSensitiveFields(input);
|
|
289
316
|
|
|
290
317
|
// All sensitive keys must be redacted
|
|
291
|
-
expect(redacted.value).toBe(
|
|
292
|
-
expect(redacted.password).toBe(
|
|
293
|
-
expect((redacted.nested as Record<string, unknown>).token).toBe(
|
|
318
|
+
expect(redacted.value).toBe("<redacted />");
|
|
319
|
+
expect(redacted.password).toBe("<redacted />");
|
|
320
|
+
expect((redacted.nested as Record<string, unknown>).token).toBe(
|
|
321
|
+
"<redacted />",
|
|
322
|
+
);
|
|
294
323
|
// Non-sensitive keys preserved
|
|
295
|
-
expect(redacted.action).toBe(
|
|
296
|
-
expect(redacted.service).toBe(
|
|
297
|
-
expect((redacted.nested as Record<string, unknown>).safe).toBe(
|
|
324
|
+
expect(redacted.action).toBe("store");
|
|
325
|
+
expect(redacted.service).toBe("github");
|
|
326
|
+
expect((redacted.nested as Record<string, unknown>).safe).toBe(
|
|
327
|
+
"this is fine",
|
|
328
|
+
);
|
|
298
329
|
});
|
|
299
|
-
} else if (tc.component ===
|
|
330
|
+
} else if (tc.component === "ipc_decode") {
|
|
300
331
|
// PR 24 — IPC decode log hygiene: the TS daemon's IPC parser must
|
|
301
332
|
// not log raw message content that could contain secrets.
|
|
302
333
|
// Logging metadata (line length, error type) is acceptable; logging
|
|
@@ -304,8 +335,8 @@ describe('Invariant 3: secrets never logged in plaintext', () => {
|
|
|
304
335
|
test(`${tc.label}`, () => {
|
|
305
336
|
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
306
337
|
const ipcSrc = readFileSync(
|
|
307
|
-
resolve(thisDir,
|
|
308
|
-
|
|
338
|
+
resolve(thisDir, "../daemon/ipc-protocol.ts"),
|
|
339
|
+
"utf-8",
|
|
309
340
|
);
|
|
310
341
|
// Verify log calls never include raw content fields — only safe
|
|
311
342
|
// metadata like lineLength and errorType are permitted.
|
|
@@ -326,8 +357,8 @@ describe('Invariant 3: secrets never logged in plaintext', () => {
|
|
|
326
357
|
test(`${tc.label}`, () => {
|
|
327
358
|
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
328
359
|
const prompterSrc = readFileSync(
|
|
329
|
-
resolve(thisDir,
|
|
330
|
-
|
|
360
|
+
resolve(thisDir, "../permissions/secret-prompter.ts"),
|
|
361
|
+
"utf-8",
|
|
331
362
|
);
|
|
332
363
|
|
|
333
364
|
// Extract all log.* call arguments: log.warn({...}, 'msg')
|
|
@@ -337,19 +368,29 @@ describe('Invariant 3: secrets never logged in plaintext', () => {
|
|
|
337
368
|
let match;
|
|
338
369
|
while ((match = logCallPattern.exec(prompterSrc)) != null) {
|
|
339
370
|
// Collect field names from the structured log object
|
|
340
|
-
const fields = match[1]
|
|
371
|
+
const fields = match[1]
|
|
372
|
+
.split(",")
|
|
373
|
+
.map((f) => f.trim().split(":")[0].trim());
|
|
341
374
|
loggedFields.push(...fields);
|
|
342
375
|
}
|
|
343
376
|
|
|
344
377
|
// None of the logged fields should be sensitive credential fields
|
|
345
|
-
const sensitiveFields = [
|
|
378
|
+
const sensitiveFields = [
|
|
379
|
+
"value",
|
|
380
|
+
"secret",
|
|
381
|
+
"password",
|
|
382
|
+
"token",
|
|
383
|
+
"api_key",
|
|
384
|
+
"credentials",
|
|
385
|
+
];
|
|
346
386
|
for (const field of loggedFields) {
|
|
347
387
|
expect(sensitiveFields).not.toContain(field);
|
|
348
388
|
}
|
|
349
389
|
|
|
350
390
|
// Additionally verify the resolveSecret method never logs its value parameter
|
|
351
391
|
// by checking that log calls in resolveSecret only reference requestId
|
|
352
|
-
const resolveBlock =
|
|
392
|
+
const resolveBlock =
|
|
393
|
+
prompterSrc.match(/resolveSecret[\s\S]*?^\s{2}\}/m)?.[0] ?? "";
|
|
353
394
|
expect(resolveBlock).not.toMatch(/log\.\w+\(.*\bvalue\b/);
|
|
354
395
|
});
|
|
355
396
|
}
|
|
@@ -360,14 +401,14 @@ describe('Invariant 3: secrets never logged in plaintext', () => {
|
|
|
360
401
|
// Invariant 4 — Usage-Constrained Credentials (Tool + Domain Policy)
|
|
361
402
|
// ---------------------------------------------------------------------------
|
|
362
403
|
|
|
363
|
-
describe(
|
|
404
|
+
describe("Invariant 4: credentials only used for allowed purpose", () => {
|
|
364
405
|
let broker: CredentialBroker;
|
|
365
406
|
|
|
366
407
|
beforeEach(() => {
|
|
367
408
|
mkdirSync(TEST_DIR, { recursive: true });
|
|
368
409
|
_setStorePath(STORE_PATH);
|
|
369
410
|
_resetBackend();
|
|
370
|
-
_setMetadataPath(join(TEST_DIR,
|
|
411
|
+
_setMetadataPath(join(TEST_DIR, "metadata.json"));
|
|
371
412
|
broker = new CredentialBroker();
|
|
372
413
|
});
|
|
373
414
|
|
|
@@ -382,15 +423,15 @@ describe('Invariant 4: credentials only used for allowed purpose', () => {
|
|
|
382
423
|
// PRs 19-20 — tool + domain policy enforcement in broker
|
|
383
424
|
test(`${tc.label}`, async () => {
|
|
384
425
|
// Set up credential with the specified policy
|
|
385
|
-
upsertCredentialMetadata(tc.credentialId,
|
|
426
|
+
upsertCredentialMetadata(tc.credentialId, "token", {
|
|
386
427
|
allowedTools: tc.allowedTools,
|
|
387
428
|
allowedDomains: tc.allowedDomains,
|
|
388
429
|
});
|
|
389
|
-
setSecureKey(`credential:${tc.credentialId}:token`,
|
|
430
|
+
setSecureKey(`credential:${tc.credentialId}:token`, "test-secret-value");
|
|
390
431
|
|
|
391
432
|
const result = await broker.browserFill({
|
|
392
433
|
service: tc.credentialId,
|
|
393
|
-
field:
|
|
434
|
+
field: "token",
|
|
394
435
|
toolName: tc.requestingTool,
|
|
395
436
|
domain: tc.requestDomain,
|
|
396
437
|
fill: async () => {},
|
|
@@ -406,18 +447,18 @@ describe('Invariant 4: credentials only used for allowed purpose', () => {
|
|
|
406
447
|
}
|
|
407
448
|
|
|
408
449
|
// PR 20 — domain policy uses registrable-domain matching
|
|
409
|
-
test(
|
|
410
|
-
upsertCredentialMetadata(
|
|
411
|
-
allowedTools: [
|
|
412
|
-
allowedDomains: [
|
|
450
|
+
test("domain policy allows subdomains of registrable domain", async () => {
|
|
451
|
+
upsertCredentialMetadata("github", "token", {
|
|
452
|
+
allowedTools: ["browser_fill_credential"],
|
|
453
|
+
allowedDomains: ["github.com"],
|
|
413
454
|
});
|
|
414
|
-
setSecureKey(
|
|
455
|
+
setSecureKey("credential:github:token", "ghp_secret123");
|
|
415
456
|
|
|
416
457
|
const result = await broker.browserFill({
|
|
417
|
-
service:
|
|
418
|
-
field:
|
|
419
|
-
toolName:
|
|
420
|
-
domain:
|
|
458
|
+
service: "github",
|
|
459
|
+
field: "token",
|
|
460
|
+
toolName: "browser_fill_credential",
|
|
461
|
+
domain: "login.github.com",
|
|
421
462
|
fill: async () => {},
|
|
422
463
|
});
|
|
423
464
|
|
|
@@ -425,19 +466,21 @@ describe('Invariant 4: credentials only used for allowed purpose', () => {
|
|
|
425
466
|
});
|
|
426
467
|
|
|
427
468
|
// PR 18 — vault policy fields with strict defaults
|
|
428
|
-
test(
|
|
469
|
+
test("credential without explicit policy gets strict defaults (deny all)", () => {
|
|
429
470
|
// A credential stored without allowed_tools defaults to empty array,
|
|
430
471
|
// which the broker's isToolAllowed check fails closed on.
|
|
431
|
-
upsertCredentialMetadata(
|
|
472
|
+
upsertCredentialMetadata("test-svc", "pass", {});
|
|
432
473
|
|
|
433
474
|
const result = broker.authorize({
|
|
434
|
-
service:
|
|
435
|
-
field:
|
|
436
|
-
toolName:
|
|
475
|
+
service: "test-svc",
|
|
476
|
+
field: "pass",
|
|
477
|
+
toolName: "browser_fill_credential",
|
|
437
478
|
});
|
|
438
479
|
|
|
439
480
|
expect(result.authorized).toBe(false);
|
|
440
|
-
expect(!result.authorized && result.reason).toContain(
|
|
481
|
+
expect(!result.authorized && result.reason).toContain(
|
|
482
|
+
"No tools are currently allowed",
|
|
483
|
+
);
|
|
441
484
|
});
|
|
442
485
|
});
|
|
443
486
|
|
|
@@ -445,21 +488,21 @@ describe('Invariant 4: credentials only used for allowed purpose', () => {
|
|
|
445
488
|
// Cross-Cutting — One-Time Send Override
|
|
446
489
|
// ---------------------------------------------------------------------------
|
|
447
490
|
|
|
448
|
-
describe(
|
|
449
|
-
test(
|
|
450
|
-
const delivery:
|
|
451
|
-
expect(delivery).toBe(
|
|
491
|
+
describe("One-time send override", () => {
|
|
492
|
+
test("transient_send delivery type is defined in SecretPromptResult", () => {
|
|
493
|
+
const delivery: "store" | "transient_send" = "transient_send";
|
|
494
|
+
expect(delivery).toBe("transient_send");
|
|
452
495
|
});
|
|
453
496
|
|
|
454
|
-
test(
|
|
497
|
+
test("allowOneTimeSend defaults to false in config", () => {
|
|
455
498
|
expect(DEFAULT_CONFIG.secretDetection.allowOneTimeSend).toBe(false);
|
|
456
499
|
});
|
|
457
500
|
|
|
458
|
-
test(
|
|
459
|
-
expect(DEFAULT_CONFIG.secretDetection.action).toBe(
|
|
501
|
+
test("default secretDetection.action is redact", () => {
|
|
502
|
+
expect(DEFAULT_CONFIG.secretDetection.action).toBe("redact");
|
|
460
503
|
});
|
|
461
504
|
|
|
462
|
-
test(
|
|
505
|
+
test("default secretDetection.blockIngress is true", () => {
|
|
463
506
|
expect(DEFAULT_CONFIG.secretDetection.blockIngress).toBe(true);
|
|
464
507
|
});
|
|
465
508
|
});
|
|
@@ -472,130 +515,132 @@ import {
|
|
|
472
515
|
createSafeLogEntry,
|
|
473
516
|
sanitizeHeaders,
|
|
474
517
|
sanitizeUrl,
|
|
475
|
-
} from
|
|
518
|
+
} from "../tools/network/script-proxy/logging.js";
|
|
476
519
|
|
|
477
|
-
describe(
|
|
478
|
-
test(
|
|
520
|
+
describe("Invariant 5: proxy log entries never contain secrets", () => {
|
|
521
|
+
test("Authorization headers are redacted in log entries", () => {
|
|
479
522
|
const headers: Record<string, string> = {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
523
|
+
Authorization: "Bearer ghp_s3cr3tT0k3n",
|
|
524
|
+
"Content-Type": "application/json",
|
|
525
|
+
"X-Custom": "safe-value",
|
|
483
526
|
};
|
|
484
527
|
|
|
485
|
-
const sanitized = sanitizeHeaders(headers, [
|
|
528
|
+
const sanitized = sanitizeHeaders(headers, ["authorization"]);
|
|
486
529
|
|
|
487
|
-
expect(sanitized[
|
|
488
|
-
expect(sanitized[
|
|
489
|
-
expect(sanitized[
|
|
530
|
+
expect(sanitized["Authorization"]).toBe("[REDACTED]");
|
|
531
|
+
expect(sanitized["Content-Type"]).toBe("application/json");
|
|
532
|
+
expect(sanitized["X-Custom"]).toBe("safe-value");
|
|
490
533
|
});
|
|
491
534
|
|
|
492
|
-
test(
|
|
535
|
+
test("header redaction is case-insensitive", () => {
|
|
493
536
|
const headers: Record<string, string> = {
|
|
494
|
-
|
|
495
|
-
|
|
537
|
+
authorization: "Bearer secret123",
|
|
538
|
+
"X-Api-Key": "key-abc-123",
|
|
496
539
|
};
|
|
497
540
|
|
|
498
|
-
const sanitized = sanitizeHeaders(headers, [
|
|
541
|
+
const sanitized = sanitizeHeaders(headers, ["Authorization", "x-api-key"]);
|
|
499
542
|
|
|
500
|
-
expect(sanitized[
|
|
501
|
-
expect(sanitized[
|
|
543
|
+
expect(sanitized["authorization"]).toBe("[REDACTED]");
|
|
544
|
+
expect(sanitized["X-Api-Key"]).toBe("[REDACTED]");
|
|
502
545
|
});
|
|
503
546
|
|
|
504
|
-
test(
|
|
505
|
-
const url =
|
|
506
|
-
|
|
547
|
+
test("API key query params are redacted", () => {
|
|
548
|
+
const url =
|
|
549
|
+
"https://api.example.com/v1/search?api_key=sk-secret-value&q=hello";
|
|
550
|
+
const sanitized = sanitizeUrl(url, ["api_key"]);
|
|
507
551
|
|
|
508
|
-
expect(sanitized).not.toContain(
|
|
509
|
-
expect(sanitized).toContain(
|
|
510
|
-
expect(sanitized).toContain(
|
|
552
|
+
expect(sanitized).not.toContain("sk-secret-value");
|
|
553
|
+
expect(sanitized).toContain("api_key=%5BREDACTED%5D");
|
|
554
|
+
expect(sanitized).toContain("q=hello");
|
|
511
555
|
});
|
|
512
556
|
|
|
513
|
-
test(
|
|
514
|
-
const url =
|
|
515
|
-
|
|
557
|
+
test("multiple sensitive query params are all redacted", () => {
|
|
558
|
+
const url =
|
|
559
|
+
"https://api.example.com/path?token=abc123&key=def456&safe=keep";
|
|
560
|
+
const sanitized = sanitizeUrl(url, ["token", "key"]);
|
|
516
561
|
|
|
517
|
-
expect(sanitized).not.toContain(
|
|
518
|
-
expect(sanitized).not.toContain(
|
|
519
|
-
expect(sanitized).toContain(
|
|
562
|
+
expect(sanitized).not.toContain("abc123");
|
|
563
|
+
expect(sanitized).not.toContain("def456");
|
|
564
|
+
expect(sanitized).toContain("safe=keep");
|
|
520
565
|
});
|
|
521
566
|
|
|
522
|
-
test(
|
|
523
|
-
const url =
|
|
524
|
-
const sanitized = sanitizeUrl(url, [
|
|
567
|
+
test("sanitizeUrl handles path-only URLs", () => {
|
|
568
|
+
const url = "/v1/search?api_key=secret&q=hello";
|
|
569
|
+
const sanitized = sanitizeUrl(url, ["api_key"]);
|
|
525
570
|
|
|
526
|
-
expect(sanitized).not.toContain(
|
|
527
|
-
expect(sanitized).toContain(
|
|
571
|
+
expect(sanitized).not.toContain("secret");
|
|
572
|
+
expect(sanitized).toContain("q=hello");
|
|
528
573
|
// Result should still be a path, not an absolute URL
|
|
529
574
|
expect(sanitized).toMatch(/^\//);
|
|
530
575
|
});
|
|
531
576
|
|
|
532
|
-
test(
|
|
533
|
-
const url =
|
|
534
|
-
expect(sanitizeUrl(url, [
|
|
577
|
+
test("sanitizeUrl returns URL unchanged when no query string", () => {
|
|
578
|
+
const url = "https://api.example.com/v1/resource";
|
|
579
|
+
expect(sanitizeUrl(url, ["api_key"])).toBe(url);
|
|
535
580
|
});
|
|
536
581
|
|
|
537
|
-
test(
|
|
582
|
+
test("credential values from injection templates never appear in sanitized output", () => {
|
|
538
583
|
// Simulate a header-injected credential (e.g. "Authorization: Key <secret>")
|
|
539
|
-
const secretValue = [
|
|
584
|
+
const secretValue = ["Key ", "fal_", "superSecretApiKey"].join("");
|
|
540
585
|
const req = {
|
|
541
|
-
method:
|
|
542
|
-
url:
|
|
586
|
+
method: "POST",
|
|
587
|
+
url: "https://api.fal.ai/v1/generate",
|
|
543
588
|
headers: {
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
589
|
+
Authorization: secretValue,
|
|
590
|
+
"Content-Type": "application/json",
|
|
591
|
+
Host: "api.fal.ai",
|
|
547
592
|
},
|
|
548
593
|
};
|
|
549
594
|
|
|
550
|
-
const entry = createSafeLogEntry(req, [
|
|
595
|
+
const entry = createSafeLogEntry(req, ["Authorization"]);
|
|
551
596
|
const serialized = JSON.stringify(entry);
|
|
552
597
|
|
|
553
|
-
expect(serialized).not.toContain(
|
|
554
|
-
expect(serialized).not.toContain(
|
|
555
|
-
expect(entry.headers[
|
|
556
|
-
expect(entry.headers[
|
|
557
|
-
expect(entry.method).toBe(
|
|
598
|
+
expect(serialized).not.toContain("fal_");
|
|
599
|
+
expect(serialized).not.toContain("superSecretApiKey");
|
|
600
|
+
expect(entry.headers["Authorization"]).toBe("[REDACTED]");
|
|
601
|
+
expect(entry.headers["Content-Type"]).toBe("application/json");
|
|
602
|
+
expect(entry.method).toBe("POST");
|
|
558
603
|
});
|
|
559
604
|
|
|
560
|
-
test(
|
|
605
|
+
test("credential values from query injection templates never appear in sanitized output", () => {
|
|
561
606
|
// Simulate a query-injected credential (e.g. "?api_key=<secret>")
|
|
562
|
-
const secretValue = [
|
|
607
|
+
const secretValue = ["sk-live-", "abc123", "xyz789"].join("");
|
|
563
608
|
const req = {
|
|
564
|
-
method:
|
|
609
|
+
method: "GET",
|
|
565
610
|
url: `https://api.example.com/v1/data?api_key=${secretValue}&format=json`,
|
|
566
611
|
headers: {
|
|
567
|
-
|
|
612
|
+
Host: "api.example.com",
|
|
568
613
|
},
|
|
569
614
|
};
|
|
570
615
|
|
|
571
|
-
const entry = createSafeLogEntry(req, [
|
|
616
|
+
const entry = createSafeLogEntry(req, ["api_key"]);
|
|
572
617
|
const serialized = JSON.stringify(entry);
|
|
573
618
|
|
|
574
|
-
expect(serialized).not.toContain(
|
|
575
|
-
expect(serialized).not.toContain(
|
|
576
|
-
expect(serialized).not.toContain(
|
|
577
|
-
expect(entry.url).toContain(
|
|
619
|
+
expect(serialized).not.toContain("sk-live-");
|
|
620
|
+
expect(serialized).not.toContain("abc123");
|
|
621
|
+
expect(serialized).not.toContain("xyz789");
|
|
622
|
+
expect(entry.url).toContain("format=json");
|
|
578
623
|
});
|
|
579
624
|
|
|
580
|
-
test(
|
|
581
|
-
const headerSecret = [
|
|
582
|
-
const querySecret = [
|
|
625
|
+
test("createSafeLogEntry redacts both headers and query params together", () => {
|
|
626
|
+
const headerSecret = ["Bearer ", "ghp_", "tokenValue"].join("");
|
|
627
|
+
const querySecret = ["secret-", "key-", "42"].join("");
|
|
583
628
|
const req = {
|
|
584
|
-
method:
|
|
629
|
+
method: "GET",
|
|
585
630
|
url: `https://api.github.com/repos?access_token=${querySecret}`,
|
|
586
631
|
headers: {
|
|
587
|
-
|
|
588
|
-
|
|
632
|
+
Authorization: headerSecret,
|
|
633
|
+
Accept: "application/json",
|
|
589
634
|
},
|
|
590
635
|
};
|
|
591
636
|
|
|
592
|
-
const entry = createSafeLogEntry(req, [
|
|
637
|
+
const entry = createSafeLogEntry(req, ["Authorization", "access_token"]);
|
|
593
638
|
const serialized = JSON.stringify(entry);
|
|
594
639
|
|
|
595
|
-
expect(serialized).not.toContain(
|
|
596
|
-
expect(serialized).not.toContain(
|
|
597
|
-
expect(serialized).not.toContain(
|
|
598
|
-
expect(serialized).not.toContain(
|
|
599
|
-
expect(entry.headers[
|
|
640
|
+
expect(serialized).not.toContain("ghp_");
|
|
641
|
+
expect(serialized).not.toContain("tokenValue");
|
|
642
|
+
expect(serialized).not.toContain("secret-");
|
|
643
|
+
expect(serialized).not.toContain("key-42");
|
|
644
|
+
expect(entry.headers["Accept"]).toBe("application/json");
|
|
600
645
|
});
|
|
601
646
|
});
|