typescript-virtual-container 1.2.9 → 1.3.1

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.
Files changed (281) hide show
  1. package/.vscode/settings.json +0 -1
  2. package/README.md +141 -50
  3. package/biome.json +7 -0
  4. package/dist/SSHMimic/exec.d.ts.map +1 -1
  5. package/dist/SSHMimic/executor.d.ts.map +1 -1
  6. package/dist/SSHMimic/executor.js +32 -16
  7. package/dist/SSHMimic/index.d.ts.map +1 -1
  8. package/dist/SSHMimic/index.js +20 -6
  9. package/dist/VirtualFileSystem/binaryPack.d.ts.map +1 -1
  10. package/dist/VirtualFileSystem/binaryPack.js +29 -6
  11. package/dist/VirtualFileSystem/index.d.ts.map +1 -1
  12. package/dist/VirtualFileSystem/index.js +36 -13
  13. package/dist/VirtualPackageManager/index.d.ts.map +1 -1
  14. package/dist/VirtualPackageManager/index.js +192 -43
  15. package/dist/VirtualShell/index.d.ts +10 -4
  16. package/dist/VirtualShell/index.d.ts.map +1 -1
  17. package/dist/VirtualShell/index.js +18 -7
  18. package/dist/VirtualShell/shell.d.ts.map +1 -1
  19. package/dist/VirtualShell/shell.js +3 -1
  20. package/dist/VirtualShell/shellParser.d.ts.map +1 -1
  21. package/dist/VirtualUserManager/index.d.ts.map +1 -1
  22. package/dist/commands/adduser.d.ts +6 -0
  23. package/dist/commands/adduser.d.ts.map +1 -1
  24. package/dist/commands/adduser.js +6 -0
  25. package/dist/commands/alias.d.ts +5 -0
  26. package/dist/commands/alias.d.ts.map +1 -1
  27. package/dist/commands/alias.js +5 -0
  28. package/dist/commands/apt.d.ts +5 -0
  29. package/dist/commands/apt.d.ts.map +1 -1
  30. package/dist/commands/apt.js +32 -9
  31. package/dist/commands/awk.d.ts +11 -0
  32. package/dist/commands/awk.d.ts.map +1 -1
  33. package/dist/commands/awk.js +15 -2
  34. package/dist/commands/base64.d.ts +5 -0
  35. package/dist/commands/base64.d.ts.map +1 -1
  36. package/dist/commands/base64.js +9 -1
  37. package/dist/commands/cat.d.ts +5 -0
  38. package/dist/commands/cat.d.ts.map +1 -1
  39. package/dist/commands/cat.js +10 -2
  40. package/dist/commands/cd.d.ts +5 -0
  41. package/dist/commands/cd.d.ts.map +1 -1
  42. package/dist/commands/cd.js +5 -0
  43. package/dist/commands/chmod.d.ts +5 -0
  44. package/dist/commands/chmod.d.ts.map +1 -1
  45. package/dist/commands/chmod.js +5 -0
  46. package/dist/commands/cp.d.ts +5 -0
  47. package/dist/commands/cp.d.ts.map +1 -1
  48. package/dist/commands/cp.js +5 -0
  49. package/dist/commands/curl.d.ts +5 -0
  50. package/dist/commands/curl.d.ts.map +1 -1
  51. package/dist/commands/curl.js +34 -6
  52. package/dist/commands/cut.d.ts +5 -0
  53. package/dist/commands/cut.d.ts.map +1 -1
  54. package/dist/commands/cut.js +8 -1
  55. package/dist/commands/date.d.ts +5 -0
  56. package/dist/commands/date.d.ts.map +1 -1
  57. package/dist/commands/date.js +7 -1
  58. package/dist/commands/declare.d.ts +3 -0
  59. package/dist/commands/declare.d.ts.map +1 -0
  60. package/dist/commands/declare.js +39 -0
  61. package/dist/commands/diff.d.ts +5 -0
  62. package/dist/commands/diff.d.ts.map +1 -1
  63. package/dist/commands/diff.js +5 -0
  64. package/dist/commands/dpkg.d.ts +5 -0
  65. package/dist/commands/dpkg.d.ts.map +1 -1
  66. package/dist/commands/dpkg.js +24 -7
  67. package/dist/commands/du.d.ts.map +1 -1
  68. package/dist/commands/du.js +8 -2
  69. package/dist/commands/echo.d.ts +5 -0
  70. package/dist/commands/echo.d.ts.map +1 -1
  71. package/dist/commands/echo.js +13 -4
  72. package/dist/commands/env.d.ts +5 -0
  73. package/dist/commands/env.d.ts.map +1 -1
  74. package/dist/commands/env.js +11 -1
  75. package/dist/commands/exit.d.ts +5 -0
  76. package/dist/commands/exit.d.ts.map +1 -1
  77. package/dist/commands/exit.js +12 -2
  78. package/dist/commands/export.d.ts.map +1 -1
  79. package/dist/commands/export.js +3 -1
  80. package/dist/commands/find.d.ts +5 -0
  81. package/dist/commands/find.d.ts.map +1 -1
  82. package/dist/commands/find.js +5 -0
  83. package/dist/commands/free.d.ts +5 -0
  84. package/dist/commands/free.d.ts.map +1 -1
  85. package/dist/commands/free.js +5 -0
  86. package/dist/commands/grep.d.ts +5 -0
  87. package/dist/commands/grep.d.ts.map +1 -1
  88. package/dist/commands/grep.js +12 -2
  89. package/dist/commands/gzip.d.ts +5 -0
  90. package/dist/commands/gzip.d.ts.map +1 -1
  91. package/dist/commands/gzip.js +18 -2
  92. package/dist/commands/head.d.ts +5 -0
  93. package/dist/commands/head.d.ts.map +1 -1
  94. package/dist/commands/head.js +5 -0
  95. package/dist/commands/help.d.ts.map +1 -1
  96. package/dist/commands/help.js +98 -45
  97. package/dist/commands/history.d.ts +5 -0
  98. package/dist/commands/history.d.ts.map +1 -1
  99. package/dist/commands/history.js +5 -0
  100. package/dist/commands/hostname.d.ts +5 -0
  101. package/dist/commands/hostname.d.ts.map +1 -1
  102. package/dist/commands/hostname.js +5 -0
  103. package/dist/commands/id.d.ts.map +1 -1
  104. package/dist/commands/id.js +4 -1
  105. package/dist/commands/index.d.ts +2 -17
  106. package/dist/commands/index.d.ts.map +1 -1
  107. package/dist/commands/index.js +2 -340
  108. package/dist/commands/ls.d.ts.map +1 -1
  109. package/dist/commands/ls.js +3 -1
  110. package/dist/commands/lsb-release.d.ts.map +1 -1
  111. package/dist/commands/lsb-release.js +8 -2
  112. package/dist/commands/nano.js +1 -1
  113. package/dist/commands/neofetch.js +1 -1
  114. package/dist/commands/node.d.ts +9 -0
  115. package/dist/commands/node.d.ts.map +1 -0
  116. package/dist/commands/node.js +316 -0
  117. package/dist/commands/npm.d.ts +19 -0
  118. package/dist/commands/npm.d.ts.map +1 -0
  119. package/dist/commands/npm.js +109 -0
  120. package/dist/commands/ping.d.ts.map +1 -1
  121. package/dist/commands/ping.js +3 -1
  122. package/dist/commands/printf.d.ts +3 -0
  123. package/dist/commands/printf.d.ts.map +1 -0
  124. package/dist/commands/printf.js +113 -0
  125. package/dist/commands/ps.d.ts.map +1 -1
  126. package/dist/commands/ps.js +4 -1
  127. package/dist/commands/python.d.ts +30 -0
  128. package/dist/commands/python.d.ts.map +1 -0
  129. package/dist/commands/python.js +2058 -0
  130. package/dist/commands/read.d.ts +3 -0
  131. package/dist/commands/read.d.ts.map +1 -0
  132. package/dist/commands/read.js +34 -0
  133. package/dist/commands/registry.d.ts +8 -0
  134. package/dist/commands/registry.d.ts.map +1 -0
  135. package/dist/commands/registry.js +229 -0
  136. package/dist/commands/runtime.d.ts +6 -0
  137. package/dist/commands/runtime.d.ts.map +1 -0
  138. package/dist/commands/runtime.js +280 -0
  139. package/dist/commands/sed.d.ts.map +1 -1
  140. package/dist/commands/sed.js +11 -3
  141. package/dist/commands/set.d.ts.map +1 -1
  142. package/dist/commands/set.js +9 -3
  143. package/dist/commands/sh.d.ts.map +1 -1
  144. package/dist/commands/sh.js +57 -36
  145. package/dist/commands/shift.d.ts +5 -0
  146. package/dist/commands/shift.d.ts.map +1 -0
  147. package/dist/commands/shift.js +52 -0
  148. package/dist/commands/sleep.d.ts.map +1 -1
  149. package/dist/commands/sort.d.ts.map +1 -1
  150. package/dist/commands/sort.js +4 -2
  151. package/dist/commands/source.d.ts.map +1 -1
  152. package/dist/commands/source.js +5 -2
  153. package/dist/commands/sudo.js +1 -1
  154. package/dist/commands/tar.d.ts.map +1 -1
  155. package/dist/commands/tar.js +11 -3
  156. package/dist/commands/tee.d.ts.map +1 -1
  157. package/dist/commands/tee.js +8 -6
  158. package/dist/commands/test.d.ts.map +1 -1
  159. package/dist/commands/test.js +46 -24
  160. package/dist/commands/tr.d.ts.map +1 -1
  161. package/dist/commands/tr.js +3 -1
  162. package/dist/commands/true.d.ts +4 -0
  163. package/dist/commands/true.d.ts.map +1 -0
  164. package/dist/commands/true.js +14 -0
  165. package/dist/commands/type.d.ts.map +1 -1
  166. package/dist/commands/type.js +1 -1
  167. package/dist/commands/uname.d.ts.map +1 -1
  168. package/dist/commands/uname.js +4 -1
  169. package/dist/commands/uniq.d.ts.map +1 -1
  170. package/dist/commands/uptime.d.ts.map +1 -1
  171. package/dist/commands/uptime.js +4 -1
  172. package/dist/commands/wget.d.ts.map +1 -1
  173. package/dist/commands/wget.js +32 -7
  174. package/dist/commands/which.d.ts.map +1 -1
  175. package/dist/commands/xargs.d.ts.map +1 -1
  176. package/dist/commands/xargs.js +1 -1
  177. package/dist/index.d.ts +15 -14
  178. package/dist/index.d.ts.map +1 -1
  179. package/dist/index.js +9 -9
  180. package/dist/modules/linuxRootfs.d.ts +18 -1
  181. package/dist/modules/linuxRootfs.d.ts.map +1 -1
  182. package/dist/modules/linuxRootfs.js +160 -17
  183. package/dist/standalone-wo-sftp.d.ts +2 -0
  184. package/dist/standalone-wo-sftp.d.ts.map +1 -0
  185. package/dist/standalone-wo-sftp.js +30 -0
  186. package/dist/utils/expand.d.ts +50 -0
  187. package/dist/utils/expand.d.ts.map +1 -0
  188. package/dist/utils/expand.js +183 -0
  189. package/dist/utils/vfsDiff.d.ts +90 -0
  190. package/dist/utils/vfsDiff.d.ts.map +1 -0
  191. package/dist/utils/vfsDiff.js +177 -0
  192. package/package.json +2 -1
  193. package/src/SSHMimic/exec.ts +10 -1
  194. package/src/SSHMimic/executor.ts +104 -18
  195. package/src/SSHMimic/index.ts +49 -15
  196. package/src/VirtualFileSystem/binaryPack.ts +35 -8
  197. package/src/VirtualFileSystem/index.ts +78 -28
  198. package/src/VirtualPackageManager/index.ts +208 -49
  199. package/src/VirtualShell/index.ts +35 -7
  200. package/src/VirtualShell/shell.ts +23 -3
  201. package/src/VirtualShell/shellParser.ts +134 -36
  202. package/src/VirtualUserManager/index.ts +7 -2
  203. package/src/commands/adduser.ts +6 -0
  204. package/src/commands/alias.ts +5 -1
  205. package/src/commands/apt.ts +47 -17
  206. package/src/commands/awk.ts +20 -6
  207. package/src/commands/base64.ts +13 -2
  208. package/src/commands/cat.ts +13 -5
  209. package/src/commands/cd.ts +5 -0
  210. package/src/commands/chmod.ts +5 -0
  211. package/src/commands/cp.ts +5 -0
  212. package/src/commands/curl.ts +56 -12
  213. package/src/commands/cut.ts +8 -1
  214. package/src/commands/date.ts +7 -1
  215. package/src/commands/declare.ts +44 -0
  216. package/src/commands/diff.ts +17 -3
  217. package/src/commands/dpkg.ts +33 -11
  218. package/src/commands/du.ts +17 -5
  219. package/src/commands/echo.ts +22 -9
  220. package/src/commands/env.ts +11 -1
  221. package/src/commands/exit.ts +12 -2
  222. package/src/commands/export.ts +3 -1
  223. package/src/commands/find.ts +5 -0
  224. package/src/commands/free.ts +9 -2
  225. package/src/commands/grep.ts +12 -2
  226. package/src/commands/gzip.ts +28 -4
  227. package/src/commands/head.ts +5 -0
  228. package/src/commands/help.ts +121 -47
  229. package/src/commands/history.ts +7 -2
  230. package/src/commands/hostname.ts +5 -0
  231. package/src/commands/id.ts +4 -1
  232. package/src/commands/index.ts +9 -360
  233. package/src/commands/ls.ts +5 -3
  234. package/src/commands/lsb-release.ts +8 -2
  235. package/src/commands/nano.ts +1 -1
  236. package/src/commands/neofetch.ts +1 -1
  237. package/src/commands/node.ts +341 -0
  238. package/src/commands/npm.ts +132 -0
  239. package/src/commands/ping.ts +6 -2
  240. package/src/commands/printf.ts +112 -0
  241. package/src/commands/ps.ts +21 -9
  242. package/src/commands/python.ts +2229 -0
  243. package/src/commands/read.ts +41 -0
  244. package/src/commands/registry.ts +244 -0
  245. package/src/commands/runtime.ts +353 -0
  246. package/src/commands/sed.ts +27 -9
  247. package/src/commands/set.ts +9 -3
  248. package/src/commands/sh.ts +159 -55
  249. package/src/commands/shift.ts +53 -0
  250. package/src/commands/sleep.ts +2 -1
  251. package/src/commands/sort.ts +10 -6
  252. package/src/commands/source.ts +15 -3
  253. package/src/commands/sudo.ts +1 -1
  254. package/src/commands/tar.ts +28 -7
  255. package/src/commands/tee.ts +7 -1
  256. package/src/commands/test.ts +61 -26
  257. package/src/commands/tr.ts +3 -1
  258. package/src/commands/true.ts +17 -0
  259. package/src/commands/type.ts +6 -3
  260. package/src/commands/uname.ts +5 -1
  261. package/src/commands/uniq.ts +8 -2
  262. package/src/commands/uptime.ts +4 -1
  263. package/src/commands/wget.ts +51 -12
  264. package/src/commands/which.ts +5 -2
  265. package/src/commands/xargs.ts +11 -2
  266. package/src/index.ts +23 -24
  267. package/src/modules/linuxRootfs.ts +233 -30
  268. package/src/standalone-wo-sftp.ts +38 -0
  269. package/src/utils/expand.ts +238 -0
  270. package/src/utils/vfsDiff.ts +275 -0
  271. package/standalone-wo-sftp.js +507 -0
  272. package/standalone-wo-sftp.js.map +7 -0
  273. package/standalone.js +253 -191
  274. package/standalone.js.map +4 -4
  275. package/tests/bun-test-shim.ts +9 -1
  276. package/tests/command-helpers.test.ts +1 -5
  277. package/tests/new-features.test.ts +415 -5
  278. package/tests/parser-executor.test.ts +27 -27
  279. package/tests/sftp.test.ts +122 -42
  280. package/tests/users.test.ts +23 -5
  281. package/CHANGELOG.md +0 -150
@@ -8,12 +8,18 @@ import { VirtualUserManager } from "../src/VirtualUserManager";
8
8
 
9
9
  // VirtualFileSystem is now pure in-memory — no temp dir or cleanup needed.
10
10
 
11
- function connectSftp(port: number): Promise<{ client: Client; sftp: SFTPWrapper }> {
11
+ function connectSftp(
12
+ port: number,
13
+ ): Promise<{ client: Client; sftp: SFTPWrapper }> {
12
14
  return new Promise((resolve, reject) => {
13
15
  const client = new Client();
14
16
  client.on("ready", () => {
15
17
  client.sftp((err, sftp) => {
16
- if (err) { client.end(); reject(err); return; }
18
+ if (err) {
19
+ client.end();
20
+ reject(err);
21
+ return;
22
+ }
17
23
  resolve({ client, sftp });
18
24
  });
19
25
  });
@@ -22,7 +28,7 @@ function connectSftp(port: number): Promise<{ client: Client; sftp: SFTPWrapper
22
28
  host: "127.0.0.1",
23
29
  port,
24
30
  username: "root",
25
- password: "", // root has no password — any value or empty is accepted
31
+ password: "", // root has no password — any value or empty is accepted
26
32
  hostVerifier: () => true,
27
33
  });
28
34
  });
@@ -37,7 +43,11 @@ function connectSftpWithUser(
37
43
  const client = new Client();
38
44
  client.on("ready", () => {
39
45
  client.sftp((err, sftp) => {
40
- if (err) { client.end(); reject(err); return; }
46
+ if (err) {
47
+ client.end();
48
+ reject(err);
49
+ return;
50
+ }
41
51
  resolve({ client, sftp });
42
52
  });
43
53
  });
@@ -54,7 +64,7 @@ function connectSftpWithUser(
54
64
 
55
65
  describe("SftpMimic", () => {
56
66
  test("authenticates with VirtualUserManager and serves files from the VirtualFileSystem", async () => {
57
- const vfs = new VirtualFileSystem();
67
+ const vfs = new VirtualFileSystem();
58
68
  const users = new VirtualUserManager(vfs);
59
69
 
60
70
  await users.initialize();
@@ -65,26 +75,46 @@ describe("SftpMimic", () => {
65
75
  }
66
76
  vfs.writeFile(`${rootPath}/TEST.txt`, "hello world");
67
77
 
68
- const server = new SftpMimic({ port: 0, hostname: "test-sftp", vfs, users });
78
+ const server = new SftpMimic({
79
+ port: 0,
80
+ hostname: "test-sftp",
81
+ vfs,
82
+ users,
83
+ });
69
84
  const port = await server.start();
70
85
 
71
86
  try {
72
87
  const { client, sftp } = await connectSftp(port);
73
88
 
74
- const list = await new Promise<FileEntryWithStats[]>((resolve, reject) => {
75
- sftp.readdir("/home/root", (err?: Error | null, list?: FileEntryWithStats[]) => {
76
- if (err) { reject(err); return; }
77
- resolve(list || []);
78
- });
79
- });
89
+ const list = await new Promise<FileEntryWithStats[]>(
90
+ (resolve, reject) => {
91
+ sftp.readdir(
92
+ "/home/root",
93
+ (err?: Error | null, list?: FileEntryWithStats[]) => {
94
+ if (err) {
95
+ reject(err);
96
+ return;
97
+ }
98
+ resolve(list || []);
99
+ },
100
+ );
101
+ },
102
+ );
80
103
 
81
104
  expect(list.map((entry) => entry.filename)).toContain("TEST.txt");
82
105
 
83
106
  const content = await new Promise<string>((resolve, reject) => {
84
- sftp.readFile("/home/root/TEST.txt", "utf8", (err?: Error | null, data?: Buffer) => {
85
- if (err) { reject(err); return; }
86
- resolve((data || Buffer.alloc(0)).toString("utf8"));
87
- });
107
+ sftp.readFile(
108
+ "/home/root/TEST.txt",
109
+ "utf8",
110
+ (err?: Error | null, data?: Buffer) => {
111
+ if (err) {
112
+ reject(err);
113
+ return;
114
+ }
115
+ resolve((data || Buffer.alloc(0)).toString("utf8"));
116
+ },
117
+ );
88
118
  });
89
119
 
90
120
  expect(content).toBe("hello world");
@@ -95,7 +125,7 @@ describe("SftpMimic", () => {
95
125
  });
96
126
 
97
127
  test("blocks path traversal attempts outside home directory", async () => {
98
- const vfs = new VirtualFileSystem();
128
+ const vfs = new VirtualFileSystem();
99
129
  const users = new VirtualUserManager(vfs);
100
130
 
101
131
  await users.initialize();
@@ -105,7 +135,12 @@ describe("SftpMimic", () => {
105
135
  vfs.mkdir(rootPath, 0o755);
106
136
  }
107
137
 
108
- const server = new SftpMimic({ port: 0, hostname: "test-sftp", vfs, users });
138
+ const server = new SftpMimic({
139
+ port: 0,
140
+ hostname: "test-sftp",
141
+ vfs,
142
+ users,
143
+ });
109
144
  const port = await server.start();
110
145
 
111
146
  try {
@@ -113,24 +148,36 @@ describe("SftpMimic", () => {
113
148
 
114
149
  // /etc/passwd is outside /home/root — should be rejected
115
150
  const traversalAttempt = await new Promise<Error | null>((resolve) => {
116
- sftp.stat("/etc/passwd", (err?: Error | null) => { resolve(err ?? null); });
151
+ sftp.stat("/etc/passwd", (err?: Error | null) => {
152
+ resolve(err ?? null);
153
+ });
117
154
  });
118
155
 
119
156
  expect(traversalAttempt).not.toBeNull();
120
157
  expect(traversalAttempt?.message).toContain("Permission denied");
121
158
 
122
159
  // /home/root itself should work
123
- const homeAccess = await new Promise<FileEntryWithStats[]>((resolve, reject) => {
124
- sftp.readdir("/home/root", (err?: Error | null, list?: FileEntryWithStats[]) => {
125
- if (err) { reject(err); return; }
126
- resolve(list || []);
127
- });
128
- });
160
+ const homeAccess = await new Promise<FileEntryWithStats[]>(
161
+ (resolve, reject) => {
162
+ sftp.readdir(
163
+ "/home/root",
164
+ (err?: Error | null, list?: FileEntryWithStats[]) => {
165
+ if (err) {
166
+ reject(err);
167
+ return;
168
+ }
169
+ resolve(list || []);
170
+ },
171
+ );
172
+ },
173
+ );
129
174
  expect(homeAccess).toBeDefined();
130
175
 
131
176
  // Path traversal via ../.. should also be rejected
132
177
  const upTraversalAttempt = await new Promise<Error | null>((resolve) => {
133
- sftp.readdir("/home/root/../../etc", (err?: Error | null) => { resolve(err ?? null); });
178
+ sftp.readdir("/home/root/../../etc", (err?: Error | null) => {
179
+ resolve(err ?? null);
180
+ });
134
181
  });
135
182
  expect(upTraversalAttempt).not.toBeNull();
136
183
 
@@ -141,7 +188,7 @@ describe("SftpMimic", () => {
141
188
  });
142
189
 
143
190
  test("allows a user with a password to authenticate", async () => {
144
- const vfs = new VirtualFileSystem();
191
+ const vfs = new VirtualFileSystem();
145
192
  const users = new VirtualUserManager(vfs);
146
193
 
147
194
  await users.initialize();
@@ -153,18 +200,35 @@ describe("SftpMimic", () => {
153
200
  }
154
201
  vfs.writeFile("/home/alice/hello.txt", "hi alice");
155
202
 
156
- const server = new SftpMimic({ port: 0, hostname: "test-sftp", vfs, users });
203
+ const server = new SftpMimic({
204
+ port: 0,
205
+ hostname: "test-sftp",
206
+ vfs,
207
+ users,
208
+ });
157
209
  const port = await server.start();
158
210
 
159
211
  try {
160
- const { client, sftp } = await connectSftpWithUser(port, "alice", "alice-pass");
161
-
162
- const list = await new Promise<FileEntryWithStats[]>((resolve, reject) => {
163
- sftp.readdir("/home/alice", (err?: Error | null, list?: FileEntryWithStats[]) => {
164
- if (err) { reject(err); return; }
165
- resolve(list || []);
166
- });
167
- });
212
+ const { client, sftp } = await connectSftpWithUser(
213
+ port,
214
+ "alice",
215
+ "alice-pass",
216
+ );
217
+
218
+ const list = await new Promise<FileEntryWithStats[]>(
219
+ (resolve, reject) => {
220
+ sftp.readdir(
221
+ "/home/alice",
222
+ (err?: Error | null, list?: FileEntryWithStats[]) => {
223
+ if (err) {
224
+ reject(err);
225
+ return;
226
+ }
227
+ resolve(list || []);
228
+ },
229
+ );
230
+ },
231
+ );
168
232
 
169
233
  expect(list.map((e) => e.filename)).toContain("hello.txt");
170
234
 
@@ -175,13 +239,18 @@ describe("SftpMimic", () => {
175
239
  });
176
240
 
177
241
  test("rejects a user with a wrong password", async () => {
178
- const vfs = new VirtualFileSystem();
242
+ const vfs = new VirtualFileSystem();
179
243
  const users = new VirtualUserManager(vfs);
180
244
 
181
245
  await users.initialize();
182
246
  await users.addUser("bob", "correct-pass");
183
247
 
184
- const server = new SftpMimic({ port: 0, hostname: "test-sftp", vfs, users });
248
+ const server = new SftpMimic({
249
+ port: 0,
250
+ hostname: "test-sftp",
251
+ vfs,
252
+ users,
253
+ });
185
254
  const port = await server.start();
186
255
 
187
256
  try {
@@ -193,7 +262,7 @@ describe("SftpMimic", () => {
193
262
  });
194
263
 
195
264
  test("allows writing and reading back a file over SFTP", async () => {
196
- const vfs = new VirtualFileSystem();
265
+ const vfs = new VirtualFileSystem();
197
266
  const users = new VirtualUserManager(vfs);
198
267
 
199
268
  await users.initialize();
@@ -202,7 +271,12 @@ describe("SftpMimic", () => {
202
271
  vfs.mkdir("/home/root", 0o755);
203
272
  }
204
273
 
205
- const server = new SftpMimic({ port: 0, hostname: "test-sftp", vfs, users });
274
+ const server = new SftpMimic({
275
+ port: 0,
276
+ hostname: "test-sftp",
277
+ vfs,
278
+ users,
279
+ });
206
280
  const port = await server.start();
207
281
 
208
282
  try {
@@ -213,7 +287,10 @@ describe("SftpMimic", () => {
213
287
  "/home/root/written.txt",
214
288
  Buffer.from("written via sftp"),
215
289
  (err?: Error | null) => {
216
- if (err) { reject(err); return; }
290
+ if (err) {
291
+ reject(err);
292
+ return;
293
+ }
217
294
  resolve();
218
295
  },
219
296
  );
@@ -224,7 +301,10 @@ describe("SftpMimic", () => {
224
301
  "/home/root/written.txt",
225
302
  "utf8",
226
303
  (err?: Error | null, data?: Buffer) => {
227
- if (err) { reject(err); return; }
304
+ if (err) {
305
+ reject(err);
306
+ return;
307
+ }
228
308
  resolve((data || Buffer.alloc(0)).toString("utf8"));
229
309
  },
230
310
  );
@@ -2,7 +2,9 @@ import { describe, expect, test } from "bun:test";
2
2
  import VirtualFileSystem from "../src/VirtualFileSystem";
3
3
  import { VirtualUserManager } from "../src/VirtualUserManager";
4
4
 
5
- function makeVfs() { return new VirtualFileSystem(); }
5
+ function makeVfs() {
6
+ return new VirtualFileSystem();
7
+ }
6
8
 
7
9
  describe("VirtualUserManager auto sudo", () => {
8
10
  test("adds new users to sudoers by default", async () => {
@@ -40,9 +42,17 @@ describe("VirtualUserManager quotas", () => {
40
42
  await users.addUser("alice", "alice-pass");
41
43
  const startingUsage = users.getUsageBytes("alice");
42
44
  await users.setQuotaBytes("alice", startingUsage + 5);
43
- expect(() => { users.assertWriteWithinQuota("alice", "/home/alice/note.txt", "hello"); }).not.toThrow();
45
+ expect(() => {
46
+ users.assertWriteWithinQuota("alice", "/home/alice/note.txt", "hello");
47
+ }).not.toThrow();
44
48
  vfs.writeFile("/home/alice/note.txt", "hello");
45
- expect(() => { users.assertWriteWithinQuota("alice", "/home/alice/note.txt", "this exceeds the configured quota"); }).toThrow("quota exceeded for 'alice'");
49
+ expect(() => {
50
+ users.assertWriteWithinQuota(
51
+ "alice",
52
+ "/home/alice/note.txt",
53
+ "this exceeds the configured quota",
54
+ );
55
+ }).toThrow("quota exceeded for 'alice'");
46
56
  });
47
57
 
48
58
  test("does not enforce home quota outside user home", async () => {
@@ -51,7 +61,9 @@ describe("VirtualUserManager quotas", () => {
51
61
  await users.initialize();
52
62
  await users.addUser("bob", "bob-pass");
53
63
  await users.setQuotaBytes("bob", 1);
54
- expect(() => { users.assertWriteWithinQuota("bob", "/tmp/shared.txt", "large-content"); }).not.toThrow();
64
+ expect(() => {
65
+ users.assertWriteWithinQuota("bob", "/tmp/shared.txt", "large-content");
66
+ }).not.toThrow();
55
67
  });
56
68
 
57
69
  test("clearQuota removes enforced limit", async () => {
@@ -63,6 +75,12 @@ describe("VirtualUserManager quotas", () => {
63
75
  expect(users.getQuotaBytes("charlie")).toBe(2);
64
76
  await users.clearQuota("charlie");
65
77
  expect(users.getQuotaBytes("charlie")).toBeNull();
66
- expect(() => { users.assertWriteWithinQuota("charlie", "/home/charlie/file.txt", "long-content"); }).not.toThrow();
78
+ expect(() => {
79
+ users.assertWriteWithinQuota(
80
+ "charlie",
81
+ "/home/charlie/file.txt",
82
+ "long-content",
83
+ );
84
+ }).not.toThrow();
67
85
  });
68
86
  });
package/CHANGELOG.md DELETED
@@ -1,150 +0,0 @@
1
- # Changelog
2
-
3
- All notable changes to this project are documented in this file.
4
-
5
- The format is based on Keep a Changelog.
6
-
7
- ## [Unreleased]
8
-
9
- ## [1.1.5] - 2026-04-16
10
-
11
- ### Added
12
-
13
- - `HoneyPot` auditing utility for tracking and auditing SSH/SFTP activity across virtual shell, filesystem, user manager, and server components.
14
- - HoneyPot attaches event listeners to `VirtualShell`, `VirtualFileSystem`, `VirtualUserManager`, `SshMimic`, and `SftpMimic` for holistic activity capture.
15
- - HoneyPot event tracking, statistics, anomaly detection, and audit log export support.
16
- - Comprehensive HoneyPot documentation in `README.md` and dedicated example files.
17
- - Added `examples/honeypot-quickstart.ts`, `examples/honeypot-audit.ts`, `examples/honeypot-export.ts`, and `examples/README.md`.
18
- - Added `HONEYPOT.md` implementation guide for HoneyPot usage patterns and integrations.
19
-
20
- ## [1.1.4] - 2026-04-16
21
-
22
- ### Added
23
-
24
- - New SFTP server implementation (`SftpMimic`), exported as `VirtualSftpServer`.
25
- - SFTP handlers for file/directory operations: `OPEN`, `READ`, `WRITE`, `FSTAT`, `CLOSE`, `OPENDIR`, `READDIR`, `STAT`, `LSTAT`, `SETSTAT`, `FSETSTAT`, `REALPATH`, `MKDIR`, `RMDIR`, `REMOVE`, `RENAME`.
26
- - Home-directory confinement for SFTP sessions (`/home/<user>`) with path traversal blocking.
27
- - Keyboard-interactive authentication support in SFTP in addition to password auth.
28
- - Standalone runtime now starts both SSH and SFTP servers with a shared `VirtualShell`.
29
- - New SFTP test suite covering authentication, read/write flows, system-user login, and traversal protection.
30
- - **Event-Driven Integration**: Core classes now extend `EventEmitter` for full lifecycle visibility:
31
- - `VirtualShell`: `initialized`, `command`, `session:start` events
32
- - `VirtualFileSystem`: `file:read`, `file:write`, `dir:create`, `mirror:flush` events
33
- - `VirtualUserManager`: `initialized`, `user:add`, `user:delete`, `session:register`, `session:unregister` events
34
- - `SshMimic` & `SftpMimic`: `start`, `stop`, `auth:success`, `auth:failure`, `client:connect`, `client:disconnect` events
35
- - New `HoneyPot` utility for tracking and auditing server activity via event listeners.
36
-
37
- ### Changed
38
-
39
- - `VirtualFileSystem` mirror strategy now uses `.vfs/mirror` directory storage as the persistence boundary.
40
- - SSH and SFTP startup now explicitly wait for full shell/user initialization before accepting connections.
41
- - SFTP `start()` returns the actual bound port (including when configured with port `0`).
42
- - Relative SFTP request paths are resolved from the authenticated user home.
43
- - `VirtualUserManager.initialize()` auto-creates the current system user (`$USER` / `$USERNAME`) for easier local auth flows.
44
-
45
- ### Fixed
46
-
47
- - Added stronger error handling/logging for SFTP client/session/stream lifecycle events.
48
- - Fixed flaky SFTP tests by isolating each test run with temporary VFS base paths and deterministic cleanup.
49
- - Fixed `ssh-exec` test timeout by resolving the mocked exec stream completion correctly.
50
-
51
- ## [1.0.6...1.0.8] - 2026-04-15
52
-
53
- Too much refactor to list.
54
-
55
- ## [1.0.5] - 2026-04-15
56
-
57
- ### Changed
58
-
59
- - Refactored commands to use shared argument/flag parsing helpers.
60
- - Improved maintainability and consistency of argument parsing across commands.
61
-
62
- ### Fixed
63
-
64
- - Verified all refactored commands pass existing test cases without regressions.
65
-
66
- ## [1.0.4] - 2026-04-15
67
-
68
- ### Added
69
-
70
- - Shell pipeline parser and executor with support for:
71
- - Pipes (`|`)
72
- - Input redirection (`<`)
73
- - Output redirection (`>`)
74
- - Append redirection (`>>`)
75
- - New built-in commands:
76
- - `echo`
77
- - `grep`
78
- - `set`
79
- - `env`
80
- - `export`
81
- - `unset`
82
- - `sh` (with `bash` alias)
83
- - Command stdin support in runtime context so commands can consume piped input.
84
-
85
- ### Changed
86
-
87
- - Argument parsing now respects quoted strings, including for commands like `sh -c "echo hi"`.
88
- - `echo` now expands environment variables (`$VAR`) and can read from stdin when no explicit text argument is provided.
89
- - `grep` now supports stdin input (e.g. `ls | grep ".txt"`) in addition to file operands.
90
-
91
- ### Fixed
92
-
93
- - Relative file paths in redirections are now resolved from current working directory during pipeline execution.
94
- - Example fixed behavior: `echo hi > cat.txt` writes to `./cat.txt` in current virtual directory.
95
- - Pipeline chaining now correctly passes command stdout as stdin to next command.
96
-
97
- ## [1.0.2] - 2026-04-14
98
-
99
- ### Added
100
-
101
- - Governance and community files:
102
- - LICENSE
103
- - CONTRIBUTING.md
104
- - SECURITY.md
105
- - CODE_OF_CONDUCT.md
106
- - GitHub issue and PR templates
107
- - Security hardening for virtual auth storage and shell access:
108
- - Non-root commands now block access to `/virtual-env-js/.auth/**`.
109
- - Auth files are persisted with restrictive modes (`0700` for `.auth`, `0600` for `htpasswd` and `sudoers`).
110
- - Root password no longer falls back to a fixed default when `SSH_MIMIC_ROOT_PASSWORD` is unset; startup generates an ephemeral password instead.
111
- - New environment toggle `SSH_MIMIC_AUTO_SUDO_NEW_USERS` to control whether newly created users are added to sudoers by default.
112
- - README and security docs now describe the new auth hardening and configuration flags.
113
- - Added tests covering `.auth` path protection and auto-sudo behavior.
114
-
115
- ## [1.0.1] - 2026-04-14
116
-
117
- ### Added
118
-
119
- - `ls -l` / `ls --long` support with long listing format (permissions, size, updated time).
120
- - Host-command mirroring for network tools:
121
- - `curl` now runs through host `curl` via `child_process`.
122
- - `wget` now runs through host `wget` via `child_process`.
123
- - Temporary host download flow for `wget` using `/tmp` before import into VFS.
124
- - Terminal line normalization utility for command help and diagnostics rendering.
125
-
126
- ### Changed
127
-
128
- - `curl` behavior is now aligned with the host binary output and exit codes.
129
- - `curl -o` writes host command output to the virtual filesystem target path.
130
- - `wget` writes downloaded payloads to VFS after host-side transfer, preserving command semantics.
131
- - URL fetch helper now accepts host-only inputs by normalizing missing protocol to `http://`.
132
- - Auto pull-request GitHub workflow now targets any non-`main` branch instead of only `dev`.
133
- - Auto PR metadata now uses the dynamic source branch name in PR head/title/body.
134
- - Test workflow trigger scope was generalized by removing hardcoded branch filters.
135
-
136
- ### Fixed
137
-
138
- - Resolved large horizontal spacing artifacts in SSH terminal output by normalizing TTY line endings (`\r\n`) in both interactive shell and exec paths.
139
- - Reduced excessive whitespace in help output rendering (`curl --help`, `wget --help`) by normalizing tabs and over-padded spacing.
140
-
141
- ## [1.0.0] - 2026-04-14
142
-
143
- ### Added
144
-
145
- - In-memory SSH server with password authentication.
146
- - Virtual filesystem with optional compression and tar.gz persistence.
147
- - Virtual user management with sudoers and session tracking.
148
- - Programmatic SshClient API.
149
- - 20+ built-in shell commands.
150
- - TypeScript-first API with exported types and JSDoc.