@zero-transfer/classic 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 +509 -202
- 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 +506 -202
- 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,7 +4987,7 @@ function isMainModule(importMetaUrl) {
|
|
|
4799
4987
|
}
|
|
4800
4988
|
|
|
4801
4989
|
// src/providers/classic/ftp/FtpProvider.ts
|
|
4802
|
-
import { Buffer as
|
|
4990
|
+
import { Buffer as Buffer6 } from "buffer";
|
|
4803
4991
|
import { createConnection, isIP } from "net";
|
|
4804
4992
|
import {
|
|
4805
4993
|
connect as connectTls
|
|
@@ -5719,38 +5907,53 @@ async function expectCompletion(control, command, path2) {
|
|
|
5719
5907
|
const response = await control.sendCommand(command);
|
|
5720
5908
|
assertPathCommandSucceeded(response, command, path2, control.providerId);
|
|
5721
5909
|
}
|
|
5722
|
-
async function
|
|
5723
|
-
const dataConnection = await openPassiveDataCommand(control, command, path2
|
|
5910
|
+
async function readPassiveLinesCommand(control, command, path2, onLine) {
|
|
5911
|
+
const dataConnection = await openPassiveDataCommand(control, command, path2);
|
|
5724
5912
|
try {
|
|
5725
|
-
const
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
path2,
|
|
5729
|
-
control.providerId
|
|
5730
|
-
);
|
|
5913
|
+
const failure = await consumePassiveLines(dataConnection, control.operationTimeoutMs, {
|
|
5914
|
+
command,
|
|
5915
|
+
onLine,
|
|
5916
|
+
path: path2,
|
|
5917
|
+
providerId: control.providerId
|
|
5918
|
+
});
|
|
5731
5919
|
const finalResponse = await control.readFinalResponse({
|
|
5732
5920
|
command,
|
|
5733
5921
|
operation: "data command completion",
|
|
5734
5922
|
path: path2
|
|
5735
5923
|
});
|
|
5736
5924
|
assertPathCommandSucceeded(finalResponse, command, path2, control.providerId);
|
|
5737
|
-
|
|
5925
|
+
if (failure !== void 0) throw failure;
|
|
5738
5926
|
} catch (error) {
|
|
5739
5927
|
dataConnection.close();
|
|
5740
5928
|
throw error;
|
|
5741
5929
|
}
|
|
5742
5930
|
}
|
|
5743
5931
|
async function readDirectoryEntries(control, path2) {
|
|
5932
|
+
const entries = [];
|
|
5933
|
+
const collectEntry = (entry) => {
|
|
5934
|
+
if (entry.name === "." || entry.name === "..") return;
|
|
5935
|
+
entries.push(entry);
|
|
5936
|
+
};
|
|
5744
5937
|
try {
|
|
5745
|
-
|
|
5746
|
-
|
|
5938
|
+
await readPassiveLinesCommand(control, `MLSD ${path2}`, path2, (rawLine) => {
|
|
5939
|
+
const line = rawLine.trimEnd();
|
|
5940
|
+
if (line.length === 0) return;
|
|
5941
|
+
collectEntry(parseMlsdLine(line, path2));
|
|
5942
|
+
});
|
|
5943
|
+
return entries;
|
|
5747
5944
|
} catch (error) {
|
|
5748
5945
|
if (!isUnsupportedFtpCommandError(error, "MLSD")) {
|
|
5749
5946
|
throw error;
|
|
5750
5947
|
}
|
|
5751
5948
|
}
|
|
5752
|
-
|
|
5753
|
-
|
|
5949
|
+
entries.length = 0;
|
|
5950
|
+
const now = /* @__PURE__ */ new Date();
|
|
5951
|
+
await readPassiveLinesCommand(control, `LIST ${path2}`, path2, (rawLine) => {
|
|
5952
|
+
const line = rawLine.trimEnd();
|
|
5953
|
+
if (line.length === 0 || line.toLowerCase().startsWith("total ")) return;
|
|
5954
|
+
collectEntry(parseUnixListLine(line, path2, now));
|
|
5955
|
+
});
|
|
5956
|
+
return entries;
|
|
5754
5957
|
}
|
|
5755
5958
|
async function openPassiveDataCommand(control, command, path2, options = {}) {
|
|
5756
5959
|
const offset = normalizeOptionalByteCount3(options.offset, "offset", path2);
|
|
@@ -5923,22 +6126,58 @@ function openPassiveDataConnection(endpoint, timeoutMs, path2, control) {
|
|
|
5923
6126
|
}
|
|
5924
6127
|
};
|
|
5925
6128
|
}
|
|
5926
|
-
|
|
5927
|
-
|
|
6129
|
+
var MAX_LIST_LINE_BYTES = 64 * 1024;
|
|
6130
|
+
async function consumePassiveLines(dataConnection, timeoutMs, input) {
|
|
6131
|
+
let carry = Buffer6.alloc(0);
|
|
6132
|
+
let failure;
|
|
5928
6133
|
const clearIdleTimeout = setSocketTimeout(dataConnection.socket, timeoutMs, {
|
|
5929
6134
|
host: dataConnection.endpoint.host,
|
|
5930
6135
|
operation: "passive data transfer",
|
|
5931
|
-
path:
|
|
5932
|
-
providerId
|
|
6136
|
+
path: input.path,
|
|
6137
|
+
providerId: input.providerId
|
|
6138
|
+
});
|
|
6139
|
+
const overlongLineFailure = () => new ParseError({
|
|
6140
|
+
details: { command: input.command, limitBytes: MAX_LIST_LINE_BYTES, path: input.path },
|
|
6141
|
+
message: `FTP listing line exceeded ${String(MAX_LIST_LINE_BYTES)} bytes for ${input.command}`,
|
|
6142
|
+
retryable: false
|
|
5933
6143
|
});
|
|
6144
|
+
const emit = (lineBytes) => {
|
|
6145
|
+
if (failure !== void 0) return;
|
|
6146
|
+
let end = lineBytes.length;
|
|
6147
|
+
if (end > 0 && lineBytes[end - 1] === 13) end -= 1;
|
|
6148
|
+
if (end === 0) return;
|
|
6149
|
+
if (end > MAX_LIST_LINE_BYTES) {
|
|
6150
|
+
failure = overlongLineFailure();
|
|
6151
|
+
return;
|
|
6152
|
+
}
|
|
6153
|
+
try {
|
|
6154
|
+
input.onLine(lineBytes.toString("utf8", 0, end));
|
|
6155
|
+
} catch (error) {
|
|
6156
|
+
failure = error instanceof Error ? error : new Error(String(error));
|
|
6157
|
+
}
|
|
6158
|
+
};
|
|
5934
6159
|
try {
|
|
5935
6160
|
for await (const chunk of dataConnection.socket) {
|
|
5936
|
-
|
|
5937
|
-
|
|
6161
|
+
if (failure !== void 0) continue;
|
|
6162
|
+
const data = carry.length > 0 ? Buffer6.concat([carry, chunk]) : chunk;
|
|
6163
|
+
let start = 0;
|
|
6164
|
+
let newline = data.indexOf(10, start);
|
|
6165
|
+
while (newline !== -1) {
|
|
6166
|
+
emit(data.subarray(start, newline));
|
|
6167
|
+
start = newline + 1;
|
|
6168
|
+
newline = data.indexOf(10, start);
|
|
6169
|
+
}
|
|
6170
|
+
carry = Buffer6.from(data.subarray(start));
|
|
6171
|
+
if (carry.length > MAX_LIST_LINE_BYTES && failure === void 0) {
|
|
6172
|
+
failure = overlongLineFailure();
|
|
6173
|
+
}
|
|
6174
|
+
if (failure !== void 0) carry = Buffer6.alloc(0);
|
|
6175
|
+
}
|
|
6176
|
+
if (carry.length > 0) emit(carry);
|
|
5938
6177
|
} finally {
|
|
5939
6178
|
clearIdleTimeout();
|
|
5940
6179
|
}
|
|
5941
|
-
return
|
|
6180
|
+
return failure;
|
|
5942
6181
|
}
|
|
5943
6182
|
async function* createPassiveReadSource(control, dataConnection, command, path2, range, request) {
|
|
5944
6183
|
let bytesEmitted = 0;
|
|
@@ -5955,7 +6194,7 @@ async function* createPassiveReadSource(control, dataConnection, command, path2,
|
|
|
5955
6194
|
});
|
|
5956
6195
|
for await (const chunk of dataConnection.socket) {
|
|
5957
6196
|
request.throwIfAborted();
|
|
5958
|
-
const buffer =
|
|
6197
|
+
const buffer = Buffer6.from(chunk);
|
|
5959
6198
|
if (range.length === void 0) {
|
|
5960
6199
|
bytesEmitted += buffer.byteLength;
|
|
5961
6200
|
yield new Uint8Array(buffer);
|
|
@@ -6187,6 +6426,13 @@ function createTlsPinnedFingerprints(profile) {
|
|
|
6187
6426
|
if (pinnedFingerprint256 === void 0) {
|
|
6188
6427
|
return void 0;
|
|
6189
6428
|
}
|
|
6429
|
+
if (profile.tls?.rejectUnauthorized === false) {
|
|
6430
|
+
throw new ConfigurationError({
|
|
6431
|
+
message: "FTPS tls.pinnedFingerprint256 cannot be combined with rejectUnauthorized: false; pin verification runs after the TLS handshake, so chain validation must stay enabled. For self-signed certificates supply tls.ca instead of disabling validation.",
|
|
6432
|
+
protocol: FTPS_PROVIDER_ID,
|
|
6433
|
+
retryable: false
|
|
6434
|
+
});
|
|
6435
|
+
}
|
|
6190
6436
|
const fingerprints = Array.isArray(pinnedFingerprint256) ? pinnedFingerprint256 : [pinnedFingerprint256];
|
|
6191
6437
|
if (fingerprints.length === 0) {
|
|
6192
6438
|
throw new ConfigurationError({
|
|
@@ -6241,9 +6487,9 @@ function normalizeCertificateFingerprint256(certificate) {
|
|
|
6241
6487
|
}
|
|
6242
6488
|
function normalizeTlsSecretValue(value) {
|
|
6243
6489
|
if (Array.isArray(value)) {
|
|
6244
|
-
return value.map((item) =>
|
|
6490
|
+
return value.map((item) => Buffer6.isBuffer(item) ? Buffer6.from(item) : item);
|
|
6245
6491
|
}
|
|
6246
|
-
return
|
|
6492
|
+
return Buffer6.isBuffer(value) ? Buffer6.from(value) : value;
|
|
6247
6493
|
}
|
|
6248
6494
|
async function authenticateFtpSession(control, username, password, host) {
|
|
6249
6495
|
const safeUsername = assertSafeFtpArgument(username, "username");
|
|
@@ -6422,7 +6668,7 @@ function compareEntries5(left, right) {
|
|
|
6422
6668
|
return left.path.localeCompare(right.path);
|
|
6423
6669
|
}
|
|
6424
6670
|
function secretToString(value) {
|
|
6425
|
-
return
|
|
6671
|
+
return Buffer6.isBuffer(value) ? value.toString("utf8") : value;
|
|
6426
6672
|
}
|
|
6427
6673
|
|
|
6428
6674
|
// src/providers/classic/ftp/FtpFeatureParser.ts
|
|
@@ -6467,12 +6713,12 @@ function normalizeFeatureLines(input) {
|
|
|
6467
6713
|
}
|
|
6468
6714
|
|
|
6469
6715
|
// src/providers/native/sftp/NativeSftpProvider.ts
|
|
6470
|
-
import { Buffer as
|
|
6716
|
+
import { Buffer as Buffer21 } from "buffer";
|
|
6471
6717
|
import { createHash as createHash3, createPrivateKey } from "crypto";
|
|
6472
6718
|
import { createConnection as createConnection2 } from "net";
|
|
6473
6719
|
|
|
6474
6720
|
// src/protocols/ssh/binary/SshDataWriter.ts
|
|
6475
|
-
import { Buffer as
|
|
6721
|
+
import { Buffer as Buffer7 } from "buffer";
|
|
6476
6722
|
var MAX_UINT32 = 4294967295;
|
|
6477
6723
|
var MAX_UINT64 = (1n << 64n) - 1n;
|
|
6478
6724
|
var SshDataWriter = class {
|
|
@@ -6480,7 +6726,7 @@ var SshDataWriter = class {
|
|
|
6480
6726
|
length = 0;
|
|
6481
6727
|
writeByte(value) {
|
|
6482
6728
|
this.assertByte(value, "byte");
|
|
6483
|
-
const chunk =
|
|
6729
|
+
const chunk = Buffer7.alloc(1);
|
|
6484
6730
|
chunk.writeUInt8(value, 0);
|
|
6485
6731
|
return this.push(chunk);
|
|
6486
6732
|
}
|
|
@@ -6488,7 +6734,7 @@ var SshDataWriter = class {
|
|
|
6488
6734
|
return this.writeByte(value ? 1 : 0);
|
|
6489
6735
|
}
|
|
6490
6736
|
writeBytes(value) {
|
|
6491
|
-
return this.push(
|
|
6737
|
+
return this.push(Buffer7.from(value));
|
|
6492
6738
|
}
|
|
6493
6739
|
writeUint32(value) {
|
|
6494
6740
|
if (!Number.isInteger(value) || value < 0 || value > MAX_UINT32) {
|
|
@@ -6498,7 +6744,7 @@ var SshDataWriter = class {
|
|
|
6498
6744
|
retryable: false
|
|
6499
6745
|
});
|
|
6500
6746
|
}
|
|
6501
|
-
const chunk =
|
|
6747
|
+
const chunk = Buffer7.alloc(4);
|
|
6502
6748
|
chunk.writeUInt32BE(value, 0);
|
|
6503
6749
|
return this.push(chunk);
|
|
6504
6750
|
}
|
|
@@ -6510,12 +6756,12 @@ var SshDataWriter = class {
|
|
|
6510
6756
|
retryable: false
|
|
6511
6757
|
});
|
|
6512
6758
|
}
|
|
6513
|
-
const chunk =
|
|
6759
|
+
const chunk = Buffer7.alloc(8);
|
|
6514
6760
|
chunk.writeBigUInt64BE(value, 0);
|
|
6515
6761
|
return this.push(chunk);
|
|
6516
6762
|
}
|
|
6517
6763
|
writeString(value, encoding = "utf8") {
|
|
6518
|
-
const payload = typeof value === "string" ?
|
|
6764
|
+
const payload = typeof value === "string" ? Buffer7.from(value, encoding) : Buffer7.from(value);
|
|
6519
6765
|
this.writeUint32(payload.length);
|
|
6520
6766
|
return this.push(payload);
|
|
6521
6767
|
}
|
|
@@ -6537,7 +6783,7 @@ var SshDataWriter = class {
|
|
|
6537
6783
|
return this.writeString(values.join(","), "ascii");
|
|
6538
6784
|
}
|
|
6539
6785
|
toBuffer() {
|
|
6540
|
-
return
|
|
6786
|
+
return Buffer7.concat(this.chunks, this.length);
|
|
6541
6787
|
}
|
|
6542
6788
|
push(chunk) {
|
|
6543
6789
|
this.chunks.push(chunk);
|
|
@@ -6555,23 +6801,23 @@ var SshDataWriter = class {
|
|
|
6555
6801
|
}
|
|
6556
6802
|
};
|
|
6557
6803
|
function normalizePositiveMpint(value) {
|
|
6558
|
-
const input =
|
|
6804
|
+
const input = Buffer7.from(value);
|
|
6559
6805
|
let offset = 0;
|
|
6560
6806
|
while (offset < input.length && input[offset] === 0) {
|
|
6561
6807
|
offset += 1;
|
|
6562
6808
|
}
|
|
6563
6809
|
if (offset >= input.length) {
|
|
6564
|
-
return
|
|
6810
|
+
return Buffer7.alloc(0);
|
|
6565
6811
|
}
|
|
6566
6812
|
const stripped = input.subarray(offset);
|
|
6567
6813
|
if ((stripped[0] & 128) === 128) {
|
|
6568
|
-
return
|
|
6814
|
+
return Buffer7.concat([Buffer7.from([0]), stripped]);
|
|
6569
6815
|
}
|
|
6570
6816
|
return stripped;
|
|
6571
6817
|
}
|
|
6572
6818
|
|
|
6573
6819
|
// src/protocols/ssh/binary/SshDataReader.ts
|
|
6574
|
-
import { Buffer as
|
|
6820
|
+
import { Buffer as Buffer8 } from "buffer";
|
|
6575
6821
|
var SshDataReader = class {
|
|
6576
6822
|
constructor(source) {
|
|
6577
6823
|
this.source = source;
|
|
@@ -6597,18 +6843,18 @@ var SshDataReader = class {
|
|
|
6597
6843
|
this.ensureAvailable(length, "bytes");
|
|
6598
6844
|
const data = this.source.subarray(this.offset, this.offset + length);
|
|
6599
6845
|
this.offset += length;
|
|
6600
|
-
return
|
|
6846
|
+
return Buffer8.from(data);
|
|
6601
6847
|
}
|
|
6602
6848
|
readUint32() {
|
|
6603
6849
|
this.ensureAvailable(4, "uint32");
|
|
6604
|
-
const buffer =
|
|
6850
|
+
const buffer = Buffer8.from(this.source);
|
|
6605
6851
|
const value = buffer.readUInt32BE(this.offset);
|
|
6606
6852
|
this.offset += 4;
|
|
6607
6853
|
return value;
|
|
6608
6854
|
}
|
|
6609
6855
|
readUint64() {
|
|
6610
6856
|
this.ensureAvailable(8, "uint64");
|
|
6611
|
-
const buffer =
|
|
6857
|
+
const buffer = Buffer8.from(this.source);
|
|
6612
6858
|
const value = buffer.readBigUInt64BE(this.offset);
|
|
6613
6859
|
this.offset += 8;
|
|
6614
6860
|
return value;
|
|
@@ -6618,7 +6864,7 @@ var SshDataReader = class {
|
|
|
6618
6864
|
this.ensureAvailable(length, "string");
|
|
6619
6865
|
const data = this.source.subarray(this.offset, this.offset + length);
|
|
6620
6866
|
this.offset += length;
|
|
6621
|
-
return
|
|
6867
|
+
return Buffer8.from(data);
|
|
6622
6868
|
}
|
|
6623
6869
|
readUtf8String() {
|
|
6624
6870
|
return this.readString().toString("utf8");
|
|
@@ -6973,7 +7219,7 @@ function buildKiRequest(args) {
|
|
|
6973
7219
|
}
|
|
6974
7220
|
|
|
6975
7221
|
// src/protocols/ssh/auth/SshPublickeyCredentialBuilder.ts
|
|
6976
|
-
import { Buffer as
|
|
7222
|
+
import { Buffer as Buffer9 } from "buffer";
|
|
6977
7223
|
import { createPublicKey, sign as cryptoSign } from "crypto";
|
|
6978
7224
|
var ED25519_RAW_KEY_LENGTH = 32;
|
|
6979
7225
|
var ED25519_SPKI_PREFIX_LENGTH = 12;
|
|
@@ -6991,7 +7237,7 @@ function buildPublickeyCredential(options) {
|
|
|
6991
7237
|
return {
|
|
6992
7238
|
algorithmName: "ssh-ed25519",
|
|
6993
7239
|
publicKeyBlob,
|
|
6994
|
-
sign: (data) => cryptoSign(null,
|
|
7240
|
+
sign: (data) => cryptoSign(null, Buffer9.from(data), privateKey),
|
|
6995
7241
|
type: "publickey",
|
|
6996
7242
|
username
|
|
6997
7243
|
};
|
|
@@ -7009,7 +7255,7 @@ function buildPublickeyCredential(options) {
|
|
|
7009
7255
|
return {
|
|
7010
7256
|
algorithmName,
|
|
7011
7257
|
publicKeyBlob,
|
|
7012
|
-
sign: (data) => cryptoSign(hash,
|
|
7258
|
+
sign: (data) => cryptoSign(hash, Buffer9.from(data), privateKey),
|
|
7013
7259
|
type: "publickey",
|
|
7014
7260
|
username
|
|
7015
7261
|
};
|
|
@@ -7022,7 +7268,7 @@ function buildPublickeyCredential(options) {
|
|
|
7022
7268
|
}
|
|
7023
7269
|
function base64UrlToMpint(value) {
|
|
7024
7270
|
const padded = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
7025
|
-
const buffer =
|
|
7271
|
+
const buffer = Buffer9.from(padded, "base64");
|
|
7026
7272
|
return buffer;
|
|
7027
7273
|
}
|
|
7028
7274
|
function createInvalidKeyError(message) {
|
|
@@ -7148,7 +7394,7 @@ function encodeSshChannelClose(recipientChannel) {
|
|
|
7148
7394
|
}
|
|
7149
7395
|
|
|
7150
7396
|
// src/protocols/ssh/connection/SshSessionChannel.ts
|
|
7151
|
-
import { Buffer as
|
|
7397
|
+
import { Buffer as Buffer10 } from "buffer";
|
|
7152
7398
|
var INITIAL_WINDOW_SIZE = 256 * 1024;
|
|
7153
7399
|
var MAX_PACKET_SIZE = 32 * 1024;
|
|
7154
7400
|
var WINDOW_REFILL_THRESHOLD = 64 * 1024;
|
|
@@ -7306,7 +7552,7 @@ var SshSessionChannel = class {
|
|
|
7306
7552
|
this.remoteWindowRemaining,
|
|
7307
7553
|
this.remoteMaxPacketSize
|
|
7308
7554
|
);
|
|
7309
|
-
const chunk =
|
|
7555
|
+
const chunk = Buffer10.from(data.subarray(offset, offset + chunkSize));
|
|
7310
7556
|
this.transport.sendPayload(
|
|
7311
7557
|
encodeSshChannelData({ data: chunk, recipientChannel: this.remoteChannelId })
|
|
7312
7558
|
);
|
|
@@ -7568,10 +7814,10 @@ var SshConnectionManager = class {
|
|
|
7568
7814
|
};
|
|
7569
7815
|
|
|
7570
7816
|
// src/protocols/ssh/transport/SshTransportConnection.ts
|
|
7571
|
-
import { Buffer as
|
|
7817
|
+
import { Buffer as Buffer18 } from "buffer";
|
|
7572
7818
|
|
|
7573
7819
|
// src/protocols/ssh/transport/SshTransportHandshake.ts
|
|
7574
|
-
import { Buffer as
|
|
7820
|
+
import { Buffer as Buffer16 } from "buffer";
|
|
7575
7821
|
|
|
7576
7822
|
// src/protocols/ssh/transport/SshAlgorithmNegotiation.ts
|
|
7577
7823
|
var DEFAULT_SSH_ALGORITHM_PREFERENCES = {
|
|
@@ -7733,12 +7979,12 @@ function parseSshIdentificationLine(line) {
|
|
|
7733
7979
|
}
|
|
7734
7980
|
|
|
7735
7981
|
// src/protocols/ssh/transport/SshKexInit.ts
|
|
7736
|
-
import { Buffer as
|
|
7982
|
+
import { Buffer as Buffer11 } from "buffer";
|
|
7737
7983
|
import { randomBytes } from "crypto";
|
|
7738
7984
|
var SSH_MSG_KEXINIT = 20;
|
|
7739
7985
|
var KEXINIT_COOKIE_LENGTH = 16;
|
|
7740
7986
|
function encodeSshKexInitMessage(options) {
|
|
7741
|
-
const cookie = options.cookie === void 0 ? randomBytes(KEXINIT_COOKIE_LENGTH) :
|
|
7987
|
+
const cookie = options.cookie === void 0 ? randomBytes(KEXINIT_COOKIE_LENGTH) : Buffer11.from(options.cookie);
|
|
7742
7988
|
if (cookie.length !== KEXINIT_COOKIE_LENGTH) {
|
|
7743
7989
|
throw new ConfigurationError({
|
|
7744
7990
|
details: { actualLength: cookie.length, expectedLength: KEXINIT_COOKIE_LENGTH },
|
|
@@ -7808,12 +8054,12 @@ function decodeSshKexInitMessage(payload) {
|
|
|
7808
8054
|
}
|
|
7809
8055
|
|
|
7810
8056
|
// src/protocols/ssh/transport/SshKexCurve25519.ts
|
|
7811
|
-
import { Buffer as
|
|
8057
|
+
import { Buffer as Buffer12 } from "buffer";
|
|
7812
8058
|
import { createPublicKey as createPublicKey2, diffieHellman, generateKeyPairSync } from "crypto";
|
|
7813
8059
|
var SSH_MSG_KEX_ECDH_INIT = 30;
|
|
7814
8060
|
var SSH_MSG_KEX_ECDH_REPLY = 31;
|
|
7815
8061
|
var X25519_PUBLIC_KEY_LENGTH = 32;
|
|
7816
|
-
var X25519_SPKI_PREFIX =
|
|
8062
|
+
var X25519_SPKI_PREFIX = Buffer12.from("302a300506032b656e032100", "hex");
|
|
7817
8063
|
function createCurve25519Ephemeral() {
|
|
7818
8064
|
const { privateKey, publicKey } = generateKeyPairSync("x25519");
|
|
7819
8065
|
const encodedPublicKey = exportX25519PublicKeyRaw(publicKey);
|
|
@@ -7858,7 +8104,7 @@ function exportX25519PublicKeyRaw(publicKey) {
|
|
|
7858
8104
|
}
|
|
7859
8105
|
function importX25519PublicKeyRaw(raw) {
|
|
7860
8106
|
const normalized = normalizeX25519PublicKey(raw, "server");
|
|
7861
|
-
const der =
|
|
8107
|
+
const der = Buffer12.concat([X25519_SPKI_PREFIX, normalized]);
|
|
7862
8108
|
return createPublicKey2({
|
|
7863
8109
|
format: "der",
|
|
7864
8110
|
key: der,
|
|
@@ -7866,7 +8112,7 @@ function importX25519PublicKeyRaw(raw) {
|
|
|
7866
8112
|
});
|
|
7867
8113
|
}
|
|
7868
8114
|
function normalizeX25519PublicKey(value, label) {
|
|
7869
|
-
const key =
|
|
8115
|
+
const key = Buffer12.from(value);
|
|
7870
8116
|
if (key.length !== X25519_PUBLIC_KEY_LENGTH) {
|
|
7871
8117
|
throw new ConfigurationError({
|
|
7872
8118
|
details: { keyLength: key.length, label },
|
|
@@ -7879,7 +8125,7 @@ function normalizeX25519PublicKey(value, label) {
|
|
|
7879
8125
|
}
|
|
7880
8126
|
|
|
7881
8127
|
// src/protocols/ssh/transport/SshKeyDerivation.ts
|
|
7882
|
-
import { Buffer as
|
|
8128
|
+
import { Buffer as Buffer13 } from "buffer";
|
|
7883
8129
|
import { createHash } from "crypto";
|
|
7884
8130
|
function deriveSshSessionKeys(input) {
|
|
7885
8131
|
const hashAlgorithm = resolveKexHashAlgorithm(input.kexAlgorithm);
|
|
@@ -7901,7 +8147,7 @@ function deriveSshSessionKeys(input) {
|
|
|
7901
8147
|
input.negotiatedAlgorithms.encryptionServerToClient,
|
|
7902
8148
|
input.negotiatedAlgorithms.macServerToClient
|
|
7903
8149
|
);
|
|
7904
|
-
const sharedSecret =
|
|
8150
|
+
const sharedSecret = Buffer13.from(input.sharedSecret);
|
|
7905
8151
|
return {
|
|
7906
8152
|
clientToServer: {
|
|
7907
8153
|
encryptionKey: deriveMaterial(
|
|
@@ -7951,21 +8197,21 @@ function computeCurve25519ExchangeHash(input, hashAlgorithm) {
|
|
|
7951
8197
|
}
|
|
7952
8198
|
function deriveMaterial(sharedSecret, exchangeHash, sessionId, letter, length, hashAlgorithm) {
|
|
7953
8199
|
if (length <= 0) {
|
|
7954
|
-
return
|
|
8200
|
+
return Buffer13.alloc(0);
|
|
7955
8201
|
}
|
|
7956
8202
|
const result = [];
|
|
7957
8203
|
const first2 = createHash(hashAlgorithm).update(
|
|
7958
8204
|
new SshDataWriter().writeMpint(sharedSecret).writeBytes(exchangeHash).writeByte(letter.charCodeAt(0)).writeBytes(sessionId).toBuffer()
|
|
7959
8205
|
).digest();
|
|
7960
8206
|
result.push(first2);
|
|
7961
|
-
while (
|
|
7962
|
-
const previous =
|
|
8207
|
+
while (Buffer13.concat(result).length < length) {
|
|
8208
|
+
const previous = Buffer13.concat(result);
|
|
7963
8209
|
const next = createHash(hashAlgorithm).update(
|
|
7964
8210
|
new SshDataWriter().writeMpint(sharedSecret).writeBytes(exchangeHash).writeBytes(previous).toBuffer()
|
|
7965
8211
|
).digest();
|
|
7966
8212
|
result.push(next);
|
|
7967
8213
|
}
|
|
7968
|
-
return
|
|
8214
|
+
return Buffer13.concat(result).subarray(0, length);
|
|
7969
8215
|
}
|
|
7970
8216
|
function resolveKexHashAlgorithm(kexAlgorithm) {
|
|
7971
8217
|
if (kexAlgorithm === "curve25519-sha256" || kexAlgorithm === "curve25519-sha256@libssh.org") {
|
|
@@ -8053,20 +8299,21 @@ function decodeSshNewKeysMessage(payload) {
|
|
|
8053
8299
|
}
|
|
8054
8300
|
|
|
8055
8301
|
// src/protocols/ssh/transport/SshTransportPacket.ts
|
|
8056
|
-
import { Buffer as
|
|
8302
|
+
import { Buffer as Buffer14 } from "buffer";
|
|
8057
8303
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
8058
8304
|
var MIN_PADDING_LENGTH = 4;
|
|
8059
8305
|
var MIN_PACKET_LENGTH = 1 + MIN_PADDING_LENGTH;
|
|
8306
|
+
var MAX_SSH_PACKET_LENGTH = 256 * 1024;
|
|
8060
8307
|
function encodeSshTransportPacket(payload, options = {}) {
|
|
8061
|
-
const body =
|
|
8308
|
+
const body = Buffer14.from(payload);
|
|
8062
8309
|
const blockSize = normalizeBlockSize(options.blockSize ?? 8);
|
|
8063
8310
|
let paddingLength = MIN_PADDING_LENGTH;
|
|
8064
8311
|
while ((1 + body.length + paddingLength + 4) % blockSize !== 0) {
|
|
8065
8312
|
paddingLength += 1;
|
|
8066
8313
|
}
|
|
8067
|
-
const padding = options.randomPadding === false ?
|
|
8314
|
+
const padding = options.randomPadding === false ? Buffer14.alloc(paddingLength) : randomBytes2(paddingLength);
|
|
8068
8315
|
const packetLength = 1 + body.length + paddingLength;
|
|
8069
|
-
const frame =
|
|
8316
|
+
const frame = Buffer14.alloc(4 + packetLength);
|
|
8070
8317
|
frame.writeUInt32BE(packetLength, 0);
|
|
8071
8318
|
frame.writeUInt8(paddingLength, 4);
|
|
8072
8319
|
body.copy(frame, 5);
|
|
@@ -8074,7 +8321,7 @@ function encodeSshTransportPacket(payload, options = {}) {
|
|
|
8074
8321
|
return frame;
|
|
8075
8322
|
}
|
|
8076
8323
|
function decodeSshTransportPacket(frame) {
|
|
8077
|
-
const bytes =
|
|
8324
|
+
const bytes = Buffer14.from(frame);
|
|
8078
8325
|
if (bytes.length < 4 + MIN_PACKET_LENGTH) {
|
|
8079
8326
|
throw new ParseError({
|
|
8080
8327
|
details: { length: bytes.length },
|
|
@@ -8128,12 +8375,20 @@ function decodeSshTransportPacket(frame) {
|
|
|
8128
8375
|
};
|
|
8129
8376
|
}
|
|
8130
8377
|
var SshTransportPacketFramer = class {
|
|
8131
|
-
pending =
|
|
8378
|
+
pending = Buffer14.alloc(0);
|
|
8132
8379
|
push(chunk) {
|
|
8133
|
-
this.pending =
|
|
8380
|
+
this.pending = Buffer14.concat([this.pending, Buffer14.from(chunk)]);
|
|
8134
8381
|
const packets = [];
|
|
8135
8382
|
while (this.pending.length >= 4) {
|
|
8136
8383
|
const packetLength = this.pending.readUInt32BE(0);
|
|
8384
|
+
if (packetLength > MAX_SSH_PACKET_LENGTH) {
|
|
8385
|
+
throw new ParseError({
|
|
8386
|
+
details: { maxPacketLength: MAX_SSH_PACKET_LENGTH, packetLength },
|
|
8387
|
+
message: "SSH transport packet length exceeds the maximum accepted size",
|
|
8388
|
+
protocol: "sftp",
|
|
8389
|
+
retryable: false
|
|
8390
|
+
});
|
|
8391
|
+
}
|
|
8137
8392
|
const frameLength = 4 + packetLength;
|
|
8138
8393
|
if (this.pending.length < frameLength) {
|
|
8139
8394
|
break;
|
|
@@ -8149,8 +8404,8 @@ var SshTransportPacketFramer = class {
|
|
|
8149
8404
|
}
|
|
8150
8405
|
/** Returns and clears any bytes buffered but not yet part of a complete packet. */
|
|
8151
8406
|
takeRemainingBytes() {
|
|
8152
|
-
const remaining =
|
|
8153
|
-
this.pending =
|
|
8407
|
+
const remaining = Buffer14.from(this.pending);
|
|
8408
|
+
this.pending = Buffer14.alloc(0);
|
|
8154
8409
|
return remaining;
|
|
8155
8410
|
}
|
|
8156
8411
|
};
|
|
@@ -8167,10 +8422,10 @@ function normalizeBlockSize(blockSize) {
|
|
|
8167
8422
|
}
|
|
8168
8423
|
|
|
8169
8424
|
// src/protocols/ssh/transport/SshHostKeyVerification.ts
|
|
8170
|
-
import { Buffer as
|
|
8425
|
+
import { Buffer as Buffer15 } from "buffer";
|
|
8171
8426
|
import { createHash as createHash2, createPublicKey as createPublicKey3, verify as cryptoVerify } from "crypto";
|
|
8172
8427
|
var ED25519_RAW_KEY_LENGTH2 = 32;
|
|
8173
|
-
var ED25519_SPKI_PREFIX =
|
|
8428
|
+
var ED25519_SPKI_PREFIX = Buffer15.from("302a300506032b6570032100", "hex");
|
|
8174
8429
|
function verifySshHostKeySignature(input) {
|
|
8175
8430
|
const { algorithmName, publicKey } = parseHostKey(input.hostKeyBlob);
|
|
8176
8431
|
const { signatureAlgorithm, signatureBytes } = parseSignatureBlob(input.signatureBlob);
|
|
@@ -8183,9 +8438,9 @@ function verifySshHostKeySignature(input) {
|
|
|
8183
8438
|
});
|
|
8184
8439
|
}
|
|
8185
8440
|
const verified = verifySignature({
|
|
8186
|
-
data:
|
|
8441
|
+
data: Buffer15.from(input.exchangeHash),
|
|
8187
8442
|
publicKey,
|
|
8188
|
-
signature:
|
|
8443
|
+
signature: Buffer15.from(signatureBytes),
|
|
8189
8444
|
signatureAlgorithm
|
|
8190
8445
|
});
|
|
8191
8446
|
if (!verified) {
|
|
@@ -8214,7 +8469,7 @@ function parseHostKey(blob) {
|
|
|
8214
8469
|
retryable: false
|
|
8215
8470
|
});
|
|
8216
8471
|
}
|
|
8217
|
-
const spki =
|
|
8472
|
+
const spki = Buffer15.concat([ED25519_SPKI_PREFIX, raw]);
|
|
8218
8473
|
return {
|
|
8219
8474
|
algorithmName,
|
|
8220
8475
|
publicKey: createPublicKey3({ format: "der", key: spki, type: "spki" })
|
|
@@ -8320,37 +8575,37 @@ function verifySignature(input) {
|
|
|
8320
8575
|
function rsaPublicKeyFromComponents(e, n) {
|
|
8321
8576
|
const eDer = encodeAsn1Integer(e);
|
|
8322
8577
|
const nDer = encodeAsn1Integer(n);
|
|
8323
|
-
const rsaPublicKeyDer = encodeAsn1Sequence(
|
|
8324
|
-
const bitStringContent =
|
|
8325
|
-
const bitString =
|
|
8326
|
-
|
|
8578
|
+
const rsaPublicKeyDer = encodeAsn1Sequence(Buffer15.concat([nDer, eDer]));
|
|
8579
|
+
const bitStringContent = Buffer15.concat([Buffer15.from([0]), rsaPublicKeyDer]);
|
|
8580
|
+
const bitString = Buffer15.concat([
|
|
8581
|
+
Buffer15.from([3]),
|
|
8327
8582
|
encodeAsn1Length(bitStringContent.length),
|
|
8328
8583
|
bitStringContent
|
|
8329
8584
|
]);
|
|
8330
|
-
const algoId =
|
|
8331
|
-
const spki = encodeAsn1Sequence(
|
|
8585
|
+
const algoId = Buffer15.from("300d06092a864886f70d010101 0500".replace(/\s+/g, ""), "hex");
|
|
8586
|
+
const spki = encodeAsn1Sequence(Buffer15.concat([algoId, bitString]));
|
|
8332
8587
|
return createPublicKey3({ format: "der", key: spki, type: "spki" });
|
|
8333
8588
|
}
|
|
8334
8589
|
function encodeAsn1Integer(value) {
|
|
8335
8590
|
let body = value;
|
|
8336
8591
|
while (body.length > 1 && body[0] === 0) body = body.subarray(1);
|
|
8337
8592
|
if (body.length > 0 && (body[0] & 128) !== 0) {
|
|
8338
|
-
body =
|
|
8593
|
+
body = Buffer15.concat([Buffer15.from([0]), body]);
|
|
8339
8594
|
}
|
|
8340
|
-
return
|
|
8595
|
+
return Buffer15.concat([Buffer15.from([2]), encodeAsn1Length(body.length), body]);
|
|
8341
8596
|
}
|
|
8342
8597
|
function encodeAsn1Sequence(content) {
|
|
8343
|
-
return
|
|
8598
|
+
return Buffer15.concat([Buffer15.from([48]), encodeAsn1Length(content.length), content]);
|
|
8344
8599
|
}
|
|
8345
8600
|
function encodeAsn1Length(length) {
|
|
8346
|
-
if (length < 128) return
|
|
8601
|
+
if (length < 128) return Buffer15.from([length]);
|
|
8347
8602
|
const bytes = [];
|
|
8348
8603
|
let n = length;
|
|
8349
8604
|
while (n > 0) {
|
|
8350
8605
|
bytes.unshift(n & 255);
|
|
8351
8606
|
n >>>= 8;
|
|
8352
8607
|
}
|
|
8353
|
-
return
|
|
8608
|
+
return Buffer15.from([128 | bytes.length, ...bytes]);
|
|
8354
8609
|
}
|
|
8355
8610
|
var ECDSA_OID_BY_CURVE = {
|
|
8356
8611
|
nistp256: "06082a8648ce3d030107",
|
|
@@ -8371,15 +8626,15 @@ function ecdsaPublicKeyFromPoint(curveIdentifier, point) {
|
|
|
8371
8626
|
retryable: false
|
|
8372
8627
|
});
|
|
8373
8628
|
}
|
|
8374
|
-
const algoIdContent =
|
|
8629
|
+
const algoIdContent = Buffer15.from(ECDSA_ALGORITHM_OID_HEX + oidHex, "hex");
|
|
8375
8630
|
const algoId = encodeAsn1Sequence(algoIdContent);
|
|
8376
|
-
const bitStringContent =
|
|
8377
|
-
const bitString =
|
|
8378
|
-
|
|
8631
|
+
const bitStringContent = Buffer15.concat([Buffer15.from([0]), point]);
|
|
8632
|
+
const bitString = Buffer15.concat([
|
|
8633
|
+
Buffer15.from([3]),
|
|
8379
8634
|
encodeAsn1Length(bitStringContent.length),
|
|
8380
8635
|
bitStringContent
|
|
8381
8636
|
]);
|
|
8382
|
-
const spki = encodeAsn1Sequence(
|
|
8637
|
+
const spki = encodeAsn1Sequence(Buffer15.concat([algoId, bitString]));
|
|
8383
8638
|
return createPublicKey3({ format: "der", key: spki, type: "spki" });
|
|
8384
8639
|
}
|
|
8385
8640
|
function sshEcdsaSignatureToDer(sshSignature) {
|
|
@@ -8388,7 +8643,7 @@ function sshEcdsaSignatureToDer(sshSignature) {
|
|
|
8388
8643
|
const s = reader.readMpint();
|
|
8389
8644
|
const rDer = encodeAsn1Integer(r);
|
|
8390
8645
|
const sDer = encodeAsn1Integer(s);
|
|
8391
|
-
return encodeAsn1Sequence(
|
|
8646
|
+
return encodeAsn1Sequence(Buffer15.concat([rDer, sDer]));
|
|
8392
8647
|
}
|
|
8393
8648
|
|
|
8394
8649
|
// src/protocols/ssh/transport/SshTransportHandshake.ts
|
|
@@ -8420,7 +8675,7 @@ var SshTransportHandshake = class {
|
|
|
8420
8675
|
serverIdentification;
|
|
8421
8676
|
/** Creates the first outbound bytes (client identification line). */
|
|
8422
8677
|
createInitialClientBytes() {
|
|
8423
|
-
return
|
|
8678
|
+
return Buffer16.from(`${this.clientIdentificationLine}\r
|
|
8424
8679
|
`, "ascii");
|
|
8425
8680
|
}
|
|
8426
8681
|
/**
|
|
@@ -8444,7 +8699,7 @@ var SshTransportHandshake = class {
|
|
|
8444
8699
|
}
|
|
8445
8700
|
return { outbound };
|
|
8446
8701
|
}
|
|
8447
|
-
return this.pushServerBytesWithPhase(outbound,
|
|
8702
|
+
return this.pushServerBytesWithPhase(outbound, Buffer16.from(chunk));
|
|
8448
8703
|
}
|
|
8449
8704
|
getServerBannerLines() {
|
|
8450
8705
|
return this.identificationLines;
|
|
@@ -8498,12 +8753,12 @@ var SshTransportHandshake = class {
|
|
|
8498
8753
|
clientKexInitPayload: this.clientKexInitPayload,
|
|
8499
8754
|
clientPublicKey: this.pendingCurve25519.publicKey,
|
|
8500
8755
|
negotiatedAlgorithms,
|
|
8501
|
-
serverHostKey:
|
|
8756
|
+
serverHostKey: Buffer16.alloc(0),
|
|
8502
8757
|
serverIdentification: (this.serverIdentification ?? missingServerIdentificationError()).raw,
|
|
8503
|
-
serverKexInitPayload:
|
|
8504
|
-
serverPublicKey:
|
|
8505
|
-
serverSignature:
|
|
8506
|
-
sharedSecret:
|
|
8758
|
+
serverKexInitPayload: Buffer16.from(packet.payload),
|
|
8759
|
+
serverPublicKey: Buffer16.alloc(0),
|
|
8760
|
+
serverSignature: Buffer16.alloc(0),
|
|
8761
|
+
sharedSecret: Buffer16.alloc(0)
|
|
8507
8762
|
};
|
|
8508
8763
|
continue;
|
|
8509
8764
|
}
|
|
@@ -8611,24 +8866,54 @@ var SshTransportHandshake = class {
|
|
|
8611
8866
|
return { outbound };
|
|
8612
8867
|
}
|
|
8613
8868
|
};
|
|
8869
|
+
var MAX_IDENTIFICATION_LINE_BYTES = 8192;
|
|
8870
|
+
var MAX_PRE_IDENTIFICATION_LINES = 1024;
|
|
8614
8871
|
var SshIdentificationAccumulator = class {
|
|
8615
|
-
pending =
|
|
8872
|
+
pending = Buffer16.alloc(0);
|
|
8873
|
+
bannerLineCount = 0;
|
|
8616
8874
|
push(chunk) {
|
|
8617
|
-
this.pending =
|
|
8875
|
+
this.pending = Buffer16.concat([this.pending, Buffer16.from(chunk)]);
|
|
8618
8876
|
const bannerLines = [];
|
|
8619
8877
|
while (true) {
|
|
8620
8878
|
const lfIndex = this.pending.indexOf(10);
|
|
8621
|
-
if (lfIndex < 0)
|
|
8879
|
+
if (lfIndex < 0) {
|
|
8880
|
+
if (this.pending.length > MAX_IDENTIFICATION_LINE_BYTES) {
|
|
8881
|
+
throw new ProtocolError({
|
|
8882
|
+
details: { limitBytes: MAX_IDENTIFICATION_LINE_BYTES },
|
|
8883
|
+
message: "SSH identification line exceeds the maximum accepted length",
|
|
8884
|
+
protocol: "sftp",
|
|
8885
|
+
retryable: false
|
|
8886
|
+
});
|
|
8887
|
+
}
|
|
8888
|
+
break;
|
|
8889
|
+
}
|
|
8890
|
+
if (lfIndex > MAX_IDENTIFICATION_LINE_BYTES) {
|
|
8891
|
+
throw new ProtocolError({
|
|
8892
|
+
details: { limitBytes: MAX_IDENTIFICATION_LINE_BYTES },
|
|
8893
|
+
message: "SSH identification line exceeds the maximum accepted length",
|
|
8894
|
+
protocol: "sftp",
|
|
8895
|
+
retryable: false
|
|
8896
|
+
});
|
|
8897
|
+
}
|
|
8622
8898
|
const lineText = trimLineEndings(this.pending.subarray(0, lfIndex + 1).toString("ascii"));
|
|
8623
|
-
const remainder =
|
|
8899
|
+
const remainder = Buffer16.from(this.pending.subarray(lfIndex + 1));
|
|
8624
8900
|
this.pending = remainder;
|
|
8625
8901
|
if (lineText.startsWith("SSH-")) {
|
|
8626
|
-
this.pending =
|
|
8902
|
+
this.pending = Buffer16.alloc(0);
|
|
8627
8903
|
return { bannerLines, identLine: lineText, remainder };
|
|
8628
8904
|
}
|
|
8905
|
+
this.bannerLineCount += 1;
|
|
8906
|
+
if (this.bannerLineCount > MAX_PRE_IDENTIFICATION_LINES) {
|
|
8907
|
+
throw new ProtocolError({
|
|
8908
|
+
details: { limitLines: MAX_PRE_IDENTIFICATION_LINES },
|
|
8909
|
+
message: "SSH server sent too many pre-identification banner lines",
|
|
8910
|
+
protocol: "sftp",
|
|
8911
|
+
retryable: false
|
|
8912
|
+
});
|
|
8913
|
+
}
|
|
8629
8914
|
bannerLines.push(lineText);
|
|
8630
8915
|
}
|
|
8631
|
-
return { bannerLines, remainder:
|
|
8916
|
+
return { bannerLines, remainder: Buffer16.alloc(0) };
|
|
8632
8917
|
}
|
|
8633
8918
|
};
|
|
8634
8919
|
function trimLineEndings(value) {
|
|
@@ -8656,7 +8941,7 @@ function missingPendingKeyExchangeError() {
|
|
|
8656
8941
|
}
|
|
8657
8942
|
|
|
8658
8943
|
// src/protocols/ssh/transport/SshTransportProtection.ts
|
|
8659
|
-
import { Buffer as
|
|
8944
|
+
import { Buffer as Buffer17 } from "buffer";
|
|
8660
8945
|
import {
|
|
8661
8946
|
createCipheriv,
|
|
8662
8947
|
createDecipheriv,
|
|
@@ -8718,7 +9003,7 @@ var SshTransportPacketProtector = class {
|
|
|
8718
9003
|
);
|
|
8719
9004
|
const encrypted = this.cipher === void 0 ? clearPacket : this.cipher.update(clearPacket);
|
|
8720
9005
|
this.sequenceNumber = this.sequenceNumber + 1 >>> 0;
|
|
8721
|
-
return
|
|
9006
|
+
return Buffer17.concat([encrypted, mac]);
|
|
8722
9007
|
}
|
|
8723
9008
|
};
|
|
8724
9009
|
var SshTransportPacketUnprotector = class {
|
|
@@ -8744,7 +9029,7 @@ var SshTransportPacketUnprotector = class {
|
|
|
8744
9029
|
sequenceNumber;
|
|
8745
9030
|
// Streaming framing state for pushBytes()
|
|
8746
9031
|
framePartialDecrypted;
|
|
8747
|
-
framePendingRaw =
|
|
9032
|
+
framePendingRaw = Buffer17.alloc(0);
|
|
8748
9033
|
frameRemainingNeeded;
|
|
8749
9034
|
getSequenceNumber() {
|
|
8750
9035
|
return this.sequenceNumber;
|
|
@@ -8754,15 +9039,23 @@ var SshTransportPacketUnprotector = class {
|
|
|
8754
9039
|
* Maintains internal framing state across calls - pass each `data` event chunk directly.
|
|
8755
9040
|
*/
|
|
8756
9041
|
pushBytes(chunk) {
|
|
8757
|
-
this.framePendingRaw =
|
|
9042
|
+
this.framePendingRaw = Buffer17.concat([this.framePendingRaw, chunk]);
|
|
8758
9043
|
const results = [];
|
|
8759
9044
|
while (true) {
|
|
8760
9045
|
if (this.framePartialDecrypted === void 0) {
|
|
8761
9046
|
if (this.framePendingRaw.length < this.blockLength) break;
|
|
8762
9047
|
const firstBlock = this.framePendingRaw.subarray(0, this.blockLength);
|
|
8763
|
-
this.framePendingRaw =
|
|
8764
|
-
this.framePartialDecrypted = this.decipher ?
|
|
9048
|
+
this.framePendingRaw = Buffer17.from(this.framePendingRaw.subarray(this.blockLength));
|
|
9049
|
+
this.framePartialDecrypted = this.decipher ? Buffer17.from(this.decipher.update(firstBlock)) : Buffer17.from(firstBlock);
|
|
8765
9050
|
const packetLength = this.framePartialDecrypted.readUInt32BE(0);
|
|
9051
|
+
if (packetLength > MAX_SSH_PACKET_LENGTH) {
|
|
9052
|
+
throw new ProtocolError({
|
|
9053
|
+
details: { maxPacketLength: MAX_SSH_PACKET_LENGTH, packetLength },
|
|
9054
|
+
message: "SSH encrypted packet length exceeds the maximum accepted size",
|
|
9055
|
+
protocol: "sftp",
|
|
9056
|
+
retryable: false
|
|
9057
|
+
});
|
|
9058
|
+
}
|
|
8766
9059
|
const remaining = 4 + packetLength - this.blockLength + this.macLength;
|
|
8767
9060
|
if (remaining < 0) {
|
|
8768
9061
|
throw new ProtocolError({
|
|
@@ -8778,9 +9071,9 @@ var SshTransportPacketUnprotector = class {
|
|
|
8778
9071
|
if (this.framePendingRaw.length < needed) break;
|
|
8779
9072
|
const encryptedRest = this.framePendingRaw.subarray(0, needed - this.macLength);
|
|
8780
9073
|
const receivedMac = this.framePendingRaw.subarray(needed - this.macLength, needed);
|
|
8781
|
-
this.framePendingRaw =
|
|
8782
|
-
const decryptedRest = encryptedRest.length > 0 ? this.decipher ?
|
|
8783
|
-
const clearPacket =
|
|
9074
|
+
this.framePendingRaw = Buffer17.from(this.framePendingRaw.subarray(needed));
|
|
9075
|
+
const decryptedRest = encryptedRest.length > 0 ? this.decipher ? Buffer17.from(this.decipher.update(encryptedRest)) : Buffer17.from(encryptedRest) : Buffer17.alloc(0);
|
|
9076
|
+
const clearPacket = Buffer17.concat([this.framePartialDecrypted, decryptedRest]);
|
|
8784
9077
|
const expectedMac = computeMac(
|
|
8785
9078
|
this.macAlgorithm,
|
|
8786
9079
|
this.options.keys.macKey,
|
|
@@ -8803,7 +9096,7 @@ var SshTransportPacketUnprotector = class {
|
|
|
8803
9096
|
return results;
|
|
8804
9097
|
}
|
|
8805
9098
|
unprotectPayload(packet) {
|
|
8806
|
-
const frame =
|
|
9099
|
+
const frame = Buffer17.from(packet);
|
|
8807
9100
|
if (frame.length < this.macLength) {
|
|
8808
9101
|
throw new ProtocolError({
|
|
8809
9102
|
details: { length: frame.length, macLength: this.macLength },
|
|
@@ -8944,10 +9237,10 @@ function resolveMacLength(encryptionAlgorithm, macAlgorithm) {
|
|
|
8944
9237
|
}
|
|
8945
9238
|
function computeMac(macAlgorithm, macKey, sequence, packet, macLength) {
|
|
8946
9239
|
if (macLength === 0) {
|
|
8947
|
-
return
|
|
9240
|
+
return Buffer17.alloc(0);
|
|
8948
9241
|
}
|
|
8949
9242
|
const hashName = macAlgorithm === "hmac-sha2-512" ? "sha512" : "sha256";
|
|
8950
|
-
const sequenceBuffer =
|
|
9243
|
+
const sequenceBuffer = Buffer17.alloc(4);
|
|
8951
9244
|
sequenceBuffer.writeUInt32BE(sequence >>> 0, 0);
|
|
8952
9245
|
return createHmac2(hashName, macKey).update(sequenceBuffer).update(packet).digest().subarray(0, macLength);
|
|
8953
9246
|
}
|
|
@@ -9154,7 +9447,7 @@ var SshTransportConnection = class {
|
|
|
9154
9447
|
*/
|
|
9155
9448
|
sendPayload(payload) {
|
|
9156
9449
|
this.assertConnected();
|
|
9157
|
-
const frame = this.protector.protectPayload(
|
|
9450
|
+
const frame = this.protector.protectPayload(Buffer18.from(payload));
|
|
9158
9451
|
this.socket.write(frame);
|
|
9159
9452
|
this.resetKeepaliveTimer();
|
|
9160
9453
|
}
|
|
@@ -9312,7 +9605,7 @@ function parseDisconnectPayload(payload) {
|
|
|
9312
9605
|
}
|
|
9313
9606
|
|
|
9314
9607
|
// src/protocols/sftp/v3/SftpSession.ts
|
|
9315
|
-
import { Buffer as
|
|
9608
|
+
import { Buffer as Buffer20 } from "buffer";
|
|
9316
9609
|
|
|
9317
9610
|
// src/protocols/sftp/v3/SftpAttributes.ts
|
|
9318
9611
|
var SFTP_ATTR_FLAG = {
|
|
@@ -9385,7 +9678,8 @@ function decodeSftpAttributesFromReader(reader) {
|
|
|
9385
9678
|
}
|
|
9386
9679
|
|
|
9387
9680
|
// src/protocols/sftp/v3/SftpPacket.ts
|
|
9388
|
-
import { Buffer as
|
|
9681
|
+
import { Buffer as Buffer19 } from "buffer";
|
|
9682
|
+
var MAX_SFTP_PACKET_LENGTH = 256 * 1024;
|
|
9389
9683
|
var SFTP_PACKET_TYPE = {
|
|
9390
9684
|
ATTRS: 105,
|
|
9391
9685
|
CLOSE: 4,
|
|
@@ -9416,7 +9710,7 @@ var SFTP_PACKET_TYPE = {
|
|
|
9416
9710
|
WRITE: 6
|
|
9417
9711
|
};
|
|
9418
9712
|
function decodeSftpPacket(frame) {
|
|
9419
|
-
const bytes =
|
|
9713
|
+
const bytes = Buffer19.from(frame);
|
|
9420
9714
|
if (bytes.length < 5) {
|
|
9421
9715
|
throw new ParseError({
|
|
9422
9716
|
details: { length: bytes.length },
|
|
@@ -9439,12 +9733,19 @@ function decodeSftpPacket(frame) {
|
|
|
9439
9733
|
};
|
|
9440
9734
|
}
|
|
9441
9735
|
var SftpPacketFramer = class {
|
|
9442
|
-
pending =
|
|
9736
|
+
pending = Buffer19.alloc(0);
|
|
9443
9737
|
push(chunk) {
|
|
9444
|
-
this.pending =
|
|
9738
|
+
this.pending = Buffer19.concat([this.pending, Buffer19.from(chunk)]);
|
|
9445
9739
|
const packets = [];
|
|
9446
9740
|
while (this.pending.length >= 4) {
|
|
9447
9741
|
const bodyLength = this.pending.readUInt32BE(0);
|
|
9742
|
+
if (bodyLength > MAX_SFTP_PACKET_LENGTH) {
|
|
9743
|
+
throw new ParseError({
|
|
9744
|
+
details: { bodyLength, maxPacketLength: MAX_SFTP_PACKET_LENGTH },
|
|
9745
|
+
message: "SFTP packet length exceeds the maximum accepted size",
|
|
9746
|
+
retryable: false
|
|
9747
|
+
});
|
|
9748
|
+
}
|
|
9448
9749
|
const frameLength = 4 + bodyLength;
|
|
9449
9750
|
if (this.pending.length < frameLength) {
|
|
9450
9751
|
break;
|
|
@@ -9899,7 +10200,7 @@ var SftpSession = class {
|
|
|
9899
10200
|
* serializes concurrent calls so byte ordering is preserved.
|
|
9900
10201
|
*/
|
|
9901
10202
|
sendRaw(encodedMessage, requestId) {
|
|
9902
|
-
const frame =
|
|
10203
|
+
const frame = Buffer20.alloc(4 + encodedMessage.length);
|
|
9903
10204
|
frame.writeUInt32BE(encodedMessage.length, 0);
|
|
9904
10205
|
encodedMessage.copy(frame, 4);
|
|
9905
10206
|
this.channel.sendData(frame).catch((err) => {
|
|
@@ -10442,9 +10743,9 @@ function buildNativePublickeyCredential(profile, username) {
|
|
|
10442
10743
|
const passphrase = profile.ssh?.passphrase;
|
|
10443
10744
|
try {
|
|
10444
10745
|
const privateKey = createPrivateKey({
|
|
10445
|
-
key:
|
|
10746
|
+
key: Buffer21.isBuffer(keyMaterial) ? keyMaterial : keyMaterial,
|
|
10446
10747
|
...passphrase === void 0 ? {} : {
|
|
10447
|
-
passphrase:
|
|
10748
|
+
passphrase: Buffer21.isBuffer(passphrase) ? passphrase : passphrase
|
|
10448
10749
|
}
|
|
10449
10750
|
});
|
|
10450
10751
|
return buildPublickeyCredential({ privateKey, username });
|
|
@@ -10571,12 +10872,12 @@ function normalizeNativeHostKeyPins(value) {
|
|
|
10571
10872
|
const trimmed = pin.trim();
|
|
10572
10873
|
const hex = trimmed.replace(/:/g, "");
|
|
10573
10874
|
if (hex.length === 64 && /^[a-f0-9]+$/i.test(hex)) {
|
|
10574
|
-
normalized.add(
|
|
10875
|
+
normalized.add(Buffer21.from(hex, "hex").toString("base64").replace(/=+$/g, ""));
|
|
10575
10876
|
continue;
|
|
10576
10877
|
}
|
|
10577
10878
|
const bare = trimmed.startsWith("SHA256:") ? trimmed.slice("SHA256:".length) : trimmed;
|
|
10578
10879
|
const padded = bare.length % 4 === 0 ? bare : `${bare}${"=".repeat(4 - bare.length % 4)}`;
|
|
10579
|
-
normalized.add(
|
|
10880
|
+
normalized.add(Buffer21.from(padded, "base64").toString("base64").replace(/=+$/g, ""));
|
|
10580
10881
|
}
|
|
10581
10882
|
return normalized;
|
|
10582
10883
|
}
|
|
@@ -10586,7 +10887,7 @@ function parseNativeKnownHosts(source) {
|
|
|
10586
10887
|
const entries = [];
|
|
10587
10888
|
let sawNonEmpty = false;
|
|
10588
10889
|
for (const value of sources) {
|
|
10589
|
-
const text =
|
|
10890
|
+
const text = Buffer21.isBuffer(value) ? value.toString("utf8") : String(value);
|
|
10590
10891
|
if (text.length === 0) continue;
|
|
10591
10892
|
sawNonEmpty = true;
|
|
10592
10893
|
entries.push(...parseKnownHosts(text));
|
|
@@ -10659,7 +10960,7 @@ function requireNativeSftpUsername(profile) {
|
|
|
10659
10960
|
}
|
|
10660
10961
|
function resolveNativeSftpTextSecret(value) {
|
|
10661
10962
|
if (value === void 0) return void 0;
|
|
10662
|
-
const text =
|
|
10963
|
+
const text = Buffer21.isBuffer(value) ? value.toString("utf8") : value;
|
|
10663
10964
|
if (text.length === 0) return void 0;
|
|
10664
10965
|
return text;
|
|
10665
10966
|
}
|
|
@@ -10734,6 +11035,7 @@ export {
|
|
|
10734
11035
|
copyBetween,
|
|
10735
11036
|
createAtomicDeployPlan,
|
|
10736
11037
|
createBandwidthThrottle,
|
|
11038
|
+
createDefaultRetryPolicy,
|
|
10737
11039
|
createFtpProviderFactory,
|
|
10738
11040
|
createFtpsProviderFactory,
|
|
10739
11041
|
createLocalProviderFactory,
|
|
@@ -10779,8 +11081,10 @@ export {
|
|
|
10779
11081
|
parseUnixListLine,
|
|
10780
11082
|
redactCommand,
|
|
10781
11083
|
redactConnectionProfile,
|
|
11084
|
+
redactErrorForLogging,
|
|
10782
11085
|
redactObject,
|
|
10783
11086
|
redactSecretSource,
|
|
11087
|
+
redactUrlForLogging,
|
|
10784
11088
|
redactValue,
|
|
10785
11089
|
resolveConnectionProfileSecrets,
|
|
10786
11090
|
resolveOpenSshHost,
|