dsh-remote 0.8.17 → 0.8.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -63
- package/README.zh.md +40 -47
- package/cordis.patch.yml +5 -55
- package/lib/binding.js +69 -0
- package/lib/client.js +369 -136
- package/lib/hostkey.js +60 -0
- package/lib/index.js +104 -924
- package/lib/pool.js +511 -0
- package/lib/remote-fs.js +143 -0
- package/lib/routes-fs.js +221 -0
- package/lib/search.js +59 -1
- package/lib/sync.js +15 -10
- package/package.json +2 -3
package/lib/hostkey.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
// ssh2 v1.17) is unit-tested against the REAL wire shape — the v0.6.7 bug
|
|
4
4
|
// class ("mock object hid a contract drift") must not regress.
|
|
5
5
|
import { createHash } from 'node:crypto'
|
|
6
|
+
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
7
|
+
import path from 'node:path'
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* Extract the SSH host-key algorithm name from a raw SSH host-key blob
|
|
@@ -34,6 +36,64 @@ export function keyFingerprint(key) {
|
|
|
34
36
|
return createHash('sha256').update(blob).digest('base64')
|
|
35
37
|
}
|
|
36
38
|
|
|
39
|
+
export function loadKnownHosts(file) {
|
|
40
|
+
if (!file) return {}
|
|
41
|
+
try {
|
|
42
|
+
const j = JSON.parse(readFileSync(file, 'utf8'))
|
|
43
|
+
if (j && typeof j === 'object' && !Array.isArray(j)) return j
|
|
44
|
+
} catch {}
|
|
45
|
+
return {}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function isHostKeyKnown(file, host, port) {
|
|
49
|
+
return Object.prototype.hasOwnProperty.call(loadKnownHosts(file), `${host}:${port}`)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Build an ssh2 `hostVerifier` bound to the current config + known_hosts file. */
|
|
53
|
+
export function createHostKeyGuard(config, knownHostsFile) {
|
|
54
|
+
const id = () => `${config.host}:${config.port}`
|
|
55
|
+
const mode = config.hostKeyMode === 'verify' || config.hostKeyMode === 'off'
|
|
56
|
+
? config.hostKeyMode
|
|
57
|
+
: 'accept-new'
|
|
58
|
+
const read = () => loadKnownHosts(knownHostsFile)
|
|
59
|
+
const write = (kh) => {
|
|
60
|
+
if (!knownHostsFile) return
|
|
61
|
+
try { mkdirSync(path.dirname(knownHostsFile), { recursive: true }) } catch {}
|
|
62
|
+
writeFileSync(knownHostsFile, JSON.stringify(kh, null, 2))
|
|
63
|
+
}
|
|
64
|
+
const guard = {
|
|
65
|
+
mode,
|
|
66
|
+
lastError: null,
|
|
67
|
+
knownHosts: read,
|
|
68
|
+
forgetHost() {
|
|
69
|
+
const kh = read()
|
|
70
|
+
delete kh[id()]
|
|
71
|
+
write(kh)
|
|
72
|
+
},
|
|
73
|
+
verifier(key) {
|
|
74
|
+
if (mode === 'off') return true
|
|
75
|
+
const fp = keyFingerprint(key)
|
|
76
|
+
const kh = read()
|
|
77
|
+
const stored = kh[id()]
|
|
78
|
+
if (stored) {
|
|
79
|
+
if (stored.fingerprint === fp) return true
|
|
80
|
+
guard.lastError =
|
|
81
|
+
`host key for ${id()} CHANGED (stored ${stored.fingerprint}, received ${fp}) — ` +
|
|
82
|
+
'possible man-in-the-middle; run /remote-forget-key to re-trust if this is expected'
|
|
83
|
+
return false
|
|
84
|
+
}
|
|
85
|
+
if (mode === 'verify') {
|
|
86
|
+
guard.lastError = `unknown host key for ${id()} (hostKeyMode=verify) — trust it first with accept-new`
|
|
87
|
+
return false
|
|
88
|
+
}
|
|
89
|
+
kh[id()] = { algo: blobAlgorithm(key) || (key && key.algo) || 'unknown', fingerprint: fp, firstSeen: new Date().toISOString() }
|
|
90
|
+
write(kh)
|
|
91
|
+
return true
|
|
92
|
+
},
|
|
93
|
+
}
|
|
94
|
+
return guard
|
|
95
|
+
}
|
|
96
|
+
|
|
37
97
|
/** Build a fake-but-wire-shaped host-key blob for tests:
|
|
38
98
|
* `string(algo) string(32 bytes)`. */
|
|
39
99
|
export function makeKeyBlob(algo, seed) {
|