@zero-transfer/sftp 0.4.6 → 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/README.md +14 -19
- package/dist/index.cjs +428 -181
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +233 -29
- package/dist/index.d.ts +233 -29
- package/dist/index.mjs +425 -180
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -60,16 +60,16 @@ __export(sftp_exports, {
|
|
|
60
60
|
copyBetween: () => copyBetween,
|
|
61
61
|
createAtomicDeployPlan: () => createAtomicDeployPlan,
|
|
62
62
|
createBandwidthThrottle: () => createBandwidthThrottle,
|
|
63
|
+
createDefaultRetryPolicy: () => createDefaultRetryPolicy,
|
|
63
64
|
createLocalProviderFactory: () => createLocalProviderFactory,
|
|
64
65
|
createMemoryProviderFactory: () => createMemoryProviderFactory,
|
|
65
|
-
createNativeSftpProviderFactory: () => createNativeSftpProviderFactory,
|
|
66
66
|
createOAuthTokenSecretSource: () => createOAuthTokenSecretSource,
|
|
67
67
|
createPooledTransferClient: () => createPooledTransferClient,
|
|
68
68
|
createProgressEvent: () => createProgressEvent,
|
|
69
69
|
createProviderTransferExecutor: () => createProviderTransferExecutor,
|
|
70
70
|
createRemoteBrowser: () => createRemoteBrowser,
|
|
71
71
|
createRemoteManifest: () => createRemoteManifest,
|
|
72
|
-
createSftpProviderFactory: () =>
|
|
72
|
+
createSftpProviderFactory: () => createSftpProviderFactory,
|
|
73
73
|
createSyncPlan: () => createSyncPlan,
|
|
74
74
|
createTransferClient: () => createTransferClient,
|
|
75
75
|
createTransferJobsFromPlan: () => createTransferJobsFromPlan,
|
|
@@ -97,8 +97,10 @@ __export(sftp_exports, {
|
|
|
97
97
|
parseRemoteManifest: () => parseRemoteManifest,
|
|
98
98
|
redactCommand: () => redactCommand,
|
|
99
99
|
redactConnectionProfile: () => redactConnectionProfile,
|
|
100
|
+
redactErrorForLogging: () => redactErrorForLogging,
|
|
100
101
|
redactObject: () => redactObject,
|
|
101
102
|
redactSecretSource: () => redactSecretSource,
|
|
103
|
+
redactUrlForLogging: () => redactUrlForLogging,
|
|
102
104
|
redactValue: () => redactValue,
|
|
103
105
|
resolveConnectionProfileSecrets: () => resolveConnectionProfileSecrets,
|
|
104
106
|
resolveOpenSshHost: () => resolveOpenSshHost,
|
|
@@ -119,6 +121,68 @@ module.exports = __toCommonJS(sftp_exports);
|
|
|
119
121
|
// src/client/ZeroTransfer.ts
|
|
120
122
|
var import_node_events = require("events");
|
|
121
123
|
|
|
124
|
+
// src/logging/redaction.ts
|
|
125
|
+
var REDACTED = "[REDACTED]";
|
|
126
|
+
var SENSITIVE_KEY_PATTERN = /(?:password|passphrase|privatekey|token|secret|username|user)$/i;
|
|
127
|
+
var SECRET_COMMAND_PATTERN = /^(PASS|USER|ACCT)\s+(.+)$/i;
|
|
128
|
+
var URL_KEY_PATTERN = /(?:url|uri|href)$/i;
|
|
129
|
+
function isSensitiveKey(key) {
|
|
130
|
+
return SENSITIVE_KEY_PATTERN.test(key.replace(/[_-]/g, ""));
|
|
131
|
+
}
|
|
132
|
+
function redactCommand(command) {
|
|
133
|
+
return command.replace(SECRET_COMMAND_PATTERN, (_fullMatch, commandName) => {
|
|
134
|
+
return `${commandName.toUpperCase()} ${REDACTED}`;
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
function redactValue(value) {
|
|
138
|
+
if (typeof value === "string") {
|
|
139
|
+
return redactCommand(value);
|
|
140
|
+
}
|
|
141
|
+
if (Array.isArray(value)) {
|
|
142
|
+
return value.map((item) => redactValue(item));
|
|
143
|
+
}
|
|
144
|
+
if (value !== null && typeof value === "object") {
|
|
145
|
+
return redactObject(value);
|
|
146
|
+
}
|
|
147
|
+
return value;
|
|
148
|
+
}
|
|
149
|
+
function redactObject(input) {
|
|
150
|
+
return Object.fromEntries(
|
|
151
|
+
Object.entries(input).map(([key, value]) => {
|
|
152
|
+
if (isSensitiveKey(key)) {
|
|
153
|
+
return [key, REDACTED];
|
|
154
|
+
}
|
|
155
|
+
if (URL_KEY_PATTERN.test(key) && typeof value === "string") {
|
|
156
|
+
return [key, redactUrlForLogging(value)];
|
|
157
|
+
}
|
|
158
|
+
return [key, redactValue(value)];
|
|
159
|
+
})
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
function redactUrlForLogging(url) {
|
|
163
|
+
let parsed;
|
|
164
|
+
try {
|
|
165
|
+
parsed = typeof url === "string" ? new URL(url) : url;
|
|
166
|
+
} catch {
|
|
167
|
+
return REDACTED;
|
|
168
|
+
}
|
|
169
|
+
const origin = parsed.host.length > 0 ? `${parsed.protocol}//${parsed.host}` : parsed.protocol;
|
|
170
|
+
const query = parsed.search.length > 0 ? `?${REDACTED}` : "";
|
|
171
|
+
return `${origin}${parsed.pathname}${query}`;
|
|
172
|
+
}
|
|
173
|
+
function redactErrorForLogging(error) {
|
|
174
|
+
if (error !== null && typeof error === "object") {
|
|
175
|
+
const candidate = error;
|
|
176
|
+
if (typeof candidate.toJSON === "function") {
|
|
177
|
+
return redactObject(candidate.toJSON());
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (error instanceof Error) {
|
|
181
|
+
return redactObject({ message: error.message, name: error.name });
|
|
182
|
+
}
|
|
183
|
+
return { message: redactValue(typeof error === "string" ? error : String(error)) };
|
|
184
|
+
}
|
|
185
|
+
|
|
122
186
|
// src/errors/ZeroTransferError.ts
|
|
123
187
|
var ZeroTransferError = class extends Error {
|
|
124
188
|
/** Stable machine-readable error code. */
|
|
@@ -160,6 +224,11 @@ var ZeroTransferError = class extends Error {
|
|
|
160
224
|
/**
|
|
161
225
|
* Serializes the error into a plain object suitable for logs or API responses.
|
|
162
226
|
*
|
|
227
|
+
* `details` and `command` are passed through secret redaction so serialized
|
|
228
|
+
* errors never leak credentials, signed URLs, or raw protocol commands. The
|
|
229
|
+
* live {@link ZeroTransferError.details | details} property stays unredacted
|
|
230
|
+
* for programmatic consumers.
|
|
231
|
+
*
|
|
163
232
|
* @returns A JSON-safe object containing public structured error fields.
|
|
164
233
|
*/
|
|
165
234
|
toJSON() {
|
|
@@ -169,12 +238,12 @@ var ZeroTransferError = class extends Error {
|
|
|
169
238
|
message: this.message,
|
|
170
239
|
protocol: this.protocol,
|
|
171
240
|
host: this.host,
|
|
172
|
-
command: this.command,
|
|
241
|
+
command: this.command === void 0 ? void 0 : redactCommand(this.command),
|
|
173
242
|
ftpCode: this.ftpCode,
|
|
174
243
|
sftpCode: this.sftpCode,
|
|
175
244
|
path: this.path,
|
|
176
245
|
retryable: this.retryable,
|
|
177
|
-
details: this.details
|
|
246
|
+
details: this.details === void 0 ? void 0 : redactObject(this.details)
|
|
178
247
|
};
|
|
179
248
|
}
|
|
180
249
|
};
|
|
@@ -697,15 +766,20 @@ var ProviderRegistry = class {
|
|
|
697
766
|
var TransferClient = class {
|
|
698
767
|
/** Provider registry used by this client. */
|
|
699
768
|
registry;
|
|
769
|
+
/** Execution defaults applied when call sites omit their own values. */
|
|
770
|
+
defaults;
|
|
700
771
|
logger;
|
|
701
772
|
/**
|
|
702
773
|
* Creates a transfer client without opening any provider connections.
|
|
703
774
|
*
|
|
704
|
-
* @param options - Optional registry, provider factories, and
|
|
775
|
+
* @param options - Optional registry, provider factories, logger, and execution defaults.
|
|
705
776
|
*/
|
|
706
777
|
constructor(options = {}) {
|
|
707
778
|
this.registry = options.registry ?? new ProviderRegistry();
|
|
708
779
|
this.logger = options.logger ?? noopLogger;
|
|
780
|
+
if (options.defaults !== void 0) {
|
|
781
|
+
this.defaults = { ...options.defaults };
|
|
782
|
+
}
|
|
709
783
|
for (const provider of options.providers ?? []) {
|
|
710
784
|
this.registry.register(provider);
|
|
711
785
|
}
|
|
@@ -1278,18 +1352,25 @@ var TransferEngine = class {
|
|
|
1278
1352
|
for (let attemptNumber = 1; attemptNumber <= maxAttempts; attemptNumber += 1) {
|
|
1279
1353
|
this.throwIfAborted(abortScope.signal, job);
|
|
1280
1354
|
const attemptStartedAt = this.now();
|
|
1355
|
+
const attemptScope = createAttemptScope(
|
|
1356
|
+
abortScope.signal,
|
|
1357
|
+
options.timeout,
|
|
1358
|
+
job,
|
|
1359
|
+
attemptNumber
|
|
1360
|
+
);
|
|
1281
1361
|
const context = this.createExecutionContext(
|
|
1282
1362
|
job,
|
|
1283
1363
|
attemptNumber,
|
|
1284
1364
|
attemptStartedAt,
|
|
1285
1365
|
options,
|
|
1286
|
-
|
|
1366
|
+
attemptScope.signal,
|
|
1287
1367
|
(bytesTransferred) => {
|
|
1288
1368
|
latestBytesTransferred = bytesTransferred;
|
|
1289
|
-
}
|
|
1369
|
+
},
|
|
1370
|
+
attemptScope.notifyProgress
|
|
1290
1371
|
);
|
|
1291
1372
|
try {
|
|
1292
|
-
const result = await runExecutor(executor, context,
|
|
1373
|
+
const result = await runExecutor(executor, context, attemptScope.signal, job);
|
|
1293
1374
|
context.throwIfAborted();
|
|
1294
1375
|
latestBytesTransferred = result.bytesTransferred;
|
|
1295
1376
|
const completedAt = this.now();
|
|
@@ -1307,16 +1388,27 @@ var TransferEngine = class {
|
|
|
1307
1388
|
summarizeError(error)
|
|
1308
1389
|
);
|
|
1309
1390
|
attempts.push(attempt);
|
|
1310
|
-
if (error instanceof AbortError ||
|
|
1391
|
+
if (error instanceof AbortError || abortScope.signal?.aborted === true) {
|
|
1311
1392
|
throw error;
|
|
1312
1393
|
}
|
|
1313
|
-
const retryInput = {
|
|
1394
|
+
const retryInput = {
|
|
1395
|
+
attempt: attemptNumber,
|
|
1396
|
+
elapsedMs: Math.max(0, completedAt.getTime() - startedAt.getTime()),
|
|
1397
|
+
error,
|
|
1398
|
+
job
|
|
1399
|
+
};
|
|
1314
1400
|
const shouldRetry = attemptNumber < maxAttempts && (options.retry?.shouldRetry?.(retryInput) ?? isRetryable(error));
|
|
1315
1401
|
if (shouldRetry) {
|
|
1316
1402
|
options.retry?.onRetry?.(retryInput);
|
|
1403
|
+
const delayMs = normalizeDelayMs(options.retry?.getDelayMs?.(retryInput));
|
|
1404
|
+
if (delayMs > 0) {
|
|
1405
|
+
await sleepWithAbort(delayMs, abortScope.signal, job);
|
|
1406
|
+
}
|
|
1317
1407
|
continue;
|
|
1318
1408
|
}
|
|
1319
1409
|
throw createTransferFailure(job, error, attempts);
|
|
1410
|
+
} finally {
|
|
1411
|
+
attemptScope.dispose();
|
|
1320
1412
|
}
|
|
1321
1413
|
}
|
|
1322
1414
|
throw createTransferFailure(job, void 0, attempts);
|
|
@@ -1324,12 +1416,13 @@ var TransferEngine = class {
|
|
|
1324
1416
|
abortScope.dispose();
|
|
1325
1417
|
}
|
|
1326
1418
|
}
|
|
1327
|
-
createExecutionContext(job, attempt, startedAt, options, signal, updateBytesTransferred) {
|
|
1419
|
+
createExecutionContext(job, attempt, startedAt, options, signal, updateBytesTransferred, notifyProgress) {
|
|
1328
1420
|
const context = {
|
|
1329
1421
|
attempt,
|
|
1330
1422
|
job,
|
|
1331
1423
|
reportProgress: (bytesTransferred, totalBytes) => {
|
|
1332
1424
|
this.throwIfAborted(signal, job);
|
|
1425
|
+
notifyProgress();
|
|
1333
1426
|
updateBytesTransferred(bytesTransferred);
|
|
1334
1427
|
const progressInput = {
|
|
1335
1428
|
bytesTransferred,
|
|
@@ -1398,6 +1491,96 @@ function createAbortScope(parentSignal, timeout, job) {
|
|
|
1398
1491
|
signal: controller.signal
|
|
1399
1492
|
};
|
|
1400
1493
|
}
|
|
1494
|
+
function createAttemptScope(parentSignal, timeout, job, attempt) {
|
|
1495
|
+
const attemptTimeoutMs = normalizeTimeoutMs(timeout?.attemptTimeoutMs);
|
|
1496
|
+
const stallTimeoutMs = normalizeTimeoutMs(timeout?.stallTimeoutMs);
|
|
1497
|
+
if (attemptTimeoutMs === void 0 && stallTimeoutMs === void 0) {
|
|
1498
|
+
const scope = {
|
|
1499
|
+
dispose: () => void 0,
|
|
1500
|
+
notifyProgress: () => void 0
|
|
1501
|
+
};
|
|
1502
|
+
if (parentSignal !== void 0) scope.signal = parentSignal;
|
|
1503
|
+
return scope;
|
|
1504
|
+
}
|
|
1505
|
+
const controller = new AbortController();
|
|
1506
|
+
const retryable = timeout?.retryable ?? true;
|
|
1507
|
+
const abortFromParent = () => controller.abort(parentSignal?.reason);
|
|
1508
|
+
if (parentSignal?.aborted === true) {
|
|
1509
|
+
abortFromParent();
|
|
1510
|
+
} else {
|
|
1511
|
+
parentSignal?.addEventListener("abort", abortFromParent, { once: true });
|
|
1512
|
+
}
|
|
1513
|
+
const attemptTimer = attemptTimeoutMs === void 0 ? void 0 : setTimeout(() => {
|
|
1514
|
+
controller.abort(
|
|
1515
|
+
new TimeoutError({
|
|
1516
|
+
details: { attempt, attemptTimeoutMs, jobId: job.id, operation: job.operation },
|
|
1517
|
+
message: `Transfer attempt ${String(attempt)} timed out after ${String(attemptTimeoutMs)}ms: ${job.id}`,
|
|
1518
|
+
retryable
|
|
1519
|
+
})
|
|
1520
|
+
);
|
|
1521
|
+
}, attemptTimeoutMs);
|
|
1522
|
+
let stallTimer;
|
|
1523
|
+
const armStallWatchdog = () => {
|
|
1524
|
+
if (stallTimeoutMs === void 0 || controller.signal.aborted) return;
|
|
1525
|
+
if (stallTimer !== void 0) clearTimeout(stallTimer);
|
|
1526
|
+
stallTimer = setTimeout(() => {
|
|
1527
|
+
controller.abort(
|
|
1528
|
+
new TimeoutError({
|
|
1529
|
+
details: { attempt, jobId: job.id, operation: job.operation, stallTimeoutMs },
|
|
1530
|
+
message: `Transfer attempt ${String(attempt)} stalled (no progress for ${String(stallTimeoutMs)}ms): ${job.id}`,
|
|
1531
|
+
retryable
|
|
1532
|
+
})
|
|
1533
|
+
);
|
|
1534
|
+
}, stallTimeoutMs);
|
|
1535
|
+
};
|
|
1536
|
+
armStallWatchdog();
|
|
1537
|
+
return {
|
|
1538
|
+
dispose: () => {
|
|
1539
|
+
if (attemptTimer !== void 0) clearTimeout(attemptTimer);
|
|
1540
|
+
if (stallTimer !== void 0) clearTimeout(stallTimer);
|
|
1541
|
+
parentSignal?.removeEventListener("abort", abortFromParent);
|
|
1542
|
+
},
|
|
1543
|
+
notifyProgress: armStallWatchdog,
|
|
1544
|
+
signal: controller.signal
|
|
1545
|
+
};
|
|
1546
|
+
}
|
|
1547
|
+
function sleepWithAbort(delayMs, signal, job) {
|
|
1548
|
+
return new Promise((resolve, reject) => {
|
|
1549
|
+
if (signal === void 0) {
|
|
1550
|
+
setTimeout(resolve, delayMs);
|
|
1551
|
+
return;
|
|
1552
|
+
}
|
|
1553
|
+
if (signal.aborted) {
|
|
1554
|
+
reject(toAbortFailure(signal, job));
|
|
1555
|
+
return;
|
|
1556
|
+
}
|
|
1557
|
+
const rejectAbort = () => {
|
|
1558
|
+
clearTimeout(timer);
|
|
1559
|
+
reject(toAbortFailure(signal, job));
|
|
1560
|
+
};
|
|
1561
|
+
const timer = setTimeout(() => {
|
|
1562
|
+
signal.removeEventListener("abort", rejectAbort);
|
|
1563
|
+
resolve();
|
|
1564
|
+
}, delayMs);
|
|
1565
|
+
signal.addEventListener("abort", rejectAbort, { once: true });
|
|
1566
|
+
});
|
|
1567
|
+
}
|
|
1568
|
+
function toAbortFailure(signal, job) {
|
|
1569
|
+
if (signal.reason instanceof ZeroTransferError) {
|
|
1570
|
+
return signal.reason;
|
|
1571
|
+
}
|
|
1572
|
+
return new AbortError({
|
|
1573
|
+
details: { jobId: job.id, operation: job.operation },
|
|
1574
|
+
message: `Transfer job aborted: ${job.id}`,
|
|
1575
|
+
retryable: false
|
|
1576
|
+
});
|
|
1577
|
+
}
|
|
1578
|
+
function normalizeDelayMs(value) {
|
|
1579
|
+
if (value === void 0 || !Number.isFinite(value) || value <= 0) {
|
|
1580
|
+
return 0;
|
|
1581
|
+
}
|
|
1582
|
+
return Math.floor(value);
|
|
1583
|
+
}
|
|
1401
1584
|
function normalizeTimeoutMs(value) {
|
|
1402
1585
|
if (value === void 0 || !Number.isFinite(value) || value <= 0) {
|
|
1403
1586
|
return void 0;
|
|
@@ -1566,7 +1749,7 @@ async function runRoute(options) {
|
|
|
1566
1749
|
const executor = createProviderTransferExecutor({
|
|
1567
1750
|
resolveSession: ({ role }) => sessions.get(role)
|
|
1568
1751
|
});
|
|
1569
|
-
return await engine.execute(job, executor, buildExecuteOptions(options));
|
|
1752
|
+
return await engine.execute(job, executor, buildExecuteOptions(options, client));
|
|
1570
1753
|
} finally {
|
|
1571
1754
|
if (destinationSession !== void 0) {
|
|
1572
1755
|
await destinationSession.disconnect();
|
|
@@ -1603,12 +1786,14 @@ function defaultJobId(route, now) {
|
|
|
1603
1786
|
const timestamp = (now?.() ?? /* @__PURE__ */ new Date()).getTime();
|
|
1604
1787
|
return `route:${route.id}:${timestamp.toString(36)}`;
|
|
1605
1788
|
}
|
|
1606
|
-
function buildExecuteOptions(options) {
|
|
1789
|
+
function buildExecuteOptions(options, client) {
|
|
1607
1790
|
const execute = {};
|
|
1791
|
+
const retry = options.retry ?? client.defaults?.retry;
|
|
1792
|
+
const timeout = options.timeout ?? client.defaults?.timeout;
|
|
1608
1793
|
if (options.signal !== void 0) execute.signal = options.signal;
|
|
1609
|
-
if (
|
|
1794
|
+
if (retry !== void 0) execute.retry = retry;
|
|
1610
1795
|
if (options.onProgress !== void 0) execute.onProgress = options.onProgress;
|
|
1611
|
-
if (
|
|
1796
|
+
if (timeout !== void 0) execute.timeout = timeout;
|
|
1612
1797
|
if (options.bandwidthLimit !== void 0) execute.bandwidthLimit = options.bandwidthLimit;
|
|
1613
1798
|
return execute;
|
|
1614
1799
|
}
|
|
@@ -1665,41 +1850,6 @@ function defaultRouteSuffix(source, destination) {
|
|
|
1665
1850
|
return `${source}->${destination}`;
|
|
1666
1851
|
}
|
|
1667
1852
|
|
|
1668
|
-
// src/logging/redaction.ts
|
|
1669
|
-
var REDACTED = "[REDACTED]";
|
|
1670
|
-
var SENSITIVE_KEY_PATTERN = /(?:password|passphrase|privatekey|token|secret|username|user)$/i;
|
|
1671
|
-
var SECRET_COMMAND_PATTERN = /^(PASS|USER|ACCT)\s+(.+)$/i;
|
|
1672
|
-
function isSensitiveKey(key) {
|
|
1673
|
-
return SENSITIVE_KEY_PATTERN.test(key.replace(/[_-]/g, ""));
|
|
1674
|
-
}
|
|
1675
|
-
function redactCommand(command) {
|
|
1676
|
-
return command.replace(SECRET_COMMAND_PATTERN, (_fullMatch, commandName) => {
|
|
1677
|
-
return `${commandName.toUpperCase()} ${REDACTED}`;
|
|
1678
|
-
});
|
|
1679
|
-
}
|
|
1680
|
-
function redactValue(value) {
|
|
1681
|
-
if (typeof value === "string") {
|
|
1682
|
-
return redactCommand(value);
|
|
1683
|
-
}
|
|
1684
|
-
if (Array.isArray(value)) {
|
|
1685
|
-
return value.map((item) => redactValue(item));
|
|
1686
|
-
}
|
|
1687
|
-
if (value !== null && typeof value === "object") {
|
|
1688
|
-
return redactObject(value);
|
|
1689
|
-
}
|
|
1690
|
-
return value;
|
|
1691
|
-
}
|
|
1692
|
-
function redactObject(input) {
|
|
1693
|
-
return Object.fromEntries(
|
|
1694
|
-
Object.entries(input).map(([key, value]) => {
|
|
1695
|
-
if (isSensitiveKey(key)) {
|
|
1696
|
-
return [key, REDACTED];
|
|
1697
|
-
}
|
|
1698
|
-
return [key, redactValue(value)];
|
|
1699
|
-
})
|
|
1700
|
-
);
|
|
1701
|
-
}
|
|
1702
|
-
|
|
1703
1853
|
// src/profiles/SecretSource.ts
|
|
1704
1854
|
var import_node_buffer2 = require("buffer");
|
|
1705
1855
|
var import_promises = require("fs/promises");
|
|
@@ -2071,11 +2221,11 @@ var import_promises2 = require("fs/promises");
|
|
|
2071
2221
|
var import_node_path2 = __toESM(require("path"));
|
|
2072
2222
|
|
|
2073
2223
|
// src/utils/path.ts
|
|
2074
|
-
var UNSAFE_FTP_ARGUMENT_PATTERN = /[\r\n]/;
|
|
2224
|
+
var UNSAFE_FTP_ARGUMENT_PATTERN = /[\r\n\0]/;
|
|
2075
2225
|
function assertSafeFtpArgument(value, label = "path") {
|
|
2076
2226
|
if (UNSAFE_FTP_ARGUMENT_PATTERN.test(value)) {
|
|
2077
2227
|
throw new ConfigurationError({
|
|
2078
|
-
message: `Unsafe FTP ${label}: CR and
|
|
2228
|
+
message: `Unsafe FTP ${label}: CR, LF, and NUL characters are not allowed`,
|
|
2079
2229
|
retryable: false,
|
|
2080
2230
|
details: {
|
|
2081
2231
|
label
|
|
@@ -3377,7 +3527,6 @@ function expandAlgorithms(values) {
|
|
|
3377
3527
|
}
|
|
3378
3528
|
|
|
3379
3529
|
// src/profiles/importers/FileZillaImporter.ts
|
|
3380
|
-
var import_node_buffer5 = require("buffer");
|
|
3381
3530
|
function importFileZillaSites(xml) {
|
|
3382
3531
|
const events = tokenizeXml(xml);
|
|
3383
3532
|
if (events.length === 0) {
|
|
@@ -3393,7 +3542,6 @@ function importFileZillaSites(xml) {
|
|
|
3393
3542
|
const folderNamePending = [];
|
|
3394
3543
|
let inServer = false;
|
|
3395
3544
|
let serverFields = {};
|
|
3396
|
-
let serverPasswordEncoding;
|
|
3397
3545
|
let activeTag;
|
|
3398
3546
|
let captureFolderName = false;
|
|
3399
3547
|
for (const event of events) {
|
|
@@ -3406,13 +3554,9 @@ function importFileZillaSites(xml) {
|
|
|
3406
3554
|
if (event.name === "Server") {
|
|
3407
3555
|
inServer = true;
|
|
3408
3556
|
serverFields = {};
|
|
3409
|
-
serverPasswordEncoding = void 0;
|
|
3410
3557
|
continue;
|
|
3411
3558
|
}
|
|
3412
3559
|
activeTag = event.name;
|
|
3413
|
-
if (event.name === "Pass" && inServer) {
|
|
3414
|
-
serverPasswordEncoding = event.attributes["encoding"];
|
|
3415
|
-
}
|
|
3416
3560
|
if (event.name === "Name" && !inServer && folderNamePending.length > 0) {
|
|
3417
3561
|
captureFolderName = true;
|
|
3418
3562
|
}
|
|
@@ -3438,7 +3582,7 @@ function importFileZillaSites(xml) {
|
|
|
3438
3582
|
}
|
|
3439
3583
|
if (event.name === "Server") {
|
|
3440
3584
|
const folder = folderStack.filter((segment) => segment !== "");
|
|
3441
|
-
const result = buildSiteFromFields(serverFields
|
|
3585
|
+
const result = buildSiteFromFields(serverFields);
|
|
3442
3586
|
if (result.kind === "site") {
|
|
3443
3587
|
sites.push({ ...result.site, folder });
|
|
3444
3588
|
} else {
|
|
@@ -3450,7 +3594,6 @@ function importFileZillaSites(xml) {
|
|
|
3450
3594
|
}
|
|
3451
3595
|
inServer = false;
|
|
3452
3596
|
serverFields = {};
|
|
3453
|
-
serverPasswordEncoding = void 0;
|
|
3454
3597
|
activeTag = void 0;
|
|
3455
3598
|
continue;
|
|
3456
3599
|
}
|
|
@@ -3459,7 +3602,7 @@ function importFileZillaSites(xml) {
|
|
|
3459
3602
|
}
|
|
3460
3603
|
return { sites, skipped };
|
|
3461
3604
|
}
|
|
3462
|
-
function buildSiteFromFields(fields
|
|
3605
|
+
function buildSiteFromFields(fields) {
|
|
3463
3606
|
const name = (fields["Name"] ?? fields["Host"] ?? "Untitled").trim();
|
|
3464
3607
|
const host = (fields["Host"] ?? "").trim();
|
|
3465
3608
|
if (host === "") return { kind: "skipped", name };
|
|
@@ -3478,18 +3621,9 @@ function buildSiteFromFields(fields, passwordEncoding) {
|
|
|
3478
3621
|
}
|
|
3479
3622
|
const user = fields["User"]?.trim();
|
|
3480
3623
|
if (user !== void 0 && user !== "") profile.username = { value: user };
|
|
3481
|
-
let password;
|
|
3482
3624
|
const rawPass = fields["Pass"];
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
password = import_node_buffer5.Buffer.from(rawPass, "base64").toString("utf8");
|
|
3486
|
-
} else {
|
|
3487
|
-
password = rawPass;
|
|
3488
|
-
}
|
|
3489
|
-
if (password !== void 0 && password !== "") profile.password = { value: password };
|
|
3490
|
-
}
|
|
3491
|
-
const site = { name, profile };
|
|
3492
|
-
if (password !== void 0) site.password = password;
|
|
3625
|
+
const hasStoredPassword = rawPass !== void 0 && rawPass !== "";
|
|
3626
|
+
const site = { hasStoredPassword, name, profile };
|
|
3493
3627
|
const logonText = fields["Logontype"];
|
|
3494
3628
|
if (logonText !== void 0) {
|
|
3495
3629
|
const logonType = Number.parseInt(logonText.trim(), 10);
|
|
@@ -3732,6 +3866,62 @@ function mapFtp550(details) {
|
|
|
3732
3866
|
return new PermissionDeniedError(details);
|
|
3733
3867
|
}
|
|
3734
3868
|
|
|
3869
|
+
// src/transfers/createDefaultRetryPolicy.ts
|
|
3870
|
+
var DEFAULT_MAX_ATTEMPTS = 4;
|
|
3871
|
+
var DEFAULT_BASE_DELAY_MS = 250;
|
|
3872
|
+
var DEFAULT_MAX_DELAY_MS = 3e4;
|
|
3873
|
+
var DEFAULT_MAX_ELAPSED_MS = 3e5;
|
|
3874
|
+
function createDefaultRetryPolicy(options = {}) {
|
|
3875
|
+
const maxAttempts = normalizePositiveInteger(options.maxAttempts, DEFAULT_MAX_ATTEMPTS);
|
|
3876
|
+
const baseDelayMs = normalizeNonNegative(options.baseDelayMs, DEFAULT_BASE_DELAY_MS);
|
|
3877
|
+
const maxDelayMs = normalizeNonNegative(options.maxDelayMs, DEFAULT_MAX_DELAY_MS);
|
|
3878
|
+
const maxElapsedMs = normalizeNonNegative(options.maxElapsedMs, DEFAULT_MAX_ELAPSED_MS);
|
|
3879
|
+
const random = options.random ?? Math.random;
|
|
3880
|
+
return {
|
|
3881
|
+
getDelayMs(input) {
|
|
3882
|
+
const retryAfterMs = readRetryAfterMs(input.error);
|
|
3883
|
+
if (retryAfterMs !== void 0) {
|
|
3884
|
+
return retryAfterMs;
|
|
3885
|
+
}
|
|
3886
|
+
const exponentialMs = baseDelayMs * 2 ** (input.attempt - 1);
|
|
3887
|
+
const cappedMs = Math.min(maxDelayMs, exponentialMs);
|
|
3888
|
+
return Math.floor(random() * cappedMs);
|
|
3889
|
+
},
|
|
3890
|
+
maxAttempts,
|
|
3891
|
+
shouldRetry(input) {
|
|
3892
|
+
if (!(input.error instanceof ZeroTransferError) || !input.error.retryable) {
|
|
3893
|
+
return false;
|
|
3894
|
+
}
|
|
3895
|
+
if (input.elapsedMs >= maxElapsedMs) {
|
|
3896
|
+
return false;
|
|
3897
|
+
}
|
|
3898
|
+
const retryAfterMs = readRetryAfterMs(input.error);
|
|
3899
|
+
if (retryAfterMs !== void 0 && input.elapsedMs + retryAfterMs > maxElapsedMs) {
|
|
3900
|
+
return false;
|
|
3901
|
+
}
|
|
3902
|
+
return true;
|
|
3903
|
+
}
|
|
3904
|
+
};
|
|
3905
|
+
}
|
|
3906
|
+
function readRetryAfterMs(error) {
|
|
3907
|
+
if (!(error instanceof ZeroTransferError)) return void 0;
|
|
3908
|
+
const value = error.details?.["retryAfterMs"];
|
|
3909
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return void 0;
|
|
3910
|
+
return Math.floor(value);
|
|
3911
|
+
}
|
|
3912
|
+
function normalizePositiveInteger(value, fallback) {
|
|
3913
|
+
if (value === void 0 || !Number.isFinite(value) || value < 1) {
|
|
3914
|
+
return fallback;
|
|
3915
|
+
}
|
|
3916
|
+
return Math.floor(value);
|
|
3917
|
+
}
|
|
3918
|
+
function normalizeNonNegative(value, fallback) {
|
|
3919
|
+
if (value === void 0 || !Number.isFinite(value) || value < 0) {
|
|
3920
|
+
return fallback;
|
|
3921
|
+
}
|
|
3922
|
+
return Math.floor(value);
|
|
3923
|
+
}
|
|
3924
|
+
|
|
3735
3925
|
// src/transfers/TransferPlan.ts
|
|
3736
3926
|
function createTransferPlan(input) {
|
|
3737
3927
|
const plan = {
|
|
@@ -3829,8 +4019,8 @@ var TransferQueue = class {
|
|
|
3829
4019
|
this.concurrency = normalizeConcurrency(options.concurrency);
|
|
3830
4020
|
this.defaultExecutor = options.executor;
|
|
3831
4021
|
this.resolveExecutor = options.resolveExecutor;
|
|
3832
|
-
this.retry = options.retry;
|
|
3833
|
-
this.timeout = options.timeout;
|
|
4022
|
+
this.retry = options.retry ?? options.client?.defaults?.retry;
|
|
4023
|
+
this.timeout = options.timeout ?? options.client?.defaults?.timeout;
|
|
3834
4024
|
this.bandwidthLimit = options.bandwidthLimit;
|
|
3835
4025
|
this.onProgress = options.onProgress;
|
|
3836
4026
|
this.onReceipt = options.onReceipt;
|
|
@@ -4907,12 +5097,12 @@ function isMainModule(importMetaUrl) {
|
|
|
4907
5097
|
}
|
|
4908
5098
|
|
|
4909
5099
|
// src/providers/native/sftp/NativeSftpProvider.ts
|
|
4910
|
-
var
|
|
5100
|
+
var import_node_buffer19 = require("buffer");
|
|
4911
5101
|
var import_node_crypto9 = require("crypto");
|
|
4912
5102
|
var import_node_net = require("net");
|
|
4913
5103
|
|
|
4914
5104
|
// src/protocols/ssh/binary/SshDataWriter.ts
|
|
4915
|
-
var
|
|
5105
|
+
var import_node_buffer5 = require("buffer");
|
|
4916
5106
|
var MAX_UINT32 = 4294967295;
|
|
4917
5107
|
var MAX_UINT64 = (1n << 64n) - 1n;
|
|
4918
5108
|
var SshDataWriter = class {
|
|
@@ -4920,7 +5110,7 @@ var SshDataWriter = class {
|
|
|
4920
5110
|
length = 0;
|
|
4921
5111
|
writeByte(value) {
|
|
4922
5112
|
this.assertByte(value, "byte");
|
|
4923
|
-
const chunk =
|
|
5113
|
+
const chunk = import_node_buffer5.Buffer.alloc(1);
|
|
4924
5114
|
chunk.writeUInt8(value, 0);
|
|
4925
5115
|
return this.push(chunk);
|
|
4926
5116
|
}
|
|
@@ -4928,7 +5118,7 @@ var SshDataWriter = class {
|
|
|
4928
5118
|
return this.writeByte(value ? 1 : 0);
|
|
4929
5119
|
}
|
|
4930
5120
|
writeBytes(value) {
|
|
4931
|
-
return this.push(
|
|
5121
|
+
return this.push(import_node_buffer5.Buffer.from(value));
|
|
4932
5122
|
}
|
|
4933
5123
|
writeUint32(value) {
|
|
4934
5124
|
if (!Number.isInteger(value) || value < 0 || value > MAX_UINT32) {
|
|
@@ -4938,7 +5128,7 @@ var SshDataWriter = class {
|
|
|
4938
5128
|
retryable: false
|
|
4939
5129
|
});
|
|
4940
5130
|
}
|
|
4941
|
-
const chunk =
|
|
5131
|
+
const chunk = import_node_buffer5.Buffer.alloc(4);
|
|
4942
5132
|
chunk.writeUInt32BE(value, 0);
|
|
4943
5133
|
return this.push(chunk);
|
|
4944
5134
|
}
|
|
@@ -4950,12 +5140,12 @@ var SshDataWriter = class {
|
|
|
4950
5140
|
retryable: false
|
|
4951
5141
|
});
|
|
4952
5142
|
}
|
|
4953
|
-
const chunk =
|
|
5143
|
+
const chunk = import_node_buffer5.Buffer.alloc(8);
|
|
4954
5144
|
chunk.writeBigUInt64BE(value, 0);
|
|
4955
5145
|
return this.push(chunk);
|
|
4956
5146
|
}
|
|
4957
5147
|
writeString(value, encoding = "utf8") {
|
|
4958
|
-
const payload = typeof value === "string" ?
|
|
5148
|
+
const payload = typeof value === "string" ? import_node_buffer5.Buffer.from(value, encoding) : import_node_buffer5.Buffer.from(value);
|
|
4959
5149
|
this.writeUint32(payload.length);
|
|
4960
5150
|
return this.push(payload);
|
|
4961
5151
|
}
|
|
@@ -4977,7 +5167,7 @@ var SshDataWriter = class {
|
|
|
4977
5167
|
return this.writeString(values.join(","), "ascii");
|
|
4978
5168
|
}
|
|
4979
5169
|
toBuffer() {
|
|
4980
|
-
return
|
|
5170
|
+
return import_node_buffer5.Buffer.concat(this.chunks, this.length);
|
|
4981
5171
|
}
|
|
4982
5172
|
push(chunk) {
|
|
4983
5173
|
this.chunks.push(chunk);
|
|
@@ -4995,23 +5185,23 @@ var SshDataWriter = class {
|
|
|
4995
5185
|
}
|
|
4996
5186
|
};
|
|
4997
5187
|
function normalizePositiveMpint(value) {
|
|
4998
|
-
const input =
|
|
5188
|
+
const input = import_node_buffer5.Buffer.from(value);
|
|
4999
5189
|
let offset = 0;
|
|
5000
5190
|
while (offset < input.length && input[offset] === 0) {
|
|
5001
5191
|
offset += 1;
|
|
5002
5192
|
}
|
|
5003
5193
|
if (offset >= input.length) {
|
|
5004
|
-
return
|
|
5194
|
+
return import_node_buffer5.Buffer.alloc(0);
|
|
5005
5195
|
}
|
|
5006
5196
|
const stripped = input.subarray(offset);
|
|
5007
5197
|
if ((stripped[0] & 128) === 128) {
|
|
5008
|
-
return
|
|
5198
|
+
return import_node_buffer5.Buffer.concat([import_node_buffer5.Buffer.from([0]), stripped]);
|
|
5009
5199
|
}
|
|
5010
5200
|
return stripped;
|
|
5011
5201
|
}
|
|
5012
5202
|
|
|
5013
5203
|
// src/protocols/ssh/binary/SshDataReader.ts
|
|
5014
|
-
var
|
|
5204
|
+
var import_node_buffer6 = require("buffer");
|
|
5015
5205
|
var SshDataReader = class {
|
|
5016
5206
|
constructor(source) {
|
|
5017
5207
|
this.source = source;
|
|
@@ -5037,18 +5227,18 @@ var SshDataReader = class {
|
|
|
5037
5227
|
this.ensureAvailable(length, "bytes");
|
|
5038
5228
|
const data = this.source.subarray(this.offset, this.offset + length);
|
|
5039
5229
|
this.offset += length;
|
|
5040
|
-
return
|
|
5230
|
+
return import_node_buffer6.Buffer.from(data);
|
|
5041
5231
|
}
|
|
5042
5232
|
readUint32() {
|
|
5043
5233
|
this.ensureAvailable(4, "uint32");
|
|
5044
|
-
const buffer =
|
|
5234
|
+
const buffer = import_node_buffer6.Buffer.from(this.source);
|
|
5045
5235
|
const value = buffer.readUInt32BE(this.offset);
|
|
5046
5236
|
this.offset += 4;
|
|
5047
5237
|
return value;
|
|
5048
5238
|
}
|
|
5049
5239
|
readUint64() {
|
|
5050
5240
|
this.ensureAvailable(8, "uint64");
|
|
5051
|
-
const buffer =
|
|
5241
|
+
const buffer = import_node_buffer6.Buffer.from(this.source);
|
|
5052
5242
|
const value = buffer.readBigUInt64BE(this.offset);
|
|
5053
5243
|
this.offset += 8;
|
|
5054
5244
|
return value;
|
|
@@ -5058,7 +5248,7 @@ var SshDataReader = class {
|
|
|
5058
5248
|
this.ensureAvailable(length, "string");
|
|
5059
5249
|
const data = this.source.subarray(this.offset, this.offset + length);
|
|
5060
5250
|
this.offset += length;
|
|
5061
|
-
return
|
|
5251
|
+
return import_node_buffer6.Buffer.from(data);
|
|
5062
5252
|
}
|
|
5063
5253
|
readUtf8String() {
|
|
5064
5254
|
return this.readString().toString("utf8");
|
|
@@ -5413,7 +5603,7 @@ function buildKiRequest(args) {
|
|
|
5413
5603
|
}
|
|
5414
5604
|
|
|
5415
5605
|
// src/protocols/ssh/auth/SshPublickeyCredentialBuilder.ts
|
|
5416
|
-
var
|
|
5606
|
+
var import_node_buffer7 = require("buffer");
|
|
5417
5607
|
var import_node_crypto2 = require("crypto");
|
|
5418
5608
|
var ED25519_RAW_KEY_LENGTH = 32;
|
|
5419
5609
|
var ED25519_SPKI_PREFIX_LENGTH = 12;
|
|
@@ -5431,7 +5621,7 @@ function buildPublickeyCredential(options) {
|
|
|
5431
5621
|
return {
|
|
5432
5622
|
algorithmName: "ssh-ed25519",
|
|
5433
5623
|
publicKeyBlob,
|
|
5434
|
-
sign: (data) => (0, import_node_crypto2.sign)(null,
|
|
5624
|
+
sign: (data) => (0, import_node_crypto2.sign)(null, import_node_buffer7.Buffer.from(data), privateKey),
|
|
5435
5625
|
type: "publickey",
|
|
5436
5626
|
username
|
|
5437
5627
|
};
|
|
@@ -5449,7 +5639,7 @@ function buildPublickeyCredential(options) {
|
|
|
5449
5639
|
return {
|
|
5450
5640
|
algorithmName,
|
|
5451
5641
|
publicKeyBlob,
|
|
5452
|
-
sign: (data) => (0, import_node_crypto2.sign)(hash,
|
|
5642
|
+
sign: (data) => (0, import_node_crypto2.sign)(hash, import_node_buffer7.Buffer.from(data), privateKey),
|
|
5453
5643
|
type: "publickey",
|
|
5454
5644
|
username
|
|
5455
5645
|
};
|
|
@@ -5462,7 +5652,7 @@ function buildPublickeyCredential(options) {
|
|
|
5462
5652
|
}
|
|
5463
5653
|
function base64UrlToMpint(value) {
|
|
5464
5654
|
const padded = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
5465
|
-
const buffer =
|
|
5655
|
+
const buffer = import_node_buffer7.Buffer.from(padded, "base64");
|
|
5466
5656
|
return buffer;
|
|
5467
5657
|
}
|
|
5468
5658
|
function createInvalidKeyError(message) {
|
|
@@ -5588,7 +5778,7 @@ function encodeSshChannelClose(recipientChannel) {
|
|
|
5588
5778
|
}
|
|
5589
5779
|
|
|
5590
5780
|
// src/protocols/ssh/connection/SshSessionChannel.ts
|
|
5591
|
-
var
|
|
5781
|
+
var import_node_buffer8 = require("buffer");
|
|
5592
5782
|
var INITIAL_WINDOW_SIZE = 256 * 1024;
|
|
5593
5783
|
var MAX_PACKET_SIZE = 32 * 1024;
|
|
5594
5784
|
var WINDOW_REFILL_THRESHOLD = 64 * 1024;
|
|
@@ -5746,7 +5936,7 @@ var SshSessionChannel = class {
|
|
|
5746
5936
|
this.remoteWindowRemaining,
|
|
5747
5937
|
this.remoteMaxPacketSize
|
|
5748
5938
|
);
|
|
5749
|
-
const chunk =
|
|
5939
|
+
const chunk = import_node_buffer8.Buffer.from(data.subarray(offset, offset + chunkSize));
|
|
5750
5940
|
this.transport.sendPayload(
|
|
5751
5941
|
encodeSshChannelData({ data: chunk, recipientChannel: this.remoteChannelId })
|
|
5752
5942
|
);
|
|
@@ -6008,10 +6198,10 @@ var SshConnectionManager = class {
|
|
|
6008
6198
|
};
|
|
6009
6199
|
|
|
6010
6200
|
// src/protocols/ssh/transport/SshTransportConnection.ts
|
|
6011
|
-
var
|
|
6201
|
+
var import_node_buffer16 = require("buffer");
|
|
6012
6202
|
|
|
6013
6203
|
// src/protocols/ssh/transport/SshTransportHandshake.ts
|
|
6014
|
-
var
|
|
6204
|
+
var import_node_buffer14 = require("buffer");
|
|
6015
6205
|
|
|
6016
6206
|
// src/protocols/ssh/transport/SshAlgorithmNegotiation.ts
|
|
6017
6207
|
var DEFAULT_SSH_ALGORITHM_PREFERENCES = {
|
|
@@ -6173,12 +6363,12 @@ function parseSshIdentificationLine(line) {
|
|
|
6173
6363
|
}
|
|
6174
6364
|
|
|
6175
6365
|
// src/protocols/ssh/transport/SshKexInit.ts
|
|
6176
|
-
var
|
|
6366
|
+
var import_node_buffer9 = require("buffer");
|
|
6177
6367
|
var import_node_crypto3 = require("crypto");
|
|
6178
6368
|
var SSH_MSG_KEXINIT = 20;
|
|
6179
6369
|
var KEXINIT_COOKIE_LENGTH = 16;
|
|
6180
6370
|
function encodeSshKexInitMessage(options) {
|
|
6181
|
-
const cookie = options.cookie === void 0 ? (0, import_node_crypto3.randomBytes)(KEXINIT_COOKIE_LENGTH) :
|
|
6371
|
+
const cookie = options.cookie === void 0 ? (0, import_node_crypto3.randomBytes)(KEXINIT_COOKIE_LENGTH) : import_node_buffer9.Buffer.from(options.cookie);
|
|
6182
6372
|
if (cookie.length !== KEXINIT_COOKIE_LENGTH) {
|
|
6183
6373
|
throw new ConfigurationError({
|
|
6184
6374
|
details: { actualLength: cookie.length, expectedLength: KEXINIT_COOKIE_LENGTH },
|
|
@@ -6248,12 +6438,12 @@ function decodeSshKexInitMessage(payload) {
|
|
|
6248
6438
|
}
|
|
6249
6439
|
|
|
6250
6440
|
// src/protocols/ssh/transport/SshKexCurve25519.ts
|
|
6251
|
-
var
|
|
6441
|
+
var import_node_buffer10 = require("buffer");
|
|
6252
6442
|
var import_node_crypto4 = require("crypto");
|
|
6253
6443
|
var SSH_MSG_KEX_ECDH_INIT = 30;
|
|
6254
6444
|
var SSH_MSG_KEX_ECDH_REPLY = 31;
|
|
6255
6445
|
var X25519_PUBLIC_KEY_LENGTH = 32;
|
|
6256
|
-
var X25519_SPKI_PREFIX =
|
|
6446
|
+
var X25519_SPKI_PREFIX = import_node_buffer10.Buffer.from("302a300506032b656e032100", "hex");
|
|
6257
6447
|
function createCurve25519Ephemeral() {
|
|
6258
6448
|
const { privateKey, publicKey } = (0, import_node_crypto4.generateKeyPairSync)("x25519");
|
|
6259
6449
|
const encodedPublicKey = exportX25519PublicKeyRaw(publicKey);
|
|
@@ -6298,7 +6488,7 @@ function exportX25519PublicKeyRaw(publicKey) {
|
|
|
6298
6488
|
}
|
|
6299
6489
|
function importX25519PublicKeyRaw(raw) {
|
|
6300
6490
|
const normalized = normalizeX25519PublicKey(raw, "server");
|
|
6301
|
-
const der =
|
|
6491
|
+
const der = import_node_buffer10.Buffer.concat([X25519_SPKI_PREFIX, normalized]);
|
|
6302
6492
|
return (0, import_node_crypto4.createPublicKey)({
|
|
6303
6493
|
format: "der",
|
|
6304
6494
|
key: der,
|
|
@@ -6306,7 +6496,7 @@ function importX25519PublicKeyRaw(raw) {
|
|
|
6306
6496
|
});
|
|
6307
6497
|
}
|
|
6308
6498
|
function normalizeX25519PublicKey(value, label) {
|
|
6309
|
-
const key =
|
|
6499
|
+
const key = import_node_buffer10.Buffer.from(value);
|
|
6310
6500
|
if (key.length !== X25519_PUBLIC_KEY_LENGTH) {
|
|
6311
6501
|
throw new ConfigurationError({
|
|
6312
6502
|
details: { keyLength: key.length, label },
|
|
@@ -6319,7 +6509,7 @@ function normalizeX25519PublicKey(value, label) {
|
|
|
6319
6509
|
}
|
|
6320
6510
|
|
|
6321
6511
|
// src/protocols/ssh/transport/SshKeyDerivation.ts
|
|
6322
|
-
var
|
|
6512
|
+
var import_node_buffer11 = require("buffer");
|
|
6323
6513
|
var import_node_crypto5 = require("crypto");
|
|
6324
6514
|
function deriveSshSessionKeys(input) {
|
|
6325
6515
|
const hashAlgorithm = resolveKexHashAlgorithm(input.kexAlgorithm);
|
|
@@ -6341,7 +6531,7 @@ function deriveSshSessionKeys(input) {
|
|
|
6341
6531
|
input.negotiatedAlgorithms.encryptionServerToClient,
|
|
6342
6532
|
input.negotiatedAlgorithms.macServerToClient
|
|
6343
6533
|
);
|
|
6344
|
-
const sharedSecret =
|
|
6534
|
+
const sharedSecret = import_node_buffer11.Buffer.from(input.sharedSecret);
|
|
6345
6535
|
return {
|
|
6346
6536
|
clientToServer: {
|
|
6347
6537
|
encryptionKey: deriveMaterial(
|
|
@@ -6391,21 +6581,21 @@ function computeCurve25519ExchangeHash(input, hashAlgorithm) {
|
|
|
6391
6581
|
}
|
|
6392
6582
|
function deriveMaterial(sharedSecret, exchangeHash, sessionId, letter, length, hashAlgorithm) {
|
|
6393
6583
|
if (length <= 0) {
|
|
6394
|
-
return
|
|
6584
|
+
return import_node_buffer11.Buffer.alloc(0);
|
|
6395
6585
|
}
|
|
6396
6586
|
const result = [];
|
|
6397
6587
|
const first2 = (0, import_node_crypto5.createHash)(hashAlgorithm).update(
|
|
6398
6588
|
new SshDataWriter().writeMpint(sharedSecret).writeBytes(exchangeHash).writeByte(letter.charCodeAt(0)).writeBytes(sessionId).toBuffer()
|
|
6399
6589
|
).digest();
|
|
6400
6590
|
result.push(first2);
|
|
6401
|
-
while (
|
|
6402
|
-
const previous =
|
|
6591
|
+
while (import_node_buffer11.Buffer.concat(result).length < length) {
|
|
6592
|
+
const previous = import_node_buffer11.Buffer.concat(result);
|
|
6403
6593
|
const next = (0, import_node_crypto5.createHash)(hashAlgorithm).update(
|
|
6404
6594
|
new SshDataWriter().writeMpint(sharedSecret).writeBytes(exchangeHash).writeBytes(previous).toBuffer()
|
|
6405
6595
|
).digest();
|
|
6406
6596
|
result.push(next);
|
|
6407
6597
|
}
|
|
6408
|
-
return
|
|
6598
|
+
return import_node_buffer11.Buffer.concat(result).subarray(0, length);
|
|
6409
6599
|
}
|
|
6410
6600
|
function resolveKexHashAlgorithm(kexAlgorithm) {
|
|
6411
6601
|
if (kexAlgorithm === "curve25519-sha256" || kexAlgorithm === "curve25519-sha256@libssh.org") {
|
|
@@ -6493,20 +6683,21 @@ function decodeSshNewKeysMessage(payload) {
|
|
|
6493
6683
|
}
|
|
6494
6684
|
|
|
6495
6685
|
// src/protocols/ssh/transport/SshTransportPacket.ts
|
|
6496
|
-
var
|
|
6686
|
+
var import_node_buffer12 = require("buffer");
|
|
6497
6687
|
var import_node_crypto6 = require("crypto");
|
|
6498
6688
|
var MIN_PADDING_LENGTH = 4;
|
|
6499
6689
|
var MIN_PACKET_LENGTH = 1 + MIN_PADDING_LENGTH;
|
|
6690
|
+
var MAX_SSH_PACKET_LENGTH = 256 * 1024;
|
|
6500
6691
|
function encodeSshTransportPacket(payload, options = {}) {
|
|
6501
|
-
const body =
|
|
6692
|
+
const body = import_node_buffer12.Buffer.from(payload);
|
|
6502
6693
|
const blockSize = normalizeBlockSize(options.blockSize ?? 8);
|
|
6503
6694
|
let paddingLength = MIN_PADDING_LENGTH;
|
|
6504
6695
|
while ((1 + body.length + paddingLength + 4) % blockSize !== 0) {
|
|
6505
6696
|
paddingLength += 1;
|
|
6506
6697
|
}
|
|
6507
|
-
const padding = options.randomPadding === false ?
|
|
6698
|
+
const padding = options.randomPadding === false ? import_node_buffer12.Buffer.alloc(paddingLength) : (0, import_node_crypto6.randomBytes)(paddingLength);
|
|
6508
6699
|
const packetLength = 1 + body.length + paddingLength;
|
|
6509
|
-
const frame =
|
|
6700
|
+
const frame = import_node_buffer12.Buffer.alloc(4 + packetLength);
|
|
6510
6701
|
frame.writeUInt32BE(packetLength, 0);
|
|
6511
6702
|
frame.writeUInt8(paddingLength, 4);
|
|
6512
6703
|
body.copy(frame, 5);
|
|
@@ -6514,7 +6705,7 @@ function encodeSshTransportPacket(payload, options = {}) {
|
|
|
6514
6705
|
return frame;
|
|
6515
6706
|
}
|
|
6516
6707
|
function decodeSshTransportPacket(frame) {
|
|
6517
|
-
const bytes =
|
|
6708
|
+
const bytes = import_node_buffer12.Buffer.from(frame);
|
|
6518
6709
|
if (bytes.length < 4 + MIN_PACKET_LENGTH) {
|
|
6519
6710
|
throw new ParseError({
|
|
6520
6711
|
details: { length: bytes.length },
|
|
@@ -6568,12 +6759,20 @@ function decodeSshTransportPacket(frame) {
|
|
|
6568
6759
|
};
|
|
6569
6760
|
}
|
|
6570
6761
|
var SshTransportPacketFramer = class {
|
|
6571
|
-
pending =
|
|
6762
|
+
pending = import_node_buffer12.Buffer.alloc(0);
|
|
6572
6763
|
push(chunk) {
|
|
6573
|
-
this.pending =
|
|
6764
|
+
this.pending = import_node_buffer12.Buffer.concat([this.pending, import_node_buffer12.Buffer.from(chunk)]);
|
|
6574
6765
|
const packets = [];
|
|
6575
6766
|
while (this.pending.length >= 4) {
|
|
6576
6767
|
const packetLength = this.pending.readUInt32BE(0);
|
|
6768
|
+
if (packetLength > MAX_SSH_PACKET_LENGTH) {
|
|
6769
|
+
throw new ParseError({
|
|
6770
|
+
details: { maxPacketLength: MAX_SSH_PACKET_LENGTH, packetLength },
|
|
6771
|
+
message: "SSH transport packet length exceeds the maximum accepted size",
|
|
6772
|
+
protocol: "sftp",
|
|
6773
|
+
retryable: false
|
|
6774
|
+
});
|
|
6775
|
+
}
|
|
6577
6776
|
const frameLength = 4 + packetLength;
|
|
6578
6777
|
if (this.pending.length < frameLength) {
|
|
6579
6778
|
break;
|
|
@@ -6589,8 +6788,8 @@ var SshTransportPacketFramer = class {
|
|
|
6589
6788
|
}
|
|
6590
6789
|
/** Returns and clears any bytes buffered but not yet part of a complete packet. */
|
|
6591
6790
|
takeRemainingBytes() {
|
|
6592
|
-
const remaining =
|
|
6593
|
-
this.pending =
|
|
6791
|
+
const remaining = import_node_buffer12.Buffer.from(this.pending);
|
|
6792
|
+
this.pending = import_node_buffer12.Buffer.alloc(0);
|
|
6594
6793
|
return remaining;
|
|
6595
6794
|
}
|
|
6596
6795
|
};
|
|
@@ -6607,10 +6806,10 @@ function normalizeBlockSize(blockSize) {
|
|
|
6607
6806
|
}
|
|
6608
6807
|
|
|
6609
6808
|
// src/protocols/ssh/transport/SshHostKeyVerification.ts
|
|
6610
|
-
var
|
|
6809
|
+
var import_node_buffer13 = require("buffer");
|
|
6611
6810
|
var import_node_crypto7 = require("crypto");
|
|
6612
6811
|
var ED25519_RAW_KEY_LENGTH2 = 32;
|
|
6613
|
-
var ED25519_SPKI_PREFIX =
|
|
6812
|
+
var ED25519_SPKI_PREFIX = import_node_buffer13.Buffer.from("302a300506032b6570032100", "hex");
|
|
6614
6813
|
function verifySshHostKeySignature(input) {
|
|
6615
6814
|
const { algorithmName, publicKey } = parseHostKey(input.hostKeyBlob);
|
|
6616
6815
|
const { signatureAlgorithm, signatureBytes } = parseSignatureBlob(input.signatureBlob);
|
|
@@ -6623,9 +6822,9 @@ function verifySshHostKeySignature(input) {
|
|
|
6623
6822
|
});
|
|
6624
6823
|
}
|
|
6625
6824
|
const verified = verifySignature({
|
|
6626
|
-
data:
|
|
6825
|
+
data: import_node_buffer13.Buffer.from(input.exchangeHash),
|
|
6627
6826
|
publicKey,
|
|
6628
|
-
signature:
|
|
6827
|
+
signature: import_node_buffer13.Buffer.from(signatureBytes),
|
|
6629
6828
|
signatureAlgorithm
|
|
6630
6829
|
});
|
|
6631
6830
|
if (!verified) {
|
|
@@ -6654,7 +6853,7 @@ function parseHostKey(blob) {
|
|
|
6654
6853
|
retryable: false
|
|
6655
6854
|
});
|
|
6656
6855
|
}
|
|
6657
|
-
const spki =
|
|
6856
|
+
const spki = import_node_buffer13.Buffer.concat([ED25519_SPKI_PREFIX, raw]);
|
|
6658
6857
|
return {
|
|
6659
6858
|
algorithmName,
|
|
6660
6859
|
publicKey: (0, import_node_crypto7.createPublicKey)({ format: "der", key: spki, type: "spki" })
|
|
@@ -6760,37 +6959,37 @@ function verifySignature(input) {
|
|
|
6760
6959
|
function rsaPublicKeyFromComponents(e, n) {
|
|
6761
6960
|
const eDer = encodeAsn1Integer(e);
|
|
6762
6961
|
const nDer = encodeAsn1Integer(n);
|
|
6763
|
-
const rsaPublicKeyDer = encodeAsn1Sequence(
|
|
6764
|
-
const bitStringContent =
|
|
6765
|
-
const bitString =
|
|
6766
|
-
|
|
6962
|
+
const rsaPublicKeyDer = encodeAsn1Sequence(import_node_buffer13.Buffer.concat([nDer, eDer]));
|
|
6963
|
+
const bitStringContent = import_node_buffer13.Buffer.concat([import_node_buffer13.Buffer.from([0]), rsaPublicKeyDer]);
|
|
6964
|
+
const bitString = import_node_buffer13.Buffer.concat([
|
|
6965
|
+
import_node_buffer13.Buffer.from([3]),
|
|
6767
6966
|
encodeAsn1Length(bitStringContent.length),
|
|
6768
6967
|
bitStringContent
|
|
6769
6968
|
]);
|
|
6770
|
-
const algoId =
|
|
6771
|
-
const spki = encodeAsn1Sequence(
|
|
6969
|
+
const algoId = import_node_buffer13.Buffer.from("300d06092a864886f70d010101 0500".replace(/\s+/g, ""), "hex");
|
|
6970
|
+
const spki = encodeAsn1Sequence(import_node_buffer13.Buffer.concat([algoId, bitString]));
|
|
6772
6971
|
return (0, import_node_crypto7.createPublicKey)({ format: "der", key: spki, type: "spki" });
|
|
6773
6972
|
}
|
|
6774
6973
|
function encodeAsn1Integer(value) {
|
|
6775
6974
|
let body = value;
|
|
6776
6975
|
while (body.length > 1 && body[0] === 0) body = body.subarray(1);
|
|
6777
6976
|
if (body.length > 0 && (body[0] & 128) !== 0) {
|
|
6778
|
-
body =
|
|
6977
|
+
body = import_node_buffer13.Buffer.concat([import_node_buffer13.Buffer.from([0]), body]);
|
|
6779
6978
|
}
|
|
6780
|
-
return
|
|
6979
|
+
return import_node_buffer13.Buffer.concat([import_node_buffer13.Buffer.from([2]), encodeAsn1Length(body.length), body]);
|
|
6781
6980
|
}
|
|
6782
6981
|
function encodeAsn1Sequence(content) {
|
|
6783
|
-
return
|
|
6982
|
+
return import_node_buffer13.Buffer.concat([import_node_buffer13.Buffer.from([48]), encodeAsn1Length(content.length), content]);
|
|
6784
6983
|
}
|
|
6785
6984
|
function encodeAsn1Length(length) {
|
|
6786
|
-
if (length < 128) return
|
|
6985
|
+
if (length < 128) return import_node_buffer13.Buffer.from([length]);
|
|
6787
6986
|
const bytes = [];
|
|
6788
6987
|
let n = length;
|
|
6789
6988
|
while (n > 0) {
|
|
6790
6989
|
bytes.unshift(n & 255);
|
|
6791
6990
|
n >>>= 8;
|
|
6792
6991
|
}
|
|
6793
|
-
return
|
|
6992
|
+
return import_node_buffer13.Buffer.from([128 | bytes.length, ...bytes]);
|
|
6794
6993
|
}
|
|
6795
6994
|
var ECDSA_OID_BY_CURVE = {
|
|
6796
6995
|
nistp256: "06082a8648ce3d030107",
|
|
@@ -6811,15 +7010,15 @@ function ecdsaPublicKeyFromPoint(curveIdentifier, point) {
|
|
|
6811
7010
|
retryable: false
|
|
6812
7011
|
});
|
|
6813
7012
|
}
|
|
6814
|
-
const algoIdContent =
|
|
7013
|
+
const algoIdContent = import_node_buffer13.Buffer.from(ECDSA_ALGORITHM_OID_HEX + oidHex, "hex");
|
|
6815
7014
|
const algoId = encodeAsn1Sequence(algoIdContent);
|
|
6816
|
-
const bitStringContent =
|
|
6817
|
-
const bitString =
|
|
6818
|
-
|
|
7015
|
+
const bitStringContent = import_node_buffer13.Buffer.concat([import_node_buffer13.Buffer.from([0]), point]);
|
|
7016
|
+
const bitString = import_node_buffer13.Buffer.concat([
|
|
7017
|
+
import_node_buffer13.Buffer.from([3]),
|
|
6819
7018
|
encodeAsn1Length(bitStringContent.length),
|
|
6820
7019
|
bitStringContent
|
|
6821
7020
|
]);
|
|
6822
|
-
const spki = encodeAsn1Sequence(
|
|
7021
|
+
const spki = encodeAsn1Sequence(import_node_buffer13.Buffer.concat([algoId, bitString]));
|
|
6823
7022
|
return (0, import_node_crypto7.createPublicKey)({ format: "der", key: spki, type: "spki" });
|
|
6824
7023
|
}
|
|
6825
7024
|
function sshEcdsaSignatureToDer(sshSignature) {
|
|
@@ -6828,7 +7027,7 @@ function sshEcdsaSignatureToDer(sshSignature) {
|
|
|
6828
7027
|
const s = reader.readMpint();
|
|
6829
7028
|
const rDer = encodeAsn1Integer(r);
|
|
6830
7029
|
const sDer = encodeAsn1Integer(s);
|
|
6831
|
-
return encodeAsn1Sequence(
|
|
7030
|
+
return encodeAsn1Sequence(import_node_buffer13.Buffer.concat([rDer, sDer]));
|
|
6832
7031
|
}
|
|
6833
7032
|
|
|
6834
7033
|
// src/protocols/ssh/transport/SshTransportHandshake.ts
|
|
@@ -6860,7 +7059,7 @@ var SshTransportHandshake = class {
|
|
|
6860
7059
|
serverIdentification;
|
|
6861
7060
|
/** Creates the first outbound bytes (client identification line). */
|
|
6862
7061
|
createInitialClientBytes() {
|
|
6863
|
-
return
|
|
7062
|
+
return import_node_buffer14.Buffer.from(`${this.clientIdentificationLine}\r
|
|
6864
7063
|
`, "ascii");
|
|
6865
7064
|
}
|
|
6866
7065
|
/**
|
|
@@ -6884,7 +7083,7 @@ var SshTransportHandshake = class {
|
|
|
6884
7083
|
}
|
|
6885
7084
|
return { outbound };
|
|
6886
7085
|
}
|
|
6887
|
-
return this.pushServerBytesWithPhase(outbound,
|
|
7086
|
+
return this.pushServerBytesWithPhase(outbound, import_node_buffer14.Buffer.from(chunk));
|
|
6888
7087
|
}
|
|
6889
7088
|
getServerBannerLines() {
|
|
6890
7089
|
return this.identificationLines;
|
|
@@ -6938,12 +7137,12 @@ var SshTransportHandshake = class {
|
|
|
6938
7137
|
clientKexInitPayload: this.clientKexInitPayload,
|
|
6939
7138
|
clientPublicKey: this.pendingCurve25519.publicKey,
|
|
6940
7139
|
negotiatedAlgorithms,
|
|
6941
|
-
serverHostKey:
|
|
7140
|
+
serverHostKey: import_node_buffer14.Buffer.alloc(0),
|
|
6942
7141
|
serverIdentification: (this.serverIdentification ?? missingServerIdentificationError()).raw,
|
|
6943
|
-
serverKexInitPayload:
|
|
6944
|
-
serverPublicKey:
|
|
6945
|
-
serverSignature:
|
|
6946
|
-
sharedSecret:
|
|
7142
|
+
serverKexInitPayload: import_node_buffer14.Buffer.from(packet.payload),
|
|
7143
|
+
serverPublicKey: import_node_buffer14.Buffer.alloc(0),
|
|
7144
|
+
serverSignature: import_node_buffer14.Buffer.alloc(0),
|
|
7145
|
+
sharedSecret: import_node_buffer14.Buffer.alloc(0)
|
|
6947
7146
|
};
|
|
6948
7147
|
continue;
|
|
6949
7148
|
}
|
|
@@ -7051,24 +7250,54 @@ var SshTransportHandshake = class {
|
|
|
7051
7250
|
return { outbound };
|
|
7052
7251
|
}
|
|
7053
7252
|
};
|
|
7253
|
+
var MAX_IDENTIFICATION_LINE_BYTES = 8192;
|
|
7254
|
+
var MAX_PRE_IDENTIFICATION_LINES = 1024;
|
|
7054
7255
|
var SshIdentificationAccumulator = class {
|
|
7055
|
-
pending =
|
|
7256
|
+
pending = import_node_buffer14.Buffer.alloc(0);
|
|
7257
|
+
bannerLineCount = 0;
|
|
7056
7258
|
push(chunk) {
|
|
7057
|
-
this.pending =
|
|
7259
|
+
this.pending = import_node_buffer14.Buffer.concat([this.pending, import_node_buffer14.Buffer.from(chunk)]);
|
|
7058
7260
|
const bannerLines = [];
|
|
7059
7261
|
while (true) {
|
|
7060
7262
|
const lfIndex = this.pending.indexOf(10);
|
|
7061
|
-
if (lfIndex < 0)
|
|
7263
|
+
if (lfIndex < 0) {
|
|
7264
|
+
if (this.pending.length > MAX_IDENTIFICATION_LINE_BYTES) {
|
|
7265
|
+
throw new ProtocolError({
|
|
7266
|
+
details: { limitBytes: MAX_IDENTIFICATION_LINE_BYTES },
|
|
7267
|
+
message: "SSH identification line exceeds the maximum accepted length",
|
|
7268
|
+
protocol: "sftp",
|
|
7269
|
+
retryable: false
|
|
7270
|
+
});
|
|
7271
|
+
}
|
|
7272
|
+
break;
|
|
7273
|
+
}
|
|
7274
|
+
if (lfIndex > MAX_IDENTIFICATION_LINE_BYTES) {
|
|
7275
|
+
throw new ProtocolError({
|
|
7276
|
+
details: { limitBytes: MAX_IDENTIFICATION_LINE_BYTES },
|
|
7277
|
+
message: "SSH identification line exceeds the maximum accepted length",
|
|
7278
|
+
protocol: "sftp",
|
|
7279
|
+
retryable: false
|
|
7280
|
+
});
|
|
7281
|
+
}
|
|
7062
7282
|
const lineText = trimLineEndings(this.pending.subarray(0, lfIndex + 1).toString("ascii"));
|
|
7063
|
-
const remainder =
|
|
7283
|
+
const remainder = import_node_buffer14.Buffer.from(this.pending.subarray(lfIndex + 1));
|
|
7064
7284
|
this.pending = remainder;
|
|
7065
7285
|
if (lineText.startsWith("SSH-")) {
|
|
7066
|
-
this.pending =
|
|
7286
|
+
this.pending = import_node_buffer14.Buffer.alloc(0);
|
|
7067
7287
|
return { bannerLines, identLine: lineText, remainder };
|
|
7068
7288
|
}
|
|
7289
|
+
this.bannerLineCount += 1;
|
|
7290
|
+
if (this.bannerLineCount > MAX_PRE_IDENTIFICATION_LINES) {
|
|
7291
|
+
throw new ProtocolError({
|
|
7292
|
+
details: { limitLines: MAX_PRE_IDENTIFICATION_LINES },
|
|
7293
|
+
message: "SSH server sent too many pre-identification banner lines",
|
|
7294
|
+
protocol: "sftp",
|
|
7295
|
+
retryable: false
|
|
7296
|
+
});
|
|
7297
|
+
}
|
|
7069
7298
|
bannerLines.push(lineText);
|
|
7070
7299
|
}
|
|
7071
|
-
return { bannerLines, remainder:
|
|
7300
|
+
return { bannerLines, remainder: import_node_buffer14.Buffer.alloc(0) };
|
|
7072
7301
|
}
|
|
7073
7302
|
};
|
|
7074
7303
|
function trimLineEndings(value) {
|
|
@@ -7096,7 +7325,7 @@ function missingPendingKeyExchangeError() {
|
|
|
7096
7325
|
}
|
|
7097
7326
|
|
|
7098
7327
|
// src/protocols/ssh/transport/SshTransportProtection.ts
|
|
7099
|
-
var
|
|
7328
|
+
var import_node_buffer15 = require("buffer");
|
|
7100
7329
|
var import_node_crypto8 = require("crypto");
|
|
7101
7330
|
function createSshTransportProtectionContext(input) {
|
|
7102
7331
|
return {
|
|
@@ -7153,7 +7382,7 @@ var SshTransportPacketProtector = class {
|
|
|
7153
7382
|
);
|
|
7154
7383
|
const encrypted = this.cipher === void 0 ? clearPacket : this.cipher.update(clearPacket);
|
|
7155
7384
|
this.sequenceNumber = this.sequenceNumber + 1 >>> 0;
|
|
7156
|
-
return
|
|
7385
|
+
return import_node_buffer15.Buffer.concat([encrypted, mac]);
|
|
7157
7386
|
}
|
|
7158
7387
|
};
|
|
7159
7388
|
var SshTransportPacketUnprotector = class {
|
|
@@ -7179,7 +7408,7 @@ var SshTransportPacketUnprotector = class {
|
|
|
7179
7408
|
sequenceNumber;
|
|
7180
7409
|
// Streaming framing state for pushBytes()
|
|
7181
7410
|
framePartialDecrypted;
|
|
7182
|
-
framePendingRaw =
|
|
7411
|
+
framePendingRaw = import_node_buffer15.Buffer.alloc(0);
|
|
7183
7412
|
frameRemainingNeeded;
|
|
7184
7413
|
getSequenceNumber() {
|
|
7185
7414
|
return this.sequenceNumber;
|
|
@@ -7189,15 +7418,23 @@ var SshTransportPacketUnprotector = class {
|
|
|
7189
7418
|
* Maintains internal framing state across calls - pass each `data` event chunk directly.
|
|
7190
7419
|
*/
|
|
7191
7420
|
pushBytes(chunk) {
|
|
7192
|
-
this.framePendingRaw =
|
|
7421
|
+
this.framePendingRaw = import_node_buffer15.Buffer.concat([this.framePendingRaw, chunk]);
|
|
7193
7422
|
const results = [];
|
|
7194
7423
|
while (true) {
|
|
7195
7424
|
if (this.framePartialDecrypted === void 0) {
|
|
7196
7425
|
if (this.framePendingRaw.length < this.blockLength) break;
|
|
7197
7426
|
const firstBlock = this.framePendingRaw.subarray(0, this.blockLength);
|
|
7198
|
-
this.framePendingRaw =
|
|
7199
|
-
this.framePartialDecrypted = this.decipher ?
|
|
7427
|
+
this.framePendingRaw = import_node_buffer15.Buffer.from(this.framePendingRaw.subarray(this.blockLength));
|
|
7428
|
+
this.framePartialDecrypted = this.decipher ? import_node_buffer15.Buffer.from(this.decipher.update(firstBlock)) : import_node_buffer15.Buffer.from(firstBlock);
|
|
7200
7429
|
const packetLength = this.framePartialDecrypted.readUInt32BE(0);
|
|
7430
|
+
if (packetLength > MAX_SSH_PACKET_LENGTH) {
|
|
7431
|
+
throw new ProtocolError({
|
|
7432
|
+
details: { maxPacketLength: MAX_SSH_PACKET_LENGTH, packetLength },
|
|
7433
|
+
message: "SSH encrypted packet length exceeds the maximum accepted size",
|
|
7434
|
+
protocol: "sftp",
|
|
7435
|
+
retryable: false
|
|
7436
|
+
});
|
|
7437
|
+
}
|
|
7201
7438
|
const remaining = 4 + packetLength - this.blockLength + this.macLength;
|
|
7202
7439
|
if (remaining < 0) {
|
|
7203
7440
|
throw new ProtocolError({
|
|
@@ -7213,9 +7450,9 @@ var SshTransportPacketUnprotector = class {
|
|
|
7213
7450
|
if (this.framePendingRaw.length < needed) break;
|
|
7214
7451
|
const encryptedRest = this.framePendingRaw.subarray(0, needed - this.macLength);
|
|
7215
7452
|
const receivedMac = this.framePendingRaw.subarray(needed - this.macLength, needed);
|
|
7216
|
-
this.framePendingRaw =
|
|
7217
|
-
const decryptedRest = encryptedRest.length > 0 ? this.decipher ?
|
|
7218
|
-
const clearPacket =
|
|
7453
|
+
this.framePendingRaw = import_node_buffer15.Buffer.from(this.framePendingRaw.subarray(needed));
|
|
7454
|
+
const decryptedRest = encryptedRest.length > 0 ? this.decipher ? import_node_buffer15.Buffer.from(this.decipher.update(encryptedRest)) : import_node_buffer15.Buffer.from(encryptedRest) : import_node_buffer15.Buffer.alloc(0);
|
|
7455
|
+
const clearPacket = import_node_buffer15.Buffer.concat([this.framePartialDecrypted, decryptedRest]);
|
|
7219
7456
|
const expectedMac = computeMac(
|
|
7220
7457
|
this.macAlgorithm,
|
|
7221
7458
|
this.options.keys.macKey,
|
|
@@ -7238,7 +7475,7 @@ var SshTransportPacketUnprotector = class {
|
|
|
7238
7475
|
return results;
|
|
7239
7476
|
}
|
|
7240
7477
|
unprotectPayload(packet) {
|
|
7241
|
-
const frame =
|
|
7478
|
+
const frame = import_node_buffer15.Buffer.from(packet);
|
|
7242
7479
|
if (frame.length < this.macLength) {
|
|
7243
7480
|
throw new ProtocolError({
|
|
7244
7481
|
details: { length: frame.length, macLength: this.macLength },
|
|
@@ -7379,10 +7616,10 @@ function resolveMacLength(encryptionAlgorithm, macAlgorithm) {
|
|
|
7379
7616
|
}
|
|
7380
7617
|
function computeMac(macAlgorithm, macKey, sequence, packet, macLength) {
|
|
7381
7618
|
if (macLength === 0) {
|
|
7382
|
-
return
|
|
7619
|
+
return import_node_buffer15.Buffer.alloc(0);
|
|
7383
7620
|
}
|
|
7384
7621
|
const hashName = macAlgorithm === "hmac-sha2-512" ? "sha512" : "sha256";
|
|
7385
|
-
const sequenceBuffer =
|
|
7622
|
+
const sequenceBuffer = import_node_buffer15.Buffer.alloc(4);
|
|
7386
7623
|
sequenceBuffer.writeUInt32BE(sequence >>> 0, 0);
|
|
7387
7624
|
return (0, import_node_crypto8.createHmac)(hashName, macKey).update(sequenceBuffer).update(packet).digest().subarray(0, macLength);
|
|
7388
7625
|
}
|
|
@@ -7589,7 +7826,7 @@ var SshTransportConnection = class {
|
|
|
7589
7826
|
*/
|
|
7590
7827
|
sendPayload(payload) {
|
|
7591
7828
|
this.assertConnected();
|
|
7592
|
-
const frame = this.protector.protectPayload(
|
|
7829
|
+
const frame = this.protector.protectPayload(import_node_buffer16.Buffer.from(payload));
|
|
7593
7830
|
this.socket.write(frame);
|
|
7594
7831
|
this.resetKeepaliveTimer();
|
|
7595
7832
|
}
|
|
@@ -7747,7 +7984,7 @@ function parseDisconnectPayload(payload) {
|
|
|
7747
7984
|
}
|
|
7748
7985
|
|
|
7749
7986
|
// src/protocols/sftp/v3/SftpSession.ts
|
|
7750
|
-
var
|
|
7987
|
+
var import_node_buffer18 = require("buffer");
|
|
7751
7988
|
|
|
7752
7989
|
// src/protocols/sftp/v3/SftpAttributes.ts
|
|
7753
7990
|
var SFTP_ATTR_FLAG = {
|
|
@@ -7820,7 +8057,8 @@ function decodeSftpAttributesFromReader(reader) {
|
|
|
7820
8057
|
}
|
|
7821
8058
|
|
|
7822
8059
|
// src/protocols/sftp/v3/SftpPacket.ts
|
|
7823
|
-
var
|
|
8060
|
+
var import_node_buffer17 = require("buffer");
|
|
8061
|
+
var MAX_SFTP_PACKET_LENGTH = 256 * 1024;
|
|
7824
8062
|
var SFTP_PACKET_TYPE = {
|
|
7825
8063
|
ATTRS: 105,
|
|
7826
8064
|
CLOSE: 4,
|
|
@@ -7851,7 +8089,7 @@ var SFTP_PACKET_TYPE = {
|
|
|
7851
8089
|
WRITE: 6
|
|
7852
8090
|
};
|
|
7853
8091
|
function decodeSftpPacket(frame) {
|
|
7854
|
-
const bytes =
|
|
8092
|
+
const bytes = import_node_buffer17.Buffer.from(frame);
|
|
7855
8093
|
if (bytes.length < 5) {
|
|
7856
8094
|
throw new ParseError({
|
|
7857
8095
|
details: { length: bytes.length },
|
|
@@ -7874,12 +8112,19 @@ function decodeSftpPacket(frame) {
|
|
|
7874
8112
|
};
|
|
7875
8113
|
}
|
|
7876
8114
|
var SftpPacketFramer = class {
|
|
7877
|
-
pending =
|
|
8115
|
+
pending = import_node_buffer17.Buffer.alloc(0);
|
|
7878
8116
|
push(chunk) {
|
|
7879
|
-
this.pending =
|
|
8117
|
+
this.pending = import_node_buffer17.Buffer.concat([this.pending, import_node_buffer17.Buffer.from(chunk)]);
|
|
7880
8118
|
const packets = [];
|
|
7881
8119
|
while (this.pending.length >= 4) {
|
|
7882
8120
|
const bodyLength = this.pending.readUInt32BE(0);
|
|
8121
|
+
if (bodyLength > MAX_SFTP_PACKET_LENGTH) {
|
|
8122
|
+
throw new ParseError({
|
|
8123
|
+
details: { bodyLength, maxPacketLength: MAX_SFTP_PACKET_LENGTH },
|
|
8124
|
+
message: "SFTP packet length exceeds the maximum accepted size",
|
|
8125
|
+
retryable: false
|
|
8126
|
+
});
|
|
8127
|
+
}
|
|
7883
8128
|
const frameLength = 4 + bodyLength;
|
|
7884
8129
|
if (this.pending.length < frameLength) {
|
|
7885
8130
|
break;
|
|
@@ -8334,7 +8579,7 @@ var SftpSession = class {
|
|
|
8334
8579
|
* serializes concurrent calls so byte ordering is preserved.
|
|
8335
8580
|
*/
|
|
8336
8581
|
sendRaw(encodedMessage, requestId) {
|
|
8337
|
-
const frame =
|
|
8582
|
+
const frame = import_node_buffer18.Buffer.alloc(4 + encodedMessage.length);
|
|
8338
8583
|
frame.writeUInt32BE(encodedMessage.length, 0);
|
|
8339
8584
|
encodedMessage.copy(frame, 4);
|
|
8340
8585
|
this.channel.sendData(frame).catch((err) => {
|
|
@@ -8481,7 +8726,7 @@ function buildNativeSftpCapabilities(maxConcurrency) {
|
|
|
8481
8726
|
var NATIVE_SFTP_PROVIDER_CAPABILITIES = buildNativeSftpCapabilities(
|
|
8482
8727
|
NATIVE_SFTP_DEFAULT_MAX_CONCURRENCY
|
|
8483
8728
|
);
|
|
8484
|
-
function
|
|
8729
|
+
function createSftpProviderFactory(options = {}) {
|
|
8485
8730
|
validateNativeSftpOptions(options);
|
|
8486
8731
|
const capabilities = buildNativeSftpCapabilities(
|
|
8487
8732
|
options.maxConcurrency ?? NATIVE_SFTP_DEFAULT_MAX_CONCURRENCY
|
|
@@ -8877,9 +9122,9 @@ function buildNativePublickeyCredential(profile, username) {
|
|
|
8877
9122
|
const passphrase = profile.ssh?.passphrase;
|
|
8878
9123
|
try {
|
|
8879
9124
|
const privateKey = (0, import_node_crypto9.createPrivateKey)({
|
|
8880
|
-
key:
|
|
9125
|
+
key: import_node_buffer19.Buffer.isBuffer(keyMaterial) ? keyMaterial : keyMaterial,
|
|
8881
9126
|
...passphrase === void 0 ? {} : {
|
|
8882
|
-
passphrase:
|
|
9127
|
+
passphrase: import_node_buffer19.Buffer.isBuffer(passphrase) ? passphrase : passphrase
|
|
8883
9128
|
}
|
|
8884
9129
|
});
|
|
8885
9130
|
return buildPublickeyCredential({ privateKey, username });
|
|
@@ -9006,12 +9251,12 @@ function normalizeNativeHostKeyPins(value) {
|
|
|
9006
9251
|
const trimmed = pin.trim();
|
|
9007
9252
|
const hex = trimmed.replace(/:/g, "");
|
|
9008
9253
|
if (hex.length === 64 && /^[a-f0-9]+$/i.test(hex)) {
|
|
9009
|
-
normalized.add(
|
|
9254
|
+
normalized.add(import_node_buffer19.Buffer.from(hex, "hex").toString("base64").replace(/=+$/g, ""));
|
|
9010
9255
|
continue;
|
|
9011
9256
|
}
|
|
9012
9257
|
const bare = trimmed.startsWith("SHA256:") ? trimmed.slice("SHA256:".length) : trimmed;
|
|
9013
9258
|
const padded = bare.length % 4 === 0 ? bare : `${bare}${"=".repeat(4 - bare.length % 4)}`;
|
|
9014
|
-
normalized.add(
|
|
9259
|
+
normalized.add(import_node_buffer19.Buffer.from(padded, "base64").toString("base64").replace(/=+$/g, ""));
|
|
9015
9260
|
}
|
|
9016
9261
|
return normalized;
|
|
9017
9262
|
}
|
|
@@ -9021,7 +9266,7 @@ function parseNativeKnownHosts(source) {
|
|
|
9021
9266
|
const entries = [];
|
|
9022
9267
|
let sawNonEmpty = false;
|
|
9023
9268
|
for (const value of sources) {
|
|
9024
|
-
const text =
|
|
9269
|
+
const text = import_node_buffer19.Buffer.isBuffer(value) ? value.toString("utf8") : String(value);
|
|
9025
9270
|
if (text.length === 0) continue;
|
|
9026
9271
|
sawNonEmpty = true;
|
|
9027
9272
|
entries.push(...parseKnownHosts(text));
|
|
@@ -9094,7 +9339,7 @@ function requireNativeSftpUsername(profile) {
|
|
|
9094
9339
|
}
|
|
9095
9340
|
function resolveNativeSftpTextSecret(value) {
|
|
9096
9341
|
if (value === void 0) return void 0;
|
|
9097
|
-
const text =
|
|
9342
|
+
const text = import_node_buffer19.Buffer.isBuffer(value) ? value.toString("utf8") : value;
|
|
9098
9343
|
if (text.length === 0) return void 0;
|
|
9099
9344
|
return text;
|
|
9100
9345
|
}
|
|
@@ -9169,9 +9414,9 @@ function validateNativeSftpOptions(options) {
|
|
|
9169
9414
|
copyBetween,
|
|
9170
9415
|
createAtomicDeployPlan,
|
|
9171
9416
|
createBandwidthThrottle,
|
|
9417
|
+
createDefaultRetryPolicy,
|
|
9172
9418
|
createLocalProviderFactory,
|
|
9173
9419
|
createMemoryProviderFactory,
|
|
9174
|
-
createNativeSftpProviderFactory,
|
|
9175
9420
|
createOAuthTokenSecretSource,
|
|
9176
9421
|
createPooledTransferClient,
|
|
9177
9422
|
createProgressEvent,
|
|
@@ -9206,8 +9451,10 @@ function validateNativeSftpOptions(options) {
|
|
|
9206
9451
|
parseRemoteManifest,
|
|
9207
9452
|
redactCommand,
|
|
9208
9453
|
redactConnectionProfile,
|
|
9454
|
+
redactErrorForLogging,
|
|
9209
9455
|
redactObject,
|
|
9210
9456
|
redactSecretSource,
|
|
9457
|
+
redactUrlForLogging,
|
|
9211
9458
|
redactValue,
|
|
9212
9459
|
resolveConnectionProfileSecrets,
|
|
9213
9460
|
resolveOpenSshHost,
|