@vellumai/assistant 0.4.46 → 0.4.48
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/ARCHITECTURE.md +5 -5
- package/docs/architecture/security.md +5 -5
- package/package.json +1 -1
- package/src/__tests__/browser-fill-credential.test.ts +5 -2
- package/src/__tests__/bundled-skill-retrieval-guard.test.ts +2 -1
- package/src/__tests__/channel-readiness-routes.test.ts +20 -19
- package/src/__tests__/cli.test.ts +23 -0
- package/src/__tests__/credential-broker-browser-fill.test.ts +23 -22
- package/src/__tests__/credential-broker-server-use.test.ts +22 -21
- package/src/__tests__/credential-broker.test.ts +2 -1
- package/src/__tests__/credential-metadata-store.test.ts +240 -18
- package/src/__tests__/credential-resolve.test.ts +5 -4
- package/src/__tests__/credential-security-e2e.test.ts +8 -8
- package/src/__tests__/credential-security-invariants.test.ts +104 -6
- package/src/__tests__/credential-vault-unit.test.ts +22 -20
- package/src/__tests__/credential-vault.test.ts +284 -12
- package/src/__tests__/credentials-cli.test.ts +11 -6
- package/src/__tests__/gateway-only-enforcement.test.ts +4 -2
- package/src/__tests__/gemini-image-service.test.ts +75 -45
- package/src/__tests__/gemini-provider.test.ts +9 -6
- package/src/__tests__/guardian-action-conversation-turn.test.ts +1 -33
- package/src/__tests__/guardian-action-copy-generator.test.ts +0 -20
- package/src/__tests__/guardian-action-followup-executor.test.ts +1 -28
- package/src/__tests__/guardian-action-followup-store.test.ts +1 -1
- package/src/__tests__/guardian-grant-minting.test.ts +35 -0
- package/src/__tests__/integration-status.test.ts +53 -21
- package/src/__tests__/managed-proxy-context.test.ts +5 -3
- package/src/__tests__/media-generate-image.test.ts +63 -2
- package/src/__tests__/media-reuse-story.e2e.test.ts +7 -3
- package/src/__tests__/messaging-send-tool.test.ts +4 -6
- package/src/__tests__/provider-fail-open-selection.test.ts +3 -1
- package/src/__tests__/provider-managed-proxy-integration.test.ts +70 -6
- package/src/__tests__/schema-transforms.test.ts +226 -0
- package/src/__tests__/script-proxy-injection-runtime.test.ts +23 -13
- package/src/__tests__/script-proxy-policy-runtime.test.ts +1 -1
- package/src/__tests__/script-proxy-session-manager.test.ts +1 -1
- package/src/__tests__/secret-onetime-send.test.ts +5 -3
- package/src/__tests__/session-messaging-secret-redirect.test.ts +5 -4
- package/src/__tests__/skills-uninstall.test.ts +2 -2
- package/src/__tests__/skills.test.ts +0 -9
- package/src/__tests__/slack-channel-config.test.ts +9 -8
- package/src/__tests__/slack-share-routes.test.ts +11 -6
- package/src/__tests__/telegram-bot-username-resolution.test.ts +3 -0
- package/src/__tests__/twilio-config.test.ts +2 -1
- package/src/__tests__/twilio-provider.test.ts +4 -2
- package/src/__tests__/twilio-routes.test.ts +5 -4
- package/src/calls/call-domain.ts +7 -4
- package/src/calls/twilio-config.ts +2 -1
- package/src/calls/twilio-provider.ts +2 -1
- package/src/calls/twilio-rest.ts +2 -1
- package/src/cli/commands/browser-relay.ts +40 -15
- package/src/cli/commands/credentials.ts +9 -8
- package/src/cli/commands/oauth.ts +1 -1
- package/src/cli.ts +3 -2
- package/src/config/bundled-skills/claude-code/TOOLS.json +0 -4
- package/src/config/bundled-skills/contacts/tools/google-contacts.ts +29 -32
- package/src/config/bundled-skills/gmail/SKILL.md +4 -4
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +54 -61
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +25 -28
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +14 -17
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +39 -44
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +61 -58
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +50 -49
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +11 -13
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +148 -146
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +4 -7
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +175 -173
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +4 -7
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +71 -76
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +32 -38
- package/src/config/bundled-skills/google-calendar/SKILL.md +2 -2
- package/src/config/bundled-skills/google-calendar/calendar-client.ts +70 -29
- package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +9 -10
- package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +5 -6
- package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +4 -5
- package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +14 -15
- package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +37 -37
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +4 -9
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +24 -3
- package/src/config/bundled-skills/messaging/SKILL.md +6 -6
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +62 -63
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +15 -16
- package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +4 -5
- package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +6 -7
- package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +4 -5
- package/src/config/bundled-skills/messaging/tools/messaging-read.ts +14 -15
- package/src/config/bundled-skills/messaging/tools/messaging-search.ts +4 -5
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +128 -128
- package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +33 -34
- package/src/config/bundled-skills/messaging/tools/shared.ts +11 -11
- package/src/config/bundled-skills/slack/tools/shared.ts +4 -10
- package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +4 -5
- package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +15 -16
- package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +4 -5
- package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +4 -5
- package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +4 -5
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +95 -92
- package/src/config/loader.ts +6 -0
- package/src/daemon/computer-use-session.ts +7 -1
- package/src/daemon/guardian-action-generators.ts +4 -5
- package/src/daemon/handlers/config-slack-channel.ts +37 -20
- package/src/daemon/handlers/config-telegram.ts +33 -20
- package/src/daemon/lifecycle.ts +9 -1
- package/src/daemon/message-types/integrations.ts +1 -0
- package/src/daemon/ride-shotgun-handler.ts +3 -1
- package/src/daemon/session-messaging.ts +3 -1
- package/src/daemon/session-tool-setup.ts +18 -2
- package/src/daemon/session.ts +1 -1
- package/src/email/providers/index.ts +2 -1
- package/src/instrument.ts +15 -1
- package/src/media/app-icon-generator.ts +30 -4
- package/src/media/avatar-router.ts +26 -3
- package/src/media/gemini-image-service.ts +28 -2
- package/src/memory/guardian-action-store.ts +1 -1
- package/src/memory/schema/guardian.ts +1 -1
- package/src/messaging/provider.ts +16 -10
- package/src/messaging/providers/gmail/adapter.ts +40 -23
- package/src/messaging/providers/gmail/client.ts +203 -122
- package/src/messaging/providers/gmail/people-client.ts +26 -18
- package/src/messaging/providers/slack/adapter.ts +29 -19
- package/src/messaging/providers/slack/client.ts +265 -78
- package/src/messaging/providers/telegram-bot/adapter.ts +5 -4
- package/src/messaging/providers/whatsapp/adapter.ts +6 -3
- package/src/messaging/registry.ts +2 -1
- package/src/oauth/byo-connection.test.ts +436 -0
- package/src/oauth/byo-connection.ts +112 -0
- package/src/oauth/connect-orchestrator.ts +27 -0
- package/src/oauth/connection-resolver.ts +34 -0
- package/src/oauth/connection.ts +38 -0
- package/src/oauth/platform-connection.test.ts +163 -0
- package/src/oauth/platform-connection.ts +110 -0
- package/src/oauth/provider-base-urls.ts +21 -0
- package/src/oauth/provider-profiles.ts +1 -1
- package/src/oauth/token-persistence.ts +20 -20
- package/src/permissions/checker.ts +5 -1
- package/src/prompts/system-prompt.ts +49 -12
- package/src/providers/gemini/client.ts +15 -6
- package/src/providers/managed-proxy/constants.ts +2 -2
- package/src/providers/managed-proxy/context.ts +5 -1
- package/src/providers/ratelimit.ts +17 -0
- package/src/providers/registry.ts +2 -2
- package/src/runtime/AGENTS.md +17 -0
- package/src/runtime/channel-invite-transports/telegram.ts +2 -1
- package/src/runtime/channel-readiness-service.ts +168 -195
- package/src/runtime/channel-readiness-types.ts +4 -0
- package/src/runtime/guardian-action-conversation-turn.ts +1 -3
- package/src/runtime/guardian-action-followup-executor.ts +1 -1
- package/src/runtime/guardian-action-message-composer.ts +3 -23
- package/src/runtime/http-server.ts +9 -4
- package/src/runtime/http-types.ts +0 -1
- package/src/runtime/middleware/rate-limiter.ts +74 -20
- package/src/runtime/routes/channel-readiness-routes.ts +2 -0
- package/src/runtime/routes/diagnostics-routes.ts +11 -9
- package/src/runtime/routes/guardian-approval-interception.ts +20 -5
- package/src/runtime/routes/integrations/slack/share.ts +3 -2
- package/src/runtime/routes/integrations/twilio.ts +6 -5
- package/src/runtime/routes/secret-routes.ts +3 -2
- package/src/runtime/routes/settings-routes.ts +75 -17
- package/src/runtime/telegram-streaming-delivery.test.ts +132 -0
- package/src/runtime/telegram-streaming-delivery.ts +11 -1
- package/src/schedule/integration-status.ts +5 -4
- package/src/security/credential-key.ts +170 -0
- package/src/security/token-manager.ts +36 -7
- package/src/tools/apps/definitions.ts +0 -5
- package/src/tools/assets/materialize.ts +0 -5
- package/src/tools/assets/search.ts +0 -5
- package/src/tools/browser/headless-browser.ts +1 -67
- package/src/tools/claude-code/claude-code.ts +0 -5
- package/src/tools/computer-use/request-computer-control.ts +0 -5
- package/src/tools/credentials/broker.ts +6 -4
- package/src/tools/credentials/metadata-store.ts +72 -20
- package/src/tools/credentials/resolve.ts +2 -1
- package/src/tools/credentials/vault.ts +77 -16
- package/src/tools/filesystem/edit.ts +1 -6
- package/src/tools/filesystem/read.ts +0 -5
- package/src/tools/filesystem/write.ts +1 -6
- package/src/tools/host-filesystem/edit.ts +1 -6
- package/src/tools/host-filesystem/read.ts +1 -6
- package/src/tools/host-filesystem/write.ts +1 -6
- package/src/tools/mcp/mcp-tool-factory.ts +18 -1
- package/src/tools/memory/definitions.ts +0 -5
- package/src/tools/network/web-fetch.ts +0 -5
- package/src/tools/network/web-search.ts +0 -5
- package/src/tools/schema-transforms.ts +99 -0
- package/src/tools/skills/load.ts +0 -5
- package/src/tools/swarm/delegate.ts +0 -5
- package/src/tools/system/avatar-generator.ts +0 -5
- package/src/tools/ui-surface/definitions.ts +0 -15
- package/src/tools/watch/screen-watch.ts +0 -5
- package/src/version.ts +10 -0
- package/src/watcher/providers/github.ts +51 -52
- package/src/watcher/providers/gmail.ts +88 -80
- package/src/watcher/providers/google-calendar.ts +93 -86
- package/src/watcher/providers/linear.ts +87 -93
package/ARCHITECTURE.md
CHANGED
|
@@ -347,8 +347,8 @@ Both tokens are stored in the secure key store (macOS Keychain with encrypted fi
|
|
|
347
347
|
|
|
348
348
|
| Secure key | Content |
|
|
349
349
|
| ------------------------------------ | -------------------------------------------------------------------------- |
|
|
350
|
-
| `credential
|
|
351
|
-
| `credential
|
|
350
|
+
| `credential/slack_channel/bot_token` | Slack bot token (used for `chat.postMessage` and `auth.test`) |
|
|
351
|
+
| `credential/slack_channel/app_token` | Slack app token (`xapp-...`, used for Socket Mode `apps.connections.open`) |
|
|
352
352
|
|
|
353
353
|
Workspace metadata (team ID, team name, bot user ID, bot username) is stored as JSON in the credential metadata store under `('slack_channel', 'bot_token')`.
|
|
354
354
|
|
|
@@ -641,7 +641,7 @@ The assistant feature-flag resolver (`src/config/assistant-feature-flags.ts`) is
|
|
|
641
641
|
| **3. `skill_load` tool** | `executeSkillLoad()` in `tools/skills/load.ts` | If the model attempts to load a flagged-off skill by name, the tool returns an error: `"skill is currently unavailable (disabled by feature flag)"`. |
|
|
642
642
|
| **4. Runtime tool projection** | `projectSkillTools()` in `daemon/session-skill-tools.ts` | Even if a skill was previously active in a session (has `<loaded_skill>` markers in history), the per-turn projection drops it when the flag is OFF. Already-registered tools are unregistered. |
|
|
643
643
|
| **5. Included child skills** | `executeSkillLoad()` in `tools/skills/load.ts` | When a parent skill includes children via the `includes` directive, each child is independently checked against its feature flag. Flagged-off children are silently excluded from the loaded skill content. |
|
|
644
|
-
| **6. Skill install gate** | `
|
|
644
|
+
| **6. Skill install gate** | `handleSkillsInstall()` in `daemon/handlers/skills.ts` | When a client requests skill installation, the handler checks the skill's feature flag before proceeding. If the flag is OFF, the install is rejected with an error. |
|
|
645
645
|
|
|
646
646
|
All six enforcement points derive the flag key via `skillFlagKey(skill)` — which returns `undefined` for ungated skills, short-circuiting the check — and then call `isAssistantFeatureFlagEnabled(flagKey, config)` for consistency.
|
|
647
647
|
|
|
@@ -657,7 +657,7 @@ All six enforcement points derive the flag key via `skillFlagKey(skill)` — whi
|
|
|
657
657
|
| `src/tools/skills/load.ts` | `executeSkillLoad()` — enforcement points 3 and 5 |
|
|
658
658
|
| `src/daemon/session-skill-tools.ts` | `projectSkillTools()` — enforcement point 4 |
|
|
659
659
|
| `src/config/schema.ts` | `assistantFeatureFlagValues` field definition in `AssistantConfig` (Zod schema) |
|
|
660
|
-
| `src/daemon/handlers/skills.ts` | `handleSkillsList()` — uses `resolveSkillStates()` for client responses; `
|
|
660
|
+
| `src/daemon/handlers/skills.ts` | `handleSkillsList()` — uses `resolveSkillStates()` for client responses; `handleSkillsInstall()` — enforcement point 6 |
|
|
661
661
|
| `meta/feature-flags/feature-flag-registry.json` | Unified feature flag registry (repo root) — all declared flags with scope, label, default values, and descriptions |
|
|
662
662
|
| `src/config/feature-flag-registry.json` | Bundled copy of the unified registry for compiled binary resolution |
|
|
663
663
|
|
|
@@ -669,7 +669,7 @@ All six enforcement points derive the flag key via `skillFlagKey(skill)` — whi
|
|
|
669
669
|
graph LR
|
|
670
670
|
subgraph "macOS Keychain"
|
|
671
671
|
K1["API Key<br/>service: vellum-assistant<br/>account: anthropic<br/>stored via /usr/bin/security CLI"]
|
|
672
|
-
K2["Credential Secrets<br/>key: credential
|
|
672
|
+
K2["Credential Secrets<br/>key: credential/{service}/{field}<br/>stored via secure-keys.ts<br/>(encrypted file fallback if Keychain unavailable)"]
|
|
673
673
|
end
|
|
674
674
|
|
|
675
675
|
subgraph "UserDefaults (plist)"
|
|
@@ -179,7 +179,7 @@ File tool candidates include canonical (symlink-resolved) absolute paths via `no
|
|
|
179
179
|
| `assistant/src/permissions/checker.ts` | `classifyRisk()`, `check()`, `buildCommandCandidates()`, allowlist/scope generation |
|
|
180
180
|
| `assistant/src/permissions/shell-identity.ts` | `analyzeShellCommand()`, `deriveShellActionKeys()`, `buildShellCommandCandidates()`, `buildShellAllowlistOptions()` — parser-based shell command identity and action key derivation |
|
|
181
181
|
| `assistant/src/permissions/trust-store.ts` | Rule persistence, `findHighestPriorityRule()`, execution-target matching, starter bundle |
|
|
182
|
-
| `assistant/src/permissions/prompter.ts` | HTTP prompt flow: `confirmation_request` → `confirmation_response`
|
|
182
|
+
| `assistant/src/permissions/prompter.ts` | HTTP prompt flow: `confirmation_request` → `confirmation_response` |
|
|
183
183
|
| `assistant/src/permissions/defaults.ts` | Default rule templates (system ask rules for host tools, CU, etc.) |
|
|
184
184
|
| `assistant/src/skills/version-hash.ts` | `computeSkillVersionHash()` — deterministic SHA-256 of skill source files |
|
|
185
185
|
| `assistant/src/skills/path-classifier.ts` | `isSkillSourcePath()`, `normalizeFilePath()`, skill root detection |
|
|
@@ -233,7 +233,7 @@ sequenceDiagram
|
|
|
233
233
|
UI->>HTTP: secret_response {requestId, value, delivery: "store"}
|
|
234
234
|
HTTP->>Prompter: resolve(value, "store")
|
|
235
235
|
Prompter->>Vault: {value, delivery: "store"}
|
|
236
|
-
Vault->>Keychain: setSecureKey("credential
|
|
236
|
+
Vault->>Keychain: setSecureKey("credential/svc/field", value)
|
|
237
237
|
Vault->>Model: "Credential stored securely" (no value in output)
|
|
238
238
|
else One-Time Send (if enabled)
|
|
239
239
|
UI->>HTTP: secret_response {requestId, value, delivery: "transient_send"}
|
|
@@ -272,7 +272,7 @@ graph TB
|
|
|
272
272
|
TOOL["Tool (e.g. browser_fill_credential)"] --> BROKER["CredentialBroker.use(service, field, tool, domain)"]
|
|
273
273
|
BROKER --> POLICY{"Check policy:<br/>allowedTools + allowedDomains"}
|
|
274
274
|
POLICY -->|denied| REJECT["PolicyDenied error"]
|
|
275
|
-
POLICY -->|allowed| FETCH["getSecureKey(credential
|
|
275
|
+
POLICY -->|allowed| FETCH["getSecureKey(credential/svc/field)"]
|
|
276
276
|
FETCH --> INJECT["Inject value into tool execution<br/>(never returned to model)"]
|
|
277
277
|
```
|
|
278
278
|
|
|
@@ -290,7 +290,7 @@ The `allowOneTimeSend` config gate (default: `false`) enables a secondary "Send
|
|
|
290
290
|
|
|
291
291
|
| Component | Location | What it stores |
|
|
292
292
|
| ------------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
293
|
-
| Secret values | macOS Keychain (primary) or encrypted file fallback | Encrypted credential values keyed as `credential
|
|
293
|
+
| Secret values | macOS Keychain (primary) or encrypted file fallback | Encrypted credential values keyed as `credential/{service}/{field}`. Falls back to encrypted file backend on Linux/headless or when Keychain is unavailable. |
|
|
294
294
|
| Credential metadata | `~/.vellum/workspace/data/credentials/metadata.json` | Service, field, label, policy (allowedTools, allowedDomains), timestamps |
|
|
295
295
|
| Config | `~/.vellum/workspace/config.*` | `secretDetection` settings: enabled, action, entropyThreshold, allowOneTimeSend |
|
|
296
296
|
|
|
@@ -303,7 +303,7 @@ The `allowOneTimeSend` config gate (default: `false`) enables a secondary "Send
|
|
|
303
303
|
| `assistant/src/tools/credentials/metadata-store.ts` | JSON file metadata CRUD for credential records |
|
|
304
304
|
| `assistant/src/tools/credentials/broker.ts` | Brokered credential access with policy enforcement and transient send |
|
|
305
305
|
| `assistant/src/tools/credentials/policy-validate.ts` | Policy input validation (allowedTools, allowedDomains) |
|
|
306
|
-
| `assistant/src/permissions/secret-prompter.ts` | HTTP secret_request/secret_response flow
|
|
306
|
+
| `assistant/src/permissions/secret-prompter.ts` | HTTP secret_request/secret_response flow |
|
|
307
307
|
| `assistant/src/security/secret-scanner.ts` | Regex + entropy-based secret detection |
|
|
308
308
|
| `assistant/src/security/secret-ingress.ts` | Inbound message secret blocking |
|
|
309
309
|
| `clients/macos/.../SecretPromptManager.swift` | Floating panel UI for secure credential entry |
|
package/package.json
CHANGED
|
@@ -76,6 +76,7 @@ mock.module("../tools/credentials/metadata-store.js", () => ({
|
|
|
76
76
|
_setMetadataPath: () => {},
|
|
77
77
|
}));
|
|
78
78
|
|
|
79
|
+
import { credentialKey } from "../security/credential-key.js";
|
|
79
80
|
import { executeBrowserFillCredential } from "../tools/browser/browser-execution.js";
|
|
80
81
|
import type { ToolContext } from "../tools/types.js";
|
|
81
82
|
|
|
@@ -142,7 +143,9 @@ describe("executeBrowserFillCredential", () => {
|
|
|
142
143
|
'[data-vellum-eid="e1"]',
|
|
143
144
|
"super-secret-password",
|
|
144
145
|
);
|
|
145
|
-
expect(mockGetSecureKey).toHaveBeenCalledWith(
|
|
146
|
+
expect(mockGetSecureKey).toHaveBeenCalledWith(
|
|
147
|
+
credentialKey("gmail", "password"),
|
|
148
|
+
);
|
|
146
149
|
});
|
|
147
150
|
|
|
148
151
|
test("fills credential by CSS selector", async () => {
|
|
@@ -297,7 +300,7 @@ describe("executeBrowserFillCredential", () => {
|
|
|
297
300
|
"password",
|
|
298
301
|
);
|
|
299
302
|
expect(mockGetSecureKey).toHaveBeenCalledWith(
|
|
300
|
-
"
|
|
303
|
+
credentialKey("gmail", "password"),
|
|
301
304
|
);
|
|
302
305
|
});
|
|
303
306
|
|
|
@@ -90,7 +90,7 @@ const GATEWAY_RETRIEVAL_BANLIST: Array<{
|
|
|
90
90
|
bannedSnippets: [
|
|
91
91
|
'curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/',
|
|
92
92
|
"security find-generic-password",
|
|
93
|
-
"secret-tool lookup service vellum-assistant account credential
|
|
93
|
+
"secret-tool lookup service vellum-assistant account credential/ngrok/authtoken",
|
|
94
94
|
],
|
|
95
95
|
},
|
|
96
96
|
{
|
|
@@ -119,6 +119,7 @@ const KEYCHAIN_ALLOWLIST = new Set<string>([
|
|
|
119
119
|
const KEYCHAIN_PATTERNS = [
|
|
120
120
|
"security find-generic-password",
|
|
121
121
|
"secret-tool lookup service vellum-assistant account credential:",
|
|
122
|
+
"secret-tool lookup service vellum-assistant account credential/",
|
|
122
123
|
];
|
|
123
124
|
|
|
124
125
|
const HOST_BASH_RETRIEVAL_ALLOWLIST = new Set<string>([
|
|
@@ -52,6 +52,7 @@ mock.module("../email/service.js", () => ({
|
|
|
52
52
|
// ---------------------------------------------------------------------------
|
|
53
53
|
|
|
54
54
|
import { createReadinessService } from "../runtime/channel-readiness-service.js";
|
|
55
|
+
import { credentialKey } from "../security/credential-key.js";
|
|
55
56
|
|
|
56
57
|
// ---------------------------------------------------------------------------
|
|
57
58
|
// Tests
|
|
@@ -197,10 +198,10 @@ describe("channel readiness routes — email and WhatsApp probes", () => {
|
|
|
197
198
|
|
|
198
199
|
test("reports ready when all Meta credentials and display number are configured", async () => {
|
|
199
200
|
mockSecureKeys = {
|
|
200
|
-
"
|
|
201
|
-
"
|
|
202
|
-
"
|
|
203
|
-
"
|
|
201
|
+
[credentialKey("whatsapp", "phone_number_id")]: "123456789",
|
|
202
|
+
[credentialKey("whatsapp", "access_token")]: "EAAxxxxxx",
|
|
203
|
+
[credentialKey("whatsapp", "app_secret")]: "abc123",
|
|
204
|
+
[credentialKey("whatsapp", "webhook_verify_token")]: "my-verify-token",
|
|
204
205
|
};
|
|
205
206
|
mockRawConfig = {
|
|
206
207
|
whatsapp: { phoneNumber: "+15551234567" },
|
|
@@ -215,10 +216,10 @@ describe("channel readiness routes — email and WhatsApp probes", () => {
|
|
|
215
216
|
|
|
216
217
|
test("reports not ready when display phone number is missing", async () => {
|
|
217
218
|
mockSecureKeys = {
|
|
218
|
-
"
|
|
219
|
-
"
|
|
220
|
-
"
|
|
221
|
-
"
|
|
219
|
+
[credentialKey("whatsapp", "phone_number_id")]: "123456789",
|
|
220
|
+
[credentialKey("whatsapp", "access_token")]: "EAAxxxxxx",
|
|
221
|
+
[credentialKey("whatsapp", "app_secret")]: "abc123",
|
|
222
|
+
[credentialKey("whatsapp", "webhook_verify_token")]: "my-verify-token",
|
|
222
223
|
};
|
|
223
224
|
mockRawConfig = {
|
|
224
225
|
ingress: { publicBaseUrl: "https://example.com", enabled: true },
|
|
@@ -236,10 +237,10 @@ describe("channel readiness routes — email and WhatsApp probes", () => {
|
|
|
236
237
|
|
|
237
238
|
test("checks each Meta credential individually", async () => {
|
|
238
239
|
mockSecureKeys = {
|
|
239
|
-
"
|
|
240
|
+
[credentialKey("whatsapp", "phone_number_id")]: "123456789",
|
|
240
241
|
// access_token missing
|
|
241
|
-
"
|
|
242
|
-
"
|
|
242
|
+
[credentialKey("whatsapp", "app_secret")]: "abc123",
|
|
243
|
+
[credentialKey("whatsapp", "webhook_verify_token")]: "my-verify-token",
|
|
243
244
|
};
|
|
244
245
|
mockRawConfig = {
|
|
245
246
|
ingress: { publicBaseUrl: "https://example.com", enabled: true },
|
|
@@ -272,10 +273,10 @@ describe("channel readiness routes — email and WhatsApp probes", () => {
|
|
|
272
273
|
|
|
273
274
|
test("checks invite policy", async () => {
|
|
274
275
|
mockSecureKeys = {
|
|
275
|
-
"
|
|
276
|
-
"
|
|
277
|
-
"
|
|
278
|
-
"
|
|
276
|
+
[credentialKey("whatsapp", "phone_number_id")]: "123456789",
|
|
277
|
+
[credentialKey("whatsapp", "access_token")]: "EAAxxxxxx",
|
|
278
|
+
[credentialKey("whatsapp", "app_secret")]: "abc123",
|
|
279
|
+
[credentialKey("whatsapp", "webhook_verify_token")]: "my-verify-token",
|
|
279
280
|
};
|
|
280
281
|
mockRawConfig = {
|
|
281
282
|
whatsapp: { phoneNumber: "+15551234567" },
|
|
@@ -293,10 +294,10 @@ describe("channel readiness routes — email and WhatsApp probes", () => {
|
|
|
293
294
|
|
|
294
295
|
test("checks ingress configuration", async () => {
|
|
295
296
|
mockSecureKeys = {
|
|
296
|
-
"
|
|
297
|
-
"
|
|
298
|
-
"
|
|
299
|
-
"
|
|
297
|
+
[credentialKey("whatsapp", "phone_number_id")]: "123456789",
|
|
298
|
+
[credentialKey("whatsapp", "access_token")]: "EAAxxxxxx",
|
|
299
|
+
[credentialKey("whatsapp", "app_secret")]: "abc123",
|
|
300
|
+
[credentialKey("whatsapp", "webhook_verify_token")]: "my-verify-token",
|
|
300
301
|
};
|
|
301
302
|
mockRawConfig = {
|
|
302
303
|
whatsapp: { phoneNumber: "+15551234567" },
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import { describe, expect, test } from "bun:test";
|
|
2
3
|
|
|
3
4
|
import {
|
|
@@ -66,3 +67,25 @@ describe("formatConfirmationCommandPreview", () => {
|
|
|
66
67
|
expect(preview).toBe("edit /tmp/sample.txt");
|
|
67
68
|
});
|
|
68
69
|
});
|
|
70
|
+
|
|
71
|
+
const UUID_RE =
|
|
72
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
73
|
+
|
|
74
|
+
describe("new-session conversationKey format", () => {
|
|
75
|
+
test("uses a valid UUID, not a timestamp", () => {
|
|
76
|
+
// Mirror the key construction in startCli()
|
|
77
|
+
const key = `builtin-cli:${randomUUID()}`;
|
|
78
|
+
const suffix = key.replace("builtin-cli:", "");
|
|
79
|
+
|
|
80
|
+
expect(suffix).toMatch(UUID_RE);
|
|
81
|
+
// A numeric timestamp would parse to a finite number; a UUID must not.
|
|
82
|
+
expect(Number.isFinite(Number(suffix))).toBe(false);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("generates unique keys across calls", () => {
|
|
86
|
+
const key1 = `builtin-cli:${randomUUID()}`;
|
|
87
|
+
const key2 = `builtin-cli:${randomUUID()}`;
|
|
88
|
+
|
|
89
|
+
expect(key1).not.toBe(key2);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -49,6 +49,7 @@ mock.module("../tools/registry.js", () => ({
|
|
|
49
49
|
// Imports under test
|
|
50
50
|
// ---------------------------------------------------------------------------
|
|
51
51
|
|
|
52
|
+
import { credentialKey } from "../security/credential-key.js";
|
|
52
53
|
import { setSecureKey } from "../security/secure-keys.js";
|
|
53
54
|
import { CredentialBroker } from "../tools/credentials/broker.js";
|
|
54
55
|
import {
|
|
@@ -92,7 +93,7 @@ describe("CredentialBroker.browserFill", () => {
|
|
|
92
93
|
upsertCredentialMetadata("github", "token", {
|
|
93
94
|
allowedTools: ["browser_fill_credential"],
|
|
94
95
|
});
|
|
95
|
-
setSecureKey("
|
|
96
|
+
setSecureKey(credentialKey("github", "token"), "ghp_secret123");
|
|
96
97
|
|
|
97
98
|
let filledValue: string | undefined;
|
|
98
99
|
const result = await broker.browserFill({
|
|
@@ -114,7 +115,7 @@ describe("CredentialBroker.browserFill", () => {
|
|
|
114
115
|
upsertCredentialMetadata("github", "token", {
|
|
115
116
|
allowedTools: ["browser_fill_credential"],
|
|
116
117
|
});
|
|
117
|
-
setSecureKey("
|
|
118
|
+
setSecureKey(credentialKey("github", "token"), "ghp_secret123");
|
|
118
119
|
|
|
119
120
|
const result = await broker.browserFill({
|
|
120
121
|
service: "github",
|
|
@@ -167,7 +168,7 @@ describe("CredentialBroker.browserFill", () => {
|
|
|
167
168
|
upsertCredentialMetadata("github", "token", {
|
|
168
169
|
allowedTools: ["browser_fill_credential"],
|
|
169
170
|
});
|
|
170
|
-
setSecureKey("
|
|
171
|
+
setSecureKey(credentialKey("github", "token"), "ghp_secret123");
|
|
171
172
|
|
|
172
173
|
const result = await broker.browserFill({
|
|
173
174
|
service: "github",
|
|
@@ -193,8 +194,8 @@ describe("CredentialBroker.browserFill", () => {
|
|
|
193
194
|
upsertCredentialMetadata("github", "password", {
|
|
194
195
|
allowedTools: ["browser_fill_credential"],
|
|
195
196
|
});
|
|
196
|
-
setSecureKey("
|
|
197
|
-
setSecureKey("
|
|
197
|
+
setSecureKey(credentialKey("github", "username"), "octocat");
|
|
198
|
+
setSecureKey(credentialKey("github", "password"), "hunter2");
|
|
198
199
|
|
|
199
200
|
const filled: Record<string, string> = {};
|
|
200
201
|
|
|
@@ -227,7 +228,7 @@ describe("CredentialBroker.browserFill", () => {
|
|
|
227
228
|
allowedTools: ["browser_fill_credential"],
|
|
228
229
|
allowedDomains: ["github.com"],
|
|
229
230
|
});
|
|
230
|
-
setSecureKey("
|
|
231
|
+
setSecureKey(credentialKey("github", "token"), "ghp_secret123");
|
|
231
232
|
|
|
232
233
|
const result = await broker.browserFill({
|
|
233
234
|
service: "github",
|
|
@@ -245,7 +246,7 @@ describe("CredentialBroker.browserFill", () => {
|
|
|
245
246
|
allowedTools: ["browser_fill_credential"],
|
|
246
247
|
allowedDomains: ["github.com"],
|
|
247
248
|
});
|
|
248
|
-
setSecureKey("
|
|
249
|
+
setSecureKey(credentialKey("github", "token"), "ghp_secret123");
|
|
249
250
|
|
|
250
251
|
const result = await broker.browserFill({
|
|
251
252
|
service: "github",
|
|
@@ -263,7 +264,7 @@ describe("CredentialBroker.browserFill", () => {
|
|
|
263
264
|
allowedTools: ["browser_fill_credential"],
|
|
264
265
|
allowedDomains: ["github.com"],
|
|
265
266
|
});
|
|
266
|
-
setSecureKey("
|
|
267
|
+
setSecureKey(credentialKey("github", "token"), "ghp_secret123");
|
|
267
268
|
|
|
268
269
|
const result = await broker.browserFill({
|
|
269
270
|
service: "github",
|
|
@@ -286,7 +287,7 @@ describe("CredentialBroker.browserFill", () => {
|
|
|
286
287
|
allowedTools: ["browser_fill_credential"],
|
|
287
288
|
allowedDomains: ["github.com"],
|
|
288
289
|
});
|
|
289
|
-
setSecureKey("
|
|
290
|
+
setSecureKey(credentialKey("github", "token"), "ghp_secret123");
|
|
290
291
|
|
|
291
292
|
const result = await broker.browserFill({
|
|
292
293
|
service: "github",
|
|
@@ -305,7 +306,7 @@ describe("CredentialBroker.browserFill", () => {
|
|
|
305
306
|
upsertCredentialMetadata("github", "token", {
|
|
306
307
|
allowedTools: ["browser_fill_credential"],
|
|
307
308
|
});
|
|
308
|
-
setSecureKey("
|
|
309
|
+
setSecureKey(credentialKey("github", "token"), "ghp_secret123");
|
|
309
310
|
|
|
310
311
|
// No domain provided and no allowedDomains policy — should succeed
|
|
311
312
|
const result = await broker.browserFill({
|
|
@@ -322,7 +323,7 @@ describe("CredentialBroker.browserFill", () => {
|
|
|
322
323
|
upsertCredentialMetadata("github", "token", {
|
|
323
324
|
allowedTools: ["other_tool"],
|
|
324
325
|
});
|
|
325
|
-
setSecureKey("
|
|
326
|
+
setSecureKey(credentialKey("github", "token"), "ghp_secret123");
|
|
326
327
|
|
|
327
328
|
let fillCalled = false;
|
|
328
329
|
const result = await broker.browserFill({
|
|
@@ -343,7 +344,7 @@ describe("CredentialBroker.browserFill", () => {
|
|
|
343
344
|
|
|
344
345
|
test("denies fill when allowedTools is empty (fail-closed)", async () => {
|
|
345
346
|
upsertCredentialMetadata("github", "token", { allowedTools: [] });
|
|
346
|
-
setSecureKey("
|
|
347
|
+
setSecureKey(credentialKey("github", "token"), "ghp_secret123");
|
|
347
348
|
|
|
348
349
|
const result = await broker.browserFill({
|
|
349
350
|
service: "github",
|
|
@@ -362,7 +363,7 @@ describe("CredentialBroker.browserFill", () => {
|
|
|
362
363
|
upsertCredentialMetadata("github", "token", {
|
|
363
364
|
allowedTools: ["browser_fill_credential"],
|
|
364
365
|
});
|
|
365
|
-
setSecureKey("
|
|
366
|
+
setSecureKey(credentialKey("github", "token"), "ghp_supersecret");
|
|
366
367
|
|
|
367
368
|
const result = await broker.browserFill({
|
|
368
369
|
service: "github",
|
|
@@ -388,7 +389,7 @@ describe("CredentialBroker.browserFill", () => {
|
|
|
388
389
|
allowedTools: ["s3_upload", "cloudfront_invalidate"],
|
|
389
390
|
allowedDomains: [],
|
|
390
391
|
});
|
|
391
|
-
setSecureKey("
|
|
392
|
+
setSecureKey(credentialKey("aws", "access_key"), "AKIA_test");
|
|
392
393
|
|
|
393
394
|
let fillCalled = false;
|
|
394
395
|
const result = await broker.browserFill({
|
|
@@ -413,7 +414,7 @@ describe("CredentialBroker.browserFill", () => {
|
|
|
413
414
|
allowedTools: ["browser_fill_credential"],
|
|
414
415
|
allowedDomains: ["github.com"],
|
|
415
416
|
});
|
|
416
|
-
setSecureKey("
|
|
417
|
+
setSecureKey(credentialKey("github", "pat"), "ghp_fill_test");
|
|
417
418
|
|
|
418
419
|
let fillCalled = false;
|
|
419
420
|
const result = await broker.browserFill({
|
|
@@ -437,7 +438,7 @@ describe("CredentialBroker.browserFill", () => {
|
|
|
437
438
|
allowedTools: ["slack_post"],
|
|
438
439
|
allowedDomains: ["slack.com"],
|
|
439
440
|
});
|
|
440
|
-
setSecureKey("
|
|
441
|
+
setSecureKey(credentialKey("slack", "bot_token"), "xoxb-test");
|
|
441
442
|
|
|
442
443
|
const result = await broker.browserFill({
|
|
443
444
|
service: "slack",
|
|
@@ -460,7 +461,7 @@ describe("CredentialBroker.browserFill", () => {
|
|
|
460
461
|
upsertCredentialMetadata("custom", "key", {
|
|
461
462
|
allowedTools: [],
|
|
462
463
|
});
|
|
463
|
-
setSecureKey("
|
|
464
|
+
setSecureKey(credentialKey("custom", "key"), "secret");
|
|
464
465
|
|
|
465
466
|
const result = await broker.browserFill({
|
|
466
467
|
service: "custom",
|
|
@@ -490,7 +491,7 @@ describe("CredentialBroker.browserFill", () => {
|
|
|
490
491
|
upsertCredentialMetadata("github", "token", {
|
|
491
492
|
allowedTools: ["browser_fill_credential"],
|
|
492
493
|
});
|
|
493
|
-
setSecureKey("
|
|
494
|
+
setSecureKey(credentialKey("github", "token"), "ghp_updated");
|
|
494
495
|
|
|
495
496
|
let filledValue: string | undefined;
|
|
496
497
|
const result = await broker.browserFill({
|
|
@@ -514,8 +515,8 @@ describe("CredentialBroker.browserFill", () => {
|
|
|
514
515
|
upsertCredentialMetadata("github", "password", {
|
|
515
516
|
allowedTools: ["other_tool"],
|
|
516
517
|
});
|
|
517
|
-
setSecureKey("
|
|
518
|
-
setSecureKey("
|
|
518
|
+
setSecureKey(credentialKey("github", "username"), "octocat");
|
|
519
|
+
setSecureKey(credentialKey("github", "password"), "hunter2");
|
|
519
520
|
|
|
520
521
|
// username allows browser_fill_credential
|
|
521
522
|
const r1 = await broker.browserFill({
|
|
@@ -548,8 +549,8 @@ describe("CredentialBroker.browserFill", () => {
|
|
|
548
549
|
allowedTools: ["browser_fill_credential"],
|
|
549
550
|
allowedDomains: ["gitlab.com"],
|
|
550
551
|
});
|
|
551
|
-
setSecureKey("
|
|
552
|
-
setSecureKey("
|
|
552
|
+
setSecureKey(credentialKey("github", "token"), "gh_tok");
|
|
553
|
+
setSecureKey(credentialKey("gitlab", "token"), "gl_tok");
|
|
553
554
|
|
|
554
555
|
// github credential on github.com succeeds
|
|
555
556
|
let filled1 = "";
|
|
@@ -48,6 +48,7 @@ mock.module("../tools/registry.js", () => ({
|
|
|
48
48
|
// Imports under test
|
|
49
49
|
// ---------------------------------------------------------------------------
|
|
50
50
|
|
|
51
|
+
import { credentialKey } from "../security/credential-key.js";
|
|
51
52
|
import { setSecureKey } from "../security/secure-keys.js";
|
|
52
53
|
import { CredentialBroker } from "../tools/credentials/broker.js";
|
|
53
54
|
import {
|
|
@@ -85,7 +86,7 @@ describe("CredentialBroker.serverUse", () => {
|
|
|
85
86
|
upsertCredentialMetadata("vercel", "api_token", {
|
|
86
87
|
allowedTools: ["publish_page"],
|
|
87
88
|
});
|
|
88
|
-
setSecureKey("
|
|
89
|
+
setSecureKey(credentialKey("vercel", "api_token"), "test-vercel-token");
|
|
89
90
|
|
|
90
91
|
const result = await broker.serverUse({
|
|
91
92
|
service: "vercel",
|
|
@@ -109,7 +110,7 @@ describe("CredentialBroker.serverUse", () => {
|
|
|
109
110
|
upsertCredentialMetadata("vercel", "api_token", {
|
|
110
111
|
allowedTools: ["publish_page"],
|
|
111
112
|
});
|
|
112
|
-
setSecureKey("
|
|
113
|
+
setSecureKey(credentialKey("vercel", "api_token"), "test-vercel-token");
|
|
113
114
|
|
|
114
115
|
const result = await broker.serverUse({
|
|
115
116
|
service: "vercel",
|
|
@@ -161,7 +162,7 @@ describe("CredentialBroker.serverUse", () => {
|
|
|
161
162
|
upsertCredentialMetadata("vercel", "api_token", {
|
|
162
163
|
allowedTools: ["publish_page"],
|
|
163
164
|
});
|
|
164
|
-
setSecureKey("
|
|
165
|
+
setSecureKey(credentialKey("vercel", "api_token"), "test-vercel-token");
|
|
165
166
|
|
|
166
167
|
const result = await broker.serverUse({
|
|
167
168
|
service: "vercel",
|
|
@@ -182,7 +183,7 @@ describe("CredentialBroker.serverUse", () => {
|
|
|
182
183
|
upsertCredentialMetadata("vercel", "api_token", {
|
|
183
184
|
allowedTools: ["publish_page"],
|
|
184
185
|
});
|
|
185
|
-
setSecureKey("
|
|
186
|
+
setSecureKey(credentialKey("vercel", "api_token"), "test-vercel-token");
|
|
186
187
|
|
|
187
188
|
const result = await broker.serverUse({
|
|
188
189
|
service: "vercel",
|
|
@@ -201,7 +202,7 @@ describe("CredentialBroker.serverUse", () => {
|
|
|
201
202
|
allowedTools: ["publish_page"],
|
|
202
203
|
allowedDomains: ["vercel.com"],
|
|
203
204
|
});
|
|
204
|
-
setSecureKey("
|
|
205
|
+
setSecureKey(credentialKey("vercel", "api_token"), "test-vercel-token");
|
|
205
206
|
|
|
206
207
|
const result = await broker.serverUse({
|
|
207
208
|
service: "vercel",
|
|
@@ -222,7 +223,7 @@ describe("CredentialBroker.serverUse", () => {
|
|
|
222
223
|
allowedTools: ["publish_page"],
|
|
223
224
|
allowedDomains: [],
|
|
224
225
|
});
|
|
225
|
-
setSecureKey("
|
|
226
|
+
setSecureKey(credentialKey("vercel", "api_token"), "test-vercel-token");
|
|
226
227
|
|
|
227
228
|
const result = await broker.serverUse({
|
|
228
229
|
service: "vercel",
|
|
@@ -247,7 +248,7 @@ describe("CredentialBroker.serverUse", () => {
|
|
|
247
248
|
upsertCredentialMetadata("aws", "access_key", {
|
|
248
249
|
allowedTools: ["deploy_lambda", "s3_upload"],
|
|
249
250
|
});
|
|
250
|
-
setSecureKey("
|
|
251
|
+
setSecureKey(credentialKey("aws", "access_key"), "AKIA_test");
|
|
251
252
|
|
|
252
253
|
const result = await broker.serverUse({
|
|
253
254
|
service: "aws",
|
|
@@ -270,7 +271,7 @@ describe("CredentialBroker.serverUse", () => {
|
|
|
270
271
|
upsertCredentialMetadata("stripe", "secret_key", {
|
|
271
272
|
allowedTools: [],
|
|
272
273
|
});
|
|
273
|
-
setSecureKey("
|
|
274
|
+
setSecureKey(credentialKey("stripe", "secret_key"), "sk_test_xyz");
|
|
274
275
|
|
|
275
276
|
const result = await broker.serverUse({
|
|
276
277
|
service: "stripe",
|
|
@@ -291,7 +292,7 @@ describe("CredentialBroker.serverUse", () => {
|
|
|
291
292
|
allowedTools: ["git_push"],
|
|
292
293
|
allowedDomains: ["github.com"],
|
|
293
294
|
});
|
|
294
|
-
setSecureKey("
|
|
295
|
+
setSecureKey(credentialKey("github", "oauth_token"), "gho_test");
|
|
295
296
|
|
|
296
297
|
const result = await broker.serverUse({
|
|
297
298
|
service: "github",
|
|
@@ -322,7 +323,7 @@ describe("CredentialBroker.serverUse", () => {
|
|
|
322
323
|
upsertCredentialMetadata("vercel", "api_token", {
|
|
323
324
|
allowedTools: ["publish_page", "unpublish_page"],
|
|
324
325
|
});
|
|
325
|
-
setSecureKey("
|
|
326
|
+
setSecureKey(credentialKey("vercel", "api_token"), "tok_updated");
|
|
326
327
|
|
|
327
328
|
const result = await broker.serverUse({
|
|
328
329
|
service: "vercel",
|
|
@@ -342,8 +343,8 @@ describe("CredentialBroker.serverUse", () => {
|
|
|
342
343
|
upsertCredentialMetadata("vercel", "deploy_hook", {
|
|
343
344
|
allowedTools: ["trigger_deploy"],
|
|
344
345
|
});
|
|
345
|
-
setSecureKey("
|
|
346
|
-
setSecureKey("
|
|
346
|
+
setSecureKey(credentialKey("vercel", "api_token"), "tok_api");
|
|
347
|
+
setSecureKey(credentialKey("vercel", "deploy_hook"), "hook_secret");
|
|
347
348
|
|
|
348
349
|
// api_token should deny trigger_deploy
|
|
349
350
|
const r1 = await broker.serverUse({
|
|
@@ -377,8 +378,8 @@ describe("CredentialBroker.serverUse", () => {
|
|
|
377
378
|
upsertCredentialMetadata("gitlab", "api_token", {
|
|
378
379
|
allowedTools: ["gitlab_api"],
|
|
379
380
|
});
|
|
380
|
-
setSecureKey("
|
|
381
|
-
setSecureKey("
|
|
381
|
+
setSecureKey(credentialKey("github", "api_token"), "gh_tok");
|
|
382
|
+
setSecureKey(credentialKey("gitlab", "api_token"), "gl_tok");
|
|
382
383
|
|
|
383
384
|
// github credential should not serve gitlab tool
|
|
384
385
|
const r1 = broker.serverUseById({
|
|
@@ -395,8 +396,8 @@ describe("CredentialBroker.serverUse", () => {
|
|
|
395
396
|
upsertCredentialMetadata("gitlab", "api_token", {
|
|
396
397
|
allowedTools: ["gitlab_api"],
|
|
397
398
|
});
|
|
398
|
-
setSecureKey("
|
|
399
|
-
setSecureKey("
|
|
399
|
+
setSecureKey(credentialKey("github", "api_token"), "gh_tok");
|
|
400
|
+
setSecureKey(credentialKey("gitlab", "api_token"), "gl_tok");
|
|
400
401
|
|
|
401
402
|
// github credential should not serve gitlab tool
|
|
402
403
|
const r1 = await broker.serverUse({
|
|
@@ -458,7 +459,7 @@ describe("CredentialBroker.serverUseById", () => {
|
|
|
458
459
|
},
|
|
459
460
|
],
|
|
460
461
|
});
|
|
461
|
-
setSecureKey("
|
|
462
|
+
setSecureKey(credentialKey("fal", "api_key"), "fal-secret-key");
|
|
462
463
|
|
|
463
464
|
const result = broker.serverUseById({
|
|
464
465
|
credentialId: meta.credentialId,
|
|
@@ -483,7 +484,7 @@ describe("CredentialBroker.serverUseById", () => {
|
|
|
483
484
|
const meta = upsertCredentialMetadata("fal", "api_key", {
|
|
484
485
|
allowedTools: ["media_proxy"],
|
|
485
486
|
});
|
|
486
|
-
setSecureKey("
|
|
487
|
+
setSecureKey(credentialKey("fal", "api_key"), "fal-secret-key");
|
|
487
488
|
|
|
488
489
|
const result = broker.serverUseById({
|
|
489
490
|
credentialId: meta.credentialId,
|
|
@@ -514,7 +515,7 @@ describe("CredentialBroker.serverUseById", () => {
|
|
|
514
515
|
allowedTools: ["media_proxy"],
|
|
515
516
|
allowedDomains: ["github.com"],
|
|
516
517
|
});
|
|
517
|
-
setSecureKey("
|
|
518
|
+
setSecureKey(credentialKey("github", "oauth_token"), "gho_test");
|
|
518
519
|
|
|
519
520
|
const result = broker.serverUseById({
|
|
520
521
|
credentialId: meta.credentialId,
|
|
@@ -531,7 +532,7 @@ describe("CredentialBroker.serverUseById", () => {
|
|
|
531
532
|
const meta = upsertCredentialMetadata("vercel", "api_token", {
|
|
532
533
|
allowedTools: ["media_proxy"],
|
|
533
534
|
});
|
|
534
|
-
setSecureKey("
|
|
535
|
+
setSecureKey(credentialKey("vercel", "api_token"), "test-vercel-token");
|
|
535
536
|
|
|
536
537
|
const result = broker.serverUseById({
|
|
537
538
|
credentialId: meta.credentialId,
|
|
@@ -547,7 +548,7 @@ describe("CredentialBroker.serverUseById", () => {
|
|
|
547
548
|
const meta = upsertCredentialMetadata("stripe", "secret_key", {
|
|
548
549
|
allowedTools: [],
|
|
549
550
|
});
|
|
550
|
-
setSecureKey("
|
|
551
|
+
setSecureKey(credentialKey("stripe", "secret_key"), "sk_test_xyz");
|
|
551
552
|
|
|
552
553
|
const result = broker.serverUseById({
|
|
553
554
|
credentialId: meta.credentialId,
|
|
@@ -3,6 +3,7 @@ import { tmpdir } from "node:os";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
5
5
|
|
|
6
|
+
import { credentialKey } from "../security/credential-key.js";
|
|
6
7
|
import { CredentialBroker } from "../tools/credentials/broker.js";
|
|
7
8
|
import {
|
|
8
9
|
_setMetadataPath,
|
|
@@ -125,7 +126,7 @@ describe("CredentialBroker", () => {
|
|
|
125
126
|
|
|
126
127
|
const result = broker.consume(auth.token.tokenId);
|
|
127
128
|
expect(result.success).toBe(true);
|
|
128
|
-
expect(result.storageKey).toBe("
|
|
129
|
+
expect(result.storageKey).toBe(credentialKey("github", "token"));
|
|
129
130
|
});
|
|
130
131
|
|
|
131
132
|
test("rejects double consumption", () => {
|