@zhangfengshun/dsh-remote-ssh 2.3.7 → 2.3.9
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/CHANGELOG.md +11 -0
- package/lib/client.js +1367 -1367
- package/lib/index.js +29 -5
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -336,8 +336,22 @@ class CommandSession {
|
|
|
336
336
|
}
|
|
337
337
|
}
|
|
338
338
|
|
|
339
|
+
/** Windows 下优先解析系统自带 OpenSSH 的绝对路径:从 git-bash 启动 dsh web 时,
|
|
340
|
+
* 子进程 PATH 会把 Git 自带的 MSYS2 ssh 排在系统 OpenSSH 之前(HOME/config/agent
|
|
341
|
+
* 语义不同,导致密钥认证失败——issue:git-bash 启动场景)。System32\OpenSSH 是
|
|
342
|
+
* Win10 1809+ 的标准安装位置,不存在则回落 PATH(维持旧行为)。结果模块级缓存。 */
|
|
343
|
+
let cachedSshPath;
|
|
344
|
+
function sshExecutablePath() {
|
|
345
|
+
if (cachedSshPath !== undefined) return cachedSshPath;
|
|
346
|
+
if (process.platform === "win32") {
|
|
347
|
+
const sysSsh = join(process.env.SystemRoot || "C:\\Windows", "System32", "OpenSSH", "ssh.exe");
|
|
348
|
+
if (existsSync(sysSsh)) return (cachedSshPath = sysSsh);
|
|
349
|
+
}
|
|
350
|
+
return (cachedSshPath = "ssh");
|
|
351
|
+
}
|
|
352
|
+
|
|
339
353
|
function sshArgv(p, remoteCmd, tty, extraOpts) {
|
|
340
|
-
const opts = [
|
|
354
|
+
const opts = [sshExecutablePath()];
|
|
341
355
|
if (tty) opts.push("-tt");
|
|
342
356
|
opts.push("-p", String(p.port || 22));
|
|
343
357
|
opts.push("-o", "StrictHostKeyChecking=accept-new");
|
|
@@ -945,13 +959,18 @@ function normExec(r) {
|
|
|
945
959
|
// HTTP 工具
|
|
946
960
|
// ---------------------------------------------------------------------------
|
|
947
961
|
|
|
948
|
-
function isTrusted(req) {
|
|
962
|
+
function isTrusted(req, requireRequestedWith) {
|
|
949
963
|
const secFetchSite = req.headers["sec-fetch-site"];
|
|
950
964
|
if (secFetchSite === "cross-site") return false;
|
|
951
965
|
const host = req.headers["host"];
|
|
952
966
|
if (!host) return false;
|
|
953
967
|
const hostname = String(host).split(":")[0].replace(/^\[/, "").replace(/\]$/, "");
|
|
954
|
-
if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "0.0.0.0")
|
|
968
|
+
if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "0.0.0.0") {
|
|
969
|
+
// CSRF 加固(PR #4 的服务端半边):/remote-ssh/api/* 仅由本插件客户端调用,
|
|
970
|
+
// 其请求必带 x-requested-with;跨站 no-cors POST 无法携带非简单头,预检也会被拒。
|
|
971
|
+
if (requireRequestedWith && String(req.headers["x-requested-with"] || "").trim() !== "XMLHttpRequest") return false;
|
|
972
|
+
return true;
|
|
973
|
+
}
|
|
955
974
|
return false;
|
|
956
975
|
}
|
|
957
976
|
|
|
@@ -1503,7 +1522,9 @@ function apply(ctx, config) {
|
|
|
1503
1522
|
kind: "prefix",
|
|
1504
1523
|
path: "/remote-ssh/api",
|
|
1505
1524
|
handler: async (req, res) => {
|
|
1506
|
-
|
|
1525
|
+
// CSRF 加固:/remote-ssh/api/* 仅由本插件客户端调用,强制校验其必带的
|
|
1526
|
+
// x-requested-with 头(跨站攻击者无法在 no-cors POST 中携带非简单头)。
|
|
1527
|
+
if (!isTrusted(req, true)) { writeJson(res, 403, { ok: false, error: { code: "csrf", message: "missing x-requested-with header" } }); return; }
|
|
1507
1528
|
if (req.method !== "POST") { writeError(res, new Error("method not allowed"), 405); return; }
|
|
1508
1529
|
const pathname = new URL(req.url || "/", "http://dsh.internal").pathname;
|
|
1509
1530
|
if (!pathname.startsWith(API_BASE)) { writeError(res, new Error("not-found"), 404); return; }
|
|
@@ -1847,10 +1868,13 @@ function apply(ctx, config) {
|
|
|
1847
1868
|
" const j = JSON.parse(fs.readFileSync(info, 'utf8'));",
|
|
1848
1869
|
" if (j.keyPath && j.keyPath !== '' && j.host && j.user) {",
|
|
1849
1870
|
" const port = j.port || 22;",
|
|
1871
|
+
" // Windows 下优先解析系统自带 OpenSSH 绝对路径(避免 git-bash 启动时解析到 MSYS2 ssh)",
|
|
1872
|
+
" const sysSsh = path.join(process.env.SystemRoot || 'C:\\\\Windows', 'System32', 'OpenSSH', 'ssh.exe');",
|
|
1873
|
+
" const sshBin = fs.existsSync(sysSsh) ? sysSsh : 'ssh';",
|
|
1850
1874
|
" // ExitOnForwardFailure=no:ssh config 里的 RemoteForward 端口被占时终端仍能打开",
|
|
1851
1875
|
" const args = ['-tt', '-o', 'StrictHostKeyChecking=no', '-o', 'ExitOnForwardFailure=no', '-p', String(port), '-i', j.keyPath, j.user + '@' + j.host];",
|
|
1852
1876
|
" if (j.proxyJump) { args.splice(2, 0, '-J', j.proxyJump); }",
|
|
1853
|
-
" const ssh = spawn(
|
|
1877
|
+
" const ssh = spawn(sshBin, args, { stdio: 'inherit' });",
|
|
1854
1878
|
" ssh.on('exit', function(c) { process.exit(c == null ? 0 : c); });",
|
|
1855
1879
|
" return;",
|
|
1856
1880
|
" }",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhangfengshun/dsh-remote-ssh",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.9",
|
|
4
4
|
"description": "DSH web plugin: VSCode Remote-SSH-like remote development (SSH to supercomputers/servers, remote workspace, file explorer, integrated terminal), integrated with dsh-better-sidebar and DSH settings.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh",
|