@zero-transfer/sftp 0.4.7 → 0.4.8
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/dist/index.cjs +426 -177
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +228 -24
- package/dist/index.d.ts +228 -24
- package/dist/index.mjs +423 -177
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,68 @@
|
|
|
1
1
|
// src/client/ZeroTransfer.ts
|
|
2
2
|
import { EventEmitter } from "events";
|
|
3
3
|
|
|
4
|
+
// src/logging/redaction.ts
|
|
5
|
+
var REDACTED = "[REDACTED]";
|
|
6
|
+
var SENSITIVE_KEY_PATTERN = /(?:password|passphrase|privatekey|token|secret|username|user)$/i;
|
|
7
|
+
var SECRET_COMMAND_PATTERN = /^(PASS|USER|ACCT)\s+(.+)$/i;
|
|
8
|
+
var URL_KEY_PATTERN = /(?:url|uri|href)$/i;
|
|
9
|
+
function isSensitiveKey(key) {
|
|
10
|
+
return SENSITIVE_KEY_PATTERN.test(key.replace(/[_-]/g, ""));
|
|
11
|
+
}
|
|
12
|
+
function redactCommand(command) {
|
|
13
|
+
return command.replace(SECRET_COMMAND_PATTERN, (_fullMatch, commandName) => {
|
|
14
|
+
return `${commandName.toUpperCase()} ${REDACTED}`;
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
function redactValue(value) {
|
|
18
|
+
if (typeof value === "string") {
|
|
19
|
+
return redactCommand(value);
|
|
20
|
+
}
|
|
21
|
+
if (Array.isArray(value)) {
|
|
22
|
+
return value.map((item) => redactValue(item));
|
|
23
|
+
}
|
|
24
|
+
if (value !== null && typeof value === "object") {
|
|
25
|
+
return redactObject(value);
|
|
26
|
+
}
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
function redactObject(input) {
|
|
30
|
+
return Object.fromEntries(
|
|
31
|
+
Object.entries(input).map(([key, value]) => {
|
|
32
|
+
if (isSensitiveKey(key)) {
|
|
33
|
+
return [key, REDACTED];
|
|
34
|
+
}
|
|
35
|
+
if (URL_KEY_PATTERN.test(key) && typeof value === "string") {
|
|
36
|
+
return [key, redactUrlForLogging(value)];
|
|
37
|
+
}
|
|
38
|
+
return [key, redactValue(value)];
|
|
39
|
+
})
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
function redactUrlForLogging(url) {
|
|
43
|
+
let parsed;
|
|
44
|
+
try {
|
|
45
|
+
parsed = typeof url === "string" ? new URL(url) : url;
|
|
46
|
+
} catch {
|
|
47
|
+
return REDACTED;
|
|
48
|
+
}
|
|
49
|
+
const origin = parsed.host.length > 0 ? `${parsed.protocol}//${parsed.host}` : parsed.protocol;
|
|
50
|
+
const query = parsed.search.length > 0 ? `?${REDACTED}` : "";
|
|
51
|
+
return `${origin}${parsed.pathname}${query}`;
|
|
52
|
+
}
|
|
53
|
+
function redactErrorForLogging(error) {
|
|
54
|
+
if (error !== null && typeof error === "object") {
|
|
55
|
+
const candidate = error;
|
|
56
|
+
if (typeof candidate.toJSON === "function") {
|
|
57
|
+
return redactObject(candidate.toJSON());
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (error instanceof Error) {
|
|
61
|
+
return redactObject({ message: error.message, name: error.name });
|
|
62
|
+
}
|
|
63
|
+
return { message: redactValue(typeof error === "string" ? error : String(error)) };
|
|
64
|
+
}
|
|
65
|
+
|
|
4
66
|
// src/errors/ZeroTransferError.ts
|
|
5
67
|
var ZeroTransferError = class extends Error {
|
|
6
68
|
/** Stable machine-readable error code. */
|
|
@@ -42,6 +104,11 @@ var ZeroTransferError = class extends Error {
|
|
|
42
104
|
/**
|
|
43
105
|
* Serializes the error into a plain object suitable for logs or API responses.
|
|
44
106
|
*
|
|
107
|
+
* `details` and `command` are passed through secret redaction so serialized
|
|
108
|
+
* errors never leak credentials, signed URLs, or raw protocol commands. The
|
|
109
|
+
* live {@link ZeroTransferError.details | details} property stays unredacted
|
|
110
|
+
* for programmatic consumers.
|
|
111
|
+
*
|
|
45
112
|
* @returns A JSON-safe object containing public structured error fields.
|
|
46
113
|
*/
|
|
47
114
|
toJSON() {
|
|
@@ -51,12 +118,12 @@ var ZeroTransferError = class extends Error {
|
|
|
51
118
|
message: this.message,
|
|
52
119
|
protocol: this.protocol,
|
|
53
120
|
host: this.host,
|
|
54
|
-
command: this.command,
|
|
121
|
+
command: this.command === void 0 ? void 0 : redactCommand(this.command),
|
|
55
122
|
ftpCode: this.ftpCode,
|
|
56
123
|
sftpCode: this.sftpCode,
|
|
57
124
|
path: this.path,
|
|
58
125
|
retryable: this.retryable,
|
|
59
|
-
details: this.details
|
|
126
|
+
details: this.details === void 0 ? void 0 : redactObject(this.details)
|
|
60
127
|
};
|
|
61
128
|
}
|
|
62
129
|
};
|
|
@@ -579,15 +646,20 @@ var ProviderRegistry = class {
|
|
|
579
646
|
var TransferClient = class {
|
|
580
647
|
/** Provider registry used by this client. */
|
|
581
648
|
registry;
|
|
649
|
+
/** Execution defaults applied when call sites omit their own values. */
|
|
650
|
+
defaults;
|
|
582
651
|
logger;
|
|
583
652
|
/**
|
|
584
653
|
* Creates a transfer client without opening any provider connections.
|
|
585
654
|
*
|
|
586
|
-
* @param options - Optional registry, provider factories, and
|
|
655
|
+
* @param options - Optional registry, provider factories, logger, and execution defaults.
|
|
587
656
|
*/
|
|
588
657
|
constructor(options = {}) {
|
|
589
658
|
this.registry = options.registry ?? new ProviderRegistry();
|
|
590
659
|
this.logger = options.logger ?? noopLogger;
|
|
660
|
+
if (options.defaults !== void 0) {
|
|
661
|
+
this.defaults = { ...options.defaults };
|
|
662
|
+
}
|
|
591
663
|
for (const provider of options.providers ?? []) {
|
|
592
664
|
this.registry.register(provider);
|
|
593
665
|
}
|
|
@@ -1160,18 +1232,25 @@ var TransferEngine = class {
|
|
|
1160
1232
|
for (let attemptNumber = 1; attemptNumber <= maxAttempts; attemptNumber += 1) {
|
|
1161
1233
|
this.throwIfAborted(abortScope.signal, job);
|
|
1162
1234
|
const attemptStartedAt = this.now();
|
|
1235
|
+
const attemptScope = createAttemptScope(
|
|
1236
|
+
abortScope.signal,
|
|
1237
|
+
options.timeout,
|
|
1238
|
+
job,
|
|
1239
|
+
attemptNumber
|
|
1240
|
+
);
|
|
1163
1241
|
const context = this.createExecutionContext(
|
|
1164
1242
|
job,
|
|
1165
1243
|
attemptNumber,
|
|
1166
1244
|
attemptStartedAt,
|
|
1167
1245
|
options,
|
|
1168
|
-
|
|
1246
|
+
attemptScope.signal,
|
|
1169
1247
|
(bytesTransferred) => {
|
|
1170
1248
|
latestBytesTransferred = bytesTransferred;
|
|
1171
|
-
}
|
|
1249
|
+
},
|
|
1250
|
+
attemptScope.notifyProgress
|
|
1172
1251
|
);
|
|
1173
1252
|
try {
|
|
1174
|
-
const result = await runExecutor(executor, context,
|
|
1253
|
+
const result = await runExecutor(executor, context, attemptScope.signal, job);
|
|
1175
1254
|
context.throwIfAborted();
|
|
1176
1255
|
latestBytesTransferred = result.bytesTransferred;
|
|
1177
1256
|
const completedAt = this.now();
|
|
@@ -1189,16 +1268,27 @@ var TransferEngine = class {
|
|
|
1189
1268
|
summarizeError(error)
|
|
1190
1269
|
);
|
|
1191
1270
|
attempts.push(attempt);
|
|
1192
|
-
if (error instanceof AbortError ||
|
|
1271
|
+
if (error instanceof AbortError || abortScope.signal?.aborted === true) {
|
|
1193
1272
|
throw error;
|
|
1194
1273
|
}
|
|
1195
|
-
const retryInput = {
|
|
1274
|
+
const retryInput = {
|
|
1275
|
+
attempt: attemptNumber,
|
|
1276
|
+
elapsedMs: Math.max(0, completedAt.getTime() - startedAt.getTime()),
|
|
1277
|
+
error,
|
|
1278
|
+
job
|
|
1279
|
+
};
|
|
1196
1280
|
const shouldRetry = attemptNumber < maxAttempts && (options.retry?.shouldRetry?.(retryInput) ?? isRetryable(error));
|
|
1197
1281
|
if (shouldRetry) {
|
|
1198
1282
|
options.retry?.onRetry?.(retryInput);
|
|
1283
|
+
const delayMs = normalizeDelayMs(options.retry?.getDelayMs?.(retryInput));
|
|
1284
|
+
if (delayMs > 0) {
|
|
1285
|
+
await sleepWithAbort(delayMs, abortScope.signal, job);
|
|
1286
|
+
}
|
|
1199
1287
|
continue;
|
|
1200
1288
|
}
|
|
1201
1289
|
throw createTransferFailure(job, error, attempts);
|
|
1290
|
+
} finally {
|
|
1291
|
+
attemptScope.dispose();
|
|
1202
1292
|
}
|
|
1203
1293
|
}
|
|
1204
1294
|
throw createTransferFailure(job, void 0, attempts);
|
|
@@ -1206,12 +1296,13 @@ var TransferEngine = class {
|
|
|
1206
1296
|
abortScope.dispose();
|
|
1207
1297
|
}
|
|
1208
1298
|
}
|
|
1209
|
-
createExecutionContext(job, attempt, startedAt, options, signal, updateBytesTransferred) {
|
|
1299
|
+
createExecutionContext(job, attempt, startedAt, options, signal, updateBytesTransferred, notifyProgress) {
|
|
1210
1300
|
const context = {
|
|
1211
1301
|
attempt,
|
|
1212
1302
|
job,
|
|
1213
1303
|
reportProgress: (bytesTransferred, totalBytes) => {
|
|
1214
1304
|
this.throwIfAborted(signal, job);
|
|
1305
|
+
notifyProgress();
|
|
1215
1306
|
updateBytesTransferred(bytesTransferred);
|
|
1216
1307
|
const progressInput = {
|
|
1217
1308
|
bytesTransferred,
|
|
@@ -1280,6 +1371,96 @@ function createAbortScope(parentSignal, timeout, job) {
|
|
|
1280
1371
|
signal: controller.signal
|
|
1281
1372
|
};
|
|
1282
1373
|
}
|
|
1374
|
+
function createAttemptScope(parentSignal, timeout, job, attempt) {
|
|
1375
|
+
const attemptTimeoutMs = normalizeTimeoutMs(timeout?.attemptTimeoutMs);
|
|
1376
|
+
const stallTimeoutMs = normalizeTimeoutMs(timeout?.stallTimeoutMs);
|
|
1377
|
+
if (attemptTimeoutMs === void 0 && stallTimeoutMs === void 0) {
|
|
1378
|
+
const scope = {
|
|
1379
|
+
dispose: () => void 0,
|
|
1380
|
+
notifyProgress: () => void 0
|
|
1381
|
+
};
|
|
1382
|
+
if (parentSignal !== void 0) scope.signal = parentSignal;
|
|
1383
|
+
return scope;
|
|
1384
|
+
}
|
|
1385
|
+
const controller = new AbortController();
|
|
1386
|
+
const retryable = timeout?.retryable ?? true;
|
|
1387
|
+
const abortFromParent = () => controller.abort(parentSignal?.reason);
|
|
1388
|
+
if (parentSignal?.aborted === true) {
|
|
1389
|
+
abortFromParent();
|
|
1390
|
+
} else {
|
|
1391
|
+
parentSignal?.addEventListener("abort", abortFromParent, { once: true });
|
|
1392
|
+
}
|
|
1393
|
+
const attemptTimer = attemptTimeoutMs === void 0 ? void 0 : setTimeout(() => {
|
|
1394
|
+
controller.abort(
|
|
1395
|
+
new TimeoutError({
|
|
1396
|
+
details: { attempt, attemptTimeoutMs, jobId: job.id, operation: job.operation },
|
|
1397
|
+
message: `Transfer attempt ${String(attempt)} timed out after ${String(attemptTimeoutMs)}ms: ${job.id}`,
|
|
1398
|
+
retryable
|
|
1399
|
+
})
|
|
1400
|
+
);
|
|
1401
|
+
}, attemptTimeoutMs);
|
|
1402
|
+
let stallTimer;
|
|
1403
|
+
const armStallWatchdog = () => {
|
|
1404
|
+
if (stallTimeoutMs === void 0 || controller.signal.aborted) return;
|
|
1405
|
+
if (stallTimer !== void 0) clearTimeout(stallTimer);
|
|
1406
|
+
stallTimer = setTimeout(() => {
|
|
1407
|
+
controller.abort(
|
|
1408
|
+
new TimeoutError({
|
|
1409
|
+
details: { attempt, jobId: job.id, operation: job.operation, stallTimeoutMs },
|
|
1410
|
+
message: `Transfer attempt ${String(attempt)} stalled (no progress for ${String(stallTimeoutMs)}ms): ${job.id}`,
|
|
1411
|
+
retryable
|
|
1412
|
+
})
|
|
1413
|
+
);
|
|
1414
|
+
}, stallTimeoutMs);
|
|
1415
|
+
};
|
|
1416
|
+
armStallWatchdog();
|
|
1417
|
+
return {
|
|
1418
|
+
dispose: () => {
|
|
1419
|
+
if (attemptTimer !== void 0) clearTimeout(attemptTimer);
|
|
1420
|
+
if (stallTimer !== void 0) clearTimeout(stallTimer);
|
|
1421
|
+
parentSignal?.removeEventListener("abort", abortFromParent);
|
|
1422
|
+
},
|
|
1423
|
+
notifyProgress: armStallWatchdog,
|
|
1424
|
+
signal: controller.signal
|
|
1425
|
+
};
|
|
1426
|
+
}
|
|
1427
|
+
function sleepWithAbort(delayMs, signal, job) {
|
|
1428
|
+
return new Promise((resolve, reject) => {
|
|
1429
|
+
if (signal === void 0) {
|
|
1430
|
+
setTimeout(resolve, delayMs);
|
|
1431
|
+
return;
|
|
1432
|
+
}
|
|
1433
|
+
if (signal.aborted) {
|
|
1434
|
+
reject(toAbortFailure(signal, job));
|
|
1435
|
+
return;
|
|
1436
|
+
}
|
|
1437
|
+
const rejectAbort = () => {
|
|
1438
|
+
clearTimeout(timer);
|
|
1439
|
+
reject(toAbortFailure(signal, job));
|
|
1440
|
+
};
|
|
1441
|
+
const timer = setTimeout(() => {
|
|
1442
|
+
signal.removeEventListener("abort", rejectAbort);
|
|
1443
|
+
resolve();
|
|
1444
|
+
}, delayMs);
|
|
1445
|
+
signal.addEventListener("abort", rejectAbort, { once: true });
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1448
|
+
function toAbortFailure(signal, job) {
|
|
1449
|
+
if (signal.reason instanceof ZeroTransferError) {
|
|
1450
|
+
return signal.reason;
|
|
1451
|
+
}
|
|
1452
|
+
return new AbortError({
|
|
1453
|
+
details: { jobId: job.id, operation: job.operation },
|
|
1454
|
+
message: `Transfer job aborted: ${job.id}`,
|
|
1455
|
+
retryable: false
|
|
1456
|
+
});
|
|
1457
|
+
}
|
|
1458
|
+
function normalizeDelayMs(value) {
|
|
1459
|
+
if (value === void 0 || !Number.isFinite(value) || value <= 0) {
|
|
1460
|
+
return 0;
|
|
1461
|
+
}
|
|
1462
|
+
return Math.floor(value);
|
|
1463
|
+
}
|
|
1283
1464
|
function normalizeTimeoutMs(value) {
|
|
1284
1465
|
if (value === void 0 || !Number.isFinite(value) || value <= 0) {
|
|
1285
1466
|
return void 0;
|
|
@@ -1448,7 +1629,7 @@ async function runRoute(options) {
|
|
|
1448
1629
|
const executor = createProviderTransferExecutor({
|
|
1449
1630
|
resolveSession: ({ role }) => sessions.get(role)
|
|
1450
1631
|
});
|
|
1451
|
-
return await engine.execute(job, executor, buildExecuteOptions(options));
|
|
1632
|
+
return await engine.execute(job, executor, buildExecuteOptions(options, client));
|
|
1452
1633
|
} finally {
|
|
1453
1634
|
if (destinationSession !== void 0) {
|
|
1454
1635
|
await destinationSession.disconnect();
|
|
@@ -1485,12 +1666,14 @@ function defaultJobId(route, now) {
|
|
|
1485
1666
|
const timestamp = (now?.() ?? /* @__PURE__ */ new Date()).getTime();
|
|
1486
1667
|
return `route:${route.id}:${timestamp.toString(36)}`;
|
|
1487
1668
|
}
|
|
1488
|
-
function buildExecuteOptions(options) {
|
|
1669
|
+
function buildExecuteOptions(options, client) {
|
|
1489
1670
|
const execute = {};
|
|
1671
|
+
const retry = options.retry ?? client.defaults?.retry;
|
|
1672
|
+
const timeout = options.timeout ?? client.defaults?.timeout;
|
|
1490
1673
|
if (options.signal !== void 0) execute.signal = options.signal;
|
|
1491
|
-
if (
|
|
1674
|
+
if (retry !== void 0) execute.retry = retry;
|
|
1492
1675
|
if (options.onProgress !== void 0) execute.onProgress = options.onProgress;
|
|
1493
|
-
if (
|
|
1676
|
+
if (timeout !== void 0) execute.timeout = timeout;
|
|
1494
1677
|
if (options.bandwidthLimit !== void 0) execute.bandwidthLimit = options.bandwidthLimit;
|
|
1495
1678
|
return execute;
|
|
1496
1679
|
}
|
|
@@ -1547,41 +1730,6 @@ function defaultRouteSuffix(source, destination) {
|
|
|
1547
1730
|
return `${source}->${destination}`;
|
|
1548
1731
|
}
|
|
1549
1732
|
|
|
1550
|
-
// src/logging/redaction.ts
|
|
1551
|
-
var REDACTED = "[REDACTED]";
|
|
1552
|
-
var SENSITIVE_KEY_PATTERN = /(?:password|passphrase|privatekey|token|secret|username|user)$/i;
|
|
1553
|
-
var SECRET_COMMAND_PATTERN = /^(PASS|USER|ACCT)\s+(.+)$/i;
|
|
1554
|
-
function isSensitiveKey(key) {
|
|
1555
|
-
return SENSITIVE_KEY_PATTERN.test(key.replace(/[_-]/g, ""));
|
|
1556
|
-
}
|
|
1557
|
-
function redactCommand(command) {
|
|
1558
|
-
return command.replace(SECRET_COMMAND_PATTERN, (_fullMatch, commandName) => {
|
|
1559
|
-
return `${commandName.toUpperCase()} ${REDACTED}`;
|
|
1560
|
-
});
|
|
1561
|
-
}
|
|
1562
|
-
function redactValue(value) {
|
|
1563
|
-
if (typeof value === "string") {
|
|
1564
|
-
return redactCommand(value);
|
|
1565
|
-
}
|
|
1566
|
-
if (Array.isArray(value)) {
|
|
1567
|
-
return value.map((item) => redactValue(item));
|
|
1568
|
-
}
|
|
1569
|
-
if (value !== null && typeof value === "object") {
|
|
1570
|
-
return redactObject(value);
|
|
1571
|
-
}
|
|
1572
|
-
return value;
|
|
1573
|
-
}
|
|
1574
|
-
function redactObject(input) {
|
|
1575
|
-
return Object.fromEntries(
|
|
1576
|
-
Object.entries(input).map(([key, value]) => {
|
|
1577
|
-
if (isSensitiveKey(key)) {
|
|
1578
|
-
return [key, REDACTED];
|
|
1579
|
-
}
|
|
1580
|
-
return [key, redactValue(value)];
|
|
1581
|
-
})
|
|
1582
|
-
);
|
|
1583
|
-
}
|
|
1584
|
-
|
|
1585
1733
|
// src/profiles/SecretSource.ts
|
|
1586
1734
|
import { Buffer as Buffer3 } from "buffer";
|
|
1587
1735
|
import { readFile } from "fs/promises";
|
|
@@ -1963,11 +2111,11 @@ import {
|
|
|
1963
2111
|
import path from "path";
|
|
1964
2112
|
|
|
1965
2113
|
// src/utils/path.ts
|
|
1966
|
-
var UNSAFE_FTP_ARGUMENT_PATTERN = /[\r\n]/;
|
|
2114
|
+
var UNSAFE_FTP_ARGUMENT_PATTERN = /[\r\n\0]/;
|
|
1967
2115
|
function assertSafeFtpArgument(value, label = "path") {
|
|
1968
2116
|
if (UNSAFE_FTP_ARGUMENT_PATTERN.test(value)) {
|
|
1969
2117
|
throw new ConfigurationError({
|
|
1970
|
-
message: `Unsafe FTP ${label}: CR and
|
|
2118
|
+
message: `Unsafe FTP ${label}: CR, LF, and NUL characters are not allowed`,
|
|
1971
2119
|
retryable: false,
|
|
1972
2120
|
details: {
|
|
1973
2121
|
label
|
|
@@ -3269,7 +3417,6 @@ function expandAlgorithms(values) {
|
|
|
3269
3417
|
}
|
|
3270
3418
|
|
|
3271
3419
|
// src/profiles/importers/FileZillaImporter.ts
|
|
3272
|
-
import { Buffer as Buffer6 } from "buffer";
|
|
3273
3420
|
function importFileZillaSites(xml) {
|
|
3274
3421
|
const events = tokenizeXml(xml);
|
|
3275
3422
|
if (events.length === 0) {
|
|
@@ -3285,7 +3432,6 @@ function importFileZillaSites(xml) {
|
|
|
3285
3432
|
const folderNamePending = [];
|
|
3286
3433
|
let inServer = false;
|
|
3287
3434
|
let serverFields = {};
|
|
3288
|
-
let serverPasswordEncoding;
|
|
3289
3435
|
let activeTag;
|
|
3290
3436
|
let captureFolderName = false;
|
|
3291
3437
|
for (const event of events) {
|
|
@@ -3298,13 +3444,9 @@ function importFileZillaSites(xml) {
|
|
|
3298
3444
|
if (event.name === "Server") {
|
|
3299
3445
|
inServer = true;
|
|
3300
3446
|
serverFields = {};
|
|
3301
|
-
serverPasswordEncoding = void 0;
|
|
3302
3447
|
continue;
|
|
3303
3448
|
}
|
|
3304
3449
|
activeTag = event.name;
|
|
3305
|
-
if (event.name === "Pass" && inServer) {
|
|
3306
|
-
serverPasswordEncoding = event.attributes["encoding"];
|
|
3307
|
-
}
|
|
3308
3450
|
if (event.name === "Name" && !inServer && folderNamePending.length > 0) {
|
|
3309
3451
|
captureFolderName = true;
|
|
3310
3452
|
}
|
|
@@ -3330,7 +3472,7 @@ function importFileZillaSites(xml) {
|
|
|
3330
3472
|
}
|
|
3331
3473
|
if (event.name === "Server") {
|
|
3332
3474
|
const folder = folderStack.filter((segment) => segment !== "");
|
|
3333
|
-
const result = buildSiteFromFields(serverFields
|
|
3475
|
+
const result = buildSiteFromFields(serverFields);
|
|
3334
3476
|
if (result.kind === "site") {
|
|
3335
3477
|
sites.push({ ...result.site, folder });
|
|
3336
3478
|
} else {
|
|
@@ -3342,7 +3484,6 @@ function importFileZillaSites(xml) {
|
|
|
3342
3484
|
}
|
|
3343
3485
|
inServer = false;
|
|
3344
3486
|
serverFields = {};
|
|
3345
|
-
serverPasswordEncoding = void 0;
|
|
3346
3487
|
activeTag = void 0;
|
|
3347
3488
|
continue;
|
|
3348
3489
|
}
|
|
@@ -3351,7 +3492,7 @@ function importFileZillaSites(xml) {
|
|
|
3351
3492
|
}
|
|
3352
3493
|
return { sites, skipped };
|
|
3353
3494
|
}
|
|
3354
|
-
function buildSiteFromFields(fields
|
|
3495
|
+
function buildSiteFromFields(fields) {
|
|
3355
3496
|
const name = (fields["Name"] ?? fields["Host"] ?? "Untitled").trim();
|
|
3356
3497
|
const host = (fields["Host"] ?? "").trim();
|
|
3357
3498
|
if (host === "") return { kind: "skipped", name };
|
|
@@ -3370,18 +3511,9 @@ function buildSiteFromFields(fields, passwordEncoding) {
|
|
|
3370
3511
|
}
|
|
3371
3512
|
const user = fields["User"]?.trim();
|
|
3372
3513
|
if (user !== void 0 && user !== "") profile.username = { value: user };
|
|
3373
|
-
let password;
|
|
3374
3514
|
const rawPass = fields["Pass"];
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
password = Buffer6.from(rawPass, "base64").toString("utf8");
|
|
3378
|
-
} else {
|
|
3379
|
-
password = rawPass;
|
|
3380
|
-
}
|
|
3381
|
-
if (password !== void 0 && password !== "") profile.password = { value: password };
|
|
3382
|
-
}
|
|
3383
|
-
const site = { name, profile };
|
|
3384
|
-
if (password !== void 0) site.password = password;
|
|
3515
|
+
const hasStoredPassword = rawPass !== void 0 && rawPass !== "";
|
|
3516
|
+
const site = { hasStoredPassword, name, profile };
|
|
3385
3517
|
const logonText = fields["Logontype"];
|
|
3386
3518
|
if (logonText !== void 0) {
|
|
3387
3519
|
const logonType = Number.parseInt(logonText.trim(), 10);
|
|
@@ -3624,6 +3756,62 @@ function mapFtp550(details) {
|
|
|
3624
3756
|
return new PermissionDeniedError(details);
|
|
3625
3757
|
}
|
|
3626
3758
|
|
|
3759
|
+
// src/transfers/createDefaultRetryPolicy.ts
|
|
3760
|
+
var DEFAULT_MAX_ATTEMPTS = 4;
|
|
3761
|
+
var DEFAULT_BASE_DELAY_MS = 250;
|
|
3762
|
+
var DEFAULT_MAX_DELAY_MS = 3e4;
|
|
3763
|
+
var DEFAULT_MAX_ELAPSED_MS = 3e5;
|
|
3764
|
+
function createDefaultRetryPolicy(options = {}) {
|
|
3765
|
+
const maxAttempts = normalizePositiveInteger(options.maxAttempts, DEFAULT_MAX_ATTEMPTS);
|
|
3766
|
+
const baseDelayMs = normalizeNonNegative(options.baseDelayMs, DEFAULT_BASE_DELAY_MS);
|
|
3767
|
+
const maxDelayMs = normalizeNonNegative(options.maxDelayMs, DEFAULT_MAX_DELAY_MS);
|
|
3768
|
+
const maxElapsedMs = normalizeNonNegative(options.maxElapsedMs, DEFAULT_MAX_ELAPSED_MS);
|
|
3769
|
+
const random = options.random ?? Math.random;
|
|
3770
|
+
return {
|
|
3771
|
+
getDelayMs(input) {
|
|
3772
|
+
const retryAfterMs = readRetryAfterMs(input.error);
|
|
3773
|
+
if (retryAfterMs !== void 0) {
|
|
3774
|
+
return retryAfterMs;
|
|
3775
|
+
}
|
|
3776
|
+
const exponentialMs = baseDelayMs * 2 ** (input.attempt - 1);
|
|
3777
|
+
const cappedMs = Math.min(maxDelayMs, exponentialMs);
|
|
3778
|
+
return Math.floor(random() * cappedMs);
|
|
3779
|
+
},
|
|
3780
|
+
maxAttempts,
|
|
3781
|
+
shouldRetry(input) {
|
|
3782
|
+
if (!(input.error instanceof ZeroTransferError) || !input.error.retryable) {
|
|
3783
|
+
return false;
|
|
3784
|
+
}
|
|
3785
|
+
if (input.elapsedMs >= maxElapsedMs) {
|
|
3786
|
+
return false;
|
|
3787
|
+
}
|
|
3788
|
+
const retryAfterMs = readRetryAfterMs(input.error);
|
|
3789
|
+
if (retryAfterMs !== void 0 && input.elapsedMs + retryAfterMs > maxElapsedMs) {
|
|
3790
|
+
return false;
|
|
3791
|
+
}
|
|
3792
|
+
return true;
|
|
3793
|
+
}
|
|
3794
|
+
};
|
|
3795
|
+
}
|
|
3796
|
+
function readRetryAfterMs(error) {
|
|
3797
|
+
if (!(error instanceof ZeroTransferError)) return void 0;
|
|
3798
|
+
const value = error.details?.["retryAfterMs"];
|
|
3799
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return void 0;
|
|
3800
|
+
return Math.floor(value);
|
|
3801
|
+
}
|
|
3802
|
+
function normalizePositiveInteger(value, fallback) {
|
|
3803
|
+
if (value === void 0 || !Number.isFinite(value) || value < 1) {
|
|
3804
|
+
return fallback;
|
|
3805
|
+
}
|
|
3806
|
+
return Math.floor(value);
|
|
3807
|
+
}
|
|
3808
|
+
function normalizeNonNegative(value, fallback) {
|
|
3809
|
+
if (value === void 0 || !Number.isFinite(value) || value < 0) {
|
|
3810
|
+
return fallback;
|
|
3811
|
+
}
|
|
3812
|
+
return Math.floor(value);
|
|
3813
|
+
}
|
|
3814
|
+
|
|
3627
3815
|
// src/transfers/TransferPlan.ts
|
|
3628
3816
|
function createTransferPlan(input) {
|
|
3629
3817
|
const plan = {
|
|
@@ -3721,8 +3909,8 @@ var TransferQueue = class {
|
|
|
3721
3909
|
this.concurrency = normalizeConcurrency(options.concurrency);
|
|
3722
3910
|
this.defaultExecutor = options.executor;
|
|
3723
3911
|
this.resolveExecutor = options.resolveExecutor;
|
|
3724
|
-
this.retry = options.retry;
|
|
3725
|
-
this.timeout = options.timeout;
|
|
3912
|
+
this.retry = options.retry ?? options.client?.defaults?.retry;
|
|
3913
|
+
this.timeout = options.timeout ?? options.client?.defaults?.timeout;
|
|
3726
3914
|
this.bandwidthLimit = options.bandwidthLimit;
|
|
3727
3915
|
this.onProgress = options.onProgress;
|
|
3728
3916
|
this.onReceipt = options.onReceipt;
|
|
@@ -4799,12 +4987,12 @@ function isMainModule(importMetaUrl) {
|
|
|
4799
4987
|
}
|
|
4800
4988
|
|
|
4801
4989
|
// src/providers/native/sftp/NativeSftpProvider.ts
|
|
4802
|
-
import { Buffer as
|
|
4990
|
+
import { Buffer as Buffer20 } from "buffer";
|
|
4803
4991
|
import { createHash as createHash3, createPrivateKey } from "crypto";
|
|
4804
4992
|
import { createConnection } from "net";
|
|
4805
4993
|
|
|
4806
4994
|
// src/protocols/ssh/binary/SshDataWriter.ts
|
|
4807
|
-
import { Buffer as
|
|
4995
|
+
import { Buffer as Buffer6 } from "buffer";
|
|
4808
4996
|
var MAX_UINT32 = 4294967295;
|
|
4809
4997
|
var MAX_UINT64 = (1n << 64n) - 1n;
|
|
4810
4998
|
var SshDataWriter = class {
|
|
@@ -4812,7 +5000,7 @@ var SshDataWriter = class {
|
|
|
4812
5000
|
length = 0;
|
|
4813
5001
|
writeByte(value) {
|
|
4814
5002
|
this.assertByte(value, "byte");
|
|
4815
|
-
const chunk =
|
|
5003
|
+
const chunk = Buffer6.alloc(1);
|
|
4816
5004
|
chunk.writeUInt8(value, 0);
|
|
4817
5005
|
return this.push(chunk);
|
|
4818
5006
|
}
|
|
@@ -4820,7 +5008,7 @@ var SshDataWriter = class {
|
|
|
4820
5008
|
return this.writeByte(value ? 1 : 0);
|
|
4821
5009
|
}
|
|
4822
5010
|
writeBytes(value) {
|
|
4823
|
-
return this.push(
|
|
5011
|
+
return this.push(Buffer6.from(value));
|
|
4824
5012
|
}
|
|
4825
5013
|
writeUint32(value) {
|
|
4826
5014
|
if (!Number.isInteger(value) || value < 0 || value > MAX_UINT32) {
|
|
@@ -4830,7 +5018,7 @@ var SshDataWriter = class {
|
|
|
4830
5018
|
retryable: false
|
|
4831
5019
|
});
|
|
4832
5020
|
}
|
|
4833
|
-
const chunk =
|
|
5021
|
+
const chunk = Buffer6.alloc(4);
|
|
4834
5022
|
chunk.writeUInt32BE(value, 0);
|
|
4835
5023
|
return this.push(chunk);
|
|
4836
5024
|
}
|
|
@@ -4842,12 +5030,12 @@ var SshDataWriter = class {
|
|
|
4842
5030
|
retryable: false
|
|
4843
5031
|
});
|
|
4844
5032
|
}
|
|
4845
|
-
const chunk =
|
|
5033
|
+
const chunk = Buffer6.alloc(8);
|
|
4846
5034
|
chunk.writeBigUInt64BE(value, 0);
|
|
4847
5035
|
return this.push(chunk);
|
|
4848
5036
|
}
|
|
4849
5037
|
writeString(value, encoding = "utf8") {
|
|
4850
|
-
const payload = typeof value === "string" ?
|
|
5038
|
+
const payload = typeof value === "string" ? Buffer6.from(value, encoding) : Buffer6.from(value);
|
|
4851
5039
|
this.writeUint32(payload.length);
|
|
4852
5040
|
return this.push(payload);
|
|
4853
5041
|
}
|
|
@@ -4869,7 +5057,7 @@ var SshDataWriter = class {
|
|
|
4869
5057
|
return this.writeString(values.join(","), "ascii");
|
|
4870
5058
|
}
|
|
4871
5059
|
toBuffer() {
|
|
4872
|
-
return
|
|
5060
|
+
return Buffer6.concat(this.chunks, this.length);
|
|
4873
5061
|
}
|
|
4874
5062
|
push(chunk) {
|
|
4875
5063
|
this.chunks.push(chunk);
|
|
@@ -4887,23 +5075,23 @@ var SshDataWriter = class {
|
|
|
4887
5075
|
}
|
|
4888
5076
|
};
|
|
4889
5077
|
function normalizePositiveMpint(value) {
|
|
4890
|
-
const input =
|
|
5078
|
+
const input = Buffer6.from(value);
|
|
4891
5079
|
let offset = 0;
|
|
4892
5080
|
while (offset < input.length && input[offset] === 0) {
|
|
4893
5081
|
offset += 1;
|
|
4894
5082
|
}
|
|
4895
5083
|
if (offset >= input.length) {
|
|
4896
|
-
return
|
|
5084
|
+
return Buffer6.alloc(0);
|
|
4897
5085
|
}
|
|
4898
5086
|
const stripped = input.subarray(offset);
|
|
4899
5087
|
if ((stripped[0] & 128) === 128) {
|
|
4900
|
-
return
|
|
5088
|
+
return Buffer6.concat([Buffer6.from([0]), stripped]);
|
|
4901
5089
|
}
|
|
4902
5090
|
return stripped;
|
|
4903
5091
|
}
|
|
4904
5092
|
|
|
4905
5093
|
// src/protocols/ssh/binary/SshDataReader.ts
|
|
4906
|
-
import { Buffer as
|
|
5094
|
+
import { Buffer as Buffer7 } from "buffer";
|
|
4907
5095
|
var SshDataReader = class {
|
|
4908
5096
|
constructor(source) {
|
|
4909
5097
|
this.source = source;
|
|
@@ -4929,18 +5117,18 @@ var SshDataReader = class {
|
|
|
4929
5117
|
this.ensureAvailable(length, "bytes");
|
|
4930
5118
|
const data = this.source.subarray(this.offset, this.offset + length);
|
|
4931
5119
|
this.offset += length;
|
|
4932
|
-
return
|
|
5120
|
+
return Buffer7.from(data);
|
|
4933
5121
|
}
|
|
4934
5122
|
readUint32() {
|
|
4935
5123
|
this.ensureAvailable(4, "uint32");
|
|
4936
|
-
const buffer =
|
|
5124
|
+
const buffer = Buffer7.from(this.source);
|
|
4937
5125
|
const value = buffer.readUInt32BE(this.offset);
|
|
4938
5126
|
this.offset += 4;
|
|
4939
5127
|
return value;
|
|
4940
5128
|
}
|
|
4941
5129
|
readUint64() {
|
|
4942
5130
|
this.ensureAvailable(8, "uint64");
|
|
4943
|
-
const buffer =
|
|
5131
|
+
const buffer = Buffer7.from(this.source);
|
|
4944
5132
|
const value = buffer.readBigUInt64BE(this.offset);
|
|
4945
5133
|
this.offset += 8;
|
|
4946
5134
|
return value;
|
|
@@ -4950,7 +5138,7 @@ var SshDataReader = class {
|
|
|
4950
5138
|
this.ensureAvailable(length, "string");
|
|
4951
5139
|
const data = this.source.subarray(this.offset, this.offset + length);
|
|
4952
5140
|
this.offset += length;
|
|
4953
|
-
return
|
|
5141
|
+
return Buffer7.from(data);
|
|
4954
5142
|
}
|
|
4955
5143
|
readUtf8String() {
|
|
4956
5144
|
return this.readString().toString("utf8");
|
|
@@ -5305,7 +5493,7 @@ function buildKiRequest(args) {
|
|
|
5305
5493
|
}
|
|
5306
5494
|
|
|
5307
5495
|
// src/protocols/ssh/auth/SshPublickeyCredentialBuilder.ts
|
|
5308
|
-
import { Buffer as
|
|
5496
|
+
import { Buffer as Buffer8 } from "buffer";
|
|
5309
5497
|
import { createPublicKey, sign as cryptoSign } from "crypto";
|
|
5310
5498
|
var ED25519_RAW_KEY_LENGTH = 32;
|
|
5311
5499
|
var ED25519_SPKI_PREFIX_LENGTH = 12;
|
|
@@ -5323,7 +5511,7 @@ function buildPublickeyCredential(options) {
|
|
|
5323
5511
|
return {
|
|
5324
5512
|
algorithmName: "ssh-ed25519",
|
|
5325
5513
|
publicKeyBlob,
|
|
5326
|
-
sign: (data) => cryptoSign(null,
|
|
5514
|
+
sign: (data) => cryptoSign(null, Buffer8.from(data), privateKey),
|
|
5327
5515
|
type: "publickey",
|
|
5328
5516
|
username
|
|
5329
5517
|
};
|
|
@@ -5341,7 +5529,7 @@ function buildPublickeyCredential(options) {
|
|
|
5341
5529
|
return {
|
|
5342
5530
|
algorithmName,
|
|
5343
5531
|
publicKeyBlob,
|
|
5344
|
-
sign: (data) => cryptoSign(hash,
|
|
5532
|
+
sign: (data) => cryptoSign(hash, Buffer8.from(data), privateKey),
|
|
5345
5533
|
type: "publickey",
|
|
5346
5534
|
username
|
|
5347
5535
|
};
|
|
@@ -5354,7 +5542,7 @@ function buildPublickeyCredential(options) {
|
|
|
5354
5542
|
}
|
|
5355
5543
|
function base64UrlToMpint(value) {
|
|
5356
5544
|
const padded = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
5357
|
-
const buffer =
|
|
5545
|
+
const buffer = Buffer8.from(padded, "base64");
|
|
5358
5546
|
return buffer;
|
|
5359
5547
|
}
|
|
5360
5548
|
function createInvalidKeyError(message) {
|
|
@@ -5480,7 +5668,7 @@ function encodeSshChannelClose(recipientChannel) {
|
|
|
5480
5668
|
}
|
|
5481
5669
|
|
|
5482
5670
|
// src/protocols/ssh/connection/SshSessionChannel.ts
|
|
5483
|
-
import { Buffer as
|
|
5671
|
+
import { Buffer as Buffer9 } from "buffer";
|
|
5484
5672
|
var INITIAL_WINDOW_SIZE = 256 * 1024;
|
|
5485
5673
|
var MAX_PACKET_SIZE = 32 * 1024;
|
|
5486
5674
|
var WINDOW_REFILL_THRESHOLD = 64 * 1024;
|
|
@@ -5638,7 +5826,7 @@ var SshSessionChannel = class {
|
|
|
5638
5826
|
this.remoteWindowRemaining,
|
|
5639
5827
|
this.remoteMaxPacketSize
|
|
5640
5828
|
);
|
|
5641
|
-
const chunk =
|
|
5829
|
+
const chunk = Buffer9.from(data.subarray(offset, offset + chunkSize));
|
|
5642
5830
|
this.transport.sendPayload(
|
|
5643
5831
|
encodeSshChannelData({ data: chunk, recipientChannel: this.remoteChannelId })
|
|
5644
5832
|
);
|
|
@@ -5900,10 +6088,10 @@ var SshConnectionManager = class {
|
|
|
5900
6088
|
};
|
|
5901
6089
|
|
|
5902
6090
|
// src/protocols/ssh/transport/SshTransportConnection.ts
|
|
5903
|
-
import { Buffer as
|
|
6091
|
+
import { Buffer as Buffer17 } from "buffer";
|
|
5904
6092
|
|
|
5905
6093
|
// src/protocols/ssh/transport/SshTransportHandshake.ts
|
|
5906
|
-
import { Buffer as
|
|
6094
|
+
import { Buffer as Buffer15 } from "buffer";
|
|
5907
6095
|
|
|
5908
6096
|
// src/protocols/ssh/transport/SshAlgorithmNegotiation.ts
|
|
5909
6097
|
var DEFAULT_SSH_ALGORITHM_PREFERENCES = {
|
|
@@ -6065,12 +6253,12 @@ function parseSshIdentificationLine(line) {
|
|
|
6065
6253
|
}
|
|
6066
6254
|
|
|
6067
6255
|
// src/protocols/ssh/transport/SshKexInit.ts
|
|
6068
|
-
import { Buffer as
|
|
6256
|
+
import { Buffer as Buffer10 } from "buffer";
|
|
6069
6257
|
import { randomBytes } from "crypto";
|
|
6070
6258
|
var SSH_MSG_KEXINIT = 20;
|
|
6071
6259
|
var KEXINIT_COOKIE_LENGTH = 16;
|
|
6072
6260
|
function encodeSshKexInitMessage(options) {
|
|
6073
|
-
const cookie = options.cookie === void 0 ? randomBytes(KEXINIT_COOKIE_LENGTH) :
|
|
6261
|
+
const cookie = options.cookie === void 0 ? randomBytes(KEXINIT_COOKIE_LENGTH) : Buffer10.from(options.cookie);
|
|
6074
6262
|
if (cookie.length !== KEXINIT_COOKIE_LENGTH) {
|
|
6075
6263
|
throw new ConfigurationError({
|
|
6076
6264
|
details: { actualLength: cookie.length, expectedLength: KEXINIT_COOKIE_LENGTH },
|
|
@@ -6140,12 +6328,12 @@ function decodeSshKexInitMessage(payload) {
|
|
|
6140
6328
|
}
|
|
6141
6329
|
|
|
6142
6330
|
// src/protocols/ssh/transport/SshKexCurve25519.ts
|
|
6143
|
-
import { Buffer as
|
|
6331
|
+
import { Buffer as Buffer11 } from "buffer";
|
|
6144
6332
|
import { createPublicKey as createPublicKey2, diffieHellman, generateKeyPairSync } from "crypto";
|
|
6145
6333
|
var SSH_MSG_KEX_ECDH_INIT = 30;
|
|
6146
6334
|
var SSH_MSG_KEX_ECDH_REPLY = 31;
|
|
6147
6335
|
var X25519_PUBLIC_KEY_LENGTH = 32;
|
|
6148
|
-
var X25519_SPKI_PREFIX =
|
|
6336
|
+
var X25519_SPKI_PREFIX = Buffer11.from("302a300506032b656e032100", "hex");
|
|
6149
6337
|
function createCurve25519Ephemeral() {
|
|
6150
6338
|
const { privateKey, publicKey } = generateKeyPairSync("x25519");
|
|
6151
6339
|
const encodedPublicKey = exportX25519PublicKeyRaw(publicKey);
|
|
@@ -6190,7 +6378,7 @@ function exportX25519PublicKeyRaw(publicKey) {
|
|
|
6190
6378
|
}
|
|
6191
6379
|
function importX25519PublicKeyRaw(raw) {
|
|
6192
6380
|
const normalized = normalizeX25519PublicKey(raw, "server");
|
|
6193
|
-
const der =
|
|
6381
|
+
const der = Buffer11.concat([X25519_SPKI_PREFIX, normalized]);
|
|
6194
6382
|
return createPublicKey2({
|
|
6195
6383
|
format: "der",
|
|
6196
6384
|
key: der,
|
|
@@ -6198,7 +6386,7 @@ function importX25519PublicKeyRaw(raw) {
|
|
|
6198
6386
|
});
|
|
6199
6387
|
}
|
|
6200
6388
|
function normalizeX25519PublicKey(value, label) {
|
|
6201
|
-
const key =
|
|
6389
|
+
const key = Buffer11.from(value);
|
|
6202
6390
|
if (key.length !== X25519_PUBLIC_KEY_LENGTH) {
|
|
6203
6391
|
throw new ConfigurationError({
|
|
6204
6392
|
details: { keyLength: key.length, label },
|
|
@@ -6211,7 +6399,7 @@ function normalizeX25519PublicKey(value, label) {
|
|
|
6211
6399
|
}
|
|
6212
6400
|
|
|
6213
6401
|
// src/protocols/ssh/transport/SshKeyDerivation.ts
|
|
6214
|
-
import { Buffer as
|
|
6402
|
+
import { Buffer as Buffer12 } from "buffer";
|
|
6215
6403
|
import { createHash } from "crypto";
|
|
6216
6404
|
function deriveSshSessionKeys(input) {
|
|
6217
6405
|
const hashAlgorithm = resolveKexHashAlgorithm(input.kexAlgorithm);
|
|
@@ -6233,7 +6421,7 @@ function deriveSshSessionKeys(input) {
|
|
|
6233
6421
|
input.negotiatedAlgorithms.encryptionServerToClient,
|
|
6234
6422
|
input.negotiatedAlgorithms.macServerToClient
|
|
6235
6423
|
);
|
|
6236
|
-
const sharedSecret =
|
|
6424
|
+
const sharedSecret = Buffer12.from(input.sharedSecret);
|
|
6237
6425
|
return {
|
|
6238
6426
|
clientToServer: {
|
|
6239
6427
|
encryptionKey: deriveMaterial(
|
|
@@ -6283,21 +6471,21 @@ function computeCurve25519ExchangeHash(input, hashAlgorithm) {
|
|
|
6283
6471
|
}
|
|
6284
6472
|
function deriveMaterial(sharedSecret, exchangeHash, sessionId, letter, length, hashAlgorithm) {
|
|
6285
6473
|
if (length <= 0) {
|
|
6286
|
-
return
|
|
6474
|
+
return Buffer12.alloc(0);
|
|
6287
6475
|
}
|
|
6288
6476
|
const result = [];
|
|
6289
6477
|
const first2 = createHash(hashAlgorithm).update(
|
|
6290
6478
|
new SshDataWriter().writeMpint(sharedSecret).writeBytes(exchangeHash).writeByte(letter.charCodeAt(0)).writeBytes(sessionId).toBuffer()
|
|
6291
6479
|
).digest();
|
|
6292
6480
|
result.push(first2);
|
|
6293
|
-
while (
|
|
6294
|
-
const previous =
|
|
6481
|
+
while (Buffer12.concat(result).length < length) {
|
|
6482
|
+
const previous = Buffer12.concat(result);
|
|
6295
6483
|
const next = createHash(hashAlgorithm).update(
|
|
6296
6484
|
new SshDataWriter().writeMpint(sharedSecret).writeBytes(exchangeHash).writeBytes(previous).toBuffer()
|
|
6297
6485
|
).digest();
|
|
6298
6486
|
result.push(next);
|
|
6299
6487
|
}
|
|
6300
|
-
return
|
|
6488
|
+
return Buffer12.concat(result).subarray(0, length);
|
|
6301
6489
|
}
|
|
6302
6490
|
function resolveKexHashAlgorithm(kexAlgorithm) {
|
|
6303
6491
|
if (kexAlgorithm === "curve25519-sha256" || kexAlgorithm === "curve25519-sha256@libssh.org") {
|
|
@@ -6385,20 +6573,21 @@ function decodeSshNewKeysMessage(payload) {
|
|
|
6385
6573
|
}
|
|
6386
6574
|
|
|
6387
6575
|
// src/protocols/ssh/transport/SshTransportPacket.ts
|
|
6388
|
-
import { Buffer as
|
|
6576
|
+
import { Buffer as Buffer13 } from "buffer";
|
|
6389
6577
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
6390
6578
|
var MIN_PADDING_LENGTH = 4;
|
|
6391
6579
|
var MIN_PACKET_LENGTH = 1 + MIN_PADDING_LENGTH;
|
|
6580
|
+
var MAX_SSH_PACKET_LENGTH = 256 * 1024;
|
|
6392
6581
|
function encodeSshTransportPacket(payload, options = {}) {
|
|
6393
|
-
const body =
|
|
6582
|
+
const body = Buffer13.from(payload);
|
|
6394
6583
|
const blockSize = normalizeBlockSize(options.blockSize ?? 8);
|
|
6395
6584
|
let paddingLength = MIN_PADDING_LENGTH;
|
|
6396
6585
|
while ((1 + body.length + paddingLength + 4) % blockSize !== 0) {
|
|
6397
6586
|
paddingLength += 1;
|
|
6398
6587
|
}
|
|
6399
|
-
const padding = options.randomPadding === false ?
|
|
6588
|
+
const padding = options.randomPadding === false ? Buffer13.alloc(paddingLength) : randomBytes2(paddingLength);
|
|
6400
6589
|
const packetLength = 1 + body.length + paddingLength;
|
|
6401
|
-
const frame =
|
|
6590
|
+
const frame = Buffer13.alloc(4 + packetLength);
|
|
6402
6591
|
frame.writeUInt32BE(packetLength, 0);
|
|
6403
6592
|
frame.writeUInt8(paddingLength, 4);
|
|
6404
6593
|
body.copy(frame, 5);
|
|
@@ -6406,7 +6595,7 @@ function encodeSshTransportPacket(payload, options = {}) {
|
|
|
6406
6595
|
return frame;
|
|
6407
6596
|
}
|
|
6408
6597
|
function decodeSshTransportPacket(frame) {
|
|
6409
|
-
const bytes =
|
|
6598
|
+
const bytes = Buffer13.from(frame);
|
|
6410
6599
|
if (bytes.length < 4 + MIN_PACKET_LENGTH) {
|
|
6411
6600
|
throw new ParseError({
|
|
6412
6601
|
details: { length: bytes.length },
|
|
@@ -6460,12 +6649,20 @@ function decodeSshTransportPacket(frame) {
|
|
|
6460
6649
|
};
|
|
6461
6650
|
}
|
|
6462
6651
|
var SshTransportPacketFramer = class {
|
|
6463
|
-
pending =
|
|
6652
|
+
pending = Buffer13.alloc(0);
|
|
6464
6653
|
push(chunk) {
|
|
6465
|
-
this.pending =
|
|
6654
|
+
this.pending = Buffer13.concat([this.pending, Buffer13.from(chunk)]);
|
|
6466
6655
|
const packets = [];
|
|
6467
6656
|
while (this.pending.length >= 4) {
|
|
6468
6657
|
const packetLength = this.pending.readUInt32BE(0);
|
|
6658
|
+
if (packetLength > MAX_SSH_PACKET_LENGTH) {
|
|
6659
|
+
throw new ParseError({
|
|
6660
|
+
details: { maxPacketLength: MAX_SSH_PACKET_LENGTH, packetLength },
|
|
6661
|
+
message: "SSH transport packet length exceeds the maximum accepted size",
|
|
6662
|
+
protocol: "sftp",
|
|
6663
|
+
retryable: false
|
|
6664
|
+
});
|
|
6665
|
+
}
|
|
6469
6666
|
const frameLength = 4 + packetLength;
|
|
6470
6667
|
if (this.pending.length < frameLength) {
|
|
6471
6668
|
break;
|
|
@@ -6481,8 +6678,8 @@ var SshTransportPacketFramer = class {
|
|
|
6481
6678
|
}
|
|
6482
6679
|
/** Returns and clears any bytes buffered but not yet part of a complete packet. */
|
|
6483
6680
|
takeRemainingBytes() {
|
|
6484
|
-
const remaining =
|
|
6485
|
-
this.pending =
|
|
6681
|
+
const remaining = Buffer13.from(this.pending);
|
|
6682
|
+
this.pending = Buffer13.alloc(0);
|
|
6486
6683
|
return remaining;
|
|
6487
6684
|
}
|
|
6488
6685
|
};
|
|
@@ -6499,10 +6696,10 @@ function normalizeBlockSize(blockSize) {
|
|
|
6499
6696
|
}
|
|
6500
6697
|
|
|
6501
6698
|
// src/protocols/ssh/transport/SshHostKeyVerification.ts
|
|
6502
|
-
import { Buffer as
|
|
6699
|
+
import { Buffer as Buffer14 } from "buffer";
|
|
6503
6700
|
import { createHash as createHash2, createPublicKey as createPublicKey3, verify as cryptoVerify } from "crypto";
|
|
6504
6701
|
var ED25519_RAW_KEY_LENGTH2 = 32;
|
|
6505
|
-
var ED25519_SPKI_PREFIX =
|
|
6702
|
+
var ED25519_SPKI_PREFIX = Buffer14.from("302a300506032b6570032100", "hex");
|
|
6506
6703
|
function verifySshHostKeySignature(input) {
|
|
6507
6704
|
const { algorithmName, publicKey } = parseHostKey(input.hostKeyBlob);
|
|
6508
6705
|
const { signatureAlgorithm, signatureBytes } = parseSignatureBlob(input.signatureBlob);
|
|
@@ -6515,9 +6712,9 @@ function verifySshHostKeySignature(input) {
|
|
|
6515
6712
|
});
|
|
6516
6713
|
}
|
|
6517
6714
|
const verified = verifySignature({
|
|
6518
|
-
data:
|
|
6715
|
+
data: Buffer14.from(input.exchangeHash),
|
|
6519
6716
|
publicKey,
|
|
6520
|
-
signature:
|
|
6717
|
+
signature: Buffer14.from(signatureBytes),
|
|
6521
6718
|
signatureAlgorithm
|
|
6522
6719
|
});
|
|
6523
6720
|
if (!verified) {
|
|
@@ -6546,7 +6743,7 @@ function parseHostKey(blob) {
|
|
|
6546
6743
|
retryable: false
|
|
6547
6744
|
});
|
|
6548
6745
|
}
|
|
6549
|
-
const spki =
|
|
6746
|
+
const spki = Buffer14.concat([ED25519_SPKI_PREFIX, raw]);
|
|
6550
6747
|
return {
|
|
6551
6748
|
algorithmName,
|
|
6552
6749
|
publicKey: createPublicKey3({ format: "der", key: spki, type: "spki" })
|
|
@@ -6652,37 +6849,37 @@ function verifySignature(input) {
|
|
|
6652
6849
|
function rsaPublicKeyFromComponents(e, n) {
|
|
6653
6850
|
const eDer = encodeAsn1Integer(e);
|
|
6654
6851
|
const nDer = encodeAsn1Integer(n);
|
|
6655
|
-
const rsaPublicKeyDer = encodeAsn1Sequence(
|
|
6656
|
-
const bitStringContent =
|
|
6657
|
-
const bitString =
|
|
6658
|
-
|
|
6852
|
+
const rsaPublicKeyDer = encodeAsn1Sequence(Buffer14.concat([nDer, eDer]));
|
|
6853
|
+
const bitStringContent = Buffer14.concat([Buffer14.from([0]), rsaPublicKeyDer]);
|
|
6854
|
+
const bitString = Buffer14.concat([
|
|
6855
|
+
Buffer14.from([3]),
|
|
6659
6856
|
encodeAsn1Length(bitStringContent.length),
|
|
6660
6857
|
bitStringContent
|
|
6661
6858
|
]);
|
|
6662
|
-
const algoId =
|
|
6663
|
-
const spki = encodeAsn1Sequence(
|
|
6859
|
+
const algoId = Buffer14.from("300d06092a864886f70d010101 0500".replace(/\s+/g, ""), "hex");
|
|
6860
|
+
const spki = encodeAsn1Sequence(Buffer14.concat([algoId, bitString]));
|
|
6664
6861
|
return createPublicKey3({ format: "der", key: spki, type: "spki" });
|
|
6665
6862
|
}
|
|
6666
6863
|
function encodeAsn1Integer(value) {
|
|
6667
6864
|
let body = value;
|
|
6668
6865
|
while (body.length > 1 && body[0] === 0) body = body.subarray(1);
|
|
6669
6866
|
if (body.length > 0 && (body[0] & 128) !== 0) {
|
|
6670
|
-
body =
|
|
6867
|
+
body = Buffer14.concat([Buffer14.from([0]), body]);
|
|
6671
6868
|
}
|
|
6672
|
-
return
|
|
6869
|
+
return Buffer14.concat([Buffer14.from([2]), encodeAsn1Length(body.length), body]);
|
|
6673
6870
|
}
|
|
6674
6871
|
function encodeAsn1Sequence(content) {
|
|
6675
|
-
return
|
|
6872
|
+
return Buffer14.concat([Buffer14.from([48]), encodeAsn1Length(content.length), content]);
|
|
6676
6873
|
}
|
|
6677
6874
|
function encodeAsn1Length(length) {
|
|
6678
|
-
if (length < 128) return
|
|
6875
|
+
if (length < 128) return Buffer14.from([length]);
|
|
6679
6876
|
const bytes = [];
|
|
6680
6877
|
let n = length;
|
|
6681
6878
|
while (n > 0) {
|
|
6682
6879
|
bytes.unshift(n & 255);
|
|
6683
6880
|
n >>>= 8;
|
|
6684
6881
|
}
|
|
6685
|
-
return
|
|
6882
|
+
return Buffer14.from([128 | bytes.length, ...bytes]);
|
|
6686
6883
|
}
|
|
6687
6884
|
var ECDSA_OID_BY_CURVE = {
|
|
6688
6885
|
nistp256: "06082a8648ce3d030107",
|
|
@@ -6703,15 +6900,15 @@ function ecdsaPublicKeyFromPoint(curveIdentifier, point) {
|
|
|
6703
6900
|
retryable: false
|
|
6704
6901
|
});
|
|
6705
6902
|
}
|
|
6706
|
-
const algoIdContent =
|
|
6903
|
+
const algoIdContent = Buffer14.from(ECDSA_ALGORITHM_OID_HEX + oidHex, "hex");
|
|
6707
6904
|
const algoId = encodeAsn1Sequence(algoIdContent);
|
|
6708
|
-
const bitStringContent =
|
|
6709
|
-
const bitString =
|
|
6710
|
-
|
|
6905
|
+
const bitStringContent = Buffer14.concat([Buffer14.from([0]), point]);
|
|
6906
|
+
const bitString = Buffer14.concat([
|
|
6907
|
+
Buffer14.from([3]),
|
|
6711
6908
|
encodeAsn1Length(bitStringContent.length),
|
|
6712
6909
|
bitStringContent
|
|
6713
6910
|
]);
|
|
6714
|
-
const spki = encodeAsn1Sequence(
|
|
6911
|
+
const spki = encodeAsn1Sequence(Buffer14.concat([algoId, bitString]));
|
|
6715
6912
|
return createPublicKey3({ format: "der", key: spki, type: "spki" });
|
|
6716
6913
|
}
|
|
6717
6914
|
function sshEcdsaSignatureToDer(sshSignature) {
|
|
@@ -6720,7 +6917,7 @@ function sshEcdsaSignatureToDer(sshSignature) {
|
|
|
6720
6917
|
const s = reader.readMpint();
|
|
6721
6918
|
const rDer = encodeAsn1Integer(r);
|
|
6722
6919
|
const sDer = encodeAsn1Integer(s);
|
|
6723
|
-
return encodeAsn1Sequence(
|
|
6920
|
+
return encodeAsn1Sequence(Buffer14.concat([rDer, sDer]));
|
|
6724
6921
|
}
|
|
6725
6922
|
|
|
6726
6923
|
// src/protocols/ssh/transport/SshTransportHandshake.ts
|
|
@@ -6752,7 +6949,7 @@ var SshTransportHandshake = class {
|
|
|
6752
6949
|
serverIdentification;
|
|
6753
6950
|
/** Creates the first outbound bytes (client identification line). */
|
|
6754
6951
|
createInitialClientBytes() {
|
|
6755
|
-
return
|
|
6952
|
+
return Buffer15.from(`${this.clientIdentificationLine}\r
|
|
6756
6953
|
`, "ascii");
|
|
6757
6954
|
}
|
|
6758
6955
|
/**
|
|
@@ -6776,7 +6973,7 @@ var SshTransportHandshake = class {
|
|
|
6776
6973
|
}
|
|
6777
6974
|
return { outbound };
|
|
6778
6975
|
}
|
|
6779
|
-
return this.pushServerBytesWithPhase(outbound,
|
|
6976
|
+
return this.pushServerBytesWithPhase(outbound, Buffer15.from(chunk));
|
|
6780
6977
|
}
|
|
6781
6978
|
getServerBannerLines() {
|
|
6782
6979
|
return this.identificationLines;
|
|
@@ -6830,12 +7027,12 @@ var SshTransportHandshake = class {
|
|
|
6830
7027
|
clientKexInitPayload: this.clientKexInitPayload,
|
|
6831
7028
|
clientPublicKey: this.pendingCurve25519.publicKey,
|
|
6832
7029
|
negotiatedAlgorithms,
|
|
6833
|
-
serverHostKey:
|
|
7030
|
+
serverHostKey: Buffer15.alloc(0),
|
|
6834
7031
|
serverIdentification: (this.serverIdentification ?? missingServerIdentificationError()).raw,
|
|
6835
|
-
serverKexInitPayload:
|
|
6836
|
-
serverPublicKey:
|
|
6837
|
-
serverSignature:
|
|
6838
|
-
sharedSecret:
|
|
7032
|
+
serverKexInitPayload: Buffer15.from(packet.payload),
|
|
7033
|
+
serverPublicKey: Buffer15.alloc(0),
|
|
7034
|
+
serverSignature: Buffer15.alloc(0),
|
|
7035
|
+
sharedSecret: Buffer15.alloc(0)
|
|
6839
7036
|
};
|
|
6840
7037
|
continue;
|
|
6841
7038
|
}
|
|
@@ -6943,24 +7140,54 @@ var SshTransportHandshake = class {
|
|
|
6943
7140
|
return { outbound };
|
|
6944
7141
|
}
|
|
6945
7142
|
};
|
|
7143
|
+
var MAX_IDENTIFICATION_LINE_BYTES = 8192;
|
|
7144
|
+
var MAX_PRE_IDENTIFICATION_LINES = 1024;
|
|
6946
7145
|
var SshIdentificationAccumulator = class {
|
|
6947
|
-
pending =
|
|
7146
|
+
pending = Buffer15.alloc(0);
|
|
7147
|
+
bannerLineCount = 0;
|
|
6948
7148
|
push(chunk) {
|
|
6949
|
-
this.pending =
|
|
7149
|
+
this.pending = Buffer15.concat([this.pending, Buffer15.from(chunk)]);
|
|
6950
7150
|
const bannerLines = [];
|
|
6951
7151
|
while (true) {
|
|
6952
7152
|
const lfIndex = this.pending.indexOf(10);
|
|
6953
|
-
if (lfIndex < 0)
|
|
7153
|
+
if (lfIndex < 0) {
|
|
7154
|
+
if (this.pending.length > MAX_IDENTIFICATION_LINE_BYTES) {
|
|
7155
|
+
throw new ProtocolError({
|
|
7156
|
+
details: { limitBytes: MAX_IDENTIFICATION_LINE_BYTES },
|
|
7157
|
+
message: "SSH identification line exceeds the maximum accepted length",
|
|
7158
|
+
protocol: "sftp",
|
|
7159
|
+
retryable: false
|
|
7160
|
+
});
|
|
7161
|
+
}
|
|
7162
|
+
break;
|
|
7163
|
+
}
|
|
7164
|
+
if (lfIndex > MAX_IDENTIFICATION_LINE_BYTES) {
|
|
7165
|
+
throw new ProtocolError({
|
|
7166
|
+
details: { limitBytes: MAX_IDENTIFICATION_LINE_BYTES },
|
|
7167
|
+
message: "SSH identification line exceeds the maximum accepted length",
|
|
7168
|
+
protocol: "sftp",
|
|
7169
|
+
retryable: false
|
|
7170
|
+
});
|
|
7171
|
+
}
|
|
6954
7172
|
const lineText = trimLineEndings(this.pending.subarray(0, lfIndex + 1).toString("ascii"));
|
|
6955
|
-
const remainder =
|
|
7173
|
+
const remainder = Buffer15.from(this.pending.subarray(lfIndex + 1));
|
|
6956
7174
|
this.pending = remainder;
|
|
6957
7175
|
if (lineText.startsWith("SSH-")) {
|
|
6958
|
-
this.pending =
|
|
7176
|
+
this.pending = Buffer15.alloc(0);
|
|
6959
7177
|
return { bannerLines, identLine: lineText, remainder };
|
|
6960
7178
|
}
|
|
7179
|
+
this.bannerLineCount += 1;
|
|
7180
|
+
if (this.bannerLineCount > MAX_PRE_IDENTIFICATION_LINES) {
|
|
7181
|
+
throw new ProtocolError({
|
|
7182
|
+
details: { limitLines: MAX_PRE_IDENTIFICATION_LINES },
|
|
7183
|
+
message: "SSH server sent too many pre-identification banner lines",
|
|
7184
|
+
protocol: "sftp",
|
|
7185
|
+
retryable: false
|
|
7186
|
+
});
|
|
7187
|
+
}
|
|
6961
7188
|
bannerLines.push(lineText);
|
|
6962
7189
|
}
|
|
6963
|
-
return { bannerLines, remainder:
|
|
7190
|
+
return { bannerLines, remainder: Buffer15.alloc(0) };
|
|
6964
7191
|
}
|
|
6965
7192
|
};
|
|
6966
7193
|
function trimLineEndings(value) {
|
|
@@ -6988,7 +7215,7 @@ function missingPendingKeyExchangeError() {
|
|
|
6988
7215
|
}
|
|
6989
7216
|
|
|
6990
7217
|
// src/protocols/ssh/transport/SshTransportProtection.ts
|
|
6991
|
-
import { Buffer as
|
|
7218
|
+
import { Buffer as Buffer16 } from "buffer";
|
|
6992
7219
|
import {
|
|
6993
7220
|
createCipheriv,
|
|
6994
7221
|
createDecipheriv,
|
|
@@ -7050,7 +7277,7 @@ var SshTransportPacketProtector = class {
|
|
|
7050
7277
|
);
|
|
7051
7278
|
const encrypted = this.cipher === void 0 ? clearPacket : this.cipher.update(clearPacket);
|
|
7052
7279
|
this.sequenceNumber = this.sequenceNumber + 1 >>> 0;
|
|
7053
|
-
return
|
|
7280
|
+
return Buffer16.concat([encrypted, mac]);
|
|
7054
7281
|
}
|
|
7055
7282
|
};
|
|
7056
7283
|
var SshTransportPacketUnprotector = class {
|
|
@@ -7076,7 +7303,7 @@ var SshTransportPacketUnprotector = class {
|
|
|
7076
7303
|
sequenceNumber;
|
|
7077
7304
|
// Streaming framing state for pushBytes()
|
|
7078
7305
|
framePartialDecrypted;
|
|
7079
|
-
framePendingRaw =
|
|
7306
|
+
framePendingRaw = Buffer16.alloc(0);
|
|
7080
7307
|
frameRemainingNeeded;
|
|
7081
7308
|
getSequenceNumber() {
|
|
7082
7309
|
return this.sequenceNumber;
|
|
@@ -7086,15 +7313,23 @@ var SshTransportPacketUnprotector = class {
|
|
|
7086
7313
|
* Maintains internal framing state across calls - pass each `data` event chunk directly.
|
|
7087
7314
|
*/
|
|
7088
7315
|
pushBytes(chunk) {
|
|
7089
|
-
this.framePendingRaw =
|
|
7316
|
+
this.framePendingRaw = Buffer16.concat([this.framePendingRaw, chunk]);
|
|
7090
7317
|
const results = [];
|
|
7091
7318
|
while (true) {
|
|
7092
7319
|
if (this.framePartialDecrypted === void 0) {
|
|
7093
7320
|
if (this.framePendingRaw.length < this.blockLength) break;
|
|
7094
7321
|
const firstBlock = this.framePendingRaw.subarray(0, this.blockLength);
|
|
7095
|
-
this.framePendingRaw =
|
|
7096
|
-
this.framePartialDecrypted = this.decipher ?
|
|
7322
|
+
this.framePendingRaw = Buffer16.from(this.framePendingRaw.subarray(this.blockLength));
|
|
7323
|
+
this.framePartialDecrypted = this.decipher ? Buffer16.from(this.decipher.update(firstBlock)) : Buffer16.from(firstBlock);
|
|
7097
7324
|
const packetLength = this.framePartialDecrypted.readUInt32BE(0);
|
|
7325
|
+
if (packetLength > MAX_SSH_PACKET_LENGTH) {
|
|
7326
|
+
throw new ProtocolError({
|
|
7327
|
+
details: { maxPacketLength: MAX_SSH_PACKET_LENGTH, packetLength },
|
|
7328
|
+
message: "SSH encrypted packet length exceeds the maximum accepted size",
|
|
7329
|
+
protocol: "sftp",
|
|
7330
|
+
retryable: false
|
|
7331
|
+
});
|
|
7332
|
+
}
|
|
7098
7333
|
const remaining = 4 + packetLength - this.blockLength + this.macLength;
|
|
7099
7334
|
if (remaining < 0) {
|
|
7100
7335
|
throw new ProtocolError({
|
|
@@ -7110,9 +7345,9 @@ var SshTransportPacketUnprotector = class {
|
|
|
7110
7345
|
if (this.framePendingRaw.length < needed) break;
|
|
7111
7346
|
const encryptedRest = this.framePendingRaw.subarray(0, needed - this.macLength);
|
|
7112
7347
|
const receivedMac = this.framePendingRaw.subarray(needed - this.macLength, needed);
|
|
7113
|
-
this.framePendingRaw =
|
|
7114
|
-
const decryptedRest = encryptedRest.length > 0 ? this.decipher ?
|
|
7115
|
-
const clearPacket =
|
|
7348
|
+
this.framePendingRaw = Buffer16.from(this.framePendingRaw.subarray(needed));
|
|
7349
|
+
const decryptedRest = encryptedRest.length > 0 ? this.decipher ? Buffer16.from(this.decipher.update(encryptedRest)) : Buffer16.from(encryptedRest) : Buffer16.alloc(0);
|
|
7350
|
+
const clearPacket = Buffer16.concat([this.framePartialDecrypted, decryptedRest]);
|
|
7116
7351
|
const expectedMac = computeMac(
|
|
7117
7352
|
this.macAlgorithm,
|
|
7118
7353
|
this.options.keys.macKey,
|
|
@@ -7135,7 +7370,7 @@ var SshTransportPacketUnprotector = class {
|
|
|
7135
7370
|
return results;
|
|
7136
7371
|
}
|
|
7137
7372
|
unprotectPayload(packet) {
|
|
7138
|
-
const frame =
|
|
7373
|
+
const frame = Buffer16.from(packet);
|
|
7139
7374
|
if (frame.length < this.macLength) {
|
|
7140
7375
|
throw new ProtocolError({
|
|
7141
7376
|
details: { length: frame.length, macLength: this.macLength },
|
|
@@ -7276,10 +7511,10 @@ function resolveMacLength(encryptionAlgorithm, macAlgorithm) {
|
|
|
7276
7511
|
}
|
|
7277
7512
|
function computeMac(macAlgorithm, macKey, sequence, packet, macLength) {
|
|
7278
7513
|
if (macLength === 0) {
|
|
7279
|
-
return
|
|
7514
|
+
return Buffer16.alloc(0);
|
|
7280
7515
|
}
|
|
7281
7516
|
const hashName = macAlgorithm === "hmac-sha2-512" ? "sha512" : "sha256";
|
|
7282
|
-
const sequenceBuffer =
|
|
7517
|
+
const sequenceBuffer = Buffer16.alloc(4);
|
|
7283
7518
|
sequenceBuffer.writeUInt32BE(sequence >>> 0, 0);
|
|
7284
7519
|
return createHmac2(hashName, macKey).update(sequenceBuffer).update(packet).digest().subarray(0, macLength);
|
|
7285
7520
|
}
|
|
@@ -7486,7 +7721,7 @@ var SshTransportConnection = class {
|
|
|
7486
7721
|
*/
|
|
7487
7722
|
sendPayload(payload) {
|
|
7488
7723
|
this.assertConnected();
|
|
7489
|
-
const frame = this.protector.protectPayload(
|
|
7724
|
+
const frame = this.protector.protectPayload(Buffer17.from(payload));
|
|
7490
7725
|
this.socket.write(frame);
|
|
7491
7726
|
this.resetKeepaliveTimer();
|
|
7492
7727
|
}
|
|
@@ -7644,7 +7879,7 @@ function parseDisconnectPayload(payload) {
|
|
|
7644
7879
|
}
|
|
7645
7880
|
|
|
7646
7881
|
// src/protocols/sftp/v3/SftpSession.ts
|
|
7647
|
-
import { Buffer as
|
|
7882
|
+
import { Buffer as Buffer19 } from "buffer";
|
|
7648
7883
|
|
|
7649
7884
|
// src/protocols/sftp/v3/SftpAttributes.ts
|
|
7650
7885
|
var SFTP_ATTR_FLAG = {
|
|
@@ -7717,7 +7952,8 @@ function decodeSftpAttributesFromReader(reader) {
|
|
|
7717
7952
|
}
|
|
7718
7953
|
|
|
7719
7954
|
// src/protocols/sftp/v3/SftpPacket.ts
|
|
7720
|
-
import { Buffer as
|
|
7955
|
+
import { Buffer as Buffer18 } from "buffer";
|
|
7956
|
+
var MAX_SFTP_PACKET_LENGTH = 256 * 1024;
|
|
7721
7957
|
var SFTP_PACKET_TYPE = {
|
|
7722
7958
|
ATTRS: 105,
|
|
7723
7959
|
CLOSE: 4,
|
|
@@ -7748,7 +7984,7 @@ var SFTP_PACKET_TYPE = {
|
|
|
7748
7984
|
WRITE: 6
|
|
7749
7985
|
};
|
|
7750
7986
|
function decodeSftpPacket(frame) {
|
|
7751
|
-
const bytes =
|
|
7987
|
+
const bytes = Buffer18.from(frame);
|
|
7752
7988
|
if (bytes.length < 5) {
|
|
7753
7989
|
throw new ParseError({
|
|
7754
7990
|
details: { length: bytes.length },
|
|
@@ -7771,12 +8007,19 @@ function decodeSftpPacket(frame) {
|
|
|
7771
8007
|
};
|
|
7772
8008
|
}
|
|
7773
8009
|
var SftpPacketFramer = class {
|
|
7774
|
-
pending =
|
|
8010
|
+
pending = Buffer18.alloc(0);
|
|
7775
8011
|
push(chunk) {
|
|
7776
|
-
this.pending =
|
|
8012
|
+
this.pending = Buffer18.concat([this.pending, Buffer18.from(chunk)]);
|
|
7777
8013
|
const packets = [];
|
|
7778
8014
|
while (this.pending.length >= 4) {
|
|
7779
8015
|
const bodyLength = this.pending.readUInt32BE(0);
|
|
8016
|
+
if (bodyLength > MAX_SFTP_PACKET_LENGTH) {
|
|
8017
|
+
throw new ParseError({
|
|
8018
|
+
details: { bodyLength, maxPacketLength: MAX_SFTP_PACKET_LENGTH },
|
|
8019
|
+
message: "SFTP packet length exceeds the maximum accepted size",
|
|
8020
|
+
retryable: false
|
|
8021
|
+
});
|
|
8022
|
+
}
|
|
7780
8023
|
const frameLength = 4 + bodyLength;
|
|
7781
8024
|
if (this.pending.length < frameLength) {
|
|
7782
8025
|
break;
|
|
@@ -8231,7 +8474,7 @@ var SftpSession = class {
|
|
|
8231
8474
|
* serializes concurrent calls so byte ordering is preserved.
|
|
8232
8475
|
*/
|
|
8233
8476
|
sendRaw(encodedMessage, requestId) {
|
|
8234
|
-
const frame =
|
|
8477
|
+
const frame = Buffer19.alloc(4 + encodedMessage.length);
|
|
8235
8478
|
frame.writeUInt32BE(encodedMessage.length, 0);
|
|
8236
8479
|
encodedMessage.copy(frame, 4);
|
|
8237
8480
|
this.channel.sendData(frame).catch((err) => {
|
|
@@ -8774,9 +9017,9 @@ function buildNativePublickeyCredential(profile, username) {
|
|
|
8774
9017
|
const passphrase = profile.ssh?.passphrase;
|
|
8775
9018
|
try {
|
|
8776
9019
|
const privateKey = createPrivateKey({
|
|
8777
|
-
key:
|
|
9020
|
+
key: Buffer20.isBuffer(keyMaterial) ? keyMaterial : keyMaterial,
|
|
8778
9021
|
...passphrase === void 0 ? {} : {
|
|
8779
|
-
passphrase:
|
|
9022
|
+
passphrase: Buffer20.isBuffer(passphrase) ? passphrase : passphrase
|
|
8780
9023
|
}
|
|
8781
9024
|
});
|
|
8782
9025
|
return buildPublickeyCredential({ privateKey, username });
|
|
@@ -8903,12 +9146,12 @@ function normalizeNativeHostKeyPins(value) {
|
|
|
8903
9146
|
const trimmed = pin.trim();
|
|
8904
9147
|
const hex = trimmed.replace(/:/g, "");
|
|
8905
9148
|
if (hex.length === 64 && /^[a-f0-9]+$/i.test(hex)) {
|
|
8906
|
-
normalized.add(
|
|
9149
|
+
normalized.add(Buffer20.from(hex, "hex").toString("base64").replace(/=+$/g, ""));
|
|
8907
9150
|
continue;
|
|
8908
9151
|
}
|
|
8909
9152
|
const bare = trimmed.startsWith("SHA256:") ? trimmed.slice("SHA256:".length) : trimmed;
|
|
8910
9153
|
const padded = bare.length % 4 === 0 ? bare : `${bare}${"=".repeat(4 - bare.length % 4)}`;
|
|
8911
|
-
normalized.add(
|
|
9154
|
+
normalized.add(Buffer20.from(padded, "base64").toString("base64").replace(/=+$/g, ""));
|
|
8912
9155
|
}
|
|
8913
9156
|
return normalized;
|
|
8914
9157
|
}
|
|
@@ -8918,7 +9161,7 @@ function parseNativeKnownHosts(source) {
|
|
|
8918
9161
|
const entries = [];
|
|
8919
9162
|
let sawNonEmpty = false;
|
|
8920
9163
|
for (const value of sources) {
|
|
8921
|
-
const text =
|
|
9164
|
+
const text = Buffer20.isBuffer(value) ? value.toString("utf8") : String(value);
|
|
8922
9165
|
if (text.length === 0) continue;
|
|
8923
9166
|
sawNonEmpty = true;
|
|
8924
9167
|
entries.push(...parseKnownHosts(text));
|
|
@@ -8991,7 +9234,7 @@ function requireNativeSftpUsername(profile) {
|
|
|
8991
9234
|
}
|
|
8992
9235
|
function resolveNativeSftpTextSecret(value) {
|
|
8993
9236
|
if (value === void 0) return void 0;
|
|
8994
|
-
const text =
|
|
9237
|
+
const text = Buffer20.isBuffer(value) ? value.toString("utf8") : value;
|
|
8995
9238
|
if (text.length === 0) return void 0;
|
|
8996
9239
|
return text;
|
|
8997
9240
|
}
|
|
@@ -9065,6 +9308,7 @@ export {
|
|
|
9065
9308
|
copyBetween,
|
|
9066
9309
|
createAtomicDeployPlan,
|
|
9067
9310
|
createBandwidthThrottle,
|
|
9311
|
+
createDefaultRetryPolicy,
|
|
9068
9312
|
createLocalProviderFactory,
|
|
9069
9313
|
createMemoryProviderFactory,
|
|
9070
9314
|
createOAuthTokenSecretSource,
|
|
@@ -9101,8 +9345,10 @@ export {
|
|
|
9101
9345
|
parseRemoteManifest,
|
|
9102
9346
|
redactCommand,
|
|
9103
9347
|
redactConnectionProfile,
|
|
9348
|
+
redactErrorForLogging,
|
|
9104
9349
|
redactObject,
|
|
9105
9350
|
redactSecretSource,
|
|
9351
|
+
redactUrlForLogging,
|
|
9106
9352
|
redactValue,
|
|
9107
9353
|
resolveConnectionProfileSecrets,
|
|
9108
9354
|
resolveOpenSshHost,
|