typescript-virtual-container 1.2.0 → 1.2.2

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 (49) hide show
  1. package/.github/workflows/publish.yml +25 -0
  2. package/README.md +25 -8
  3. package/benchmark-results.txt +40 -0
  4. package/benchmark-virtualshell.ts +96 -0
  5. package/dist/Honeypot/index.d.ts.map +1 -1
  6. package/dist/Honeypot/index.js +9 -0
  7. package/dist/SSHClient/index.d.ts +0 -14
  8. package/dist/SSHClient/index.d.ts.map +1 -1
  9. package/dist/SSHClient/index.js +19 -0
  10. package/dist/SSHMimic/index.d.ts +0 -7
  11. package/dist/SSHMimic/index.d.ts.map +1 -1
  12. package/dist/SSHMimic/index.js +5 -0
  13. package/dist/SSHMimic/sftp.d.ts.map +1 -1
  14. package/dist/SSHMimic/sftp.js +5 -0
  15. package/dist/VirtualFileSystem/index.d.ts +0 -7
  16. package/dist/VirtualFileSystem/index.d.ts.map +1 -1
  17. package/dist/VirtualFileSystem/index.js +18 -0
  18. package/dist/VirtualShell/index.d.ts.map +1 -1
  19. package/dist/VirtualShell/index.js +14 -1
  20. package/dist/VirtualUserManager/index.d.ts +4 -1
  21. package/dist/VirtualUserManager/index.d.ts.map +1 -1
  22. package/dist/VirtualUserManager/index.js +72 -13
  23. package/dist/utils/perfLogger.d.ts +9 -0
  24. package/dist/utils/perfLogger.d.ts.map +1 -0
  25. package/dist/utils/perfLogger.js +49 -0
  26. package/package.json +6 -3
  27. package/src/Honeypot/index.ts +11 -0
  28. package/src/SSHClient/index.ts +23 -1
  29. package/src/SSHMimic/index.ts +6 -0
  30. package/src/SSHMimic/sftp.ts +8 -1
  31. package/src/VirtualFileSystem/index.ts +20 -0
  32. package/src/VirtualShell/index.ts +18 -1
  33. package/src/VirtualUserManager/index.ts +103 -26
  34. package/src/utils/perfLogger.ts +72 -0
  35. package/dist/VirtualFileSystem/archive.d.ts +0 -5
  36. package/dist/VirtualFileSystem/archive.d.ts.map +0 -1
  37. package/dist/VirtualFileSystem/archive.js +0 -56
  38. package/dist/VirtualFileSystem/snapshot.d.ts +0 -5
  39. package/dist/VirtualFileSystem/snapshot.d.ts.map +0 -1
  40. package/dist/VirtualFileSystem/snapshot.js +0 -59
  41. package/dist/VirtualFileSystem/tree.d.ts +0 -3
  42. package/dist/VirtualFileSystem/tree.d.ts.map +0 -1
  43. package/dist/VirtualFileSystem/tree.js +0 -19
  44. package/dist/honeypot.d.ts +0 -132
  45. package/dist/honeypot.d.ts.map +0 -1
  46. package/dist/honeypot.js +0 -289
  47. package/src/VirtualFileSystem/archive.ts +0 -74
  48. package/src/VirtualFileSystem/snapshot.ts +0 -84
  49. package/src/VirtualFileSystem/tree.ts +0 -34
package/dist/honeypot.js DELETED
@@ -1,289 +0,0 @@
1
- /**
2
- * Honeypot tracking and auditing module for virtual shell events.
3
- *
4
- * Attaches listeners to VirtualShell, VirtualFileSystem, VirtualUserManager,
5
- * SshMimic, and SftpMimic instances to log all activity for security auditing,
6
- * anomaly detection, and forensic analysis.
7
- *
8
- * @module honeypot
9
- */
10
- /**
11
- * HoneyPot audit and event tracking utility.
12
- *
13
- * Singleton-like helper that attaches listeners to virtual shell components
14
- * and maintains an audit log of all activity.
15
- */
16
- export class HoneyPot {
17
- auditLog = [];
18
- stats = {
19
- authAttempts: 0,
20
- authSuccesses: 0,
21
- authFailures: 0,
22
- commands: 0,
23
- fileWrites: 0,
24
- fileReads: 0,
25
- sessionStarts: 0,
26
- sessionEnds: 0,
27
- userCreated: 0,
28
- userDeleted: 0,
29
- clientConnects: 0,
30
- clientDisconnects: 0,
31
- };
32
- maxLogSize;
33
- /**
34
- * Creates a new HoneyPot instance.
35
- *
36
- * @param maxLogSize Maximum audit log entries to retain (default: 10000).
37
- */
38
- constructor(maxLogSize = 10000) {
39
- this.maxLogSize = maxLogSize;
40
- }
41
- /**
42
- * Attaches honeypot listeners to all provided event emitters.
43
- *
44
- * @param shell VirtualShell instance.
45
- * @param vfs VirtualFileSystem instance.
46
- * @param users VirtualUserManager instance.
47
- * @param ssh SshMimic instance (optional).
48
- * @param sftp SftpMimic instance (optional).
49
- */
50
- attach(shell, vfs, users, ssh, sftp) {
51
- this.attachVirtualShell(shell);
52
- this.attachVirtualFileSystem(vfs);
53
- this.attachVirtualUserManager(users);
54
- if (ssh) {
55
- this.attachSshMimic(ssh);
56
- }
57
- if (sftp) {
58
- this.attachSftpMimic(sftp);
59
- }
60
- }
61
- /**
62
- * Attaches to VirtualShell events.
63
- */
64
- attachVirtualShell(shell) {
65
- shell.on("initialized", () => {
66
- this.log("VirtualShell", "initialized", {});
67
- });
68
- shell.on("command", (data) => {
69
- this.stats.commands++;
70
- this.log("VirtualShell", "command", data);
71
- });
72
- shell.on("session:start", (data) => {
73
- this.stats.sessionStarts++;
74
- this.log("VirtualShell", "session:start", data);
75
- });
76
- }
77
- /**
78
- * Attaches to VirtualFileSystem events.
79
- */
80
- attachVirtualFileSystem(vfs) {
81
- vfs.on("file:read", (data) => {
82
- this.stats.fileReads++;
83
- this.log("VirtualFileSystem", "file:read", data);
84
- });
85
- vfs.on("file:write", (data) => {
86
- this.stats.fileWrites++;
87
- this.log("VirtualFileSystem", "file:write", data);
88
- });
89
- vfs.on("dir:create", (data) => {
90
- this.log("VirtualFileSystem", "dir:create", data);
91
- });
92
- vfs.on("mirror:flush", () => {
93
- this.log("VirtualFileSystem", "mirror:flush", {});
94
- });
95
- }
96
- /**
97
- * Attaches to VirtualUserManager events.
98
- */
99
- attachVirtualUserManager(users) {
100
- users.on("initialized", () => {
101
- this.log("VirtualUserManager", "initialized", {});
102
- });
103
- users.on("user:add", (data) => {
104
- this.stats.userCreated++;
105
- this.log("VirtualUserManager", "user:add", data);
106
- });
107
- users.on("user:delete", (data) => {
108
- this.stats.userDeleted++;
109
- this.log("VirtualUserManager", "user:delete", data);
110
- });
111
- users.on("session:register", (data) => {
112
- this.log("VirtualUserManager", "session:register", data);
113
- });
114
- users.on("session:unregister", (data) => {
115
- this.stats.sessionEnds++;
116
- this.log("VirtualUserManager", "session:unregister", data);
117
- });
118
- }
119
- /**
120
- * Attaches to SshMimic events.
121
- */
122
- attachSshMimic(ssh) {
123
- ssh.on("start", (data) => {
124
- this.log("SshMimic", "start", data);
125
- });
126
- ssh.on("stop", () => {
127
- this.log("SshMimic", "stop", {});
128
- });
129
- ssh.on("auth:success", (data) => {
130
- this.stats.authAttempts++;
131
- this.stats.authSuccesses++;
132
- this.log("SshMimic", "auth:success", data);
133
- });
134
- ssh.on("auth:failure", (data) => {
135
- this.stats.authAttempts++;
136
- this.stats.authFailures++;
137
- this.log("SshMimic", "auth:failure", data);
138
- });
139
- ssh.on("client:connect", () => {
140
- this.stats.clientConnects++;
141
- this.log("SshMimic", "client:connect", {});
142
- });
143
- ssh.on("client:disconnect", (data) => {
144
- this.stats.clientDisconnects++;
145
- this.log("SshMimic", "client:disconnect", data);
146
- });
147
- }
148
- /**
149
- * Attaches to SftpMimic events.
150
- */
151
- attachSftpMimic(sftp) {
152
- sftp.on("start", (data) => {
153
- this.log("SftpMimic", "start", data);
154
- });
155
- sftp.on("stop", () => {
156
- this.log("SftpMimic", "stop", {});
157
- });
158
- sftp.on("auth:success", (data) => {
159
- this.stats.authAttempts++;
160
- this.stats.authSuccesses++;
161
- this.log("SftpMimic", "auth:success", data);
162
- });
163
- sftp.on("auth:failure", (data) => {
164
- this.stats.authAttempts++;
165
- this.stats.authFailures++;
166
- this.log("SftpMimic", "auth:failure", data);
167
- });
168
- sftp.on("client:connect", () => {
169
- this.stats.clientConnects++;
170
- this.log("SftpMimic", "client:connect", {});
171
- });
172
- sftp.on("client:disconnect", (data) => {
173
- this.stats.clientDisconnects++;
174
- this.log("SftpMimic", "client:disconnect", data);
175
- });
176
- }
177
- /**
178
- * Records an audit log entry.
179
- *
180
- * @param source Event source (e.g., "SshMimic", "VirtualFileSystem").
181
- * @param type Event type.
182
- * @param details Event-specific data.
183
- */
184
- log(source, type, details) {
185
- const entry = {
186
- timestamp: new Date().toISOString(),
187
- type,
188
- source,
189
- details,
190
- };
191
- this.auditLog.push(entry);
192
- // Trim log if exceeds max size
193
- if (this.auditLog.length > this.maxLogSize) {
194
- this.auditLog = this.auditLog.slice(-this.maxLogSize);
195
- }
196
- // Console output for real-time monitoring
197
- console.log(`[AUDIT] ${entry.timestamp} | ${source} | ${type}`, details);
198
- }
199
- /**
200
- * Returns audit log entries matching optional filters.
201
- *
202
- * @param type Optional event type filter.
203
- * @param source Optional source filter.
204
- * @returns Filtered audit log entries.
205
- */
206
- getAuditLog(type, source) {
207
- return this.auditLog.filter((entry) => (!type || entry.type === type) && (!source || entry.source === source));
208
- }
209
- /**
210
- * Returns current activity statistics.
211
- *
212
- * @returns Snapshot of honeypot stats.
213
- */
214
- getStats() {
215
- return Object.freeze({ ...this.stats });
216
- }
217
- /**
218
- * Clears audit log and resets statistics.
219
- */
220
- reset() {
221
- this.auditLog = [];
222
- this.stats = {
223
- authAttempts: 0,
224
- authSuccesses: 0,
225
- authFailures: 0,
226
- commands: 0,
227
- fileWrites: 0,
228
- fileReads: 0,
229
- sessionStarts: 0,
230
- sessionEnds: 0,
231
- userCreated: 0,
232
- userDeleted: 0,
233
- clientConnects: 0,
234
- clientDisconnects: 0,
235
- };
236
- }
237
- /**
238
- * Returns recent log entries in reverse chronological order.
239
- *
240
- * @param limit Number of recent entries to return (default: 100).
241
- * @returns Recent audit log entries.
242
- */
243
- getRecent(limit = 100) {
244
- return this.auditLog.slice(Math.max(0, this.auditLog.length - limit));
245
- }
246
- /**
247
- * Detects potential security issues based on activity patterns.
248
- *
249
- * @returns Array of anomalies detected.
250
- */
251
- detectAnomalies() {
252
- const anomalies = [];
253
- // High auth failure rate
254
- if (this.stats.authAttempts > 0 &&
255
- this.stats.authFailures / this.stats.authAttempts > 0.5) {
256
- anomalies.push({
257
- type: "high_auth_failure_rate",
258
- severity: "medium",
259
- message: `Auth failure rate: ${((this.stats.authFailures / this.stats.authAttempts) * 100).toFixed(1)}%`,
260
- });
261
- }
262
- // Excessive auth failures in short time
263
- if (this.stats.authFailures > 10) {
264
- anomalies.push({
265
- type: "excessive_auth_failures",
266
- severity: "high",
267
- message: `${this.stats.authFailures} authentication failures detected`,
268
- });
269
- }
270
- // Unusual command execution volume
271
- if (this.stats.commands > 1000) {
272
- anomalies.push({
273
- type: "high_command_volume",
274
- severity: "low",
275
- message: `${this.stats.commands} commands executed`,
276
- });
277
- }
278
- // Unusual file write volume
279
- if (this.stats.fileWrites > 500) {
280
- anomalies.push({
281
- type: "high_write_volume",
282
- severity: "medium",
283
- message: `${this.stats.fileWrites} file write operations`,
284
- });
285
- }
286
- return anomalies;
287
- }
288
- }
289
- export default HoneyPot;
@@ -1,74 +0,0 @@
1
- import { promises as fs } from "node:fs";
2
- import * as tarStream from "tar-stream";
3
- import type { VfsSnapshot } from "../types/vfs";
4
-
5
- export async function archiveExists(archivePath: string): Promise<boolean> {
6
- try {
7
- await fs.access(archivePath);
8
- return true;
9
- } catch {
10
- return false;
11
- }
12
- }
13
-
14
- export async function createTarBuffer(snapshotJson: string): Promise<Buffer> {
15
- const pack = tarStream.pack();
16
- const chunks: Buffer[] = [];
17
-
18
- const finished = new Promise<Buffer>((resolve, reject) => {
19
- pack.on("data", (chunk: Buffer) => chunks.push(Buffer.from(chunk)));
20
- pack.on("error", reject);
21
- pack.on("end", () => resolve(Buffer.concat(chunks)));
22
- });
23
-
24
- pack.entry(
25
- { name: "snapshot.json", mode: 0o600 },
26
- snapshotJson,
27
- (error?: Error | null) => {
28
- if (error) {
29
- return;
30
- }
31
-
32
- pack.finalize();
33
- },
34
- );
35
-
36
- return finished;
37
- }
38
-
39
- export async function readSnapshotFromTar(
40
- tarBuffer: Buffer,
41
- ): Promise<VfsSnapshot> {
42
- return new Promise<VfsSnapshot>((resolve, reject) => {
43
- const extract = tarStream.extract();
44
- let snapshotText = "";
45
- let found = false;
46
-
47
- extract.on("entry", (header, stream, next) => {
48
- if (header.name === "snapshot.json") {
49
- found = true;
50
- stream.on("data", (chunk: Buffer) => {
51
- snapshotText += chunk.toString("utf8");
52
- });
53
- stream.on("end", next);
54
- stream.resume();
55
- return;
56
- }
57
-
58
- stream.resume();
59
- stream.on("end", next);
60
- });
61
-
62
- extract.on("finish", () => {
63
- if (!found) {
64
- reject(new Error("snapshot.json missing from archive"));
65
- return;
66
- }
67
-
68
- resolve(JSON.parse(snapshotText) as VfsSnapshot);
69
- });
70
-
71
- extract.on("error", reject);
72
- extract.end(tarBuffer);
73
- });
74
- }
@@ -1,84 +0,0 @@
1
- import type {
2
- VfsSnapshot,
3
- VfsSnapshotDirectoryNode,
4
- VfsSnapshotNode,
5
- } from "../types/vfs";
6
- import type { InternalDirectoryNode, InternalNode } from "./internalTypes";
7
-
8
- function serializeNode(node: InternalNode): VfsSnapshotNode {
9
- if (node.type === "file") {
10
- return {
11
- type: "file",
12
- name: node.name,
13
- mode: node.mode,
14
- createdAt: node.createdAt.toISOString(),
15
- updatedAt: node.updatedAt.toISOString(),
16
- compressed: node.compressed,
17
- contentBase64: node.content.toString("base64"),
18
- };
19
- }
20
-
21
- return serializeDirectory(node);
22
- }
23
-
24
- function serializeDirectory(
25
- node: InternalDirectoryNode,
26
- ): VfsSnapshotDirectoryNode {
27
- return {
28
- type: "directory",
29
- name: node.name,
30
- mode: node.mode,
31
- createdAt: node.createdAt.toISOString(),
32
- updatedAt: node.updatedAt.toISOString(),
33
- children: Array.from(node.children.values()).map((child) =>
34
- serializeNode(child),
35
- ),
36
- };
37
- }
38
-
39
- function deserializeNode(node: VfsSnapshotNode): InternalNode {
40
- if (node.type === "file") {
41
- return {
42
- type: "file",
43
- name: node.name,
44
- mode: node.mode,
45
- createdAt: new Date(node.createdAt),
46
- updatedAt: new Date(node.updatedAt),
47
- content: Buffer.from(node.contentBase64, "base64"),
48
- compressed: node.compressed,
49
- };
50
- }
51
-
52
- return deserializeDirectory(node);
53
- }
54
-
55
- function deserializeDirectory(
56
- node: VfsSnapshotDirectoryNode,
57
- ): InternalDirectoryNode {
58
- return {
59
- type: "directory",
60
- name: node.name,
61
- mode: node.mode,
62
- createdAt: new Date(node.createdAt),
63
- updatedAt: new Date(node.updatedAt),
64
- children: new Map<string, InternalNode>(
65
- node.children.map((child) => [child.name, deserializeNode(child)]),
66
- ),
67
- };
68
- }
69
-
70
- export function createSnapshot(root: InternalDirectoryNode): VfsSnapshot {
71
- return { root: serializeDirectory(root) };
72
- }
73
-
74
- export function applySnapshot(
75
- rootTarget: InternalDirectoryNode,
76
- snapshot: VfsSnapshot,
77
- ): void {
78
- const root = deserializeDirectory(snapshot.root);
79
- rootTarget.name = root.name;
80
- rootTarget.mode = root.mode;
81
- rootTarget.createdAt = root.createdAt;
82
- rootTarget.updatedAt = root.updatedAt;
83
- rootTarget.children = root.children;
84
- }
@@ -1,34 +0,0 @@
1
- import type { InternalDirectoryNode } from "./internalTypes";
2
-
3
- function walkTree(
4
- node: InternalDirectoryNode,
5
- indent: string,
6
- lines: string[],
7
- ): void {
8
- const entries = Array.from(node.children.entries()).sort(([a], [b]) =>
9
- a.localeCompare(b),
10
- );
11
-
12
- entries.forEach(([name, child], index) => {
13
- const isLast = index === entries.length - 1;
14
- const branch = isLast ? "`-- " : "|-- ";
15
- const nextIndent = indent + (isLast ? " " : "| ");
16
-
17
- if (child.type === "file") {
18
- lines.push(`${indent}${branch}${name}${child.compressed ? " [gz]" : ""}`);
19
- return;
20
- }
21
-
22
- lines.push(`${indent}${branch}${name}/`);
23
- walkTree(child, nextIndent, lines);
24
- });
25
- }
26
-
27
- export function renderTree(
28
- node: InternalDirectoryNode,
29
- rootLabel: string,
30
- ): string {
31
- const lines: string[] = [rootLabel];
32
- walkTree(node, "", lines);
33
- return lines.join("\n");
34
- }