typescript-virtual-container 1.3.4 → 1.4.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.
- package/.vscode/settings.json +0 -1
- package/README.md +674 -1504
- package/benchmark-results.txt +21 -21
- package/builds/self-standalone.js +282 -332
- package/builds/self-standalone.js.map +4 -4
- package/builds/standalone-wo-sftp.js +218 -282
- package/builds/standalone-wo-sftp.js.map +4 -4
- package/builds/standalone.js +271 -335
- package/builds/standalone.js.map +4 -4
- package/builds/web-full-api.min.js +3 -3
- package/builds/web-full-api.min.js.map +4 -4
- package/builds/web.min.js +2 -2
- package/builds/web.min.js.map +4 -4
- package/bun.lock +14 -12
- package/dist/SSHClient/index.d.ts.map +1 -1
- package/dist/SSHClient/index.js +5 -3
- package/dist/SSHMimic/executor.d.ts +1 -3
- package/dist/SSHMimic/executor.d.ts.map +1 -1
- package/dist/SSHMimic/executor.js +20 -22
- package/dist/SSHMimic/index.d.ts.map +1 -1
- package/dist/SSHMimic/index.js +5 -3
- package/dist/SSHMimic/sftp.d.ts.map +1 -1
- package/dist/SSHMimic/sftp.js +26 -21
- package/dist/VirtualPackageManager/index.d.ts.map +1 -1
- package/dist/VirtualPackageManager/index.js +29 -1
- package/dist/VirtualShell/shell.d.ts.map +1 -1
- package/dist/VirtualShell/shell.js +25 -3
- package/dist/VirtualShell/shellParser.d.ts +1 -8
- package/dist/VirtualShell/shellParser.d.ts.map +1 -1
- package/dist/VirtualShell/shellParser.js +2 -81
- package/dist/VirtualUserManager/index.d.ts +7 -1
- package/dist/VirtualUserManager/index.d.ts.map +1 -1
- package/dist/VirtualUserManager/index.js +47 -16
- package/dist/commands/adduser.d.ts +10 -4
- package/dist/commands/adduser.d.ts.map +1 -1
- package/dist/commands/adduser.js +75 -12
- package/dist/commands/alias.d.ts +5 -0
- package/dist/commands/alias.d.ts.map +1 -1
- package/dist/commands/alias.js +5 -0
- package/dist/commands/apt.d.ts +5 -0
- package/dist/commands/apt.d.ts.map +1 -1
- package/dist/commands/apt.js +5 -0
- package/dist/commands/awk.d.ts +10 -8
- package/dist/commands/awk.d.ts.map +1 -1
- package/dist/commands/awk.js +156 -28
- package/dist/commands/cd.d.ts.map +1 -1
- package/dist/commands/cd.js +0 -3
- package/dist/commands/clear.d.ts +5 -0
- package/dist/commands/clear.d.ts.map +1 -1
- package/dist/commands/clear.js +5 -0
- package/dist/commands/command-helpers.d.ts.map +1 -1
- package/dist/commands/command-helpers.js +8 -0
- package/dist/commands/curl.d.ts.map +1 -1
- package/dist/commands/curl.js +2 -1
- package/dist/commands/declare.d.ts +5 -0
- package/dist/commands/declare.d.ts.map +1 -1
- package/dist/commands/declare.js +5 -0
- package/dist/commands/deluser.d.ts +12 -0
- package/dist/commands/deluser.d.ts.map +1 -1
- package/dist/commands/deluser.js +72 -6
- package/dist/commands/df.d.ts +5 -0
- package/dist/commands/df.d.ts.map +1 -1
- package/dist/commands/df.js +5 -0
- package/dist/commands/du.d.ts +5 -0
- package/dist/commands/du.d.ts.map +1 -1
- package/dist/commands/du.js +5 -0
- package/dist/commands/export.d.ts +5 -0
- package/dist/commands/export.d.ts.map +1 -1
- package/dist/commands/export.js +5 -0
- package/dist/commands/grep.d.ts.map +1 -1
- package/dist/commands/grep.js +22 -4
- package/dist/commands/groups.d.ts +5 -0
- package/dist/commands/groups.d.ts.map +1 -1
- package/dist/commands/groups.js +5 -0
- package/dist/commands/gzip.d.ts +5 -2
- package/dist/commands/gzip.d.ts.map +1 -1
- package/dist/commands/gzip.js +54 -28
- package/dist/commands/head.d.ts.map +1 -1
- package/dist/commands/head.js +12 -3
- package/dist/commands/htop.d.ts +5 -0
- package/dist/commands/htop.d.ts.map +1 -1
- package/dist/commands/htop.js +5 -0
- package/dist/commands/kill.d.ts +5 -0
- package/dist/commands/kill.d.ts.map +1 -1
- package/dist/commands/kill.js +5 -0
- package/dist/commands/ln.d.ts +2 -0
- package/dist/commands/ln.d.ts.map +1 -1
- package/dist/commands/ln.js +22 -0
- package/dist/commands/ls.d.ts.map +1 -1
- package/dist/commands/ls.js +15 -0
- package/dist/commands/lsb-release.d.ts +5 -0
- package/dist/commands/lsb-release.d.ts.map +1 -1
- package/dist/commands/lsb-release.js +5 -0
- package/dist/commands/man.d.ts.map +1 -1
- package/dist/commands/man.js +30 -136
- package/dist/commands/mkdir.d.ts +5 -0
- package/dist/commands/mkdir.d.ts.map +1 -1
- package/dist/commands/mkdir.js +5 -0
- package/dist/commands/mv.d.ts +5 -0
- package/dist/commands/mv.d.ts.map +1 -1
- package/dist/commands/mv.js +5 -0
- package/dist/commands/nano.d.ts +5 -0
- package/dist/commands/nano.d.ts.map +1 -1
- package/dist/commands/nano.js +5 -0
- package/dist/commands/neofetch.d.ts +5 -0
- package/dist/commands/neofetch.d.ts.map +1 -1
- package/dist/commands/neofetch.js +14 -5
- package/dist/commands/passwd.d.ts +8 -0
- package/dist/commands/passwd.d.ts.map +1 -1
- package/dist/commands/passwd.js +32 -11
- package/dist/commands/ping.d.ts +5 -0
- package/dist/commands/ping.d.ts.map +1 -1
- package/dist/commands/ping.js +5 -0
- package/dist/commands/printf.d.ts +5 -0
- package/dist/commands/printf.d.ts.map +1 -1
- package/dist/commands/printf.js +43 -12
- package/dist/commands/ps.d.ts +5 -0
- package/dist/commands/ps.d.ts.map +1 -1
- package/dist/commands/ps.js +5 -0
- package/dist/commands/read.d.ts +5 -0
- package/dist/commands/read.d.ts.map +1 -1
- package/dist/commands/read.js +5 -0
- package/dist/commands/registry.d.ts.map +1 -1
- package/dist/commands/registry.js +4 -1
- package/dist/commands/rm.d.ts +5 -0
- package/dist/commands/rm.d.ts.map +1 -1
- package/dist/commands/rm.js +5 -0
- package/dist/commands/runtime.d.ts.map +1 -1
- package/dist/commands/runtime.js +1 -57
- package/dist/commands/sed.d.ts +5 -0
- package/dist/commands/sed.d.ts.map +1 -1
- package/dist/commands/sed.js +5 -0
- package/dist/commands/set.d.ts +5 -6
- package/dist/commands/set.d.ts.map +1 -1
- package/dist/commands/set.js +5 -22
- package/dist/commands/sh.d.ts +6 -0
- package/dist/commands/sh.d.ts.map +1 -1
- package/dist/commands/sh.js +6 -0
- package/dist/commands/shift.d.ts +10 -0
- package/dist/commands/shift.d.ts.map +1 -1
- package/dist/commands/shift.js +10 -0
- package/dist/commands/sleep.d.ts +5 -0
- package/dist/commands/sleep.d.ts.map +1 -1
- package/dist/commands/sleep.js +5 -0
- package/dist/commands/sort.d.ts +5 -0
- package/dist/commands/sort.d.ts.map +1 -1
- package/dist/commands/sort.js +5 -0
- package/dist/commands/source.d.ts +5 -0
- package/dist/commands/source.d.ts.map +1 -1
- package/dist/commands/source.js +5 -0
- package/dist/commands/stat.d.ts +7 -0
- package/dist/commands/stat.d.ts.map +1 -0
- package/dist/commands/stat.js +56 -0
- package/dist/commands/su.d.ts +13 -0
- package/dist/commands/su.d.ts.map +1 -1
- package/dist/commands/su.js +45 -14
- package/dist/commands/sudo.d.ts.map +1 -1
- package/dist/commands/sudo.js +5 -0
- package/dist/commands/tail.d.ts +5 -0
- package/dist/commands/tail.d.ts.map +1 -1
- package/dist/commands/tail.js +15 -3
- package/dist/commands/tar.d.ts +5 -0
- package/dist/commands/tar.d.ts.map +1 -1
- package/dist/commands/tar.js +40 -10
- package/dist/commands/tee.d.ts +5 -0
- package/dist/commands/tee.d.ts.map +1 -1
- package/dist/commands/tee.js +5 -0
- package/dist/commands/touch.d.ts +5 -0
- package/dist/commands/touch.d.ts.map +1 -1
- package/dist/commands/touch.js +5 -0
- package/dist/commands/tr.d.ts.map +1 -1
- package/dist/commands/tr.js +45 -10
- package/dist/commands/tree.d.ts +5 -0
- package/dist/commands/tree.d.ts.map +1 -1
- package/dist/commands/tree.js +5 -0
- package/dist/commands/true.d.ts +10 -0
- package/dist/commands/true.d.ts.map +1 -1
- package/dist/commands/true.js +10 -0
- package/dist/commands/type.d.ts +5 -0
- package/dist/commands/type.d.ts.map +1 -1
- package/dist/commands/type.js +5 -0
- package/dist/commands/uname.d.ts +5 -0
- package/dist/commands/uname.d.ts.map +1 -1
- package/dist/commands/uname.js +5 -0
- package/dist/commands/uniq.d.ts +5 -0
- package/dist/commands/uniq.d.ts.map +1 -1
- package/dist/commands/uniq.js +5 -0
- package/dist/commands/unset.d.ts +5 -0
- package/dist/commands/unset.d.ts.map +1 -1
- package/dist/commands/unset.js +5 -0
- package/dist/commands/uptime.d.ts +5 -0
- package/dist/commands/uptime.d.ts.map +1 -1
- package/dist/commands/uptime.js +5 -0
- package/dist/commands/wc.d.ts +5 -0
- package/dist/commands/wc.d.ts.map +1 -1
- package/dist/commands/wc.js +5 -0
- package/dist/commands/wget.d.ts +5 -0
- package/dist/commands/wget.d.ts.map +1 -1
- package/dist/commands/wget.js +16 -1
- package/dist/commands/who.d.ts +5 -0
- package/dist/commands/who.d.ts.map +1 -1
- package/dist/commands/who.js +5 -0
- package/dist/commands/whoami.d.ts +5 -0
- package/dist/commands/whoami.d.ts.map +1 -1
- package/dist/commands/whoami.js +5 -0
- package/dist/commands/xargs.d.ts +5 -0
- package/dist/commands/xargs.d.ts.map +1 -1
- package/dist/commands/xargs.js +5 -0
- package/dist/self-standalone.js +254 -30
- package/dist/types/commands.d.ts +36 -0
- package/dist/types/commands.d.ts.map +1 -1
- package/dist/utils/tokenize.d.ts +20 -0
- package/dist/utils/tokenize.d.ts.map +1 -0
- package/dist/utils/tokenize.js +74 -0
- package/examples/web.min.js +2 -2
- package/package.json +2 -2
- package/src/SSHClient/index.ts +6 -3
- package/src/SSHMimic/executor.ts +21 -44
- package/src/SSHMimic/index.ts +7 -5
- package/src/SSHMimic/sftp.ts +28 -21
- package/src/VirtualPackageManager/index.ts +29 -1
- package/src/VirtualShell/shell.ts +34 -4
- package/src/VirtualShell/shellParser.ts +2 -103
- package/src/VirtualUserManager/index.ts +43 -19
- package/src/commands/adduser.ts +86 -13
- package/src/commands/alias.ts +5 -0
- package/src/commands/apt.ts +5 -0
- package/src/commands/awk.ts +154 -29
- package/src/commands/cd.ts +0 -4
- package/src/commands/clear.ts +5 -0
- package/src/commands/command-helpers.ts +9 -0
- package/src/commands/curl.ts +2 -1
- package/src/commands/declare.ts +5 -0
- package/src/commands/deluser.ts +84 -7
- package/src/commands/df.ts +5 -0
- package/src/commands/du.ts +5 -0
- package/src/commands/export.ts +5 -0
- package/src/commands/grep.ts +21 -8
- package/src/commands/groups.ts +5 -0
- package/src/commands/gzip.ts +61 -28
- package/src/commands/head.ts +14 -4
- package/src/commands/htop.ts +5 -0
- package/src/commands/kill.ts +5 -0
- package/src/commands/ln.ts +22 -0
- package/src/commands/ls.ts +17 -0
- package/src/commands/lsb-release.ts +5 -0
- package/src/commands/man.ts +38 -143
- package/src/commands/manuals/adduser.txt +11 -0
- package/src/commands/manuals/apt-cache.txt +12 -0
- package/src/commands/manuals/apt.txt +20 -0
- package/src/commands/manuals/awk.txt +13 -0
- package/src/commands/manuals/cat.txt +14 -0
- package/src/commands/manuals/cd.txt +16 -0
- package/src/commands/manuals/chmod.txt +16 -0
- package/src/commands/manuals/clear.txt +10 -0
- package/src/commands/manuals/cp.txt +10 -0
- package/src/commands/manuals/curl.txt +20 -0
- package/src/commands/manuals/date.txt +14 -0
- package/src/commands/manuals/declare.txt +12 -0
- package/src/commands/manuals/deluser.txt +10 -0
- package/src/commands/manuals/df.txt +10 -0
- package/src/commands/manuals/dpkg-query.txt +11 -0
- package/src/commands/manuals/dpkg.txt +14 -0
- package/src/commands/manuals/du.txt +11 -0
- package/src/commands/manuals/echo.txt +11 -0
- package/src/commands/manuals/false.txt +10 -0
- package/src/commands/manuals/find.txt +11 -0
- package/src/commands/manuals/free.txt +12 -0
- package/src/commands/manuals/grep.txt +13 -0
- package/src/commands/manuals/groups.txt +10 -0
- package/src/commands/manuals/gzip.txt +11 -0
- package/src/commands/manuals/head.txt +10 -0
- package/src/commands/manuals/help.txt +11 -0
- package/src/commands/manuals/history.txt +11 -0
- package/src/commands/manuals/hostname.txt +10 -0
- package/src/commands/manuals/id.txt +10 -0
- package/src/commands/manuals/kill.txt +13 -0
- package/src/commands/manuals/ls.txt +20 -0
- package/src/commands/manuals/lsb_release.txt +14 -0
- package/src/commands/manuals/mkdir.txt +10 -0
- package/src/commands/manuals/mv.txt +10 -0
- package/src/commands/manuals/nano.txt +11 -0
- package/src/commands/manuals/neofetch.txt +10 -0
- package/src/commands/manuals/node.txt +13 -0
- package/src/commands/manuals/npm.txt +13 -0
- package/src/commands/manuals/npx.txt +13 -0
- package/src/commands/manuals/passwd.txt +11 -0
- package/src/commands/manuals/ping.txt +10 -0
- package/src/commands/manuals/printf.txt +11 -0
- package/src/commands/manuals/ps.txt +10 -0
- package/src/commands/manuals/pwd.txt +10 -0
- package/src/commands/manuals/python3.txt +13 -0
- package/src/commands/manuals/readlink.txt +10 -0
- package/src/commands/manuals/return.txt +10 -0
- package/src/commands/manuals/rm.txt +10 -0
- package/src/commands/manuals/sed.txt +11 -0
- package/src/commands/manuals/set.txt +11 -0
- package/src/commands/manuals/shift.txt +10 -0
- package/src/commands/manuals/sleep.txt +10 -0
- package/src/commands/manuals/sort.txt +12 -0
- package/src/commands/manuals/source.txt +11 -0
- package/src/commands/manuals/ssh.txt +11 -0
- package/src/commands/manuals/stat.txt +10 -0
- package/src/commands/manuals/su.txt +13 -0
- package/src/commands/manuals/sudo.txt +11 -0
- package/src/commands/manuals/tail.txt +10 -0
- package/src/commands/manuals/tar.txt +19 -0
- package/src/commands/manuals/tee.txt +10 -0
- package/src/commands/manuals/test.txt +11 -0
- package/src/commands/manuals/touch.txt +11 -0
- package/src/commands/manuals/tr.txt +10 -0
- package/src/commands/manuals/trap.txt +10 -0
- package/src/commands/manuals/true.txt +10 -0
- package/src/commands/manuals/type.txt +10 -0
- package/src/commands/manuals/uname.txt +12 -0
- package/src/commands/manuals/uniq.txt +12 -0
- package/src/commands/manuals/unset.txt +10 -0
- package/src/commands/manuals/uptime.txt +11 -0
- package/src/commands/manuals/wc.txt +12 -0
- package/src/commands/manuals/wget.txt +12 -0
- package/src/commands/manuals/which.txt +10 -0
- package/src/commands/manuals/whoami.txt +10 -0
- package/src/commands/manuals/xargs.txt +10 -0
- package/src/commands/mkdir.ts +5 -0
- package/src/commands/mv.ts +5 -0
- package/src/commands/nano.ts +5 -0
- package/src/commands/neofetch.ts +15 -6
- package/src/commands/passwd.ts +35 -12
- package/src/commands/ping.ts +5 -0
- package/src/commands/printf.ts +30 -13
- package/src/commands/ps.ts +5 -0
- package/src/commands/read.ts +5 -0
- package/src/commands/registry.ts +4 -1
- package/src/commands/rm.ts +5 -0
- package/src/commands/runtime.ts +1 -61
- package/src/commands/sed.ts +5 -0
- package/src/commands/set.ts +5 -24
- package/src/commands/sh.ts +9 -3
- package/src/commands/shift.ts +10 -0
- package/src/commands/sleep.ts +5 -0
- package/src/commands/sort.ts +5 -0
- package/src/commands/source.ts +5 -0
- package/src/commands/stat.ts +61 -0
- package/src/commands/su.ts +54 -16
- package/src/commands/sudo.ts +5 -0
- package/src/commands/tail.ts +17 -3
- package/src/commands/tar.ts +38 -15
- package/src/commands/tee.ts +5 -0
- package/src/commands/touch.ts +5 -0
- package/src/commands/tr.ts +54 -10
- package/src/commands/tree.ts +5 -0
- package/src/commands/true.ts +10 -0
- package/src/commands/type.ts +5 -0
- package/src/commands/uname.ts +5 -0
- package/src/commands/uniq.ts +5 -0
- package/src/commands/unset.ts +5 -0
- package/src/commands/uptime.ts +5 -0
- package/src/commands/wc.ts +5 -0
- package/src/commands/wget.ts +17 -1
- package/src/commands/who.ts +5 -0
- package/src/commands/whoami.ts +5 -0
- package/src/commands/xargs.ts +5 -0
- package/src/self-standalone.ts +316 -33
- package/src/types/commands.ts +37 -0
- package/src/utils/tokenize.ts +78 -0
- package/tests/new-features.test.ts +2 -2
- package/builds/web-iife.min.js +0 -13
- package/builds/web-iife.min.js.map +0 -7
package/dist/SSHMimic/index.js
CHANGED
|
@@ -19,6 +19,9 @@ import { loadOrCreateHostKey } from "./hostKey";
|
|
|
19
19
|
* - Non-interactive exec sessions
|
|
20
20
|
*/
|
|
21
21
|
const perf = createPerfLogger("SshMimic");
|
|
22
|
+
// ── Dev-mode logger ───────────────────────────────────────────────────────────
|
|
23
|
+
const DEV = !!process.env.DEV_MODE;
|
|
24
|
+
const devLog = DEV ? console.log.bind(console) : () => { };
|
|
22
25
|
class SshMimic extends EventEmitter {
|
|
23
26
|
port;
|
|
24
27
|
server;
|
|
@@ -114,7 +117,6 @@ class SshMimic extends EventEmitter {
|
|
|
114
117
|
// ── Password auth ──────────────────────────────────────
|
|
115
118
|
if (ctx.method === "password") {
|
|
116
119
|
if (!shell.users.hasPassword(candidateUser)) {
|
|
117
|
-
console.log(`User ${candidateUser} has no password set, allowing login without verification`);
|
|
118
120
|
authUser = candidateUser;
|
|
119
121
|
sessionId = shell.users.registerSession(authUser, remoteAddress).id;
|
|
120
122
|
this.recordSuccess(remoteAddress);
|
|
@@ -217,7 +219,7 @@ class SshMimic extends EventEmitter {
|
|
|
217
219
|
return new Promise((resolve, reject) => {
|
|
218
220
|
this.server?.once("error", (err) => reject(err));
|
|
219
221
|
this.server?.listen(this.port, "0.0.0.0", () => {
|
|
220
|
-
|
|
222
|
+
devLog(`SSH Mimic listening on port ${this.port}`);
|
|
221
223
|
this.emit("start", { port: this.port });
|
|
222
224
|
resolve(this.port);
|
|
223
225
|
});
|
|
@@ -230,7 +232,7 @@ class SshMimic extends EventEmitter {
|
|
|
230
232
|
perf.mark("stop");
|
|
231
233
|
if (this.server) {
|
|
232
234
|
this.server.close(() => {
|
|
233
|
-
|
|
235
|
+
devLog("SSH Mimic stopped");
|
|
234
236
|
this.emit("stop");
|
|
235
237
|
});
|
|
236
238
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sftp.d.ts","sourceRoot":"","sources":["../../src/SSHMimic/sftp.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAI3C,OAAO,KAAK,iBAAiB,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"sftp.d.ts","sourceRoot":"","sources":["../../src/SSHMimic/sftp.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAI3C,OAAO,KAAK,iBAAiB,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAmIhE,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,GAAG,CAAC,EAAE,iBAAiB,CAAC;IACxB,KAAK,CAAC,EAAE,kBAAkB,CAAC;CAC3B;AAED,qBAAa,SAAU,SAAQ,YAAY;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAsB;IAC5C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAoB;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqB;IAC3C,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,OAAO,CAAiC;gBAEpC,EACX,IAAI,EACJ,QAA0B,EAC1B,KAAK,EACL,GAAG,EACH,KAAK,GACL,EAAE,gBAAgB;IAwBnB,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,QAAQ;IAIH,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAwK9B,IAAI,IAAI,IAAI;IAUnB;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IAiB1B;;;;;;OAMG;IACH,OAAO,CAAC,gBAAgB;IAkBxB,OAAO,CAAC,WAAW;IAcnB,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,kBAAkB;CA6a1B"}
|
package/dist/SSHMimic/sftp.js
CHANGED
|
@@ -5,6 +5,11 @@ import { Server as SshServer } from "ssh2";
|
|
|
5
5
|
import { createPerfLogger } from "../utils/perfLogger";
|
|
6
6
|
import { VirtualShell } from "../VirtualShell";
|
|
7
7
|
import { loadOrCreateHostKey } from "./hostKey";
|
|
8
|
+
// ── Dev-mode logger — silent in production ────────────────────────────────────
|
|
9
|
+
const DEV = !!process.env.DEV_MODE;
|
|
10
|
+
const devLog = DEV ? console.log.bind(console) : () => { };
|
|
11
|
+
const devWarn = DEV ? console.warn.bind(console) : () => { };
|
|
12
|
+
const devErr = DEV ? console.error.bind(console) : () => { };
|
|
8
13
|
const SFTP_STATUS_CODE = {
|
|
9
14
|
OK: 0,
|
|
10
15
|
EOF: 1,
|
|
@@ -89,7 +94,7 @@ export class SftpMimic extends EventEmitter {
|
|
|
89
94
|
this.emit("client:connect");
|
|
90
95
|
// Add error handling for the client
|
|
91
96
|
client.on("error", (error) => {
|
|
92
|
-
|
|
97
|
+
devErr(`[SFTP] Client error:`, error);
|
|
93
98
|
});
|
|
94
99
|
const acceptSession = (username) => {
|
|
95
100
|
authUser = username;
|
|
@@ -108,7 +113,7 @@ export class SftpMimic extends EventEmitter {
|
|
|
108
113
|
client.on("authentication", (ctx) => {
|
|
109
114
|
const candidateUser = ctx.username || "root";
|
|
110
115
|
remoteAddress = ctx.ip ?? remoteAddress;
|
|
111
|
-
|
|
116
|
+
devLog(`[SFTP] Auth attempt: user=${candidateUser}, method=${ctx.method}, ip=${remoteAddress}`);
|
|
112
117
|
if (ctx.method === "password") {
|
|
113
118
|
// If no password is set for the user, allow login without verification
|
|
114
119
|
if (!this.getUsers().hasPassword(candidateUser)) {
|
|
@@ -170,7 +175,7 @@ export class SftpMimic extends EventEmitter {
|
|
|
170
175
|
const session = accept();
|
|
171
176
|
// Add error handling for the session
|
|
172
177
|
session.on("error", (error) => {
|
|
173
|
-
|
|
178
|
+
devErr(`[SFTP] Session error for user=${authUser}:`, error);
|
|
174
179
|
});
|
|
175
180
|
session.on("sftp", (acceptSftp) => {
|
|
176
181
|
const sftp = acceptSftp();
|
|
@@ -186,7 +191,7 @@ export class SftpMimic extends EventEmitter {
|
|
|
186
191
|
const actualPort = address && typeof address === "object" && "port" in address
|
|
187
192
|
? address.port
|
|
188
193
|
: this.port;
|
|
189
|
-
|
|
194
|
+
devLog(`SFTP Mimic listening on port ${actualPort}`);
|
|
190
195
|
this.emit("start", { port: actualPort });
|
|
191
196
|
resolve(actualPort);
|
|
192
197
|
});
|
|
@@ -196,7 +201,7 @@ export class SftpMimic extends EventEmitter {
|
|
|
196
201
|
perf.mark("stop");
|
|
197
202
|
if (this.server) {
|
|
198
203
|
this.server.close(() => {
|
|
199
|
-
|
|
204
|
+
devLog("SFTP Mimic stopped");
|
|
200
205
|
this.emit("stop");
|
|
201
206
|
});
|
|
202
207
|
}
|
|
@@ -273,7 +278,7 @@ export class SftpMimic extends EventEmitter {
|
|
|
273
278
|
const targetPath = this.resolveRequestPath(filename, authUser);
|
|
274
279
|
// Security: Confine to home directory
|
|
275
280
|
if (!this.isPathWithinHome(targetPath, authUser)) {
|
|
276
|
-
|
|
281
|
+
devWarn(`[SFTP] Path traversal attempt blocked: user=${authUser}, path=${targetPath}`);
|
|
277
282
|
sftp.status(reqid, SFTP_STATUS_CODE.PERMISSION_DENIED);
|
|
278
283
|
return;
|
|
279
284
|
}
|
|
@@ -318,7 +323,7 @@ export class SftpMimic extends EventEmitter {
|
|
|
318
323
|
sftp.handle(reqid, handle);
|
|
319
324
|
}
|
|
320
325
|
catch (error) {
|
|
321
|
-
|
|
326
|
+
devErr("SFTP OPEN error:", error);
|
|
322
327
|
sftp.status(reqid, SFTP_STATUS_CODE.FAILURE);
|
|
323
328
|
}
|
|
324
329
|
});
|
|
@@ -377,7 +382,7 @@ export class SftpMimic extends EventEmitter {
|
|
|
377
382
|
void getVfs().flushMirror();
|
|
378
383
|
}
|
|
379
384
|
catch (error) {
|
|
380
|
-
|
|
385
|
+
devErr("SFTP CLOSE write error:", error);
|
|
381
386
|
sftp.status(reqid, SFTP_STATUS_CODE.FAILURE);
|
|
382
387
|
this.closeHandle(handle);
|
|
383
388
|
return;
|
|
@@ -390,7 +395,7 @@ export class SftpMimic extends EventEmitter {
|
|
|
390
395
|
const targetPath = this.resolveRequestPath(requestPath, authUser);
|
|
391
396
|
// Security: Confine to home directory
|
|
392
397
|
if (!this.isPathWithinHome(targetPath, authUser)) {
|
|
393
|
-
|
|
398
|
+
devWarn(`[SFTP] Path traversal attempt blocked: user=${authUser}, path=${targetPath}`);
|
|
394
399
|
sftp.status(reqid, SFTP_STATUS_CODE.PERMISSION_DENIED);
|
|
395
400
|
return;
|
|
396
401
|
}
|
|
@@ -434,7 +439,7 @@ export class SftpMimic extends EventEmitter {
|
|
|
434
439
|
const targetPath = this.resolveRequestPath(requestPath, authUser);
|
|
435
440
|
// Security: Confine to home directory
|
|
436
441
|
if (!this.isPathWithinHome(targetPath, authUser)) {
|
|
437
|
-
|
|
442
|
+
devWarn(`[SFTP] Path traversal attempt blocked: user=${authUser}, path=${targetPath}`);
|
|
438
443
|
sftp.status(reqid, SFTP_STATUS_CODE.PERMISSION_DENIED);
|
|
439
444
|
return;
|
|
440
445
|
}
|
|
@@ -450,7 +455,7 @@ export class SftpMimic extends EventEmitter {
|
|
|
450
455
|
const targetPath = this.resolveRequestPath(requestPath, authUser);
|
|
451
456
|
// Security: Confine to home directory
|
|
452
457
|
if (!this.isPathWithinHome(targetPath, authUser)) {
|
|
453
|
-
|
|
458
|
+
devWarn(`[SFTP] Path traversal attempt blocked: user=${authUser}, path=${targetPath}`);
|
|
454
459
|
sftp.status(reqid, SFTP_STATUS_CODE.PERMISSION_DENIED);
|
|
455
460
|
return;
|
|
456
461
|
}
|
|
@@ -482,7 +487,7 @@ export class SftpMimic extends EventEmitter {
|
|
|
482
487
|
const targetPath = this.resolveRequestPath(requestPath, authUser);
|
|
483
488
|
// Security: Confine to home directory
|
|
484
489
|
if (!this.isPathWithinHome(targetPath, authUser)) {
|
|
485
|
-
|
|
490
|
+
devWarn(`[SFTP] Path traversal attempt blocked: user=${authUser}, path=${targetPath}`);
|
|
486
491
|
sftp.status(reqid, SFTP_STATUS_CODE.PERMISSION_DENIED);
|
|
487
492
|
return;
|
|
488
493
|
}
|
|
@@ -500,7 +505,7 @@ export class SftpMimic extends EventEmitter {
|
|
|
500
505
|
const normalized = this.resolveRequestPath(requestPath, authUser);
|
|
501
506
|
// Security: Confine to home directory
|
|
502
507
|
if (!this.isPathWithinHome(normalized, authUser)) {
|
|
503
|
-
|
|
508
|
+
devWarn(`[SFTP] Path traversal attempt blocked: user=${authUser}, path=${normalized}`);
|
|
504
509
|
sftp.status(reqid, SFTP_STATUS_CODE.PERMISSION_DENIED);
|
|
505
510
|
return;
|
|
506
511
|
}
|
|
@@ -523,7 +528,7 @@ export class SftpMimic extends EventEmitter {
|
|
|
523
528
|
const targetPath = this.resolveRequestPath(requestPath, authUser);
|
|
524
529
|
// Security: Confine to home directory
|
|
525
530
|
if (!this.isPathWithinHome(targetPath, authUser)) {
|
|
526
|
-
|
|
531
|
+
devWarn(`[SFTP] Path traversal attempt blocked: user=${authUser}, path=${targetPath}`);
|
|
527
532
|
sftp.status(reqid, SFTP_STATUS_CODE.PERMISSION_DENIED);
|
|
528
533
|
return;
|
|
529
534
|
}
|
|
@@ -540,7 +545,7 @@ export class SftpMimic extends EventEmitter {
|
|
|
540
545
|
const targetPath = this.resolveRequestPath(requestPath, authUser);
|
|
541
546
|
// Security: Confine to home directory
|
|
542
547
|
if (!this.isPathWithinHome(targetPath, authUser)) {
|
|
543
|
-
|
|
548
|
+
devWarn(`[SFTP] Path traversal attempt blocked: user=${authUser}, path=${targetPath}`);
|
|
544
549
|
sftp.status(reqid, SFTP_STATUS_CODE.PERMISSION_DENIED);
|
|
545
550
|
return;
|
|
546
551
|
}
|
|
@@ -557,7 +562,7 @@ export class SftpMimic extends EventEmitter {
|
|
|
557
562
|
const targetPath = this.resolveRequestPath(requestPath, authUser);
|
|
558
563
|
// Security: Confine to home directory
|
|
559
564
|
if (!this.isPathWithinHome(targetPath, authUser)) {
|
|
560
|
-
|
|
565
|
+
devWarn(`[SFTP] Path traversal attempt blocked: user=${authUser}, path=${targetPath}`);
|
|
561
566
|
sftp.status(reqid, SFTP_STATUS_CODE.PERMISSION_DENIED);
|
|
562
567
|
return;
|
|
563
568
|
}
|
|
@@ -576,7 +581,7 @@ export class SftpMimic extends EventEmitter {
|
|
|
576
581
|
// Security: Confine both source and destination to home directory
|
|
577
582
|
if (!this.isPathWithinHome(fromPath, authUser) ||
|
|
578
583
|
!this.isPathWithinHome(toPath, authUser)) {
|
|
579
|
-
|
|
584
|
+
devWarn(`[SFTP] Path traversal attempt blocked: user=${authUser}, from=${fromPath}, to=${toPath}`);
|
|
580
585
|
sftp.status(reqid, SFTP_STATUS_CODE.PERMISSION_DENIED);
|
|
581
586
|
return;
|
|
582
587
|
}
|
|
@@ -596,18 +601,18 @@ export class SftpMimic extends EventEmitter {
|
|
|
596
601
|
sftp.status(reqid, SFTP_STATUS_CODE.OP_UNSUPPORTED);
|
|
597
602
|
});
|
|
598
603
|
sftp.on("error", (error) => {
|
|
599
|
-
|
|
604
|
+
devErr(`[SFTP] Stream error for user=${authUser}:`, error);
|
|
600
605
|
});
|
|
601
606
|
sftp.on("close", () => {
|
|
602
|
-
|
|
607
|
+
devLog(`[SFTP] Stream closed for user=${authUser}`);
|
|
603
608
|
this.handles.clear();
|
|
604
609
|
});
|
|
605
610
|
sftp.on("end", () => {
|
|
606
|
-
|
|
611
|
+
devLog(`[SFTP] end event for user=${authUser}`);
|
|
607
612
|
this.handles.clear();
|
|
608
613
|
});
|
|
609
614
|
sftp.on("END", () => {
|
|
610
|
-
|
|
615
|
+
devLog(`[SFTP] END event for user=${authUser}`);
|
|
611
616
|
this.handles.clear();
|
|
612
617
|
});
|
|
613
618
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/VirtualPackageManager/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,iBAAiB,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAIhE;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IACjC,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,OAAO,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gCAAgC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,KAAK,CAAC,EAAE,WAAW,EAAE,CAAC;IACtB;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,iBAAiB,EAAE,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACxE,iEAAiE;IACjE,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,iBAAiB,KAAK,IAAI,CAAC;CAC5C;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,wBAAwB;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,wBAAwB;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,0BAA0B;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,yCAAyC;IACzC,eAAe,EAAE,MAAM,CAAC;IACxB,4DAA4D;IAC5D,WAAW,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,KAAK,EAAE,MAAM,EAAE,CAAC;CAChB;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/VirtualPackageManager/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,iBAAiB,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAIhE;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IACjC,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,OAAO,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gCAAgC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,KAAK,CAAC,EAAE,WAAW,EAAE,CAAC;IACtB;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,iBAAiB,EAAE,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACxE,iEAAiE;IACjE,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,iBAAiB,KAAK,IAAI,CAAC;CAC5C;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,wBAAwB;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,wBAAwB;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,0BAA0B;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,yCAAyC;IACzC,eAAe,EAAE,MAAM,CAAC;IACxB,4DAA4D;IAC5D,WAAW,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,KAAK,EAAE,MAAM,EAAE,CAAC;CAChB;AAigBD;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,qBAAqB;IAWhC,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,KAAK;IAXvB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAuC;IACjE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA0B;IACvD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;IAC/C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA8B;IAEzD;;;OAGG;gBAEe,GAAG,EAAE,iBAAiB,EACtB,KAAK,EAAE,kBAAkB;IAG3C;;;;;OAKG;IACI,IAAI,IAAI,IAAI;IAyBnB,uDAAuD;IACvD,OAAO,CAAC,OAAO;IAsBf,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,GAAG;IASX,OAAO,CAAC,MAAM;IAed;;;;;OAKG;IACI,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS;IAMlE;;;;OAIG;IACI,aAAa,IAAI,iBAAiB,EAAE;IAI3C;;;;OAIG;IACI,aAAa,IAAI,gBAAgB,EAAE;IAM1C;;;;OAIG;IACI,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIzC;;;;OAIG;IACI,cAAc,IAAI,MAAM;IAI/B;;;;;;;;;;;;;OAaG;IACI,OAAO,CACb,KAAK,EAAE,MAAM,EAAE,EACf,IAAI,GAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAO,GAC5B;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE;IAiHvC;;;;;;;;;;;;OAYG;IACI,MAAM,CACZ,KAAK,EAAE,MAAM,EAAE,EACf,IAAI,GAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAO,GAC7C;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE;IA4DvC;;;;;;OAMG;IACI,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,EAAE;IAUhD;;;;;;OAMG;IACI,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;CAiBxC"}
|
|
@@ -472,7 +472,35 @@ const PACKAGE_REGISTRY = [
|
|
|
472
472
|
mode: 0o755,
|
|
473
473
|
},
|
|
474
474
|
],
|
|
475
|
-
},
|
|
475
|
+
}, {
|
|
476
|
+
name: "gzip",
|
|
477
|
+
version: "1.12-2",
|
|
478
|
+
section: "utils",
|
|
479
|
+
description: "GNU compression utility",
|
|
480
|
+
shortDesc: "compression utility",
|
|
481
|
+
installedSizeKb: 128,
|
|
482
|
+
files: [
|
|
483
|
+
{
|
|
484
|
+
path: "/usr/bin/gzip",
|
|
485
|
+
content: "#!/bin/sh\necho 'gzip: virtual stub'\n",
|
|
486
|
+
mode: 0o755,
|
|
487
|
+
},
|
|
488
|
+
]
|
|
489
|
+
}, {
|
|
490
|
+
name: "neofetch",
|
|
491
|
+
version: "7.1.0-1",
|
|
492
|
+
section: "utils",
|
|
493
|
+
description: "A command-line system information tool written in bash 3.2+",
|
|
494
|
+
shortDesc: "command-line system information tool",
|
|
495
|
+
installedSizeKb: 256,
|
|
496
|
+
files: [
|
|
497
|
+
{
|
|
498
|
+
path: "/usr/bin/neofetch",
|
|
499
|
+
content: "#!/bin/sh\necho 'neofetch: virtual stub'\n",
|
|
500
|
+
mode: 0o755,
|
|
501
|
+
},
|
|
502
|
+
],
|
|
503
|
+
}
|
|
476
504
|
];
|
|
477
505
|
/**
|
|
478
506
|
* Pure-TypeScript APT/dpkg package manager backed by a built-in registry.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../../src/VirtualShell/shell.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,GAAG,CAAC;AAMvD,OAAO,EAGN,KAAK,YAAY,EAEjB,MAAM,yBAAyB,CAAC;AAIjC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../../src/VirtualShell/shell.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,GAAG,CAAC;AAMvD,OAAO,EAGN,KAAK,YAAY,EAEjB,MAAM,yBAAyB,CAAC;AAIjC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAwBpD,wBAAgB,UAAU,CACzB,UAAU,EAAE,eAAe,EAC3B,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,aAAa,oBAAY,EACzB,YAAY,EAAE,YAAY,YAAyB,EACnD,KAAK,EAAE,YAAY,GACjB,IAAI,CA+nBN"}
|
|
@@ -72,7 +72,9 @@ export function startShell(properties, stream, authUser, hostname, sessionId, re
|
|
|
72
72
|
}
|
|
73
73
|
if (!challenge.commandLine) {
|
|
74
74
|
authUser = challenge.targetUser;
|
|
75
|
-
|
|
75
|
+
if (challenge.loginShell) {
|
|
76
|
+
cwd = `/home/${authUser}`;
|
|
77
|
+
}
|
|
76
78
|
shell.users.updateSession(sessionId, authUser, remoteAddress);
|
|
77
79
|
stream.write("\r\n");
|
|
78
80
|
renderLine();
|
|
@@ -300,9 +302,29 @@ export function startShell(properties, stream, authUser, hostname, sessionId, re
|
|
|
300
302
|
continue;
|
|
301
303
|
}
|
|
302
304
|
if (ch === "\r" || ch === "\n") {
|
|
303
|
-
const
|
|
305
|
+
const typed = pendingSudo.buffer;
|
|
304
306
|
pendingSudo.buffer = "";
|
|
305
|
-
|
|
307
|
+
// ── Generic onPassword handler (passwd / confirm modes) ────
|
|
308
|
+
if (pendingSudo.onPassword) {
|
|
309
|
+
const { result, nextPrompt } = await pendingSudo.onPassword(typed, shell);
|
|
310
|
+
stream.write("\r\n");
|
|
311
|
+
if (result !== null) {
|
|
312
|
+
pendingSudo = null;
|
|
313
|
+
if (result.stdout)
|
|
314
|
+
stream.write(result.stdout.replace(/\n/g, "\r\n"));
|
|
315
|
+
if (result.stderr)
|
|
316
|
+
stream.write(result.stderr.replace(/\n/g, "\r\n"));
|
|
317
|
+
renderLine();
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
if (nextPrompt)
|
|
321
|
+
pendingSudo.prompt = nextPrompt;
|
|
322
|
+
stream.write(pendingSudo.prompt);
|
|
323
|
+
}
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
// ── Default sudo mode — verify current user's password ─────
|
|
327
|
+
const valid = shell.users.verifyPassword(pendingSudo.username, typed);
|
|
306
328
|
await finishSudoPrompt(valid);
|
|
307
329
|
return;
|
|
308
330
|
}
|
|
@@ -5,15 +5,8 @@ import type { Pipeline, Script } from "../types/pipeline";
|
|
|
5
5
|
* by |).
|
|
6
6
|
*/
|
|
7
7
|
export declare function parseScript(rawInput: string): Script;
|
|
8
|
-
/**
|
|
8
|
+
/** Parse a single pipeline string (no &&/||/;) into a `Pipeline` object. */
|
|
9
9
|
export declare function parseShellPipeline(rawInput: string): Pipeline;
|
|
10
|
-
/**
|
|
11
|
-
* Expand ~ and $VAR / ${VAR} / ${VAR:-default} / $(cmd placeholder) in a
|
|
12
|
-
* token, given the current env vars and home path.
|
|
13
|
-
* Command substitution $(…) is NOT executed here — it's left as a marker so
|
|
14
|
-
* the executor can handle it.
|
|
15
|
-
*/
|
|
16
|
-
export declare function expandToken(token: string, env: Record<string, string>, authUser: string, lastExitCode?: number): string;
|
|
17
10
|
/**
|
|
18
11
|
* Expand glob patterns (*, ?, [abc]) against a list of entries.
|
|
19
12
|
* Returns the original pattern if no match.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shellParser.d.ts","sourceRoot":"","sources":["../../src/VirtualShell/shellParser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,QAAQ,EAER,MAAM,EAGN,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"shellParser.d.ts","sourceRoot":"","sources":["../../src/VirtualShell/shellParser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,QAAQ,EAER,MAAM,EAGN,MAAM,mBAAmB,CAAC;AAK3B;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUpD;AAED,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAS7D;AAID;;;GAGG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAKvE"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { tokenizeCommand } from "../utils/tokenize";
|
|
1
2
|
// ── Public API ───────────────────────────────────────────────────────────────
|
|
2
3
|
/**
|
|
3
4
|
* Parse a shell input line into a Script (sequence of statements connected
|
|
@@ -16,7 +17,7 @@ export function parseScript(rawInput) {
|
|
|
16
17
|
return { statements: [], isValid: false, error: e.message };
|
|
17
18
|
}
|
|
18
19
|
}
|
|
19
|
-
/**
|
|
20
|
+
/** Parse a single pipeline string (no &&/||/;) into a `Pipeline` object. */
|
|
20
21
|
export function parseShellPipeline(rawInput) {
|
|
21
22
|
const trimmed = rawInput.trim();
|
|
22
23
|
if (!trimmed)
|
|
@@ -30,30 +31,6 @@ export function parseShellPipeline(rawInput) {
|
|
|
30
31
|
}
|
|
31
32
|
}
|
|
32
33
|
// ── Variable & tilde expansion ────────────────────────────────────────────────
|
|
33
|
-
/**
|
|
34
|
-
* Expand ~ and $VAR / ${VAR} / ${VAR:-default} / $(cmd placeholder) in a
|
|
35
|
-
* token, given the current env vars and home path.
|
|
36
|
-
* Command substitution $(…) is NOT executed here — it's left as a marker so
|
|
37
|
-
* the executor can handle it.
|
|
38
|
-
*/
|
|
39
|
-
export function expandToken(token, env, authUser, lastExitCode = 0) {
|
|
40
|
-
// tilde expansion
|
|
41
|
-
token = token.replace(/^~(\/|$)/, `/home/${authUser}$1`);
|
|
42
|
-
// $? special var
|
|
43
|
-
token = token.replace(/\$\?/g, String(lastExitCode));
|
|
44
|
-
// $$ PID (mock)
|
|
45
|
-
token = token.replace(/\$\$/g, "1");
|
|
46
|
-
// $# argc (0 for interactive)
|
|
47
|
-
token = token.replace(/\$#/g, "0");
|
|
48
|
-
// ${VAR:-default} and ${VAR:+value}
|
|
49
|
-
token = token.replace(/\$\{([^}:]+):-([^}]*)\}/g, (_, name, def) => env[name] ?? def);
|
|
50
|
-
token = token.replace(/\$\{([^}:]+):\+([^}]*)\}/g, (_, name, val) => env[name] ? val : "");
|
|
51
|
-
// ${VAR}
|
|
52
|
-
token = token.replace(/\$\{([^}]+)\}/g, (_, name) => env[name] ?? "");
|
|
53
|
-
// $VAR (greedy: match longest valid identifier)
|
|
54
|
-
token = token.replace(/\$([A-Za-z_][A-Za-z0-9_]*)/g, (_, name) => env[name] ?? "");
|
|
55
|
-
return token;
|
|
56
|
-
}
|
|
57
34
|
/**
|
|
58
35
|
* Expand glob patterns (*, ?, [abc]) against a list of entries.
|
|
59
36
|
* Returns the original pattern if no match.
|
|
@@ -258,59 +235,3 @@ function parseCommandWithRedirections(token) {
|
|
|
258
235
|
const name = (cmdParts[0] ?? "").toLowerCase();
|
|
259
236
|
return { name, args: cmdParts.slice(1), inputFile, outputFile, appendOutput };
|
|
260
237
|
}
|
|
261
|
-
function tokenizeCommand(input) {
|
|
262
|
-
const tokens = [];
|
|
263
|
-
let current = "";
|
|
264
|
-
let inQ = false;
|
|
265
|
-
let qChar = "";
|
|
266
|
-
let i = 0;
|
|
267
|
-
while (i < input.length) {
|
|
268
|
-
const ch = input[i];
|
|
269
|
-
const next = input[i + 1];
|
|
270
|
-
if ((ch === '"' || ch === "'") && !inQ) {
|
|
271
|
-
inQ = true;
|
|
272
|
-
qChar = ch;
|
|
273
|
-
i++;
|
|
274
|
-
continue;
|
|
275
|
-
}
|
|
276
|
-
if (inQ && ch === qChar) {
|
|
277
|
-
inQ = false;
|
|
278
|
-
qChar = "";
|
|
279
|
-
i++;
|
|
280
|
-
continue;
|
|
281
|
-
}
|
|
282
|
-
if (inQ) {
|
|
283
|
-
current += ch;
|
|
284
|
-
i++;
|
|
285
|
-
continue;
|
|
286
|
-
}
|
|
287
|
-
if (ch === " ") {
|
|
288
|
-
if (current) {
|
|
289
|
-
tokens.push(current);
|
|
290
|
-
current = "";
|
|
291
|
-
}
|
|
292
|
-
i++;
|
|
293
|
-
continue;
|
|
294
|
-
}
|
|
295
|
-
if ((ch === ">" || ch === "<") && !inQ) {
|
|
296
|
-
if (current) {
|
|
297
|
-
tokens.push(current);
|
|
298
|
-
current = "";
|
|
299
|
-
}
|
|
300
|
-
if (ch === ">" && next === ">") {
|
|
301
|
-
tokens.push(">>");
|
|
302
|
-
i += 2;
|
|
303
|
-
}
|
|
304
|
-
else {
|
|
305
|
-
tokens.push(ch);
|
|
306
|
-
i++;
|
|
307
|
-
}
|
|
308
|
-
continue;
|
|
309
|
-
}
|
|
310
|
-
current += ch;
|
|
311
|
-
i++;
|
|
312
|
-
}
|
|
313
|
-
if (current)
|
|
314
|
-
tokens.push(current);
|
|
315
|
-
return tokens;
|
|
316
|
-
}
|
|
@@ -218,7 +218,13 @@ export declare class VirtualUserManager extends EventEmitter {
|
|
|
218
218
|
* @param password Plaintext password string.
|
|
219
219
|
* @returns Hex-encoded hash string.
|
|
220
220
|
*/
|
|
221
|
-
|
|
221
|
+
/**
|
|
222
|
+
* Hash a password with an optional salt.
|
|
223
|
+
* When salt is provided (verify path), the same salt is used for a
|
|
224
|
+
* deterministic hash. When omitted (create path), an empty salt is used
|
|
225
|
+
* for backward compat — callers should pass the stored salt on verify.
|
|
226
|
+
*/
|
|
227
|
+
hashPassword(password: string, salt?: string): string;
|
|
222
228
|
private validateUsername;
|
|
223
229
|
private validatePassword;
|
|
224
230
|
private readonly authorizedKeys;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/VirtualUserManager/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAI3C,OAAO,KAAK,iBAAiB,MAAM,sBAAsB,CAAC;AAE1D,gDAAgD;AAChD,MAAM,WAAW,iBAAiB;IACjC,yBAAyB;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,YAAY,EAAE,MAAM,CAAC;CACrB;AAED,2DAA2D;AAC3D,MAAM,WAAW,oBAAoB;IACpC,wCAAwC;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,iCAAiC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,sCAAsC;IACtC,aAAa,EAAE,MAAM,CAAC;IACtB,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAC;CAClB;AAYD;;;;GAIG;AACH,qBAAa,kBAAmB,SAAQ,YAAY;IAqBlD,OAAO,CAAC,QAAQ,CAAC,GAAG;IAGpB,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IAvBrC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAwC;IAC3E,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAA6B;IACrE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoC;IAC9D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAmC;IAC/D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkC;IAC7D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA2B;IACvD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAwC;IAC9D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA6B;IACpD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA2C;IAC1E,OAAO,CAAC,OAAO,CAAK;IAEpB;;;;;;OAMG;gBAEe,GAAG,EAAE,iBAAiB,EAGtB,mBAAmB,GAAE,OAAc;IAMrD;;;OAGG;IACU,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAsCxC;;;;;OAKG;IACU,aAAa,CACzB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC;IAehB;;;;OAIG;IACU,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOxD;;;;;OAKG;IACI,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAKrD;;;;;OAKG;IACI,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAU9C;;;;;;;;OAQG;IACI,sBAAsB,CAC5B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAAG,MAAM,GAC1B,IAAI;IAoCP;;;;;;OAMG;IACI,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/VirtualUserManager/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createHash, randomBytes, randomUUID, scryptSync } from "node:crypto";
|
|
1
|
+
import { createHash, randomBytes, randomUUID, scryptSync, timingSafeEqual } from "node:crypto";
|
|
2
2
|
import { EventEmitter } from "node:events";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { createPerfLogger } from "../utils/perfLogger";
|
|
@@ -66,11 +66,14 @@ export class VirtualUserManager extends EventEmitter {
|
|
|
66
66
|
// this.sudoers.add(currentUser);
|
|
67
67
|
// changed = true;
|
|
68
68
|
// }
|
|
69
|
-
const homePath = `/home/root`;
|
|
70
|
-
if (!this.vfs.exists(homePath)) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
69
|
+
// const homePath = `/home/root`;
|
|
70
|
+
// if (!this.vfs.exists(homePath)) {
|
|
71
|
+
// this.vfs.mkdir(homePath, 0o755);
|
|
72
|
+
// this.vfs.writeFile(
|
|
73
|
+
// `${homePath}/README.txt`,
|
|
74
|
+
// `Welcome to the virtual environment, root`,
|
|
75
|
+
// );
|
|
76
|
+
// }
|
|
74
77
|
if (changed) {
|
|
75
78
|
await this.persist();
|
|
76
79
|
}
|
|
@@ -177,9 +180,23 @@ export class VirtualUserManager extends EventEmitter {
|
|
|
177
180
|
perf.mark("verifyPassword");
|
|
178
181
|
const record = this.users.get(username);
|
|
179
182
|
if (!record) {
|
|
183
|
+
// Perform a dummy hash to avoid timing leakage on unknown usernames
|
|
184
|
+
this.hashPassword(password, "");
|
|
180
185
|
return false;
|
|
181
186
|
}
|
|
182
|
-
|
|
187
|
+
const computed = this.hashPassword(password, record.salt);
|
|
188
|
+
const expected = record.passwordHash;
|
|
189
|
+
// timingSafeEqual prevents timing-based password oracle attacks
|
|
190
|
+
try {
|
|
191
|
+
const a = Buffer.from(computed, "hex");
|
|
192
|
+
const b = Buffer.from(expected, "hex");
|
|
193
|
+
if (a.length !== b.length)
|
|
194
|
+
return false;
|
|
195
|
+
return timingSafeEqual(a, b);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return computed === expected;
|
|
199
|
+
}
|
|
183
200
|
}
|
|
184
201
|
/**
|
|
185
202
|
* Creates user, home directory, and sudo access entry.
|
|
@@ -478,7 +495,8 @@ export class VirtualUserManager extends EventEmitter {
|
|
|
478
495
|
return true;
|
|
479
496
|
}
|
|
480
497
|
createRecord(username, password) {
|
|
481
|
-
|
|
498
|
+
// Cache key is a hash of the inputs — never store plaintext password in memory
|
|
499
|
+
const cacheKey = createHash("sha256").update(username).update(":").update(password).digest("hex");
|
|
482
500
|
const cached = VirtualUserManager.recordCache.get(cacheKey);
|
|
483
501
|
if (cached) {
|
|
484
502
|
return cached;
|
|
@@ -487,7 +505,8 @@ export class VirtualUserManager extends EventEmitter {
|
|
|
487
505
|
const record = {
|
|
488
506
|
username,
|
|
489
507
|
salt,
|
|
490
|
-
|
|
508
|
+
// Hash uses the generated salt — verifyPassword must use record.salt
|
|
509
|
+
passwordHash: this.hashPassword(password, salt),
|
|
491
510
|
};
|
|
492
511
|
VirtualUserManager.recordCache.set(cacheKey, record);
|
|
493
512
|
return record;
|
|
@@ -502,11 +521,14 @@ export class VirtualUserManager extends EventEmitter {
|
|
|
502
521
|
*/
|
|
503
522
|
hasPassword(username) {
|
|
504
523
|
perf.mark("hasPassword");
|
|
505
|
-
if (this.getPasswordHash(username) === this.hashPassword("")) {
|
|
506
|
-
return false;
|
|
507
|
-
}
|
|
508
524
|
const record = this.users.get(username);
|
|
509
|
-
|
|
525
|
+
if (!record)
|
|
526
|
+
return false;
|
|
527
|
+
// Empty password hash computed with the record's own salt
|
|
528
|
+
const emptyHash = this.hashPassword("", record.salt);
|
|
529
|
+
if (record.passwordHash === emptyHash)
|
|
530
|
+
return false;
|
|
531
|
+
return !!record.passwordHash;
|
|
510
532
|
}
|
|
511
533
|
/**
|
|
512
534
|
* Hashes a plaintext password using scrypt (or SHA-256 in fast-hash mode).
|
|
@@ -517,11 +539,20 @@ export class VirtualUserManager extends EventEmitter {
|
|
|
517
539
|
* @param password Plaintext password string.
|
|
518
540
|
* @returns Hex-encoded hash string.
|
|
519
541
|
*/
|
|
520
|
-
|
|
542
|
+
/**
|
|
543
|
+
* Hash a password with an optional salt.
|
|
544
|
+
* When salt is provided (verify path), the same salt is used for a
|
|
545
|
+
* deterministic hash. When omitted (create path), an empty salt is used
|
|
546
|
+
* for backward compat — callers should pass the stored salt on verify.
|
|
547
|
+
*/
|
|
548
|
+
hashPassword(password, salt = "") {
|
|
521
549
|
if (VirtualUserManager.fastPasswordHash) {
|
|
522
|
-
return createHash("sha256")
|
|
550
|
+
return createHash("sha256")
|
|
551
|
+
.update(salt)
|
|
552
|
+
.update(password)
|
|
553
|
+
.digest("hex");
|
|
523
554
|
}
|
|
524
|
-
return scryptSync(password, "", 32).toString("hex");
|
|
555
|
+
return scryptSync(password, salt || "", 32).toString("hex");
|
|
525
556
|
}
|
|
526
557
|
validateUsername(username) {
|
|
527
558
|
if (!username || username.trim() === "") {
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import type { ShellModule } from "../types/commands";
|
|
2
2
|
/**
|
|
3
|
-
* Add a new user
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
3
|
+
* Add a new user interactively.
|
|
4
|
+
*
|
|
5
|
+
* Usage: `adduser <username>`
|
|
6
|
+
*
|
|
7
|
+
* Prompts for:
|
|
8
|
+
* New password: ****
|
|
9
|
+
* Retype new password: ****
|
|
10
|
+
*
|
|
11
|
+
* Mirrors the real `adduser` behaviour — password is never passed on the
|
|
12
|
+
* command line. Root-only.
|
|
7
13
|
*/
|
|
8
14
|
export declare const adduserCommand: ShellModule;
|
|
9
15
|
//# sourceMappingURL=adduser.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adduser.d.ts","sourceRoot":"","sources":["../../src/commands/adduser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"adduser.d.ts","sourceRoot":"","sources":["../../src/commands/adduser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGpE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,cAAc,EAAE,WAuF5B,CAAC"}
|