ssh-agent-workspace 1.0.4 → 1.0.6
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/README.md +471 -313
- package/dist/__tests__/SSHManager.test.js +59 -37
- package/dist/__tests__/SSHManager.test.js.map +1 -1
- package/dist/__tests__/SessionManager.test.js +53 -51
- package/dist/__tests__/SessionManager.test.js.map +1 -1
- package/dist/__tests__/StorageManager.test.js +74 -74
- package/dist/__tests__/StorageManager.test.js.map +1 -1
- package/dist/__tests__/ansi.test.js +33 -33
- package/dist/__tests__/ansi.test.js.map +1 -1
- package/dist/__tests__/security.test.js +54 -54
- package/dist/__tests__/security.test.js.map +1 -1
- package/dist/__tests__/validation.test.js +17 -17
- package/dist/__tests__/validation.test.js.map +1 -1
- package/dist/core/HostSecurityManager.d.ts.map +1 -1
- package/dist/core/HostSecurityManager.js +9 -9
- package/dist/core/HostSecurityManager.js.map +1 -1
- package/dist/core/SSHManager.d.ts +1 -1
- package/dist/core/SSHManager.d.ts.map +1 -1
- package/dist/core/SSHManager.js +31 -31
- package/dist/core/SSHManager.js.map +1 -1
- package/dist/core/SessionManager.d.ts +4 -4
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +14 -14
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/core/StorageManager.d.ts.map +1 -1
- package/dist/core/StorageManager.js +11 -11
- package/dist/core/StorageManager.js.map +1 -1
- package/dist/core/TmuxManager.d.ts +3 -3
- package/dist/core/TmuxManager.d.ts.map +1 -1
- package/dist/core/TmuxManager.js +16 -16
- package/dist/core/TmuxManager.js.map +1 -1
- package/dist/core/ToolConfigManager.d.ts.map +1 -1
- package/dist/core/ToolConfigManager.js +10 -10
- package/dist/core/ToolConfigManager.js.map +1 -1
- package/dist/index.js +35 -35
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +4 -4
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +41 -41
- package/dist/server.js.map +1 -1
- package/dist/tools/backup.d.ts +3 -3
- package/dist/tools/backup.d.ts.map +1 -1
- package/dist/tools/backup.js +86 -49
- package/dist/tools/backup.js.map +1 -1
- package/dist/tools/connect.d.ts +4 -4
- package/dist/tools/connect.d.ts.map +1 -1
- package/dist/tools/connect.js +54 -42
- package/dist/tools/connect.js.map +1 -1
- package/dist/tools/connection_status.d.ts +4 -4
- package/dist/tools/connection_status.d.ts.map +1 -1
- package/dist/tools/connection_status.js +23 -16
- package/dist/tools/connection_status.js.map +1 -1
- package/dist/tools/db_query.d.ts +3 -3
- package/dist/tools/db_query.d.ts.map +1 -1
- package/dist/tools/db_query.js +130 -78
- package/dist/tools/db_query.js.map +1 -1
- package/dist/tools/deploy.d.ts +3 -3
- package/dist/tools/deploy.d.ts.map +1 -1
- package/dist/tools/deploy.js +103 -53
- package/dist/tools/deploy.js.map +1 -1
- package/dist/tools/disconnect.d.ts +3 -3
- package/dist/tools/disconnect.d.ts.map +1 -1
- package/dist/tools/disconnect.js +16 -19
- package/dist/tools/disconnect.js.map +1 -1
- package/dist/tools/exec.d.ts +3 -3
- package/dist/tools/exec.d.ts.map +1 -1
- package/dist/tools/exec.js +40 -45
- package/dist/tools/exec.js.map +1 -1
- package/dist/tools/group_exec.d.ts +3 -3
- package/dist/tools/group_exec.d.ts.map +1 -1
- package/dist/tools/group_exec.js +60 -44
- package/dist/tools/group_exec.js.map +1 -1
- package/dist/tools/health_check.d.ts +3 -3
- package/dist/tools/health_check.d.ts.map +1 -1
- package/dist/tools/health_check.js +27 -23
- package/dist/tools/health_check.js.map +1 -1
- package/dist/tools/host_security.d.ts +1 -1
- package/dist/tools/host_security.d.ts.map +1 -1
- package/dist/tools/host_security.js +30 -36
- package/dist/tools/host_security.js.map +1 -1
- package/dist/tools/index.d.ts +23 -23
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +23 -23
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/interrupt.d.ts +3 -3
- package/dist/tools/interrupt.d.ts.map +1 -1
- package/dist/tools/interrupt.js +18 -18
- package/dist/tools/interrupt.js.map +1 -1
- package/dist/tools/list_hosts.d.ts.map +1 -1
- package/dist/tools/list_hosts.js +5 -7
- package/dist/tools/list_hosts.js.map +1 -1
- package/dist/tools/list_sessions.d.ts +1 -1
- package/dist/tools/list_sessions.js +4 -4
- package/dist/tools/list_sessions.js.map +1 -1
- package/dist/tools/read_output.d.ts +3 -3
- package/dist/tools/read_output.d.ts.map +1 -1
- package/dist/tools/read_output.js +17 -17
- package/dist/tools/read_output.js.map +1 -1
- package/dist/tools/reconnect_to_tmux.d.ts +4 -4
- package/dist/tools/reconnect_to_tmux.d.ts.map +1 -1
- package/dist/tools/reconnect_to_tmux.js +44 -37
- package/dist/tools/reconnect_to_tmux.js.map +1 -1
- package/dist/tools/send_input.d.ts +3 -3
- package/dist/tools/send_input.d.ts.map +1 -1
- package/dist/tools/send_input.js +18 -18
- package/dist/tools/send_input.js.map +1 -1
- package/dist/tools/sftp_download.d.ts +3 -3
- package/dist/tools/sftp_download.d.ts.map +1 -1
- package/dist/tools/sftp_download.js +41 -27
- package/dist/tools/sftp_download.js.map +1 -1
- package/dist/tools/sftp_list.d.ts +3 -3
- package/dist/tools/sftp_list.d.ts.map +1 -1
- package/dist/tools/sftp_list.js +35 -26
- package/dist/tools/sftp_list.js.map +1 -1
- package/dist/tools/sftp_upload.d.ts +3 -3
- package/dist/tools/sftp_upload.d.ts.map +1 -1
- package/dist/tools/sftp_upload.js +43 -29
- package/dist/tools/sftp_upload.js.map +1 -1
- package/dist/tools/ssh_tunnel.d.ts +3 -3
- package/dist/tools/ssh_tunnel.d.ts.map +1 -1
- package/dist/tools/ssh_tunnel.js +130 -76
- package/dist/tools/ssh_tunnel.js.map +1 -1
- package/dist/tools/sync.d.ts +3 -3
- package/dist/tools/sync.d.ts.map +1 -1
- package/dist/tools/sync.js +103 -61
- package/dist/tools/sync.js.map +1 -1
- package/dist/tools/tail_log.d.ts +3 -3
- package/dist/tools/tail_log.d.ts.map +1 -1
- package/dist/tools/tail_log.js +38 -26
- package/dist/tools/tail_log.js.map +1 -1
- package/dist/tools/tools_config.d.ts +1 -1
- package/dist/tools/tools_config.d.ts.map +1 -1
- package/dist/tools/tools_config.js +25 -35
- package/dist/tools/tools_config.js.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/ansi.js +1 -1
- package/dist/utils/ansi.js.map +1 -1
- package/dist/utils/logger.d.ts +1 -1
- package/dist/utils/logger.js +2 -2
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/security.d.ts +1 -1
- package/dist/utils/security.js +3 -3
- package/dist/utils/security.js.map +1 -1
- package/dist/utils/ssh.d.ts +2 -2
- package/dist/utils/ssh.js +5 -5
- package/dist/utils/ssh.js.map +1 -1
- package/dist/utils/sshConfig.d.ts +1 -1
- package/dist/utils/sshConfig.d.ts.map +1 -1
- package/dist/utils/sshConfig.js +21 -21
- package/dist/utils/sshConfig.js.map +1 -1
- package/dist/utils/validation.d.ts +1 -1
- package/dist/utils/validation.js +3 -3
- package/dist/utils/validation.js.map +1 -1
- package/docs/SECURITY.md +213 -213
- package/docs/TOOLS.md +425 -425
- package/package.json +59 -48
- package/vitest.config.ts +10 -10
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from
|
|
2
|
-
import fs from
|
|
3
|
-
import path from
|
|
4
|
-
import os from
|
|
5
|
-
import { StorageManager } from
|
|
6
|
-
const TEST_DIR = path.join(os.tmpdir(),
|
|
7
|
-
describe(
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import os from "os";
|
|
5
|
+
import { StorageManager } from "../core/StorageManager.js";
|
|
6
|
+
const TEST_DIR = path.join(os.tmpdir(), "dynamic-ssh-mcp-test-" + Date.now());
|
|
7
|
+
describe("StorageManager", () => {
|
|
8
8
|
let storage;
|
|
9
9
|
beforeEach(() => {
|
|
10
10
|
storage = new StorageManager(TEST_DIR);
|
|
@@ -15,26 +15,26 @@ describe('StorageManager', () => {
|
|
|
15
15
|
fs.rmSync(TEST_DIR, { recursive: true, force: true });
|
|
16
16
|
}
|
|
17
17
|
});
|
|
18
|
-
it(
|
|
18
|
+
it("should create storage directory on first save", () => {
|
|
19
19
|
expect(fs.existsSync(TEST_DIR)).toBe(false);
|
|
20
20
|
storage.save({
|
|
21
|
-
id:
|
|
22
|
-
host:
|
|
23
|
-
tmuxSession:
|
|
24
|
-
shell:
|
|
21
|
+
id: "sess_test123",
|
|
22
|
+
host: "prod",
|
|
23
|
+
tmuxSession: "mcp_prod_abc",
|
|
24
|
+
shell: "bash",
|
|
25
25
|
connectedAt: Date.now(),
|
|
26
26
|
lastActivity: Date.now(),
|
|
27
27
|
});
|
|
28
28
|
storage.shutdown();
|
|
29
29
|
expect(fs.existsSync(TEST_DIR)).toBe(true);
|
|
30
|
-
expect(fs.existsSync(path.join(TEST_DIR,
|
|
30
|
+
expect(fs.existsSync(path.join(TEST_DIR, "sessions.json"))).toBe(true);
|
|
31
31
|
});
|
|
32
|
-
it(
|
|
32
|
+
it("should save and retrieve sessions", () => {
|
|
33
33
|
const session = {
|
|
34
|
-
id:
|
|
35
|
-
host:
|
|
36
|
-
tmuxSession:
|
|
37
|
-
shell:
|
|
34
|
+
id: "sess_test456",
|
|
35
|
+
host: "staging",
|
|
36
|
+
tmuxSession: "mcp_staging_xyz",
|
|
37
|
+
shell: "zsh",
|
|
38
38
|
connectedAt: 1000,
|
|
39
39
|
lastActivity: 2000,
|
|
40
40
|
};
|
|
@@ -42,127 +42,127 @@ describe('StorageManager', () => {
|
|
|
42
42
|
storage.shutdown();
|
|
43
43
|
// Create a new StorageManager to test persistence
|
|
44
44
|
const storage2 = new StorageManager(TEST_DIR);
|
|
45
|
-
const retrieved = storage2.get(
|
|
45
|
+
const retrieved = storage2.get("sess_test456");
|
|
46
46
|
expect(retrieved).toBeDefined();
|
|
47
|
-
expect(retrieved.host).toBe(
|
|
48
|
-
expect(retrieved.tmuxSession).toBe(
|
|
49
|
-
expect(retrieved.shell).toBe(
|
|
47
|
+
expect(retrieved.host).toBe("staging");
|
|
48
|
+
expect(retrieved.tmuxSession).toBe("mcp_staging_xyz");
|
|
49
|
+
expect(retrieved.shell).toBe("zsh");
|
|
50
50
|
expect(retrieved.connectedAt).toBe(1000);
|
|
51
51
|
storage2.shutdown();
|
|
52
52
|
});
|
|
53
|
-
it(
|
|
53
|
+
it("should list all sessions", () => {
|
|
54
54
|
storage.save({
|
|
55
|
-
id:
|
|
56
|
-
host:
|
|
57
|
-
tmuxSession:
|
|
55
|
+
id: "sess_1",
|
|
56
|
+
host: "prod",
|
|
57
|
+
tmuxSession: "mcp_prod_1",
|
|
58
58
|
connectedAt: 1,
|
|
59
59
|
lastActivity: 1,
|
|
60
60
|
});
|
|
61
61
|
storage.save({
|
|
62
|
-
id:
|
|
63
|
-
host:
|
|
64
|
-
tmuxSession:
|
|
62
|
+
id: "sess_2",
|
|
63
|
+
host: "gpu",
|
|
64
|
+
tmuxSession: "mcp_gpu_2",
|
|
65
65
|
connectedAt: 2,
|
|
66
66
|
lastActivity: 2,
|
|
67
67
|
});
|
|
68
68
|
const list = storage.list();
|
|
69
69
|
expect(list).toHaveLength(2);
|
|
70
70
|
});
|
|
71
|
-
it(
|
|
71
|
+
it("should list sessions by host", () => {
|
|
72
72
|
storage.save({
|
|
73
|
-
id:
|
|
74
|
-
host:
|
|
75
|
-
tmuxSession:
|
|
73
|
+
id: "sess_a",
|
|
74
|
+
host: "prod",
|
|
75
|
+
tmuxSession: "mcp_prod_a",
|
|
76
76
|
connectedAt: 1,
|
|
77
77
|
lastActivity: 1,
|
|
78
78
|
});
|
|
79
79
|
storage.save({
|
|
80
|
-
id:
|
|
81
|
-
host:
|
|
82
|
-
tmuxSession:
|
|
80
|
+
id: "sess_b",
|
|
81
|
+
host: "staging",
|
|
82
|
+
tmuxSession: "mcp_staging_b",
|
|
83
83
|
connectedAt: 2,
|
|
84
84
|
lastActivity: 2,
|
|
85
85
|
});
|
|
86
86
|
storage.save({
|
|
87
|
-
id:
|
|
88
|
-
host:
|
|
89
|
-
tmuxSession:
|
|
87
|
+
id: "sess_c",
|
|
88
|
+
host: "prod",
|
|
89
|
+
tmuxSession: "mcp_prod_c",
|
|
90
90
|
connectedAt: 3,
|
|
91
91
|
lastActivity: 3,
|
|
92
92
|
});
|
|
93
|
-
const prodSessions = storage.listByHost(
|
|
93
|
+
const prodSessions = storage.listByHost("prod");
|
|
94
94
|
expect(prodSessions).toHaveLength(2);
|
|
95
|
-
expect(prodSessions.map((s) => s.id)).toContain(
|
|
96
|
-
expect(prodSessions.map((s) => s.id)).toContain(
|
|
95
|
+
expect(prodSessions.map((s) => s.id)).toContain("sess_a");
|
|
96
|
+
expect(prodSessions.map((s) => s.id)).toContain("sess_c");
|
|
97
97
|
});
|
|
98
|
-
it(
|
|
98
|
+
it("should list tmux session names", () => {
|
|
99
99
|
storage.save({
|
|
100
|
-
id:
|
|
101
|
-
host:
|
|
102
|
-
tmuxSession:
|
|
100
|
+
id: "sess_x",
|
|
101
|
+
host: "prod",
|
|
102
|
+
tmuxSession: "mcp_prod_x",
|
|
103
103
|
connectedAt: 1,
|
|
104
104
|
lastActivity: 1,
|
|
105
105
|
});
|
|
106
106
|
storage.save({
|
|
107
|
-
id:
|
|
108
|
-
host:
|
|
109
|
-
tmuxSession:
|
|
107
|
+
id: "sess_y",
|
|
108
|
+
host: "gpu",
|
|
109
|
+
tmuxSession: "mcp_gpu_y",
|
|
110
110
|
connectedAt: 2,
|
|
111
111
|
lastActivity: 2,
|
|
112
112
|
});
|
|
113
113
|
const names = storage.listTmuxSessions();
|
|
114
|
-
expect(names).toContain(
|
|
115
|
-
expect(names).toContain(
|
|
114
|
+
expect(names).toContain("mcp_prod_x");
|
|
115
|
+
expect(names).toContain("mcp_gpu_y");
|
|
116
116
|
});
|
|
117
|
-
it(
|
|
117
|
+
it("should remove sessions", () => {
|
|
118
118
|
storage.save({
|
|
119
|
-
id:
|
|
120
|
-
host:
|
|
121
|
-
tmuxSession:
|
|
119
|
+
id: "sess_rm",
|
|
120
|
+
host: "prod",
|
|
121
|
+
tmuxSession: "mcp_prod_rm",
|
|
122
122
|
connectedAt: 1,
|
|
123
123
|
lastActivity: 1,
|
|
124
124
|
});
|
|
125
|
-
expect(storage.get(
|
|
126
|
-
storage.remove(
|
|
127
|
-
expect(storage.get(
|
|
125
|
+
expect(storage.get("sess_rm")).toBeDefined();
|
|
126
|
+
storage.remove("sess_rm");
|
|
127
|
+
expect(storage.get("sess_rm")).toBeUndefined();
|
|
128
128
|
storage.shutdown();
|
|
129
129
|
// Verify removal persisted
|
|
130
130
|
const storage2 = new StorageManager(TEST_DIR);
|
|
131
|
-
expect(storage2.get(
|
|
131
|
+
expect(storage2.get("sess_rm")).toBeUndefined();
|
|
132
132
|
storage2.shutdown();
|
|
133
133
|
});
|
|
134
|
-
it(
|
|
134
|
+
it("should update existing session on save", () => {
|
|
135
135
|
storage.save({
|
|
136
|
-
id:
|
|
137
|
-
host:
|
|
138
|
-
tmuxSession:
|
|
136
|
+
id: "sess_upd",
|
|
137
|
+
host: "prod",
|
|
138
|
+
tmuxSession: "mcp_prod_upd",
|
|
139
139
|
connectedAt: 1,
|
|
140
140
|
lastActivity: 1,
|
|
141
141
|
});
|
|
142
142
|
storage.save({
|
|
143
|
-
id:
|
|
144
|
-
host:
|
|
145
|
-
tmuxSession:
|
|
146
|
-
shell:
|
|
143
|
+
id: "sess_upd",
|
|
144
|
+
host: "prod",
|
|
145
|
+
tmuxSession: "mcp_prod_upd",
|
|
146
|
+
shell: "zsh",
|
|
147
147
|
connectedAt: 1,
|
|
148
148
|
lastActivity: 9999,
|
|
149
149
|
});
|
|
150
|
-
const updated = storage.get(
|
|
151
|
-
expect(updated.shell).toBe(
|
|
150
|
+
const updated = storage.get("sess_upd");
|
|
151
|
+
expect(updated.shell).toBe("zsh");
|
|
152
152
|
expect(updated.lastActivity).toBe(9999);
|
|
153
153
|
expect(storage.list()).toHaveLength(1);
|
|
154
154
|
});
|
|
155
|
-
it(
|
|
155
|
+
it("should handle empty storage gracefully", () => {
|
|
156
156
|
const list = storage.list();
|
|
157
157
|
expect(list).toEqual([]);
|
|
158
|
-
expect(storage.get(
|
|
159
|
-
expect(storage.listByHost(
|
|
158
|
+
expect(storage.get("nonexistent")).toBeUndefined();
|
|
159
|
+
expect(storage.listByHost("nonexistent")).toEqual([]);
|
|
160
160
|
expect(storage.listTmuxSessions()).toEqual([]);
|
|
161
161
|
});
|
|
162
|
-
it(
|
|
162
|
+
it("should handle corrupted storage file gracefully", () => {
|
|
163
163
|
const dir = path.join(TEST_DIR);
|
|
164
164
|
fs.mkdirSync(dir, { recursive: true });
|
|
165
|
-
fs.writeFileSync(path.join(dir,
|
|
165
|
+
fs.writeFileSync(path.join(dir, "sessions.json"), "invalid json{{{");
|
|
166
166
|
const storage2 = new StorageManager(TEST_DIR);
|
|
167
167
|
expect(storage2.list()).toEqual([]);
|
|
168
168
|
storage2.shutdown();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StorageManager.test.js","sourceRoot":"","sources":["../../src/__tests__/StorageManager.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAE3D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,uBAAuB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;AAE9E,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;
|
|
1
|
+
{"version":3,"file":"StorageManager.test.js","sourceRoot":"","sources":["../../src/__tests__/StorageManager.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
import { describe, it, expect } from
|
|
2
|
-
import { stripAnsi } from
|
|
3
|
-
describe(
|
|
4
|
-
describe(
|
|
5
|
-
it(
|
|
6
|
-
expect(stripAnsi(
|
|
7
|
-
expect(stripAnsi(
|
|
8
|
-
});
|
|
9
|
-
it(
|
|
10
|
-
expect(stripAnsi(
|
|
11
|
-
expect(stripAnsi(
|
|
12
|
-
expect(stripAnsi(
|
|
13
|
-
});
|
|
14
|
-
it(
|
|
15
|
-
expect(stripAnsi(
|
|
16
|
-
expect(stripAnsi(
|
|
17
|
-
});
|
|
18
|
-
it(
|
|
19
|
-
const input =
|
|
20
|
-
expect(stripAnsi(input)).toBe(
|
|
21
|
-
});
|
|
22
|
-
it(
|
|
23
|
-
expect(stripAnsi(
|
|
24
|
-
});
|
|
25
|
-
it(
|
|
26
|
-
expect(stripAnsi(
|
|
27
|
-
});
|
|
28
|
-
it(
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { stripAnsi } from "../utils/ansi.js";
|
|
3
|
+
describe("ansi", () => {
|
|
4
|
+
describe("stripAnsi", () => {
|
|
5
|
+
it("should return plain text unchanged", () => {
|
|
6
|
+
expect(stripAnsi("hello world")).toBe("hello world");
|
|
7
|
+
expect(stripAnsi("no colors here")).toBe("no colors here");
|
|
8
|
+
});
|
|
9
|
+
it("should strip color codes", () => {
|
|
10
|
+
expect(stripAnsi("\u001B[31mred\u001B[0m")).toBe("red");
|
|
11
|
+
expect(stripAnsi("\u001B[32mgreen\u001B[0m")).toBe("green");
|
|
12
|
+
expect(stripAnsi("\u001B[1;31mbold red\u001B[0m")).toBe("bold red");
|
|
13
|
+
});
|
|
14
|
+
it("should strip cursor control sequences", () => {
|
|
15
|
+
expect(stripAnsi("\u001B[1Aup\u001B[1Bdown")).toBe("updown");
|
|
16
|
+
expect(stripAnsi("\u001B[2Jcleared")).toBe("cleared");
|
|
17
|
+
});
|
|
18
|
+
it("should strip complex multi-code sequences", () => {
|
|
19
|
+
const input = "\u001B[1m\u001B[33m\u001B[44mstyled\u001B[0m text";
|
|
20
|
+
expect(stripAnsi(input)).toBe("styled text");
|
|
21
|
+
});
|
|
22
|
+
it("should handle empty string", () => {
|
|
23
|
+
expect(stripAnsi("")).toBe("");
|
|
24
|
+
});
|
|
25
|
+
it("should handle string with only ANSI codes", () => {
|
|
26
|
+
expect(stripAnsi("\u001B[31m\u001B[0m")).toBe("");
|
|
27
|
+
});
|
|
28
|
+
it("should strip more escape patterns with updated regex", () => {
|
|
29
29
|
// New comprehensive regex strips ESC followed by valid CSI terminator chars
|
|
30
30
|
// 't' falls in the R-T range used as CSI terminators
|
|
31
|
-
const input =
|
|
32
|
-
expect(stripAnsi(input)).toBe(
|
|
31
|
+
const input = "text \u001B[31mred\u001B[0m normal";
|
|
32
|
+
expect(stripAnsi(input)).toBe("text red normal");
|
|
33
33
|
});
|
|
34
|
-
it(
|
|
34
|
+
it("should strip prompt-style ANSI sequences", () => {
|
|
35
35
|
// Typical colored prompt
|
|
36
|
-
const input =
|
|
37
|
-
expect(stripAnsi(input)).toBe(
|
|
36
|
+
const input = "\u001B[01;32muser@host\u001B[00m:\u001B[01;34m~$\u001B[00m ";
|
|
37
|
+
expect(stripAnsi(input)).toBe("user@host:~$ ");
|
|
38
38
|
});
|
|
39
39
|
});
|
|
40
40
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ansi.test.js","sourceRoot":"","sources":["../../src/__tests__/ansi.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE;
|
|
1
|
+
{"version":3,"file":"ansi.test.js","sourceRoot":"","sources":["../../src/__tests__/ansi.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE;IAClB,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;QACvB,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;YAC1C,MAAM,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YACrD,MAAM,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAC/D,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;YAChC,MAAM,CAAC,SAAS,CAAC,wBAAwB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACxD,MAAM,CAAC,SAAS,CAAC,0BAA0B,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC5D,MAAM,CAAC,SAAS,CAAC,+BAA+B,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACxE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;YAC7C,MAAM,CAAC,SAAS,CAAC,0BAA0B,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC7D,MAAM,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC1D,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;YACjD,MAAM,KAAK,GAAG,mDAAmD,CAAC;YAClE,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;YAClC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;YACjD,MAAM,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACtD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;YAC5D,4EAA4E;YAC5E,qDAAqD;YACrD,MAAM,KAAK,GAAG,oCAAoC,CAAC;YACnD,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QACrD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;YAChD,yBAAyB;YACzB,MAAM,KAAK,GAAG,6DAA6D,CAAC;YAC5E,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from
|
|
2
|
-
import { isReadOnlyMode, isHostAllowed, isCommandDenied } from
|
|
3
|
-
describe(
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { isReadOnlyMode, isHostAllowed, isCommandDenied } from "../utils/security.js";
|
|
3
|
+
describe("security", () => {
|
|
4
4
|
const OLD_ENV = { ...process.env };
|
|
5
5
|
beforeEach(() => {
|
|
6
6
|
process.env = { ...OLD_ENV };
|
|
@@ -8,79 +8,79 @@ describe('security', () => {
|
|
|
8
8
|
afterEach(() => {
|
|
9
9
|
process.env = OLD_ENV;
|
|
10
10
|
});
|
|
11
|
-
describe(
|
|
12
|
-
it(
|
|
11
|
+
describe("isReadOnlyMode", () => {
|
|
12
|
+
it("should return false by default", () => {
|
|
13
13
|
delete process.env.MCP_SSH_READONLY;
|
|
14
14
|
expect(isReadOnlyMode()).toBe(false);
|
|
15
15
|
});
|
|
16
|
-
it(
|
|
17
|
-
process.env.MCP_SSH_READONLY =
|
|
16
|
+
it("should return true when MCP_SSH_READONLY=true", () => {
|
|
17
|
+
process.env.MCP_SSH_READONLY = "true";
|
|
18
18
|
expect(isReadOnlyMode()).toBe(true);
|
|
19
19
|
});
|
|
20
|
-
it(
|
|
21
|
-
process.env.MCP_SSH_READONLY =
|
|
20
|
+
it("should return false for any other value", () => {
|
|
21
|
+
process.env.MCP_SSH_READONLY = "false";
|
|
22
22
|
expect(isReadOnlyMode()).toBe(false);
|
|
23
|
-
process.env.MCP_SSH_READONLY =
|
|
23
|
+
process.env.MCP_SSH_READONLY = "1";
|
|
24
24
|
expect(isReadOnlyMode()).toBe(false);
|
|
25
25
|
});
|
|
26
26
|
});
|
|
27
|
-
describe(
|
|
28
|
-
it(
|
|
27
|
+
describe("isHostAllowed", () => {
|
|
28
|
+
it("should allow all hosts when no filter set", () => {
|
|
29
29
|
delete process.env.MCP_SSH_ALLOWED_HOSTS;
|
|
30
|
-
expect(isHostAllowed(
|
|
31
|
-
expect(isHostAllowed(
|
|
30
|
+
expect(isHostAllowed("prod")).toBe(true);
|
|
31
|
+
expect(isHostAllowed("any-host")).toBe(true);
|
|
32
32
|
});
|
|
33
|
-
it(
|
|
34
|
-
process.env.MCP_SSH_ALLOWED_HOSTS =
|
|
35
|
-
expect(isHostAllowed(
|
|
36
|
-
expect(isHostAllowed(
|
|
37
|
-
expect(isHostAllowed(
|
|
33
|
+
it("should allow only listed hosts", () => {
|
|
34
|
+
process.env.MCP_SSH_ALLOWED_HOSTS = "prod,staging";
|
|
35
|
+
expect(isHostAllowed("prod")).toBe(true);
|
|
36
|
+
expect(isHostAllowed("staging")).toBe(true);
|
|
37
|
+
expect(isHostAllowed("gpu")).toBe(false);
|
|
38
38
|
});
|
|
39
|
-
it(
|
|
40
|
-
process.env.MCP_SSH_ALLOWED_HOSTS =
|
|
41
|
-
expect(isHostAllowed(
|
|
42
|
-
expect(isHostAllowed(
|
|
39
|
+
it("should trim whitespace from host entries", () => {
|
|
40
|
+
process.env.MCP_SSH_ALLOWED_HOSTS = " prod , staging ";
|
|
41
|
+
expect(isHostAllowed("prod")).toBe(true);
|
|
42
|
+
expect(isHostAllowed("staging")).toBe(true);
|
|
43
43
|
});
|
|
44
|
-
it(
|
|
45
|
-
process.env.MCP_SSH_ALLOWED_HOSTS =
|
|
46
|
-
expect(isHostAllowed(
|
|
44
|
+
it("should handle empty list as allow all", () => {
|
|
45
|
+
process.env.MCP_SSH_ALLOWED_HOSTS = "";
|
|
46
|
+
expect(isHostAllowed("prod")).toBe(true);
|
|
47
47
|
});
|
|
48
|
-
it(
|
|
49
|
-
process.env.MCP_SSH_ALLOWED_HOSTS =
|
|
50
|
-
expect(isHostAllowed(
|
|
48
|
+
it("should handle comma-only list", () => {
|
|
49
|
+
process.env.MCP_SSH_ALLOWED_HOSTS = ",";
|
|
50
|
+
expect(isHostAllowed("prod")).toBe(true);
|
|
51
51
|
});
|
|
52
52
|
});
|
|
53
|
-
describe(
|
|
54
|
-
it(
|
|
53
|
+
describe("isCommandDenied", () => {
|
|
54
|
+
it("should allow all commands when no denylist", () => {
|
|
55
55
|
delete process.env.MCP_SSH_DENYLIST_COMMANDS;
|
|
56
|
-
expect(isCommandDenied(
|
|
57
|
-
expect(isCommandDenied(
|
|
56
|
+
expect(isCommandDenied("rm -rf /")).toBe(false);
|
|
57
|
+
expect(isCommandDenied("ls -la")).toBe(false);
|
|
58
58
|
});
|
|
59
|
-
it(
|
|
60
|
-
process.env.MCP_SSH_DENYLIST_COMMANDS =
|
|
61
|
-
expect(isCommandDenied(
|
|
62
|
-
expect(isCommandDenied(
|
|
63
|
-
expect(isCommandDenied(
|
|
59
|
+
it("should block denied command patterns", () => {
|
|
60
|
+
process.env.MCP_SSH_DENYLIST_COMMANDS = "rm -rf,shutdown,halt";
|
|
61
|
+
expect(isCommandDenied("rm -rf /")).toBe(true);
|
|
62
|
+
expect(isCommandDenied("shutdown now")).toBe(true);
|
|
63
|
+
expect(isCommandDenied("sudo halt")).toBe(true);
|
|
64
64
|
});
|
|
65
|
-
it(
|
|
66
|
-
process.env.MCP_SSH_DENYLIST_COMMANDS =
|
|
67
|
-
expect(isCommandDenied(
|
|
68
|
-
expect(isCommandDenied(
|
|
65
|
+
it("should be case-insensitive", () => {
|
|
66
|
+
process.env.MCP_SSH_DENYLIST_COMMANDS = "RM -RF,ShutDown";
|
|
67
|
+
expect(isCommandDenied("rm -rf /")).toBe(true);
|
|
68
|
+
expect(isCommandDenied("SHUTDOWN now")).toBe(true);
|
|
69
69
|
});
|
|
70
|
-
it(
|
|
71
|
-
process.env.MCP_SSH_DENYLIST_COMMANDS =
|
|
72
|
-
expect(isCommandDenied(
|
|
73
|
-
expect(isCommandDenied(
|
|
74
|
-
expect(isCommandDenied(
|
|
70
|
+
it("should allow safe commands", () => {
|
|
71
|
+
process.env.MCP_SSH_DENYLIST_COMMANDS = "rm -rf,shutdown";
|
|
72
|
+
expect(isCommandDenied("ls -la")).toBe(false);
|
|
73
|
+
expect(isCommandDenied("echo hello")).toBe(false);
|
|
74
|
+
expect(isCommandDenied("npm run build")).toBe(false);
|
|
75
75
|
});
|
|
76
|
-
it(
|
|
77
|
-
process.env.MCP_SSH_DENYLIST_COMMANDS =
|
|
78
|
-
expect(isCommandDenied(
|
|
76
|
+
it("should handle empty denylist", () => {
|
|
77
|
+
process.env.MCP_SSH_DENYLIST_COMMANDS = "";
|
|
78
|
+
expect(isCommandDenied("rm -rf /")).toBe(false);
|
|
79
79
|
});
|
|
80
|
-
it(
|
|
81
|
-
process.env.MCP_SSH_DENYLIST_COMMANDS =
|
|
82
|
-
expect(isCommandDenied(
|
|
83
|
-
expect(isCommandDenied(
|
|
80
|
+
it("should trim whitespace from entries", () => {
|
|
81
|
+
process.env.MCP_SSH_DENYLIST_COMMANDS = " rm -rf , shutdown ";
|
|
82
|
+
expect(isCommandDenied("rm -rf /")).toBe(true);
|
|
83
|
+
expect(isCommandDenied("shutdown -h now")).toBe(true);
|
|
84
84
|
});
|
|
85
85
|
});
|
|
86
86
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security.test.js","sourceRoot":"","sources":["../../src/__tests__/security.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEtF,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;
|
|
1
|
+
{"version":3,"file":"security.test.js","sourceRoot":"","sources":["../../src/__tests__/security.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEtF,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;IACtB,MAAM,OAAO,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAEnC,UAAU,CAAC,GAAG,EAAE;QACZ,OAAO,CAAC,GAAG,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACX,OAAO,CAAC,GAAG,GAAG,OAAO,CAAC;IAC1B,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;QAC5B,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;YACtC,OAAO,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;YACpC,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;YACrD,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,MAAM,CAAC;YACtC,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;YAC/C,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,OAAO,CAAC;YACvC,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAErC,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,GAAG,CAAC;YACnC,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;QAC3B,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;YACjD,OAAO,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC;YACzC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACzC,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;YACtC,OAAO,CAAC,GAAG,CAAC,qBAAqB,GAAG,cAAc,CAAC;YACnD,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACzC,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC5C,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;YAChD,OAAO,CAAC,GAAG,CAAC,qBAAqB,GAAG,kBAAkB,CAAC;YACvD,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACzC,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;YAC7C,OAAO,CAAC,GAAG,CAAC,qBAAqB,GAAG,EAAE,CAAC;YACvC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;YACrC,OAAO,CAAC,GAAG,CAAC,qBAAqB,GAAG,GAAG,CAAC;YACxC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;QAC7B,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;YAClD,OAAO,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC;YAC7C,MAAM,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAChD,MAAM,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;YAC5C,OAAO,CAAC,GAAG,CAAC,yBAAyB,GAAG,sBAAsB,CAAC;YAC/D,MAAM,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC/C,MAAM,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnD,MAAM,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;YAClC,OAAO,CAAC,GAAG,CAAC,yBAAyB,GAAG,iBAAiB,CAAC;YAC1D,MAAM,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC/C,MAAM,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;YAClC,OAAO,CAAC,GAAG,CAAC,yBAAyB,GAAG,iBAAiB,CAAC;YAC1D,MAAM,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC9C,MAAM,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAClD,MAAM,CAAC,eAAe,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;YACpC,OAAO,CAAC,GAAG,CAAC,yBAAyB,GAAG,EAAE,CAAC;YAC3C,MAAM,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;YAC3C,OAAO,CAAC,GAAG,CAAC,yBAAyB,GAAG,qBAAqB,CAAC;YAC9D,MAAM,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC/C,MAAM,CAAC,eAAe,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1D,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import { describe, it, expect } from
|
|
2
|
-
import { sanitizeTmuxSessionName } from
|
|
3
|
-
describe(
|
|
4
|
-
describe(
|
|
5
|
-
it(
|
|
6
|
-
expect(sanitizeTmuxSessionName(
|
|
7
|
-
expect(sanitizeTmuxSessionName(
|
|
8
|
-
expect(sanitizeTmuxSessionName(
|
|
9
|
-
expect(sanitizeTmuxSessionName(
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { sanitizeTmuxSessionName } from "../utils/validation.js";
|
|
3
|
+
describe("validation", () => {
|
|
4
|
+
describe("sanitizeTmuxSessionName", () => {
|
|
5
|
+
it("should preserve valid names unchanged", () => {
|
|
6
|
+
expect(sanitizeTmuxSessionName("prod")).toBe("prod");
|
|
7
|
+
expect(sanitizeTmuxSessionName("my_host")).toBe("my_host");
|
|
8
|
+
expect(sanitizeTmuxSessionName("server-01")).toBe("server-01");
|
|
9
|
+
expect(sanitizeTmuxSessionName("test.example")).toBe("test.example");
|
|
10
10
|
});
|
|
11
|
-
it(
|
|
12
|
-
expect(sanitizeTmuxSessionName(
|
|
13
|
-
expect(sanitizeTmuxSessionName(
|
|
14
|
-
expect(sanitizeTmuxSessionName(
|
|
11
|
+
it("should replace invalid characters with underscore", () => {
|
|
12
|
+
expect(sanitizeTmuxSessionName("hello world")).toBe("hello_world");
|
|
13
|
+
expect(sanitizeTmuxSessionName("host@domain")).toBe("host_domain");
|
|
14
|
+
expect(sanitizeTmuxSessionName("path/name")).toBe("path_name");
|
|
15
15
|
});
|
|
16
|
-
it(
|
|
17
|
-
expect(sanitizeTmuxSessionName(
|
|
18
|
-
expect(sanitizeTmuxSessionName(
|
|
19
|
-
expect(sanitizeTmuxSessionName(
|
|
16
|
+
it("should prefix non-alpha starters", () => {
|
|
17
|
+
expect(sanitizeTmuxSessionName("123abc")).toBe("mcp_123abc");
|
|
18
|
+
expect(sanitizeTmuxSessionName("-test")).toBe("mcp_-test");
|
|
19
|
+
expect(sanitizeTmuxSessionName(".hidden")).toBe("mcp_.hidden");
|
|
20
20
|
});
|
|
21
21
|
});
|
|
22
22
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.test.js","sourceRoot":"","sources":["../../src/__tests__/validation.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAEjE,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;
|
|
1
|
+
{"version":3,"file":"validation.test.js","sourceRoot":"","sources":["../../src/__tests__/validation.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAEjE,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IACxB,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACrC,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;YAC7C,MAAM,CAAC,uBAAuB,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACrD,MAAM,CAAC,uBAAuB,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC3D,MAAM,CAAC,uBAAuB,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAC/D,MAAM,CAAC,uBAAuB,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACzE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;YACzD,MAAM,CAAC,uBAAuB,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YACnE,MAAM,CAAC,uBAAuB,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YACnE,MAAM,CAAC,uBAAuB,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACnE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;YACxC,MAAM,CAAC,uBAAuB,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAC7D,MAAM,CAAC,uBAAuB,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAC3D,MAAM,CAAC,uBAAuB,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACnE,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HostSecurityManager.d.ts","sourceRoot":"","sources":["../../src/core/HostSecurityManager.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,iBAAiB;
|
|
1
|
+
{"version":3,"file":"HostSecurityManager.d.ts","sourceRoot":"","sources":["../../src/core/HostSecurityManager.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,iBAAiB;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IAC/B,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAAC;CACrC;AAKD,qBAAa,mBAAmB;IAC5B,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,UAAU,CAAS;gBAEf,SAAS,CAAC,EAAE,MAAM;IAK9B,OAAO,CAAC,IAAI;IAWZ,OAAO,CAAC,IAAI;IAUZ,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO;IAKlC,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,IAAI;IAMlD,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE;IAIxC,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI;IAMxD,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE;IAIvC,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI;IAMvD,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI;IAIrD,MAAM,IAAI,kBAAkB;IAI5B,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;CAIjC"}
|