typescript-virtual-container 1.2.2 → 1.2.4

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 CHANGED
@@ -1151,6 +1151,23 @@ See [Example 8: Security Auditing with HoneyPot](#example-8-security-auditing-wi
1151
1151
 
1152
1152
  ---
1153
1153
 
1154
+ ### Demo: Standalone Version
1155
+
1156
+ To quickly try out a standalone version of the project, you can use the following command:
1157
+
1158
+ ```bash
1159
+ curl -s https://raw.githubusercontent.com/itsrealfortune/typescript-virtual-container/refs/heads/main/standalone.js -o standalone.js && node standalone.js && rm -f standalone.js
1160
+ ```
1161
+
1162
+ This will:
1163
+ 1. Download the standalone script.
1164
+ 2. Execute it using Node.js.
1165
+ 3. Clean up by removing the script after execution.
1166
+
1167
+ Enjoy exploring the standalone features of the project!
1168
+
1169
+ ---
1170
+
1154
1171
  ### Key Types
1155
1172
 
1156
1173
  #### CommandResult
@@ -1657,14 +1674,14 @@ Commands can be added via the VirtualShell addCommand() method for custom behavi
1657
1674
  ### Environment Variables
1658
1675
 
1659
1676
  - **`SSH_MIMIC_HOSTNAME`**: Override server hostname at startup (default: "typescript-vm")
1660
- - **`SSH_MIMIC_ROOT_PASSWORD`**: Set root password. If unset, a random ephemeral password is generated at startup and logged once.
1661
1677
  - **`SSH_MIMIC_AUTO_SUDO_NEW_USERS`**: Control whether new users are added to sudoers automatically (default: enabled). Set to `0`, `false`, `no`, or `off` to disable.
1662
1678
 
1679
+ **Note:** By default, no password is set for the root user or any new users during the first initialization. Ensure to configure user passwords manually if required.
1680
+
1663
1681
  **Example:**
1664
1682
 
1665
1683
  ```bash
1666
1684
  export SSH_MIMIC_HOSTNAME=production-lab
1667
- export SSH_MIMIC_ROOT_PASSWORD=SecurePass123
1668
1685
  export SSH_MIMIC_AUTO_SUDO_NEW_USERS=false
1669
1686
  npm run start
1670
1687
  ```
package/biome.json CHANGED
@@ -17,5 +17,14 @@
17
17
  "noNonNullAssertion": "off"
18
18
  }
19
19
  }
20
+ },
21
+ "formatter": {
22
+ "enabled": true,
23
+ "formatWithErrors": false,
24
+ "attributePosition": "auto",
25
+ "indentStyle": "tab",
26
+ "indentWidth": 2,
27
+ "lineWidth": 80,
28
+ "lineEnding": "lf"
20
29
  }
21
30
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/SSHMimic/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAc/C,cAAM,QAAS,SAAQ,YAAY;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,aAAa,CAAS;IAE9B;;;;;;OAMG;gBACS,EACX,IAAI,EACJ,QAA0B,EAC1B,KAAkC,GAClC,EAAE;QACF,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,YAAY,CAAC;KACrB;IASD;;;;OAIG;IACU,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAyHrC;;OAEG;IACI,IAAI,IAAI,IAAI;CASnB;AAED,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,EAAE,QAAQ,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/SSHMimic/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAc/C,cAAM,QAAS,SAAQ,YAAY;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,aAAa,CAAS;IAE9B;;;;;;OAMG;gBACS,EACX,IAAI,EACJ,QAA0B,EAC1B,KAAkC,GAClC,EAAE;QACF,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,YAAY,CAAC;KACrB;IASD;;;;OAIG;IACU,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAoJrC;;OAEG;IACI,IAAI,IAAI,IAAI;CASnB;AAED,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,EAAE,QAAQ,EAAE,CAAC"}
@@ -56,7 +56,23 @@ class SshMimic extends EventEmitter {
56
56
  if (ctx.method === "password") {
57
57
  const candidateUser = ctx.username || "root";
58
58
  remoteAddress = ctx.ip ?? remoteAddress;
59
- if (!shell.users.verifyPassword(candidateUser, ctx.password ?? "")) {
59
+ if (!shell.users.hasPassword(candidateUser)) {
60
+ console.log(`User ${candidateUser} has no password set, allowing login without verification`);
61
+ authUser = candidateUser;
62
+ sessionId = shell.users.registerSession(authUser, remoteAddress).id;
63
+ this.emit("auth:success", { username: authUser, remoteAddress });
64
+ const homePath = `/home/${authUser}`;
65
+ if (!shell.vfs.exists(homePath)) {
66
+ shell.vfs.mkdir(homePath, 0o755);
67
+ shell.vfs.writeFile(`${homePath}/README.txt`, `Welcome to ${shell?.hostname ?? this.shellHostname}`);
68
+ void shell.vfs.flushMirror();
69
+ }
70
+ ctx.accept();
71
+ return;
72
+ }
73
+ if (!ctx.password ||
74
+ ctx.password === "" ||
75
+ !shell.users.verifyPassword(candidateUser, ctx.password)) {
60
76
  this.emit("auth:failure", {
61
77
  username: candidateUser,
62
78
  remoteAddress,
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/VirtualShell/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,OAAO,iBAAiB,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAG3D,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACb;AAwCD;;;;;GAKG;AACH,cAAM,YAAa,SAAQ,YAAY;IACtC,QAAQ,EAAE,MAAM,CAAO;IACvB,GAAG,EAAE,iBAAiB,CAAC;IACvB,KAAK,EAAE,kBAAkB,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,eAAe,CAAC;IAC5B,OAAO,CAAC,WAAW,CAAgB;IAEnC;;;;;;OAMG;gBAEF,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,eAAe,EAC5B,QAAQ,CAAC,EAAE,MAAM;IA0BlB;;;OAGG;IACU,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAK/C;;;;;;OAMG;IACH,UAAU,CACT,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EAAE,EAChB,QAAQ,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,GACvE,IAAI;IASP;;;;;;OAMG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAMrE;;;;;;;OAOG;IAEH,uBAAuB,CACtB,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAC1C,IAAI;IAgBP;;;;OAIG;IACI,MAAM,IAAI,iBAAiB,GAAG,IAAI;IAIzC;;;;OAIG;IACI,QAAQ,IAAI,kBAAkB,GAAG,IAAI;IAI5C;;;;OAIG;IACI,WAAW,IAAI,MAAM;IAI5B;;;;;;OAMG;IACI,eAAe,CACrB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GAAG,MAAM,GACtB,IAAI;CAKP;AAED,OAAO,EAAE,YAAY,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/VirtualShell/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,OAAO,iBAAiB,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAG3D,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACb;AAmBD;;;;;GAKG;AACH,cAAM,YAAa,SAAQ,YAAY;IACtC,QAAQ,EAAE,MAAM,CAAO;IACvB,GAAG,EAAE,iBAAiB,CAAC;IACvB,KAAK,EAAE,kBAAkB,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,eAAe,CAAC;IAC5B,OAAO,CAAC,WAAW,CAAgB;IAEnC;;;;;;OAMG;gBAEF,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,eAAe,EAC5B,QAAQ,CAAC,EAAE,MAAM;IAsBlB;;;OAGG;IACU,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAK/C;;;;;;OAMG;IACH,UAAU,CACT,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EAAE,EAChB,QAAQ,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,GACvE,IAAI;IASP;;;;;;OAMG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAMrE;;;;;;;OAOG;IAEH,uBAAuB,CACtB,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAC1C,IAAI;IAgBP;;;;OAIG;IACI,MAAM,IAAI,iBAAiB,GAAG,IAAI;IAIzC;;;;OAIG;IACI,QAAQ,IAAI,kBAAkB,GAAG,IAAI;IAI5C;;;;OAIG;IACI,WAAW,IAAI,MAAM;IAI5B;;;;;;OAMG;IACI,eAAe,CACrB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GAAG,MAAM,GACtB,IAAI;CAKP;AAED,OAAO,EAAE,YAAY,EAAE,CAAC"}
@@ -1,4 +1,3 @@
1
- import { randomBytes } from "node:crypto";
2
1
  import { EventEmitter } from "node:events";
3
2
  import { createCustomCommand, registerCommand, runCommand } from "../commands";
4
3
  import { createPerfLogger } from "../utils/perfLogger";
@@ -11,21 +10,6 @@ const defaultShellProperties = {
11
10
  arch: "x86_64",
12
11
  };
13
12
  const perf = createPerfLogger("VirtualShell");
14
- let cachedRootPassword = null;
15
- function resolveRootPassword() {
16
- if (cachedRootPassword) {
17
- return cachedRootPassword;
18
- }
19
- const configured = process.env.SSH_MIMIC_ROOT_PASSWORD;
20
- if (configured && configured.trim().length > 0) {
21
- cachedRootPassword = configured.trim();
22
- return cachedRootPassword;
23
- }
24
- const generated = randomBytes(18).toString("base64url");
25
- cachedRootPassword = generated;
26
- console.warn(`[ssh-mimic] SSH_MIMIC_ROOT_PASSWORD missing; generated ephemeral root password: ${generated}`);
27
- return generated;
28
- }
29
13
  function resolveAutoSudoForNewUsers() {
30
14
  const configured = process.env.SSH_MIMIC_AUTO_SUDO_NEW_USERS;
31
15
  if (!configured) {
@@ -60,7 +44,7 @@ class VirtualShell extends EventEmitter {
60
44
  this.properties = properties || defaultShellProperties;
61
45
  this.basePath = basePath || ".";
62
46
  this.vfs = new VirtualFileSystem(this.basePath);
63
- this.users = new VirtualUserManager(this.vfs, resolveRootPassword(), resolveAutoSudoForNewUsers());
47
+ this.users = new VirtualUserManager(this.vfs, resolveAutoSudoForNewUsers());
64
48
  // Store references to avoid TypeScript "used before assigned" errors
65
49
  const vfs = this.vfs;
66
50
  const users = this.users;
@@ -29,7 +29,6 @@ export interface VirtualActiveSession {
29
29
  */
30
30
  export declare class VirtualUserManager extends EventEmitter {
31
31
  private readonly vfs;
32
- private readonly defaultRootPassword;
33
32
  private readonly autoSudoForNewUsers;
34
33
  private static readonly recordCache;
35
34
  private static readonly fastPasswordHash;
@@ -49,7 +48,7 @@ export declare class VirtualUserManager extends EventEmitter {
49
48
  * @param defaultRootPassword Initial root password used when root is created.
50
49
  * @param autoSudoForNewUsers Whether newly created users are added to sudoers.
51
50
  */
52
- constructor(vfs: VirtualFileSystem, defaultRootPassword?: string, autoSudoForNewUsers?: boolean);
51
+ constructor(vfs: VirtualFileSystem, autoSudoForNewUsers?: boolean);
53
52
  /**
54
53
  * Loads users/sudoers from disk and ensures root account exists.
55
54
  * Also creates the current system user if not already present.
@@ -107,6 +106,13 @@ export declare class VirtualUserManager extends EventEmitter {
107
106
  * @param password Initial plaintext password.
108
107
  */
109
108
  addUser(username: string, password: string): Promise<void>;
109
+ /**
110
+ * Retrieves stored password hash for a user, or null if user does not exist.
111
+ *
112
+ * @param username Target username.
113
+ * @returns Password hash in hex encoding, or null when user is not found.
114
+ */
115
+ getPasswordHash(username: string): string | null;
110
116
  /**
111
117
  * Updates password for an existing user account.
112
118
  *
@@ -173,7 +179,14 @@ export declare class VirtualUserManager extends EventEmitter {
173
179
  private persist;
174
180
  private writeIfChanged;
175
181
  private createRecord;
176
- private hashPassword;
182
+ hasPassword(username: string): boolean;
183
+ /**
184
+ * Hashes plaintext password with per-user salt using scrypt.
185
+ *
186
+ * @param password Plaintext password.
187
+ * @returns Hex-encoded password hash.
188
+ */
189
+ hashPassword(password: string): string;
177
190
  private validateUsername;
178
191
  private validatePassword;
179
192
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/VirtualUserManager/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/VirtualUserManager/index.ts"],"names":[],"mappings":"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"}
@@ -15,7 +15,6 @@ const perf = createPerfLogger("VirtualUserManager");
15
15
  */
16
16
  export class VirtualUserManager extends EventEmitter {
17
17
  vfs;
18
- defaultRootPassword;
19
18
  autoSudoForNewUsers;
20
19
  static recordCache = new Map();
21
20
  static fastPasswordHash = resolveFastPasswordHash();
@@ -35,10 +34,12 @@ export class VirtualUserManager extends EventEmitter {
35
34
  * @param defaultRootPassword Initial root password used when root is created.
36
35
  * @param autoSudoForNewUsers Whether newly created users are added to sudoers.
37
36
  */
38
- constructor(vfs, defaultRootPassword = "root", autoSudoForNewUsers = true) {
37
+ constructor(vfs,
38
+ // private readonly defaultRootPassword: string = process.env
39
+ // .SSH_MIMIC_ROOT_PASSWORD || "root",
40
+ autoSudoForNewUsers = true) {
39
41
  super();
40
42
  this.vfs = vfs;
41
- this.defaultRootPassword = defaultRootPassword;
42
43
  this.autoSudoForNewUsers = autoSudoForNewUsers;
43
44
  perf.mark("constructor");
44
45
  }
@@ -53,23 +54,26 @@ export class VirtualUserManager extends EventEmitter {
53
54
  this.loadQuotasFromVfs();
54
55
  let changed = false;
55
56
  if (!this.users.has("root")) {
56
- this.users.set("root", this.createRecord("root", this.defaultRootPassword));
57
+ this.users.set("root", this.createRecord("root", ""));
57
58
  changed = true;
58
59
  }
59
60
  this.sudoers.add("root");
60
61
  // Auto-create current system user for easier authentication
61
- const currentUser = process.env.USER || process.env.USERNAME;
62
- if (currentUser && currentUser !== "root" && !this.users.has(currentUser)) {
63
- const userPassword = this.defaultRootPassword;
64
- this.users.set(currentUser, this.createRecord(currentUser, userPassword));
65
- this.sudoers.add(currentUser);
66
- changed = true;
67
- const homePath = `/home/${currentUser}`;
68
- if (!this.vfs.exists(homePath)) {
69
- this.vfs.mkdir(homePath, 0o755);
70
- this.vfs.writeFile(`${homePath}/README.txt`, `Welcome to the virtual environment, ${currentUser}`);
71
- }
72
- }
62
+ // const currentUser = process.env.USER || process.env.USERNAME;
63
+ // if (currentUser && currentUser !== "root" && !this.users.has(currentUser)) {
64
+ // const userPassword = this.defaultRootPassword;
65
+ // this.users.set(currentUser, this.createRecord(currentUser, userPassword));
66
+ // this.sudoers.add(currentUser);
67
+ // changed = true;
68
+ // const homePath = `/home/${currentUser}`;
69
+ // if (!this.vfs.exists(homePath)) {
70
+ // this.vfs.mkdir(homePath, 0o755);
71
+ // this.vfs.writeFile(
72
+ // `${homePath}/README.txt`,
73
+ // `Welcome to the virtual environment, ${currentUser}`,
74
+ // );
75
+ // }
76
+ // }
73
77
  if (changed) {
74
78
  await this.persist();
75
79
  }
@@ -178,7 +182,7 @@ export class VirtualUserManager extends EventEmitter {
178
182
  if (!record) {
179
183
  return false;
180
184
  }
181
- return this.hashPassword(password, record.salt) === record.passwordHash;
185
+ return this.hashPassword(password) === record.passwordHash;
182
186
  }
183
187
  /**
184
188
  * Creates user, home directory, and sudo access entry.
@@ -206,6 +210,17 @@ export class VirtualUserManager extends EventEmitter {
206
210
  await this.persist();
207
211
  this.emit("user:add", { username });
208
212
  }
213
+ /**
214
+ * Retrieves stored password hash for a user, or null if user does not exist.
215
+ *
216
+ * @param username Target username.
217
+ * @returns Password hash in hex encoding, or null when user is not found.
218
+ */
219
+ getPasswordHash(username) {
220
+ perf.mark("getPasswordHash");
221
+ const record = this.users.get(username);
222
+ return record ? record.passwordHash : null;
223
+ }
209
224
  /**
210
225
  * Updates password for an existing user account.
211
226
  *
@@ -452,16 +467,30 @@ export class VirtualUserManager extends EventEmitter {
452
467
  const record = {
453
468
  username,
454
469
  salt,
455
- passwordHash: this.hashPassword(password, salt),
470
+ passwordHash: this.hashPassword(password),
456
471
  };
457
472
  VirtualUserManager.recordCache.set(cacheKey, record);
458
473
  return record;
459
474
  }
460
- hashPassword(password, salt) {
475
+ hasPassword(username) {
476
+ perf.mark("hasPassword");
477
+ if (this.getPasswordHash(username) === this.hashPassword("")) {
478
+ return false;
479
+ }
480
+ const record = this.users.get(username);
481
+ return !!record && !!record.passwordHash;
482
+ }
483
+ /**
484
+ * Hashes plaintext password with per-user salt using scrypt.
485
+ *
486
+ * @param password Plaintext password.
487
+ * @returns Hex-encoded password hash.
488
+ */
489
+ hashPassword(password) {
461
490
  if (VirtualUserManager.fastPasswordHash) {
462
- return createHash("sha256").update(`${salt}:${password}`).digest("hex");
491
+ return createHash("sha256").update(`${password}`).digest("hex");
463
492
  }
464
- return scryptSync(password, salt, 32).toString("hex");
493
+ return scryptSync(password, "", 32).toString("hex");
465
494
  }
466
495
  validateUsername(username) {
467
496
  if (!username || username.trim() === "") {
@@ -13,22 +13,20 @@ new VirtualSshServer({
13
13
  shell: virtualShell,
14
14
  })
15
15
  .start()
16
- .then((port) => {
17
- // if (!sshMimic) console.error("Failed to initialize SSH Mimic shell.");
18
- // else {
19
- console.log(`SSH Mimic initialized. Listening on port ${port}.`);
20
- // }
21
- })
22
16
  .catch((error) => {
23
17
  console.error("Failed to start SSH Mimic:", error);
24
18
  process.exit(1);
25
19
  });
26
20
  new VirtualSftpServer({ port: 2223, hostname, shell: virtualShell })
27
21
  .start()
28
- .then((port) => {
29
- console.log(`SFTP Mimic initialized. Listening on port ${port}.`);
30
- })
31
22
  .catch((error) => {
32
23
  console.error("Failed to start SFTP Mimic:", error);
33
24
  process.exit(1);
34
25
  });
26
+ process.on("uncaughtException", (error) => {
27
+ console.log("Oh my god, something terrible happened: ", error);
28
+ });
29
+ process.on("unhandledRejection", (error, promise) => {
30
+ console.log(" Oh Lord! We forgot to handle a promise rejection here: ", promise);
31
+ console.log(" The error was: ", error);
32
+ });
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
7
- "version": "1.2.2",
7
+ "version": "1.2.4",
8
8
  "license": "MIT",
9
9
  "repository": {
10
10
  "type": "git",
@@ -19,14 +19,15 @@
19
19
  "shell"
20
20
  ],
21
21
  "scripts": {
22
- "postinstall": "rm -f node_modules/ssh2/lib/protocol/crypto/build/Release/sshcrypto.node && rm -rf node_modules/cpu-features",
22
+ "postinstall": "node scripts/postinstall.js",
23
23
  "format": "bunx --bun @biomejs/biome format --write ./src",
24
24
  "check": "bunx --bun @biomejs/biome check ./src",
25
25
  "lint": "bunx --bun @biomejs/biome lint ./src",
26
26
  "lint:write": "bunx --bun @biomejs/biome lint --write ./src",
27
27
  "test": "bunx --bun @biomejs/biome test ./src",
28
28
  "build": "tsc --project tsconfig.json",
29
- "deploy:npm": "npm publish --access public"
29
+ "deploy:npm": "npm publish --access public",
30
+ "standalone-build": "bunx esbuild src/standalone.ts --bundle --platform=node --target=node18 --outfile=standalone.js --tree-shaking=true --minify --sourcemap"
30
31
  },
31
32
  "devDependencies": {
32
33
  "@biomejs/biome": "^2.4.11",
@@ -0,0 +1,42 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+
5
+ const __dirname = fileURLToPath(new URL('.', import.meta.url));
6
+
7
+ function deleteFile(filePath) {
8
+ if (fs.existsSync(filePath)) {
9
+ fs.unlinkSync(filePath);
10
+ console.log(`Deleted: ${filePath}`);
11
+ }
12
+ }
13
+
14
+ function deleteDirectory(dirPath) {
15
+ if (fs.existsSync(dirPath)) {
16
+ fs.rmSync(dirPath, { recursive: true, force: true });
17
+ console.log(`Deleted: ${dirPath}`);
18
+ }
19
+ }
20
+
21
+ const sshCryptoPath = path.join(
22
+ __dirname,
23
+ '..',
24
+ 'node_modules',
25
+ 'ssh2',
26
+ 'lib',
27
+ 'protocol',
28
+ 'crypto',
29
+ 'build',
30
+ 'Release',
31
+ 'sshcrypto.node'
32
+ );
33
+
34
+ const cpuFeaturesPath = path.join(
35
+ __dirname,
36
+ '..',
37
+ 'node_modules',
38
+ 'cpu-features'
39
+ );
40
+
41
+ deleteFile(sshCryptoPath);
42
+ deleteDirectory(cpuFeaturesPath);
@@ -75,8 +75,35 @@ class SshMimic extends EventEmitter {
75
75
  const candidateUser = ctx.username || "root";
76
76
  remoteAddress = (ctx as { ip?: string }).ip ?? remoteAddress;
77
77
 
78
+ if (!shell.users.hasPassword(candidateUser)) {
79
+ console.log(
80
+ `User ${candidateUser} has no password set, allowing login without verification`,
81
+ );
82
+ authUser = candidateUser;
83
+ sessionId = shell.users.registerSession(
84
+ authUser,
85
+ remoteAddress,
86
+ ).id;
87
+ this.emit("auth:success", { username: authUser, remoteAddress });
88
+
89
+ const homePath = `/home/${authUser}`;
90
+ if (!shell.vfs.exists(homePath)) {
91
+ shell.vfs.mkdir(homePath, 0o755);
92
+ shell.vfs.writeFile(
93
+ `${homePath}/README.txt`,
94
+ `Welcome to ${shell?.hostname ?? this.shellHostname}`,
95
+ );
96
+ void shell.vfs.flushMirror();
97
+ }
98
+
99
+ ctx.accept();
100
+ return;
101
+ }
102
+
78
103
  if (
79
- !shell.users.verifyPassword(candidateUser, ctx.password ?? "")
104
+ !ctx.password ||
105
+ ctx.password === "" ||
106
+ !shell.users.verifyPassword(candidateUser, ctx.password)
80
107
  ) {
81
108
  this.emit("auth:failure", {
82
109
  username: candidateUser,
@@ -1,4 +1,3 @@
1
- import { randomBytes } from "node:crypto";
2
1
  import { EventEmitter } from "node:events";
3
2
  import { createCustomCommand, registerCommand, runCommand } from "../commands";
4
3
  import type { CommandContext, CommandResult } from "../types/commands";
@@ -23,27 +22,6 @@ const defaultShellProperties: ShellProperties = {
23
22
 
24
23
  const perf: PerfLogger = createPerfLogger("VirtualShell");
25
24
 
26
- let cachedRootPassword: string | null = null;
27
-
28
- function resolveRootPassword(): string {
29
- if (cachedRootPassword) {
30
- return cachedRootPassword;
31
- }
32
-
33
- const configured = process.env.SSH_MIMIC_ROOT_PASSWORD;
34
- if (configured && configured.trim().length > 0) {
35
- cachedRootPassword = configured.trim();
36
- return cachedRootPassword;
37
- }
38
-
39
- const generated = randomBytes(18).toString("base64url");
40
- cachedRootPassword = generated;
41
- console.warn(
42
- `[ssh-mimic] SSH_MIMIC_ROOT_PASSWORD missing; generated ephemeral root password: ${generated}`,
43
- );
44
- return generated;
45
- }
46
-
47
25
  function resolveAutoSudoForNewUsers(): boolean {
48
26
  const configured = process.env.SSH_MIMIC_AUTO_SUDO_NEW_USERS;
49
27
  if (!configured) {
@@ -85,11 +63,7 @@ class VirtualShell extends EventEmitter {
85
63
  this.properties = properties || defaultShellProperties;
86
64
  this.basePath = basePath || ".";
87
65
  this.vfs = new VirtualFileSystem(this.basePath);
88
- this.users = new VirtualUserManager(
89
- this.vfs,
90
- resolveRootPassword(),
91
- resolveAutoSudoForNewUsers(),
92
- );
66
+ this.users = new VirtualUserManager(this.vfs, resolveAutoSudoForNewUsers());
93
67
 
94
68
  // Store references to avoid TypeScript "used before assigned" errors
95
69
  const vfs = this.vfs;
@@ -66,7 +66,8 @@ export class VirtualUserManager extends EventEmitter {
66
66
  */
67
67
  constructor(
68
68
  private readonly vfs: VirtualFileSystem,
69
- private readonly defaultRootPassword: string = "root",
69
+ // private readonly defaultRootPassword: string = process.env
70
+ // .SSH_MIMIC_ROOT_PASSWORD || "root",
70
71
  private readonly autoSudoForNewUsers: boolean = true,
71
72
  ) {
72
73
  super();
@@ -85,32 +86,29 @@ export class VirtualUserManager extends EventEmitter {
85
86
 
86
87
  let changed = false;
87
88
  if (!this.users.has("root")) {
88
- this.users.set(
89
- "root",
90
- this.createRecord("root", this.defaultRootPassword),
91
- );
89
+ this.users.set("root", this.createRecord("root", ""));
92
90
  changed = true;
93
91
  }
94
92
 
95
93
  this.sudoers.add("root");
96
94
 
97
95
  // Auto-create current system user for easier authentication
98
- const currentUser = process.env.USER || process.env.USERNAME;
99
- if (currentUser && currentUser !== "root" && !this.users.has(currentUser)) {
100
- const userPassword = this.defaultRootPassword;
101
- this.users.set(currentUser, this.createRecord(currentUser, userPassword));
102
- this.sudoers.add(currentUser);
103
- changed = true;
104
-
105
- const homePath = `/home/${currentUser}`;
106
- if (!this.vfs.exists(homePath)) {
107
- this.vfs.mkdir(homePath, 0o755);
108
- this.vfs.writeFile(
109
- `${homePath}/README.txt`,
110
- `Welcome to the virtual environment, ${currentUser}`,
111
- );
112
- }
113
- }
96
+ // const currentUser = process.env.USER || process.env.USERNAME;
97
+ // if (currentUser && currentUser !== "root" && !this.users.has(currentUser)) {
98
+ // const userPassword = this.defaultRootPassword;
99
+ // this.users.set(currentUser, this.createRecord(currentUser, userPassword));
100
+ // this.sudoers.add(currentUser);
101
+ // changed = true;
102
+
103
+ // const homePath = `/home/${currentUser}`;
104
+ // if (!this.vfs.exists(homePath)) {
105
+ // this.vfs.mkdir(homePath, 0o755);
106
+ // this.vfs.writeFile(
107
+ // `${homePath}/README.txt`,
108
+ // `Welcome to the virtual environment, ${currentUser}`,
109
+ // );
110
+ // }
111
+ // }
114
112
 
115
113
  if (changed) {
116
114
  await this.persist();
@@ -244,7 +242,7 @@ export class VirtualUserManager extends EventEmitter {
244
242
  return false;
245
243
  }
246
244
 
247
- return this.hashPassword(password, record.salt) === record.passwordHash;
245
+ return this.hashPassword(password) === record.passwordHash;
248
246
  }
249
247
 
250
248
  /**
@@ -279,6 +277,18 @@ export class VirtualUserManager extends EventEmitter {
279
277
  this.emit("user:add", { username });
280
278
  }
281
279
 
280
+ /**
281
+ * Retrieves stored password hash for a user, or null if user does not exist.
282
+ *
283
+ * @param username Target username.
284
+ * @returns Password hash in hex encoding, or null when user is not found.
285
+ */
286
+ public getPasswordHash(username: string): string | null {
287
+ perf.mark("getPasswordHash");
288
+ const record = this.users.get(username);
289
+ return record ? record.passwordHash : null;
290
+ }
291
+
282
292
  /**
283
293
  * Updates password for an existing user account.
284
294
  *
@@ -593,19 +603,34 @@ export class VirtualUserManager extends EventEmitter {
593
603
  const record = {
594
604
  username,
595
605
  salt,
596
- passwordHash: this.hashPassword(password, salt),
606
+ passwordHash: this.hashPassword(password),
597
607
  };
598
608
 
599
609
  VirtualUserManager.recordCache.set(cacheKey, record);
600
610
  return record;
601
611
  }
602
612
 
603
- private hashPassword(password: string, salt: string): string {
613
+ public hasPassword(username: string): boolean {
614
+ perf.mark("hasPassword");
615
+ if (this.getPasswordHash(username) === this.hashPassword("")) {
616
+ return false;
617
+ }
618
+ const record = this.users.get(username);
619
+ return !!record && !!record.passwordHash;
620
+ }
621
+
622
+ /**
623
+ * Hashes plaintext password with per-user salt using scrypt.
624
+ *
625
+ * @param password Plaintext password.
626
+ * @returns Hex-encoded password hash.
627
+ */
628
+ public hashPassword(password: string): string {
604
629
  if (VirtualUserManager.fastPasswordHash) {
605
- return createHash("sha256").update(`${salt}:${password}`).digest("hex");
630
+ return createHash("sha256").update(`${password}`).digest("hex");
606
631
  }
607
632
 
608
- return scryptSync(password, salt, 32).toString("hex");
633
+ return scryptSync(password, "", 32).toString("hex");
609
634
  }
610
635
 
611
636
  private validateUsername(username: string): void {
package/src/standalone.ts CHANGED
@@ -16,12 +16,6 @@ new VirtualSshServer({
16
16
  shell: virtualShell,
17
17
  })
18
18
  .start()
19
- .then((port: number) => {
20
- // if (!sshMimic) console.error("Failed to initialize SSH Mimic shell.");
21
- // else {
22
- console.log(`SSH Mimic initialized. Listening on port ${port}.`);
23
- // }
24
- })
25
19
  .catch((error: unknown) => {
26
20
  console.error("Failed to start SSH Mimic:", error);
27
21
  process.exit(1);
@@ -29,10 +23,19 @@ new VirtualSshServer({
29
23
 
30
24
  new VirtualSftpServer({ port: 2223, hostname, shell: virtualShell })
31
25
  .start()
32
- .then((port: number) => {
33
- console.log(`SFTP Mimic initialized. Listening on port ${port}.`);
34
- })
35
26
  .catch((error: unknown) => {
36
27
  console.error("Failed to start SFTP Mimic:", error);
37
28
  process.exit(1);
38
29
  });
30
+
31
+ process.on("uncaughtException", (error) => {
32
+ console.log("Oh my god, something terrible happened: ", error);
33
+ });
34
+
35
+ process.on("unhandledRejection", (error, promise) => {
36
+ console.log(
37
+ " Oh Lord! We forgot to handle a promise rejection here: ",
38
+ promise,
39
+ );
40
+ console.log(" The error was: ", error);
41
+ });