@sonoma-security/mcp-gateway 0.1.12 → 0.1.13
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/dist/__tests__/plugin-discovery.test.d.ts +12 -0
- package/dist/__tests__/plugin-discovery.test.d.ts.map +1 -0
- package/dist/__tests__/plugin-discovery.test.js +367 -0
- package/dist/__tests__/plugin-discovery.test.js.map +1 -0
- package/dist/__tests__/tool-blocking.test.d.ts +2 -0
- package/dist/__tests__/tool-blocking.test.d.ts.map +1 -0
- package/dist/__tests__/tool-blocking.test.js +256 -0
- package/dist/__tests__/tool-blocking.test.js.map +1 -0
- package/dist/auth/keychain.d.ts +34 -0
- package/dist/auth/keychain.d.ts.map +1 -0
- package/dist/auth/keychain.js +305 -0
- package/dist/auth/keychain.js.map +1 -0
- package/dist/auth/storage.d.ts +5 -6
- package/dist/auth/storage.d.ts.map +1 -1
- package/dist/auth/storage.js +72 -21
- package/dist/auth/storage.js.map +1 -1
- package/dist/auth/upstream-oauth.d.ts.map +1 -1
- package/dist/auth/upstream-oauth.js +8 -5
- package/dist/auth/upstream-oauth.js.map +1 -1
- package/dist/auth/upstream-token-store.d.ts +18 -6
- package/dist/auth/upstream-token-store.d.ts.map +1 -1
- package/dist/auth/upstream-token-store.js +127 -35
- package/dist/auth/upstream-token-store.js.map +1 -1
- package/dist/cli.js +16 -0
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +17 -7
- package/dist/config.js.map +1 -1
- package/dist/gateway.d.ts +15 -0
- package/dist/gateway.d.ts.map +1 -1
- package/dist/gateway.js +180 -49
- package/dist/gateway.js.map +1 -1
- package/dist/http-proxy.d.ts +76 -0
- package/dist/http-proxy.d.ts.map +1 -0
- package/dist/http-proxy.js +316 -0
- package/dist/http-proxy.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/pattern-matcher.d.ts +25 -0
- package/dist/pattern-matcher.d.ts.map +1 -1
- package/dist/pattern-matcher.js +65 -0
- package/dist/pattern-matcher.js.map +1 -1
- package/dist/sonoma-client.d.ts +17 -1
- package/dist/sonoma-client.d.ts.map +1 -1
- package/dist/sonoma-client.js +67 -43
- package/dist/sonoma-client.js.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { isToolBlockedByPolicy, matchesToolPattern, getPatternSpecificity, } from "../pattern-matcher.js";
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// matchesToolPattern
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
describe("matchesToolPattern", () => {
|
|
7
|
+
it("matches exact tool name", () => {
|
|
8
|
+
expect(matchesToolPattern("read_file", "read_file")).toBe(true);
|
|
9
|
+
});
|
|
10
|
+
it("rejects non-matching exact name", () => {
|
|
11
|
+
expect(matchesToolPattern("write_file", "read_file")).toBe(false);
|
|
12
|
+
});
|
|
13
|
+
it("matches prefix wildcard", () => {
|
|
14
|
+
expect(matchesToolPattern("read_file", "read_*")).toBe(true);
|
|
15
|
+
expect(matchesToolPattern("read_directory", "read_*")).toBe(true);
|
|
16
|
+
});
|
|
17
|
+
it("rejects non-matching prefix wildcard", () => {
|
|
18
|
+
expect(matchesToolPattern("write_file", "read_*")).toBe(false);
|
|
19
|
+
});
|
|
20
|
+
it("matches suffix wildcard", () => {
|
|
21
|
+
expect(matchesToolPattern("user_delete", "*_delete")).toBe(true);
|
|
22
|
+
expect(matchesToolPattern("file_delete", "*_delete")).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
it("matches infix wildcard", () => {
|
|
25
|
+
expect(matchesToolPattern("get_user_info", "get_*_info")).toBe(true);
|
|
26
|
+
expect(matchesToolPattern("get_file_info", "get_*_info")).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
it("matches universal wildcard", () => {
|
|
29
|
+
expect(matchesToolPattern("anything", "*")).toBe(true);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// getPatternSpecificity
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
describe("getPatternSpecificity", () => {
|
|
36
|
+
it("returns 0 for server-level (null)", () => {
|
|
37
|
+
expect(getPatternSpecificity(null)).toBe(0);
|
|
38
|
+
});
|
|
39
|
+
it("returns 1 for universal wildcard", () => {
|
|
40
|
+
expect(getPatternSpecificity("*")).toBe(1);
|
|
41
|
+
});
|
|
42
|
+
it("returns 100 for exact name", () => {
|
|
43
|
+
expect(getPatternSpecificity("read_file")).toBe(100);
|
|
44
|
+
});
|
|
45
|
+
it("orders wildcard patterns by non-wildcard character count", () => {
|
|
46
|
+
// "r*" has 1 non-wildcard char, "read_*" has 5
|
|
47
|
+
expect(getPatternSpecificity("r*")).toBeLessThan(getPatternSpecificity("read_*"));
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// isToolBlockedByPolicy
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
describe("isToolBlockedByPolicy", () => {
|
|
54
|
+
// -----------------------------------------------------------------------
|
|
55
|
+
// Basics
|
|
56
|
+
// -----------------------------------------------------------------------
|
|
57
|
+
it("returns false when policy list is empty", () => {
|
|
58
|
+
expect(isToolBlockedByPolicy([], "slack", "search", null)).toBe(false);
|
|
59
|
+
});
|
|
60
|
+
it("returns false when no rules match the server", () => {
|
|
61
|
+
const rules = [
|
|
62
|
+
{ identifier: "github", status: "blocked", toolPattern: null, priority: 0 },
|
|
63
|
+
];
|
|
64
|
+
expect(isToolBlockedByPolicy(rules, "slack", "search", null)).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
// -----------------------------------------------------------------------
|
|
67
|
+
// Server-level blocking
|
|
68
|
+
// -----------------------------------------------------------------------
|
|
69
|
+
it("blocks all tools when server-level rule is blocked", () => {
|
|
70
|
+
const rules = [
|
|
71
|
+
{ identifier: "slack", status: "blocked", toolPattern: null, priority: 0 },
|
|
72
|
+
];
|
|
73
|
+
expect(isToolBlockedByPolicy(rules, "slack", "search", null)).toBe(true);
|
|
74
|
+
expect(isToolBlockedByPolicy(rules, "slack", "send_message", null)).toBe(true);
|
|
75
|
+
});
|
|
76
|
+
it("allows all tools when server-level rule is allowed", () => {
|
|
77
|
+
const rules = [
|
|
78
|
+
{ identifier: "slack", status: "allowed", toolPattern: null, priority: 0 },
|
|
79
|
+
];
|
|
80
|
+
expect(isToolBlockedByPolicy(rules, "slack", "search", null)).toBe(false);
|
|
81
|
+
});
|
|
82
|
+
// -----------------------------------------------------------------------
|
|
83
|
+
// Tool-level blocking (exact name)
|
|
84
|
+
// -----------------------------------------------------------------------
|
|
85
|
+
it("blocks a specific tool by exact name", () => {
|
|
86
|
+
const rules = [
|
|
87
|
+
{ identifier: "slack", status: "blocked", toolPattern: "slack_search_public_and_private", priority: 20 },
|
|
88
|
+
];
|
|
89
|
+
expect(isToolBlockedByPolicy(rules, "slack", "slack_search_public_and_private", null)).toBe(true);
|
|
90
|
+
});
|
|
91
|
+
it("does not block other tools when only one tool is blocked", () => {
|
|
92
|
+
const rules = [
|
|
93
|
+
{ identifier: "slack", status: "blocked", toolPattern: "slack_search_public_and_private", priority: 20 },
|
|
94
|
+
];
|
|
95
|
+
expect(isToolBlockedByPolicy(rules, "slack", "slack_send_message", null)).toBe(false);
|
|
96
|
+
});
|
|
97
|
+
// -----------------------------------------------------------------------
|
|
98
|
+
// Tool-level blocking (wildcard patterns)
|
|
99
|
+
// -----------------------------------------------------------------------
|
|
100
|
+
it("blocks tools matching a wildcard pattern", () => {
|
|
101
|
+
const rules = [
|
|
102
|
+
{ identifier: "slack", status: "blocked", toolPattern: "slack_search_*", priority: 10 },
|
|
103
|
+
];
|
|
104
|
+
expect(isToolBlockedByPolicy(rules, "slack", "slack_search_public", null)).toBe(true);
|
|
105
|
+
expect(isToolBlockedByPolicy(rules, "slack", "slack_search_public_and_private", null)).toBe(true);
|
|
106
|
+
expect(isToolBlockedByPolicy(rules, "slack", "slack_send_message", null)).toBe(false);
|
|
107
|
+
});
|
|
108
|
+
// -----------------------------------------------------------------------
|
|
109
|
+
// Package name matching
|
|
110
|
+
// -----------------------------------------------------------------------
|
|
111
|
+
it("matches rules by packageName when provided", () => {
|
|
112
|
+
const rules = [
|
|
113
|
+
{ identifier: "@anthropic/slack-mcp", status: "blocked", toolPattern: "slack_search_*", priority: 10 },
|
|
114
|
+
];
|
|
115
|
+
// Server name doesn't match, but packageName does
|
|
116
|
+
expect(isToolBlockedByPolicy(rules, "slack", "slack_search_public", "@anthropic/slack-mcp")).toBe(true);
|
|
117
|
+
});
|
|
118
|
+
it("falls back to server name when packageName doesn't match", () => {
|
|
119
|
+
const rules = [
|
|
120
|
+
{ identifier: "slack", status: "blocked", toolPattern: null, priority: 0 },
|
|
121
|
+
];
|
|
122
|
+
expect(isToolBlockedByPolicy(rules, "slack", "search", "@some/other-package")).toBe(true);
|
|
123
|
+
});
|
|
124
|
+
// -----------------------------------------------------------------------
|
|
125
|
+
// Global wildcard identifier
|
|
126
|
+
// -----------------------------------------------------------------------
|
|
127
|
+
it("blocks tools from any server when identifier is *", () => {
|
|
128
|
+
const rules = [
|
|
129
|
+
{ identifier: "*", status: "blocked", toolPattern: "dangerous_*", priority: 10 },
|
|
130
|
+
];
|
|
131
|
+
expect(isToolBlockedByPolicy(rules, "slack", "dangerous_delete", null)).toBe(true);
|
|
132
|
+
expect(isToolBlockedByPolicy(rules, "github", "dangerous_reset", null)).toBe(true);
|
|
133
|
+
expect(isToolBlockedByPolicy(rules, "slack", "safe_read", null)).toBe(false);
|
|
134
|
+
});
|
|
135
|
+
// -----------------------------------------------------------------------
|
|
136
|
+
// Priority resolution
|
|
137
|
+
// -----------------------------------------------------------------------
|
|
138
|
+
it("higher priority rule wins over lower priority", () => {
|
|
139
|
+
const rules = [
|
|
140
|
+
// Server-level: allowed at priority 0
|
|
141
|
+
{ identifier: "slack", status: "allowed", toolPattern: null, priority: 0 },
|
|
142
|
+
// Tool-level: blocked at priority 20 (higher, wins)
|
|
143
|
+
{ identifier: "slack", status: "blocked", toolPattern: "slack_search_public_and_private", priority: 20 },
|
|
144
|
+
];
|
|
145
|
+
expect(isToolBlockedByPolicy(rules, "slack", "slack_search_public_and_private", null)).toBe(true);
|
|
146
|
+
// Other tools still allowed
|
|
147
|
+
expect(isToolBlockedByPolicy(rules, "slack", "slack_send_message", null)).toBe(false);
|
|
148
|
+
});
|
|
149
|
+
it("tool-level allow overrides server-level block at higher priority", () => {
|
|
150
|
+
const rules = [
|
|
151
|
+
// Server-level: blocked at priority 0
|
|
152
|
+
{ identifier: "slack", status: "blocked", toolPattern: null, priority: 0 },
|
|
153
|
+
// Tool-level: allowed at priority 20 (higher, wins)
|
|
154
|
+
{ identifier: "slack", status: "allowed", toolPattern: "slack_send_message", priority: 20 },
|
|
155
|
+
];
|
|
156
|
+
// The allowed tool is not blocked
|
|
157
|
+
expect(isToolBlockedByPolicy(rules, "slack", "slack_send_message", null)).toBe(false);
|
|
158
|
+
// Other tools still blocked by server-level rule
|
|
159
|
+
expect(isToolBlockedByPolicy(rules, "slack", "slack_search_public", null)).toBe(true);
|
|
160
|
+
});
|
|
161
|
+
// -----------------------------------------------------------------------
|
|
162
|
+
// Deny wins at same priority
|
|
163
|
+
// -----------------------------------------------------------------------
|
|
164
|
+
it("deny wins when two rules have the same priority and specificity", () => {
|
|
165
|
+
const rules = [
|
|
166
|
+
{ identifier: "slack", status: "allowed", toolPattern: "slack_search_public", priority: 20 },
|
|
167
|
+
{ identifier: "slack", status: "blocked", toolPattern: "slack_search_public", priority: 20 },
|
|
168
|
+
];
|
|
169
|
+
expect(isToolBlockedByPolicy(rules, "slack", "slack_search_public", null)).toBe(true);
|
|
170
|
+
});
|
|
171
|
+
// -----------------------------------------------------------------------
|
|
172
|
+
// Specificity resolution
|
|
173
|
+
// -----------------------------------------------------------------------
|
|
174
|
+
it("more specific pattern wins at same priority", () => {
|
|
175
|
+
const rules = [
|
|
176
|
+
// Wildcard pattern: block all search tools (less specific)
|
|
177
|
+
{ identifier: "slack", status: "blocked", toolPattern: "slack_search_*", priority: 10 },
|
|
178
|
+
// Exact name: allow this specific search tool (more specific)
|
|
179
|
+
{ identifier: "slack", status: "allowed", toolPattern: "slack_search_public", priority: 10 },
|
|
180
|
+
];
|
|
181
|
+
// Exact name (specificity 100) wins over wildcard (specificity ~22)
|
|
182
|
+
expect(isToolBlockedByPolicy(rules, "slack", "slack_search_public", null)).toBe(false);
|
|
183
|
+
// Other search tools still blocked
|
|
184
|
+
expect(isToolBlockedByPolicy(rules, "slack", "slack_search_private", null)).toBe(true);
|
|
185
|
+
});
|
|
186
|
+
// -----------------------------------------------------------------------
|
|
187
|
+
// Multiple tool blocks on same server
|
|
188
|
+
// -----------------------------------------------------------------------
|
|
189
|
+
it("blocks multiple specific tools on the same server", () => {
|
|
190
|
+
const rules = [
|
|
191
|
+
{ identifier: "slack", status: "blocked", toolPattern: "slack_search_public_and_private", priority: 20 },
|
|
192
|
+
{ identifier: "slack", status: "blocked", toolPattern: "slack_send_message", priority: 20 },
|
|
193
|
+
];
|
|
194
|
+
expect(isToolBlockedByPolicy(rules, "slack", "slack_search_public_and_private", null)).toBe(true);
|
|
195
|
+
expect(isToolBlockedByPolicy(rules, "slack", "slack_send_message", null)).toBe(true);
|
|
196
|
+
expect(isToolBlockedByPolicy(rules, "slack", "slack_read_channel", null)).toBe(false);
|
|
197
|
+
});
|
|
198
|
+
// -----------------------------------------------------------------------
|
|
199
|
+
// URL-based identifier matching
|
|
200
|
+
// -----------------------------------------------------------------------
|
|
201
|
+
it("matches rules by URL when server name and packageName don't match", () => {
|
|
202
|
+
const rules = [
|
|
203
|
+
{ identifier: "https://mcp.slack.com/mcp", status: "blocked", toolPattern: "slack_search_*", priority: 10 },
|
|
204
|
+
];
|
|
205
|
+
// Server name "slack" and packageName don't match, but URL does
|
|
206
|
+
expect(isToolBlockedByPolicy(rules, "slack", "slack_search_public", null, "https://mcp.slack.com/mcp")).toBe(true);
|
|
207
|
+
expect(isToolBlockedByPolicy(rules, "slack", "slack_send_message", null, "https://mcp.slack.com/mcp")).toBe(false);
|
|
208
|
+
});
|
|
209
|
+
// -----------------------------------------------------------------------
|
|
210
|
+
// Real-world Slack scenario: policy uses packageName, gateway has URL
|
|
211
|
+
// -----------------------------------------------------------------------
|
|
212
|
+
it("blocks Slack tools matching the real-world policy shape", () => {
|
|
213
|
+
// Mirrors the actual policy returned by the Sonoma API
|
|
214
|
+
const rules = [
|
|
215
|
+
{ identifier: "https://mcp.slack.com/mcp", status: "gateway", toolPattern: null, priority: 0 },
|
|
216
|
+
{ identifier: "slack-mcp-server", status: "blocked", toolPattern: "slack_search_public_and_private", priority: 20 },
|
|
217
|
+
{ identifier: "slack-mcp-server", status: "blocked", toolPattern: "slack_send_message", priority: 20 },
|
|
218
|
+
];
|
|
219
|
+
// Gateway config: server name "slack", no packageName, URL "https://mcp.slack.com/mcp"
|
|
220
|
+
// The tool-level rules use "slack-mcp-server" which doesn't match server name or URL,
|
|
221
|
+
// but if packageName is resolved to "slack-mcp-server" it will match
|
|
222
|
+
expect(isToolBlockedByPolicy(rules, "slack", "slack_search_public_and_private", "slack-mcp-server", "https://mcp.slack.com/mcp")).toBe(true);
|
|
223
|
+
expect(isToolBlockedByPolicy(rules, "slack", "slack_send_message", "slack-mcp-server", "https://mcp.slack.com/mcp")).toBe(true);
|
|
224
|
+
// Unblocked tools are still allowed
|
|
225
|
+
expect(isToolBlockedByPolicy(rules, "slack", "slack_read_channel", "slack-mcp-server", "https://mcp.slack.com/mcp")).toBe(false);
|
|
226
|
+
});
|
|
227
|
+
// -----------------------------------------------------------------------
|
|
228
|
+
// Extra identifiers (alias resolution from policy)
|
|
229
|
+
// -----------------------------------------------------------------------
|
|
230
|
+
it("matches rules via extraIdentifiers (alias resolution)", () => {
|
|
231
|
+
// Tool-level rules use the canonical package name
|
|
232
|
+
const rules = [
|
|
233
|
+
{ identifier: "slack-mcp-server", status: "blocked", toolPattern: "slack_search_public_and_private", priority: 20 },
|
|
234
|
+
];
|
|
235
|
+
// Gateway only knows server name "slack" and URL, but aliases resolve
|
|
236
|
+
// "slack-mcp-server" from the identifierAliases map
|
|
237
|
+
expect(isToolBlockedByPolicy(rules, "slack", "slack_search_public_and_private", null, "https://mcp.slack.com/mcp", ["slack-mcp-server"])).toBe(true);
|
|
238
|
+
// Non-blocked tools still allowed
|
|
239
|
+
expect(isToolBlockedByPolicy(rules, "slack", "slack_read_channel", null, "https://mcp.slack.com/mcp", ["slack-mcp-server"])).toBe(false);
|
|
240
|
+
});
|
|
241
|
+
it("matches the exact real-world scenario: gateway has URL, policy has packageName", () => {
|
|
242
|
+
// Real policy from the Sonoma API
|
|
243
|
+
const rules = [
|
|
244
|
+
{ identifier: "https://mcp.slack.com/mcp", status: "gateway", toolPattern: null, priority: 0 },
|
|
245
|
+
{ identifier: "slack-mcp-server", status: "blocked", toolPattern: "slack_search_public_and_private", priority: 20 },
|
|
246
|
+
{ identifier: "slack-mcp-server", status: "blocked", toolPattern: "slack_send_message", priority: 20 },
|
|
247
|
+
];
|
|
248
|
+
// Gateway: name="slack", no packageName, url="https://mcp.slack.com/mcp"
|
|
249
|
+
// identifierAliases from policy: {"https://mcp.slack.com/mcp": ["slack-mcp-server"]}
|
|
250
|
+
const aliases = ["slack-mcp-server"];
|
|
251
|
+
expect(isToolBlockedByPolicy(rules, "slack", "slack_search_public_and_private", null, "https://mcp.slack.com/mcp", aliases)).toBe(true);
|
|
252
|
+
expect(isToolBlockedByPolicy(rules, "slack", "slack_send_message", null, "https://mcp.slack.com/mcp", aliases)).toBe(true);
|
|
253
|
+
expect(isToolBlockedByPolicy(rules, "slack", "slack_read_channel", null, "https://mcp.slack.com/mcp", aliases)).toBe(false);
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
//# sourceMappingURL=tool-blocking.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-blocking.test.js","sourceRoot":"","sources":["../../src/__tests__/tool-blocking.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OS Keychain abstraction for secure credential storage.
|
|
3
|
+
*
|
|
4
|
+
* Uses native OS credential stores via CLI tools (no native modules needed):
|
|
5
|
+
* - macOS: Keychain Access via `security` CLI
|
|
6
|
+
* - Windows: Credential Manager via PowerShell
|
|
7
|
+
* - Linux/other: Falls back to encrypted file storage (existing behavior)
|
|
8
|
+
*
|
|
9
|
+
* All credentials are stored as JSON strings, keyed by (service, account).
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Store a credential in the OS keychain.
|
|
13
|
+
* @param account - Unique key within the service (e.g., "sonoma-auth", "upstream:https://mcp.slack.com")
|
|
14
|
+
* @param data - Credential data (will be JSON-stringified)
|
|
15
|
+
*/
|
|
16
|
+
export declare function keychainSet(account: string, data: unknown): void;
|
|
17
|
+
/**
|
|
18
|
+
* Retrieve a credential from the OS keychain.
|
|
19
|
+
* @returns Parsed JSON data, or null if not found
|
|
20
|
+
*/
|
|
21
|
+
export declare function keychainGet<T = unknown>(account: string): T | null;
|
|
22
|
+
/**
|
|
23
|
+
* Delete a credential from the OS keychain.
|
|
24
|
+
*/
|
|
25
|
+
export declare function keychainDelete(account: string): void;
|
|
26
|
+
/**
|
|
27
|
+
* Delete all credentials for the service from the OS keychain.
|
|
28
|
+
*/
|
|
29
|
+
export declare function keychainDeleteAll(): void;
|
|
30
|
+
/**
|
|
31
|
+
* Check whether OS keychain is available on this platform.
|
|
32
|
+
*/
|
|
33
|
+
export declare function keychainAvailable(): boolean;
|
|
34
|
+
//# sourceMappingURL=keychain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keychain.d.ts","sourceRoot":"","sources":["../../src/auth/keychain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAQH;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAWhE;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI,CAmBlE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAUpD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAUxC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAM3C"}
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OS Keychain abstraction for secure credential storage.
|
|
3
|
+
*
|
|
4
|
+
* Uses native OS credential stores via CLI tools (no native modules needed):
|
|
5
|
+
* - macOS: Keychain Access via `security` CLI
|
|
6
|
+
* - Windows: Credential Manager via PowerShell
|
|
7
|
+
* - Linux/other: Falls back to encrypted file storage (existing behavior)
|
|
8
|
+
*
|
|
9
|
+
* All credentials are stored as JSON strings, keyed by (service, account).
|
|
10
|
+
*/
|
|
11
|
+
import { execSync } from "node:child_process";
|
|
12
|
+
const SERVICE_NAME = "sonoma-mcp-gateway";
|
|
13
|
+
/**
|
|
14
|
+
* Store a credential in the OS keychain.
|
|
15
|
+
* @param account - Unique key within the service (e.g., "sonoma-auth", "upstream:https://mcp.slack.com")
|
|
16
|
+
* @param data - Credential data (will be JSON-stringified)
|
|
17
|
+
*/
|
|
18
|
+
export function keychainSet(account, data) {
|
|
19
|
+
const json = JSON.stringify(data);
|
|
20
|
+
const platform = process.platform;
|
|
21
|
+
if (platform === "darwin") {
|
|
22
|
+
macosSet(account, json);
|
|
23
|
+
}
|
|
24
|
+
else if (platform === "win32") {
|
|
25
|
+
windowsSet(account, json);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
throw new Error(`Keychain not supported on ${platform}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Retrieve a credential from the OS keychain.
|
|
33
|
+
* @returns Parsed JSON data, or null if not found
|
|
34
|
+
*/
|
|
35
|
+
export function keychainGet(account) {
|
|
36
|
+
const platform = process.platform;
|
|
37
|
+
let json;
|
|
38
|
+
if (platform === "darwin") {
|
|
39
|
+
json = macosGet(account);
|
|
40
|
+
}
|
|
41
|
+
else if (platform === "win32") {
|
|
42
|
+
json = windowsGet(account);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
throw new Error(`Keychain not supported on ${platform}`);
|
|
46
|
+
}
|
|
47
|
+
if (json === null)
|
|
48
|
+
return null;
|
|
49
|
+
try {
|
|
50
|
+
return JSON.parse(json);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Delete a credential from the OS keychain.
|
|
58
|
+
*/
|
|
59
|
+
export function keychainDelete(account) {
|
|
60
|
+
const platform = process.platform;
|
|
61
|
+
if (platform === "darwin") {
|
|
62
|
+
macosDelete(account);
|
|
63
|
+
}
|
|
64
|
+
else if (platform === "win32") {
|
|
65
|
+
windowsDelete(account);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
throw new Error(`Keychain not supported on ${platform}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Delete all credentials for the service from the OS keychain.
|
|
73
|
+
*/
|
|
74
|
+
export function keychainDeleteAll() {
|
|
75
|
+
const platform = process.platform;
|
|
76
|
+
if (platform === "darwin") {
|
|
77
|
+
macosDeleteAll();
|
|
78
|
+
}
|
|
79
|
+
else if (platform === "win32") {
|
|
80
|
+
windowsDeleteAll();
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
throw new Error(`Keychain not supported on ${platform}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Check whether OS keychain is available on this platform.
|
|
88
|
+
*/
|
|
89
|
+
export function keychainAvailable() {
|
|
90
|
+
const platform = process.platform;
|
|
91
|
+
if (platform === "darwin" || platform === "win32") {
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
// macOS Keychain via `security` CLI
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
function macosSet(account, password) {
|
|
100
|
+
// -U = update if exists, -s = service, -a = account, -w = password
|
|
101
|
+
// Accepted risk: macOS `security` CLI requires -w <password> as a CLI argument,
|
|
102
|
+
// which briefly exposes it in the process list. There is no stdin alternative for
|
|
103
|
+
// add-generic-password. The exposure window is sub-second and local-only.
|
|
104
|
+
execSync(`/usr/bin/security add-generic-password -s ${shellEscape(SERVICE_NAME)} -a ${shellEscape(account)} -U -w ${shellEscape(password)}`, { stdio: "pipe" });
|
|
105
|
+
}
|
|
106
|
+
function macosGet(account) {
|
|
107
|
+
try {
|
|
108
|
+
const result = execSync(`/usr/bin/security find-generic-password -s ${shellEscape(SERVICE_NAME)} -a ${shellEscape(account)} -w`, { stdio: "pipe", encoding: "utf-8" });
|
|
109
|
+
return result.trim();
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// Not found (exit code 44) or other error
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function macosDelete(account) {
|
|
117
|
+
try {
|
|
118
|
+
execSync(`/usr/bin/security delete-generic-password -s ${shellEscape(SERVICE_NAME)} -a ${shellEscape(account)}`, { stdio: "pipe" });
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
// Ignore if not found
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function macosDeleteAll() {
|
|
125
|
+
// Delete entries one at a time until none remain
|
|
126
|
+
for (let i = 0; i < 100; i++) {
|
|
127
|
+
try {
|
|
128
|
+
execSync(`/usr/bin/security delete-generic-password -s ${shellEscape(SERVICE_NAME)}`, { stdio: "pipe" });
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
break; // No more entries
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
// Windows Credential Manager via PowerShell
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
function windowsTargetName(account) {
|
|
139
|
+
return `${SERVICE_NAME}:${account}`;
|
|
140
|
+
}
|
|
141
|
+
function windowsSet(account, password) {
|
|
142
|
+
const target = windowsTargetName(account);
|
|
143
|
+
// Use CredWrite via .NET interop for arbitrary credential storage.
|
|
144
|
+
// Base64-encode the password to avoid escaping issues in PowerShell.
|
|
145
|
+
const b64 = Buffer.from(password, "utf-8").toString("base64");
|
|
146
|
+
const ps = `
|
|
147
|
+
$ErrorActionPreference='Stop'
|
|
148
|
+
Add-Type -TypeDefinition @"
|
|
149
|
+
using System;
|
|
150
|
+
using System.Runtime.InteropServices;
|
|
151
|
+
using System.Text;
|
|
152
|
+
|
|
153
|
+
public class CredManager {
|
|
154
|
+
[DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
|
|
155
|
+
public static extern bool CredWriteW(ref CREDENTIAL cred, uint flags);
|
|
156
|
+
[DllImport("advapi32.dll", SetLastError=true)]
|
|
157
|
+
public static extern bool CredFree(IntPtr buffer);
|
|
158
|
+
|
|
159
|
+
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
|
|
160
|
+
public struct CREDENTIAL {
|
|
161
|
+
public uint Flags;
|
|
162
|
+
public uint Type;
|
|
163
|
+
public string TargetName;
|
|
164
|
+
public string Comment;
|
|
165
|
+
public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
|
|
166
|
+
public uint CredentialBlobSize;
|
|
167
|
+
public IntPtr CredentialBlob;
|
|
168
|
+
public uint Persist;
|
|
169
|
+
public uint AttributeCount;
|
|
170
|
+
public IntPtr Attributes;
|
|
171
|
+
public string TargetAlias;
|
|
172
|
+
public string UserName;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
public static void Write(string target, string secret) {
|
|
176
|
+
byte[] bytes = Encoding.UTF8.GetBytes(secret);
|
|
177
|
+
IntPtr blob = Marshal.AllocHGlobal(bytes.Length);
|
|
178
|
+
Marshal.Copy(bytes, 0, blob, bytes.Length);
|
|
179
|
+
CREDENTIAL cred = new CREDENTIAL();
|
|
180
|
+
cred.Type = 1; // CRED_TYPE_GENERIC
|
|
181
|
+
cred.TargetName = target;
|
|
182
|
+
cred.CredentialBlobSize = (uint)bytes.Length;
|
|
183
|
+
cred.CredentialBlob = blob;
|
|
184
|
+
cred.Persist = 2; // CRED_PERSIST_LOCAL_MACHINE
|
|
185
|
+
cred.UserName = "sonoma-gateway";
|
|
186
|
+
bool ok = CredWriteW(ref cred, 0);
|
|
187
|
+
Marshal.FreeHGlobal(blob);
|
|
188
|
+
if (!ok) throw new Exception("CredWrite failed: " + Marshal.GetLastWin32Error());
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
"@
|
|
192
|
+
$targetB64 = '${Buffer.from(target, "utf-8").toString("base64")}'
|
|
193
|
+
$target = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($targetB64))
|
|
194
|
+
$secret = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('${b64}'))
|
|
195
|
+
[CredManager]::Write($target, $secret)
|
|
196
|
+
`;
|
|
197
|
+
execSync(`powershell -NoProfile -NonInteractive -Command "${ps.replace(/"/g, '\\"')}"`, {
|
|
198
|
+
stdio: "pipe",
|
|
199
|
+
windowsHide: true,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
function windowsGet(account) {
|
|
203
|
+
const target = windowsTargetName(account);
|
|
204
|
+
const ps = `
|
|
205
|
+
$ErrorActionPreference='Stop'
|
|
206
|
+
Add-Type -TypeDefinition @"
|
|
207
|
+
using System;
|
|
208
|
+
using System.Runtime.InteropServices;
|
|
209
|
+
using System.Text;
|
|
210
|
+
|
|
211
|
+
public class CredReader {
|
|
212
|
+
[DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
|
|
213
|
+
public static extern bool CredReadW(string target, uint type, uint flags, out IntPtr cred);
|
|
214
|
+
[DllImport("advapi32.dll", SetLastError=true)]
|
|
215
|
+
public static extern bool CredFree(IntPtr buffer);
|
|
216
|
+
|
|
217
|
+
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
|
|
218
|
+
public struct CREDENTIAL {
|
|
219
|
+
public uint Flags;
|
|
220
|
+
public uint Type;
|
|
221
|
+
public string TargetName;
|
|
222
|
+
public string Comment;
|
|
223
|
+
public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
|
|
224
|
+
public uint CredentialBlobSize;
|
|
225
|
+
public IntPtr CredentialBlob;
|
|
226
|
+
public uint Persist;
|
|
227
|
+
public uint AttributeCount;
|
|
228
|
+
public IntPtr Attributes;
|
|
229
|
+
public string TargetAlias;
|
|
230
|
+
public string UserName;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
public static string Read(string target) {
|
|
234
|
+
IntPtr credPtr;
|
|
235
|
+
bool ok = CredReadW(target, 1, 0, out credPtr);
|
|
236
|
+
if (!ok) return null;
|
|
237
|
+
CREDENTIAL cred = (CREDENTIAL)Marshal.PtrToStructure(credPtr, typeof(CREDENTIAL));
|
|
238
|
+
byte[] bytes = new byte[cred.CredentialBlobSize];
|
|
239
|
+
Marshal.Copy(cred.CredentialBlob, bytes, 0, bytes.Length);
|
|
240
|
+
CredFree(credPtr);
|
|
241
|
+
return Encoding.UTF8.GetString(bytes);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
"@
|
|
245
|
+
$targetB64 = '${Buffer.from(target, "utf-8").toString("base64")}'
|
|
246
|
+
$t = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($targetB64))
|
|
247
|
+
$result = [CredReader]::Read($t)
|
|
248
|
+
if ($result -eq $null) { exit 1 }
|
|
249
|
+
[Console]::Write($result)
|
|
250
|
+
`;
|
|
251
|
+
try {
|
|
252
|
+
return execSync(`powershell -NoProfile -NonInteractive -Command "${ps.replace(/"/g, '\\"')}"`, { stdio: "pipe", encoding: "utf-8", windowsHide: true });
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
function windowsDelete(account) {
|
|
259
|
+
const target = windowsTargetName(account);
|
|
260
|
+
try {
|
|
261
|
+
execSync(`cmdkey /delete:${target}`, { stdio: "pipe", windowsHide: true });
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
// Ignore if not found
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
function windowsDeleteAll() {
|
|
268
|
+
// List all credentials matching our service prefix and delete them
|
|
269
|
+
try {
|
|
270
|
+
const output = execSync("cmdkey /list", {
|
|
271
|
+
stdio: "pipe",
|
|
272
|
+
encoding: "utf-8",
|
|
273
|
+
windowsHide: true,
|
|
274
|
+
});
|
|
275
|
+
const prefix = `${SERVICE_NAME}:`;
|
|
276
|
+
for (const line of output.split("\n")) {
|
|
277
|
+
const match = line.match(/Target:\s*(.+)/i);
|
|
278
|
+
if (match) {
|
|
279
|
+
const target = match[1].trim();
|
|
280
|
+
if (target.startsWith(prefix)) {
|
|
281
|
+
try {
|
|
282
|
+
execSync(`cmdkey /delete:${target}`, { stdio: "pipe", windowsHide: true });
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
// Ignore individual delete failures
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
catch {
|
|
292
|
+
// Ignore errors
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
// ---------------------------------------------------------------------------
|
|
296
|
+
// Helpers
|
|
297
|
+
// ---------------------------------------------------------------------------
|
|
298
|
+
/**
|
|
299
|
+
* Shell-escape a string for use in a command argument.
|
|
300
|
+
* Wraps in single quotes and escapes embedded single quotes.
|
|
301
|
+
*/
|
|
302
|
+
function shellEscape(s) {
|
|
303
|
+
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
304
|
+
}
|
|
305
|
+
//# sourceMappingURL=keychain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keychain.js","sourceRoot":"","sources":["../../src/auth/keychain.ts"],"names":[],"mappings":"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"}
|