typescript-virtual-container 1.1.4 → 1.1.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.
Files changed (39) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/HONEYPOT.md +358 -0
  3. package/README.md +471 -16
  4. package/dist/Honeypot/index.d.ts +132 -0
  5. package/dist/Honeypot/index.d.ts.map +1 -0
  6. package/dist/Honeypot/index.js +289 -0
  7. package/dist/SSHMimic/index.d.ts +2 -1
  8. package/dist/SSHMimic/index.d.ts.map +1 -1
  9. package/dist/SSHMimic/index.js +12 -1
  10. package/dist/SSHMimic/sftp.d.ts +3 -1
  11. package/dist/SSHMimic/sftp.d.ts.map +1 -1
  12. package/dist/SSHMimic/sftp.js +20 -1
  13. package/dist/VirtualFileSystem/index.d.ts +2 -1
  14. package/dist/VirtualFileSystem/index.d.ts.map +1 -1
  15. package/dist/VirtualFileSystem/index.js +8 -1
  16. package/dist/VirtualShell/index.d.ts +2 -1
  17. package/dist/VirtualShell/index.d.ts.map +1 -1
  18. package/dist/VirtualShell/index.js +6 -1
  19. package/dist/VirtualUserManager/index.d.ts +2 -1
  20. package/dist/VirtualUserManager/index.d.ts.map +1 -1
  21. package/dist/VirtualUserManager/index.js +19 -1
  22. package/dist/honeypot.d.ts +132 -0
  23. package/dist/honeypot.d.ts.map +1 -0
  24. package/dist/honeypot.js +289 -0
  25. package/dist/index.d.ts +3 -1
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +2 -1
  28. package/examples/README.md +210 -0
  29. package/examples/honeypot-audit.ts +180 -0
  30. package/examples/honeypot-export.ts +253 -0
  31. package/examples/honeypot-quickstart.ts +110 -0
  32. package/package.json +1 -1
  33. package/src/Honeypot/index.ts +422 -0
  34. package/src/SSHMimic/index.ts +13 -1
  35. package/src/SSHMimic/sftp.ts +21 -1
  36. package/src/VirtualFileSystem/index.ts +8 -1
  37. package/src/VirtualShell/index.ts +6 -1
  38. package/src/VirtualUserManager/index.ts +21 -3
  39. package/src/index.ts +6 -0
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # `typescript-virtual-container`
2
2
 
3
- > In-memory SSH server with a virtual filesystem and typed programmatic API for testing, automation, and interactive shell scripting in TypeScript/JavaScript.
3
+ > In-memory SSH/SFTP server with a virtual filesystem and typed programmatic API for testing, automation, and interactive shell scripting in TypeScript/JavaScript.
4
4
 
5
5
  [![npm version](https://badge.fury.io/js/typescript-virtual-container.svg)](https://www.npmjs.com/package/typescript-virtual-container)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
@@ -34,12 +34,14 @@
34
34
 
35
35
  ## Overview
36
36
 
37
- `typescript-virtual-container` is a lightweight, fully-typed SSH server written in TypeScript that provides:
37
+ `typescript-virtual-container` is a lightweight, fully-typed SSH/SFTP runtime written in TypeScript that provides:
38
38
 
39
- - **SSH Protocol Support**: Serve SSH connections on any port with password authentication and support for both shell and exec modes.
40
- - **Virtual Filesystem**: In-memory filesystem with optional compression, persistence to disk via tar.gz snapshots, and programmatic access.
39
+ - **SSH + SFTP Protocol Support**: Serve SSH shell/exec sessions and SFTP file operations on configurable ports.
40
+ - **Virtual Filesystem**: In-memory-like developer workflow backed by a mirror directory under `.vfs/mirror`, with optional gzip compression and programmatic access.
41
41
  - **User Management**: Create, authenticate, and manage virtual users with strict password hashing (scrypt) and sudo-like privilege elevation.
42
42
  - **Programmatic Shell API**: Execute shell commands and query filesystem state directly from TypeScript without SSH overhead.
43
+ - **Event-Driven Architecture**: All core classes extend `EventEmitter` for lifecycle and operation tracking. Listen to auth events, filesystem operations, session lifecycle, and command execution for auditing and integration.
44
+ - **Security Auditing**: Built-in `HoneyPot` utility for comprehensive activity logging, event tracking, statistics collection, and anomaly detection across all components.
43
45
  - **Built-in Commands**: `ls`, `cd`, `pwd`, `cat`, `mkdir`, `touch`, `rm`, `tree`, `whoami`, `hostname`, `who`, `sudo`, `su`, `adduser`, `deluser`, `nano` (text editor), `curl`, `wget`, and a growing set of additional commands. Not everything is implemented yet, and shell compatibility is still being expanded.
44
46
  - **Full TypeScript Support**: Complete JSDoc coverage, exported types, and first-class async/await for all operations.
45
47
 
@@ -126,6 +128,31 @@ process.on("SIGTERM", () => {
126
128
  });
127
129
  ```
128
130
 
131
+ ### Running SSH + SFTP with Shared State
132
+
133
+ ```typescript
134
+ import { VirtualSftpServer, VirtualShell, VirtualSshServer } from "typescript-virtual-container";
135
+
136
+ const shell = new VirtualShell("my-container");
137
+
138
+ const ssh = new VirtualSshServer({
139
+ port: 2222,
140
+ hostname: "my-container",
141
+ shell,
142
+ });
143
+
144
+ const sftp = new VirtualSftpServer({
145
+ port: 2223,
146
+ hostname: "my-container",
147
+ shell,
148
+ });
149
+
150
+ await ssh.start();
151
+ await sftp.start();
152
+
153
+ console.log("SSH on :2222, SFTP on :2223");
154
+ ```
155
+
129
156
  ### Using the Programmatic Client API
130
157
 
131
158
  ```typescript
@@ -174,7 +201,7 @@ ssh.stop();
174
201
  └──────────────┘ └──────────┘ └──────────┘
175
202
 
176
203
  │ Backed by disk
177
- │ .vfs/mirror.tar.gz
204
+ │ .vfs/mirror
178
205
  └──────────────────────────────────┘
179
206
  ``` -->
180
207
 
@@ -182,13 +209,14 @@ ssh.stop();
182
209
 
183
210
  1. **SSH Shell Mode**: Interactive terminal session over SSH with readline, prompt, TTY resizing.
184
211
  2. **SSH Exec Mode**: Non-interactive command execution (e.g., `ssh user@host "ls -la"`).
185
- 3. **Programmatic Mode** (new): Direct TypeScript API via `SshClient`, no SSH protocol overhead.
212
+ 3. **SFTP Mode**: Remote file operations (`readdir`, `stat`, `readFile`, `writeFile`, `mkdir`, `rename`, etc.) with home-directory confinement.
213
+ 4. **Programmatic Mode**: Direct TypeScript API via `SshClient`, no SSH protocol overhead.
186
214
 
187
215
  ### Persistence
188
216
 
189
- - Filesystem state saved as gzip-compressed tar archive at `.vfs/mirror.tar.gz`
217
+ - Filesystem state is stored under `.vfs/mirror` inside the configured `basePath`
190
218
  - Users/passwords stored in virtual paths `/virtual-env-js/.auth/htpasswd` and `/virtual-env-js/.auth/sudoers`
191
- - Manual flush via `VirtualFileSystem.flushMirror()` or automatic on command completion
219
+ - `restoreMirror()` and `flushMirror()` are lightweight compatibility hooks for initialization boundaries
192
220
 
193
221
  ---
194
222
 
@@ -248,6 +276,31 @@ Cleanly closes server and all active connections.
248
276
  ssh.stop();
249
277
  ```
250
278
 
279
+ #### Events
280
+
281
+ `SshMimic` extends `EventEmitter` and emits the following events:
282
+
283
+ | Event | Data | Description |
284
+ |-------|------|-------------|
285
+ | `start` | `{ port: number }` | Server started and listening |
286
+ | `stop` | — | Server stopped |
287
+ | `auth:success` | `{ username: string; remoteAddress: string }` | User authenticated |
288
+ | `auth:failure` | `{ username: string; remoteAddress: string }` | Auth failed for user |
289
+ | `client:connect` | — | New SSH client connected |
290
+ | `client:disconnect` | `{ user: string }` | SSH client disconnected |
291
+
292
+ **Example:**
293
+
294
+ ```typescript
295
+ ssh.on("auth:success", ({ username, remoteAddress }) => {
296
+ console.log(`[SSH] User ${username} authenticated from ${remoteAddress}`);
297
+ });
298
+
299
+ ssh.on("auth:failure", ({ username }) => {
300
+ console.log(`[SSH] Auth failed for user ${username}`);
301
+ });
302
+ ```
303
+
251
304
  ##### `getVfs(): VirtualFileSystem | null`
252
305
 
253
306
  Returns the virtual filesystem instance. Null if server not started.
@@ -278,6 +331,75 @@ console.log(`Server name: ${ssh.getHostname()}`);
278
331
 
279
332
  ---
280
333
 
334
+ ### SftpMimic (SFTP Server)
335
+
336
+ SFTP server class, exported as `VirtualSftpServer` in the package entrypoint. It can run with a shared `VirtualShell` (recommended) or with explicit `vfs + users` dependencies.
337
+
338
+ #### Constructor
339
+
340
+ ```typescript
341
+ new SftpMimic(options: {
342
+ port: number;
343
+ hostname?: string;
344
+ shell?: VirtualShell;
345
+ vfs?: VirtualFileSystem;
346
+ users?: VirtualUserManager;
347
+ })
348
+ ```
349
+
350
+ - If `shell` is provided, SFTP reuses the same users/filesystem state as SSH.
351
+ - If `shell` is omitted, pass `vfs` and `users` explicitly.
352
+
353
+ #### Methods
354
+
355
+ ##### `async start(): Promise<number>`
356
+
357
+ Starts the SFTP server and returns the bound port (useful with `port: 0`).
358
+
359
+ ```typescript
360
+ const sftp = new SftpMimic({ port: 0, shell });
361
+ const boundPort = await sftp.start();
362
+ console.log(`SFTP listening on ${boundPort}`);
363
+ ```
364
+
365
+ ##### `stop(): void`
366
+
367
+ Stops the SFTP server.
368
+
369
+ ```typescript
370
+ sftp.stop();
371
+ ```
372
+
373
+ #### Behavior Notes
374
+
375
+ - Supports `password` and `keyboard-interactive` authentication.
376
+ - Resolves relative SFTP paths from `/home/<user>`.
377
+ - Confines all SFTP operations to `/home/<user>` and blocks traversal attempts outside the user home.
378
+ - Unsupported operations (`READLINK`, `SYMLINK`) return `OP_UNSUPPORTED`.
379
+
380
+ #### Events
381
+
382
+ `SftpMimic` extends `EventEmitter` and emits the following events:
383
+
384
+ | Event | Data | Description |
385
+ |-------|------|-------------|
386
+ | `start` | `{ port: number }` | SFTP server started and listening |
387
+ | `stop` | — | SFTP server stopped |
388
+ | `auth:success` | `{ username: string; remoteAddress: string }` | User authenticated for SFTP |
389
+ | `auth:failure` | `{ username: string; remoteAddress: string }` | SFTP auth failed for user |
390
+ | `client:connect` | — | New SFTP client connected |
391
+ | `client:disconnect` | `{ user: string }` | SFTP client disconnected |
392
+
393
+ **Example:**
394
+
395
+ ```typescript
396
+ sftp.on("auth:success", ({ username }) => {
397
+ console.log(`[SFTP] User ${username} authenticated`);
398
+ });
399
+ ```
400
+
401
+ ---
402
+
281
403
  ### SshClient (Programmatic Shell API)
282
404
 
283
405
  Execute shell commands against a `VirtualShell` instance without SSH overhead. Maintains connection state (current working directory) across calls.
@@ -475,7 +597,7 @@ new VirtualShell(
475
597
 
476
598
  - **hostname**: Hostname injected into command context and prompt behavior.
477
599
  - **properties**: Optional shell metadata. Defaults to `defaultShellProperties`.
478
- - **basePath**: Optional directory used to resolve `.vfs/mirror.tar.gz` (defaults to `.`).
600
+ - **basePath**: Optional directory used to resolve `.vfs/mirror` and auth storage (defaults to `.`).
479
601
 
480
602
  **Example:**
481
603
 
@@ -519,11 +641,33 @@ shell.startInteractiveSession(
519
641
  );
520
642
  ```
521
643
 
644
+ #### Events
645
+
646
+ `VirtualShell` extends `EventEmitter` and emits the following events:
647
+
648
+ | Event | Data | Description |
649
+ |-------|------|-------------|
650
+ | `initialized` | — | Shell initialization complete |
651
+ | `command` | `{ command: string; user: string; cwd: string }` | Command executed |
652
+ | `session:start` | `{ user: string; sessionId: string \| null; remoteAddress: string }` | Interactive session started |
653
+
654
+ **Example:**
655
+
656
+ ```typescript
657
+ shell.on("command", ({ command, user, cwd }) => {
658
+ console.log(`[SHELL] User ${user} executed: ${command} (cwd: ${cwd})`);
659
+ });
660
+
661
+ shell.on("session:start", ({ user, remoteAddress }) => {
662
+ console.log(`[SHELL] Session started for ${user} from ${remoteAddress}`);
663
+ });
664
+ ```
665
+
522
666
  ---
523
667
 
524
668
  ### VirtualFileSystem
525
669
 
526
- In-memory filesystem with optional gzip compression and tar.gz persistence.
670
+ Virtual filesystem abstraction backed by a mirror directory on disk, with optional gzip compression per file.
527
671
 
528
672
  #### Constructor
529
673
 
@@ -531,18 +675,41 @@ In-memory filesystem with optional gzip compression and tar.gz persistence.
531
675
  new VirtualFileSystem(baseDir?: string)
532
676
  ```
533
677
 
534
- - **baseDir**: Directory to store `.vfs/mirror.tar.gz` snapshot (default: current working directory)
678
+ - **baseDir**: Directory used for the `.vfs/mirror` root (default: current working directory)
535
679
 
536
680
  ```typescript
537
681
  const vfs = new VirtualFileSystem("./container-data");
538
- // Snapshot at ./container-data/.vfs/mirror.tar.gz
682
+ // Mirror root at ./container-data/.vfs/mirror
539
683
  ```
540
684
 
541
685
  #### Methods
542
686
 
687
+ #### Events
688
+
689
+ `VirtualFileSystem` extends `EventEmitter` and emits the following events:
690
+
691
+ | Event | Data | Description |
692
+ |-------|------|-------------|
693
+ | `file:read` | `{ path: string; size: number }` | File read |
694
+ | `file:write` | `{ path: string; size: number }` | File written |
695
+ | `dir:create` | `{ path: string; mode: number }` | Directory created |
696
+ | `mirror:flush` | — | Mirror persisted to disk |
697
+
698
+ **Example:**
699
+
700
+ ```typescript
701
+ vfs.on("file:write", ({ path, size }) => {
702
+ console.log(`[VFS] File written: ${path} (${size} bytes)`);
703
+ });
704
+
705
+ vfs.on("dir:create", ({ path, mode }) => {
706
+ console.log(`[VFS] Directory created: ${path} (mode: ${mode.toString(8)})`);
707
+ });
708
+ ```
709
+
543
710
  ##### `async restoreMirror(): Promise<void>`
544
711
 
545
- Loads filesystem state from disk snapshot. If missing, creates fresh filesystem.
712
+ Ensures mirror directory structure exists and is ready for operations.
546
713
 
547
714
  ```typescript
548
715
  await vfs.restoreMirror();
@@ -550,7 +717,7 @@ await vfs.restoreMirror();
550
717
 
551
718
  ##### `async flushMirror(): Promise<void>`
552
719
 
553
- Persists current filesystem state to disk. No-op if nothing changed.
720
+ Compatibility hook to finalize mirror boundary operations.
554
721
 
555
722
  ```typescript
556
723
  // After file modifications...
@@ -662,6 +829,18 @@ Decompresses file content (inverse of `compressFile`).
662
829
  vfs.decompressFile("/var/log/app.log");
663
830
  ```
664
831
 
832
+ **Example:**
833
+
834
+ ```typescript
835
+ vfs.on("file:write", ({ path, size }) => {
836
+ console.log(`[VFS] File written: ${path} (${size} bytes)`);
837
+ });
838
+
839
+ vfs.on("dir:create", ({ path, mode }) => {
840
+ console.log(`[VFS] Directory created: ${path} (mode: ${mode.toString(8)})`);
841
+ });
842
+ ```
843
+
665
844
  ---
666
845
 
667
846
  ### VirtualUserManager
@@ -723,6 +902,7 @@ await users.deleteUser("bob");
723
902
 
724
903
  Checks sudo access.
725
904
 
905
+
726
906
  ```typescript
727
907
  if (users.isSudoer("alice")) {
728
908
  console.log("alice can use sudo");
@@ -821,6 +1001,154 @@ sessions.forEach(s => {
821
1001
  });
822
1002
  ```
823
1003
 
1004
+ #### Events
1005
+
1006
+ `VirtualUserManager` extends `EventEmitter` and emits the following events:
1007
+
1008
+ | Event | Data | Description |
1009
+ |-------|------|-------------|
1010
+ | `initialized` | — | User manager initialization complete, root user ready |
1011
+ | `user:add` | `{ username: string }` | New user created |
1012
+ | `user:delete` | `{ username: string }` | User deleted |
1013
+ | `session:register` | `{ sessionId: string; username: string; remoteAddress: string }` | Session registered (user logged in) |
1014
+ | `session:unregister` | `{ sessionId: string; username: string }` | Session unregistered (user logged out) |
1015
+
1016
+ **Example:**
1017
+
1018
+ ```typescript
1019
+ users.on("user:add", ({ username }) => {
1020
+ console.log(`[USERS] User created: ${username}`);
1021
+ });
1022
+
1023
+ users.on("session:register", ({ sessionId, username, remoteAddress }) => {
1024
+ console.log(`[USERS] Session ${sessionId}: ${username} from ${remoteAddress}`);
1025
+ });
1026
+
1027
+ users.on("session:unregister", ({ sessionId, username }) => {
1028
+ console.log(`[USERS] Session ${sessionId} (${username}) closed`);
1029
+ });
1030
+ ```
1031
+
1032
+ ---
1033
+
1034
+ ### HoneyPot (Auditing & Event Tracking)
1035
+
1036
+ Comprehensive security auditing and event tracking utility. Attaches to all core components (VirtualShell, VirtualFileSystem, VirtualUserManager, SshMimic, SftpMimic) to log activity, track statistics, and detect anomalies.
1037
+
1038
+ #### Constructor
1039
+
1040
+ ```typescript
1041
+ new HoneyPot(maxLogSize?: number)
1042
+ ```
1043
+
1044
+ - **maxLogSize**: Maximum audit log entries to retain (default: 10000)
1045
+
1046
+ ```typescript
1047
+ const honeypot = new HoneyPot(5000); // Keep last 5000 audit entries
1048
+ ```
1049
+
1050
+ #### Methods
1051
+
1052
+ ##### `attach(shell: VirtualShell, vfs: VirtualFileSystem, users: VirtualUserManager, ssh?: SshMimic, sftp?: SftpMimic): void`
1053
+
1054
+ Attaches honeypot listeners to all provided event emitters. This wires up all audit tracking across the entire virtual environment.
1055
+
1056
+ ```typescript
1057
+ honeypot.attach(shell, vfs, users, ssh, sftp);
1058
+ // All components now emit events to honeypot
1059
+ ```
1060
+
1061
+ ##### `getAuditLog(type?: string, source?: string): AuditLogEntry[]`
1062
+
1063
+ Returns audit log entries with optional filtering by event type or source component.
1064
+
1065
+ ```typescript
1066
+ // All entries
1067
+ const allLogs = honeypot.getAuditLog();
1068
+
1069
+ // Only auth events
1070
+ const authLogs = honeypot.getAuditLog("auth:failure");
1071
+
1072
+ // Only SshMimic events
1073
+ const sshLogs = honeypot.getAuditLog(undefined, "SshMimic");
1074
+
1075
+ // Combine filters
1076
+ const sshAuthLogs = honeypot.getAuditLog("auth:success", "SshMimic");
1077
+ ```
1078
+
1079
+ ##### `getStats(): Readonly<HoneyPotStats>`
1080
+
1081
+ Returns current activity statistics snapshot.
1082
+
1083
+ ```typescript
1084
+ const stats = honeypot.getStats();
1085
+ console.log(`Auth attempts: ${stats.authAttempts}`);
1086
+ console.log(`Auth successes: ${stats.authSuccesses}`);
1087
+ console.log(`Auth failures: ${stats.authFailures}`);
1088
+ console.log(`Commands executed: ${stats.commands}`);
1089
+ console.log(`File writes: ${stats.fileWrites}`);
1090
+ console.log(`File reads: ${stats.fileReads}`);
1091
+ console.log(`Sessions started: ${stats.sessionStarts}`);
1092
+ console.log(`Sessions ended: ${stats.sessionEnds}`);
1093
+ console.log(`Users created: ${stats.userCreated}`);
1094
+ console.log(`Users deleted: ${stats.userDeleted}`);
1095
+ console.log(`Client connects: ${stats.clientConnects}`);
1096
+ console.log(`Client disconnects: ${stats.clientDisconnects}`);
1097
+ ```
1098
+
1099
+ ##### `getRecent(limit?: number): AuditLogEntry[]`
1100
+
1101
+ Returns most recent audit entries in reverse chronological order.
1102
+
1103
+ ```typescript
1104
+ const last50 = honeypot.getRecent(50);
1105
+ last50.forEach(entry => {
1106
+ console.log(`${entry.timestamp} | ${entry.source} | ${entry.type}`);
1107
+ console.log(`Details:`, entry.details);
1108
+ });
1109
+ ```
1110
+
1111
+ ##### `detectAnomalies(): Array<{ type: string; severity: "low" | "medium" | "high"; message: string }>`
1112
+
1113
+ Analyzes activity patterns and detects potential security issues.
1114
+
1115
+ ```typescript
1116
+ const anomalies = honeypot.detectAnomalies();
1117
+ anomalies.forEach(anomaly => {
1118
+ console.log(`[${anomaly.severity.toUpperCase()}] ${anomaly.type}`);
1119
+ console.log(` ${anomaly.message}`);
1120
+ });
1121
+ ```
1122
+
1123
+ Detects:
1124
+ - High authentication failure rates
1125
+ - Excessive authentication failures
1126
+ - Unusual command execution volume
1127
+ - Unusual file write volume
1128
+
1129
+ ##### `reset(): void`
1130
+
1131
+ Clears audit log and resets all statistics counters.
1132
+
1133
+ ```typescript
1134
+ honeypot.reset(); // Fresh start
1135
+ ```
1136
+
1137
+ #### Audit Log Entry Structure
1138
+
1139
+ ```typescript
1140
+ interface AuditLogEntry {
1141
+ timestamp: string; // ISO-8601 timestamp
1142
+ type: string; // Event type (e.g., "auth:success", "file:write")
1143
+ source: string; // Event source component
1144
+ details: Record<string, unknown>; // Event-specific data
1145
+ }
1146
+ ```
1147
+
1148
+ #### Example Usage
1149
+
1150
+ See [Example 8: Security Auditing with HoneyPot](#example-8-security-auditing-with-honeypot) in Usage Examples.
1151
+
824
1152
  ---
825
1153
 
826
1154
  ### Key Types
@@ -1022,7 +1350,7 @@ vfs1.writeFile("/data/report.txt", "Baseline data");
1022
1350
  await vfs1.flushMirror();
1023
1351
  ssh1.stop();
1024
1352
 
1025
- console.log("State saved to ./container/.vfs/mirror.tar.gz");
1353
+ console.log("State available under ./container/.vfs/mirror");
1026
1354
 
1027
1355
  // Later: Reload and continue
1028
1356
  const shell2 = new VirtualShell("typescript-vm", undefined, "./container");
@@ -1156,6 +1484,133 @@ ssh.stop();
1156
1484
 
1157
1485
  ---
1158
1486
 
1487
+ ### Example 8: Security Auditing with HoneyPot
1488
+
1489
+ Track all system activity, detect anomalies, and maintain security audit logs:
1490
+
1491
+ ```typescript
1492
+ import {
1493
+ VirtualSshServer,
1494
+ VirtualShell,
1495
+ SshClient,
1496
+ HoneyPot,
1497
+ } from "typescript-virtual-container";
1498
+
1499
+ const shell = new VirtualShell("typescript-vm");
1500
+ const ssh = new VirtualSshServer({ port: 2222, shell });
1501
+ await ssh.start();
1502
+
1503
+ const users = ssh.getUsers()!;
1504
+ const vfs = ssh.getVfs()!;
1505
+
1506
+ // Initialize honeypot with 5000-entry log limit
1507
+ const honeypot = new HoneyPot(5000);
1508
+ honeypot.attach(shell, vfs, users, ssh);
1509
+
1510
+ // Create users
1511
+ await users.addUser("alice", "alice123");
1512
+ await users.addUser("bob", "bob456");
1513
+
1514
+ // Simulate activity
1515
+ const alice = new SshClient(shell, "alice");
1516
+ await alice.mkdir("/home/alice/projects", true);
1517
+ await alice.writeFile("/home/alice/projects/app.txt", "My application");
1518
+ await alice.ls("/home/alice/projects");
1519
+
1520
+ const bob = new SshClient(shell, "bob");
1521
+ // Bob tries invalid operations
1522
+ await bob.readFile("/etc/shadow"); // Will fail
1523
+ await bob.writeFile("/etc/passwd", "hacked"); // Will fail
1524
+
1525
+ // Collect stats
1526
+ const stats = honeypot.getStats();
1527
+ console.log("\n=== Activity Summary ===");
1528
+ console.log(`Auth attempts: ${stats.authAttempts}`);
1529
+ console.log(`Auth successes: ${stats.authSuccesses}`);
1530
+ console.log(`Auth failures: ${stats.authFailures}`);
1531
+ console.log(`Commands executed: ${stats.commands}`);
1532
+ console.log(`File writes: ${stats.fileWrites}`);
1533
+ console.log(`File reads: ${stats.fileReads}`);
1534
+ console.log(`Sessions active: ${stats.sessionStarts}`);
1535
+ console.log(`Users created: ${stats.userCreated}`);
1536
+
1537
+ // Get recent events
1538
+ console.log("\n=== Last 5 Events ===");
1539
+ honeypot.getRecent(5).forEach((entry) => {
1540
+ console.log(`[${entry.timestamp}] ${entry.source} -> ${entry.type}`);
1541
+ console.log(` Details: ${JSON.stringify(entry.details, null, 2)}`);
1542
+ });
1543
+
1544
+ // Detect anomalies
1545
+ console.log("\n=== Security Analysis ===");
1546
+ const anomalies = honeypot.detectAnomalies();
1547
+ if (anomalies.length > 0) {
1548
+ anomalies.forEach((anomaly) => {
1549
+ console.log(
1550
+ `[${anomaly.severity.toUpperCase()}] ${anomaly.type}: ${anomaly.message}`,
1551
+ );
1552
+ });
1553
+ } else {
1554
+ console.log("No anomalies detected");
1555
+ }
1556
+
1557
+ // Filter audit log by event type
1558
+ console.log("\n=== Auth Failures ===");
1559
+ const authFailures = honeypot.getAuditLog("auth:failure");
1560
+ authFailures.forEach((entry) => {
1561
+ console.log(
1562
+ ` ${entry.details.username} from ${entry.details.remoteAddress}`,
1563
+ );
1564
+ });
1565
+
1566
+ // Filter by source component
1567
+ console.log("\n=== All SSH Events ===");
1568
+ const sshEvents = honeypot.getAuditLog(undefined, "SshMimic");
1569
+ console.log(` Total SSH events: ${sshEvents.length}`);
1570
+
1571
+ // Export full audit log (for external storage/analysis)
1572
+ const fullAuditLog = honeypot.getAuditLog();
1573
+ console.log(`\nTotal audit entries: ${fullAuditLog.length}`);
1574
+
1575
+ // Optional: Reset for next test phase
1576
+ // honeypot.reset();
1577
+
1578
+ ssh.stop();
1579
+ ```
1580
+
1581
+ **Output example:**
1582
+
1583
+ ```
1584
+ [AUDIT] 2026-04-16T10:30:45.123Z | SshMimic | start { port: 2222 }
1585
+ [AUDIT] 2026-04-16T10:30:46.234Z | VirtualUserManager | user:add { username: 'alice' }
1586
+ [AUDIT] 2026-04-16T10:30:47.345Z | VirtualUserManager | user:add { username: 'bob' }
1587
+ [AUDIT] 2026-04-16T10:30:48.456Z | VirtualShell | command { command: 'mkdir /home/alice/projects', user: 'alice', cwd: '/home/alice' }
1588
+ [AUDIT] 2026-04-16T10:30:49.567Z | VirtualFileSystem | dir:create { path: '/home/alice/projects', mode: 16877 }
1589
+ [AUDIT] 2026-04-16T10:30:50.678Z | VirtualShell | command { command: 'writeFile /home/alice/projects/app.txt', user: 'alice', cwd: '/home/alice' }
1590
+
1591
+ === Activity Summary ===
1592
+ Auth attempts: 2
1593
+ Auth successes: 2
1594
+ Auth failures: 0
1595
+ Commands executed: 8
1596
+ File writes: 1
1597
+ File reads: 2
1598
+ Sessions active: 2
1599
+ Users created: 2
1600
+
1601
+ === Last 5 Events ===
1602
+ [2026-04-16T10:30:50.678Z] VirtualShell -> command
1603
+ Details: { command: 'ls /home/alice/projects', user: 'alice', cwd: '/home/alice/projects' }
1604
+
1605
+ === Security Analysis ===
1606
+ No anomalies detected
1607
+
1608
+ === All SSH Events ===
1609
+ Total SSH events: 4
1610
+ ```
1611
+
1612
+ ---
1613
+
1159
1614
  ## Built-in Commands
1160
1615
 
1161
1616
  The following commands are currently registered and available in both SSH shell mode and via `SshClient.exec()`. Some flags and edge-case behavior are still being expanded for shell compatibility.
@@ -1293,7 +1748,7 @@ You can use it in production-like automation contexts (sandboxed command runners
1293
1748
 
1294
1749
  ### Does data persist between restarts?
1295
1750
 
1296
- Yes, if you call `flushMirror()` and use a stable `basePath`. State is restored from `.vfs/mirror.tar.gz`.
1751
+ Yes, when using a stable `basePath`. Files are stored under `.vfs/mirror`.
1297
1752
 
1298
1753
  ### Is networking fully implemented for curl/wget?
1299
1754