@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/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 logger.
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
- abortScope.signal,
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, abortScope.signal, job);
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 || error instanceof TimeoutError) {
1271
+ if (error instanceof AbortError || abortScope.signal?.aborted === true) {
1193
1272
  throw error;
1194
1273
  }
1195
- const retryInput = { attempt: attemptNumber, error, job };
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 (options.retry !== void 0) execute.retry = options.retry;
1674
+ if (retry !== void 0) execute.retry = retry;
1492
1675
  if (options.onProgress !== void 0) execute.onProgress = options.onProgress;
1493
- if (options.timeout !== void 0) execute.timeout = options.timeout;
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 LF characters are not allowed`,
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, serverPasswordEncoding);
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, passwordEncoding) {
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
- if (rawPass !== void 0 && rawPass !== "") {
3376
- if (passwordEncoding === "base64") {
3377
- password = Buffer6.from(rawPass, "base64").toString("utf8");
3378
- } else {
3379
- password = rawPass;
3380
- }
3381
- if (password !== void 0 && password !== "") profile.password = { value: password };
3382
- }
3383
- const site = { name, profile };
3384
- if (password !== void 0) site.password = password;
3515
+ const hasStoredPassword = rawPass !== void 0 && rawPass !== "";
3516
+ const site = { hasStoredPassword, name, profile };
3385
3517
  const logonText = fields["Logontype"];
3386
3518
  if (logonText !== void 0) {
3387
3519
  const logonType = Number.parseInt(logonText.trim(), 10);
@@ -3624,6 +3756,62 @@ function mapFtp550(details) {
3624
3756
  return new PermissionDeniedError(details);
3625
3757
  }
3626
3758
 
3759
+ // src/transfers/createDefaultRetryPolicy.ts
3760
+ var DEFAULT_MAX_ATTEMPTS = 4;
3761
+ var DEFAULT_BASE_DELAY_MS = 250;
3762
+ var DEFAULT_MAX_DELAY_MS = 3e4;
3763
+ var DEFAULT_MAX_ELAPSED_MS = 3e5;
3764
+ function createDefaultRetryPolicy(options = {}) {
3765
+ const maxAttempts = normalizePositiveInteger(options.maxAttempts, DEFAULT_MAX_ATTEMPTS);
3766
+ const baseDelayMs = normalizeNonNegative(options.baseDelayMs, DEFAULT_BASE_DELAY_MS);
3767
+ const maxDelayMs = normalizeNonNegative(options.maxDelayMs, DEFAULT_MAX_DELAY_MS);
3768
+ const maxElapsedMs = normalizeNonNegative(options.maxElapsedMs, DEFAULT_MAX_ELAPSED_MS);
3769
+ const random = options.random ?? Math.random;
3770
+ return {
3771
+ getDelayMs(input) {
3772
+ const retryAfterMs = readRetryAfterMs(input.error);
3773
+ if (retryAfterMs !== void 0) {
3774
+ return retryAfterMs;
3775
+ }
3776
+ const exponentialMs = baseDelayMs * 2 ** (input.attempt - 1);
3777
+ const cappedMs = Math.min(maxDelayMs, exponentialMs);
3778
+ return Math.floor(random() * cappedMs);
3779
+ },
3780
+ maxAttempts,
3781
+ shouldRetry(input) {
3782
+ if (!(input.error instanceof ZeroTransferError) || !input.error.retryable) {
3783
+ return false;
3784
+ }
3785
+ if (input.elapsedMs >= maxElapsedMs) {
3786
+ return false;
3787
+ }
3788
+ const retryAfterMs = readRetryAfterMs(input.error);
3789
+ if (retryAfterMs !== void 0 && input.elapsedMs + retryAfterMs > maxElapsedMs) {
3790
+ return false;
3791
+ }
3792
+ return true;
3793
+ }
3794
+ };
3795
+ }
3796
+ function readRetryAfterMs(error) {
3797
+ if (!(error instanceof ZeroTransferError)) return void 0;
3798
+ const value = error.details?.["retryAfterMs"];
3799
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return void 0;
3800
+ return Math.floor(value);
3801
+ }
3802
+ function normalizePositiveInteger(value, fallback) {
3803
+ if (value === void 0 || !Number.isFinite(value) || value < 1) {
3804
+ return fallback;
3805
+ }
3806
+ return Math.floor(value);
3807
+ }
3808
+ function normalizeNonNegative(value, fallback) {
3809
+ if (value === void 0 || !Number.isFinite(value) || value < 0) {
3810
+ return fallback;
3811
+ }
3812
+ return Math.floor(value);
3813
+ }
3814
+
3627
3815
  // src/transfers/TransferPlan.ts
3628
3816
  function createTransferPlan(input) {
3629
3817
  const plan = {
@@ -3721,8 +3909,8 @@ var TransferQueue = class {
3721
3909
  this.concurrency = normalizeConcurrency(options.concurrency);
3722
3910
  this.defaultExecutor = options.executor;
3723
3911
  this.resolveExecutor = options.resolveExecutor;
3724
- this.retry = options.retry;
3725
- this.timeout = options.timeout;
3912
+ this.retry = options.retry ?? options.client?.defaults?.retry;
3913
+ this.timeout = options.timeout ?? options.client?.defaults?.timeout;
3726
3914
  this.bandwidthLimit = options.bandwidthLimit;
3727
3915
  this.onProgress = options.onProgress;
3728
3916
  this.onReceipt = options.onReceipt;
@@ -4799,12 +4987,12 @@ function isMainModule(importMetaUrl) {
4799
4987
  }
4800
4988
 
4801
4989
  // src/providers/native/sftp/NativeSftpProvider.ts
4802
- import { Buffer as Buffer21 } from "buffer";
4990
+ import { Buffer as Buffer20 } from "buffer";
4803
4991
  import { createHash as createHash3, createPrivateKey } from "crypto";
4804
4992
  import { createConnection } from "net";
4805
4993
 
4806
4994
  // src/protocols/ssh/binary/SshDataWriter.ts
4807
- import { Buffer as Buffer7 } from "buffer";
4995
+ import { Buffer as Buffer6 } from "buffer";
4808
4996
  var MAX_UINT32 = 4294967295;
4809
4997
  var MAX_UINT64 = (1n << 64n) - 1n;
4810
4998
  var SshDataWriter = class {
@@ -4812,7 +5000,7 @@ var SshDataWriter = class {
4812
5000
  length = 0;
4813
5001
  writeByte(value) {
4814
5002
  this.assertByte(value, "byte");
4815
- const chunk = Buffer7.alloc(1);
5003
+ const chunk = Buffer6.alloc(1);
4816
5004
  chunk.writeUInt8(value, 0);
4817
5005
  return this.push(chunk);
4818
5006
  }
@@ -4820,7 +5008,7 @@ var SshDataWriter = class {
4820
5008
  return this.writeByte(value ? 1 : 0);
4821
5009
  }
4822
5010
  writeBytes(value) {
4823
- return this.push(Buffer7.from(value));
5011
+ return this.push(Buffer6.from(value));
4824
5012
  }
4825
5013
  writeUint32(value) {
4826
5014
  if (!Number.isInteger(value) || value < 0 || value > MAX_UINT32) {
@@ -4830,7 +5018,7 @@ var SshDataWriter = class {
4830
5018
  retryable: false
4831
5019
  });
4832
5020
  }
4833
- const chunk = Buffer7.alloc(4);
5021
+ const chunk = Buffer6.alloc(4);
4834
5022
  chunk.writeUInt32BE(value, 0);
4835
5023
  return this.push(chunk);
4836
5024
  }
@@ -4842,12 +5030,12 @@ var SshDataWriter = class {
4842
5030
  retryable: false
4843
5031
  });
4844
5032
  }
4845
- const chunk = Buffer7.alloc(8);
5033
+ const chunk = Buffer6.alloc(8);
4846
5034
  chunk.writeBigUInt64BE(value, 0);
4847
5035
  return this.push(chunk);
4848
5036
  }
4849
5037
  writeString(value, encoding = "utf8") {
4850
- const payload = typeof value === "string" ? Buffer7.from(value, encoding) : Buffer7.from(value);
5038
+ const payload = typeof value === "string" ? Buffer6.from(value, encoding) : Buffer6.from(value);
4851
5039
  this.writeUint32(payload.length);
4852
5040
  return this.push(payload);
4853
5041
  }
@@ -4869,7 +5057,7 @@ var SshDataWriter = class {
4869
5057
  return this.writeString(values.join(","), "ascii");
4870
5058
  }
4871
5059
  toBuffer() {
4872
- return Buffer7.concat(this.chunks, this.length);
5060
+ return Buffer6.concat(this.chunks, this.length);
4873
5061
  }
4874
5062
  push(chunk) {
4875
5063
  this.chunks.push(chunk);
@@ -4887,23 +5075,23 @@ var SshDataWriter = class {
4887
5075
  }
4888
5076
  };
4889
5077
  function normalizePositiveMpint(value) {
4890
- const input = Buffer7.from(value);
5078
+ const input = Buffer6.from(value);
4891
5079
  let offset = 0;
4892
5080
  while (offset < input.length && input[offset] === 0) {
4893
5081
  offset += 1;
4894
5082
  }
4895
5083
  if (offset >= input.length) {
4896
- return Buffer7.alloc(0);
5084
+ return Buffer6.alloc(0);
4897
5085
  }
4898
5086
  const stripped = input.subarray(offset);
4899
5087
  if ((stripped[0] & 128) === 128) {
4900
- return Buffer7.concat([Buffer7.from([0]), stripped]);
5088
+ return Buffer6.concat([Buffer6.from([0]), stripped]);
4901
5089
  }
4902
5090
  return stripped;
4903
5091
  }
4904
5092
 
4905
5093
  // src/protocols/ssh/binary/SshDataReader.ts
4906
- import { Buffer as Buffer8 } from "buffer";
5094
+ import { Buffer as Buffer7 } from "buffer";
4907
5095
  var SshDataReader = class {
4908
5096
  constructor(source) {
4909
5097
  this.source = source;
@@ -4929,18 +5117,18 @@ var SshDataReader = class {
4929
5117
  this.ensureAvailable(length, "bytes");
4930
5118
  const data = this.source.subarray(this.offset, this.offset + length);
4931
5119
  this.offset += length;
4932
- return Buffer8.from(data);
5120
+ return Buffer7.from(data);
4933
5121
  }
4934
5122
  readUint32() {
4935
5123
  this.ensureAvailable(4, "uint32");
4936
- const buffer = Buffer8.from(this.source);
5124
+ const buffer = Buffer7.from(this.source);
4937
5125
  const value = buffer.readUInt32BE(this.offset);
4938
5126
  this.offset += 4;
4939
5127
  return value;
4940
5128
  }
4941
5129
  readUint64() {
4942
5130
  this.ensureAvailable(8, "uint64");
4943
- const buffer = Buffer8.from(this.source);
5131
+ const buffer = Buffer7.from(this.source);
4944
5132
  const value = buffer.readBigUInt64BE(this.offset);
4945
5133
  this.offset += 8;
4946
5134
  return value;
@@ -4950,7 +5138,7 @@ var SshDataReader = class {
4950
5138
  this.ensureAvailable(length, "string");
4951
5139
  const data = this.source.subarray(this.offset, this.offset + length);
4952
5140
  this.offset += length;
4953
- return Buffer8.from(data);
5141
+ return Buffer7.from(data);
4954
5142
  }
4955
5143
  readUtf8String() {
4956
5144
  return this.readString().toString("utf8");
@@ -5305,7 +5493,7 @@ function buildKiRequest(args) {
5305
5493
  }
5306
5494
 
5307
5495
  // src/protocols/ssh/auth/SshPublickeyCredentialBuilder.ts
5308
- import { Buffer as Buffer9 } from "buffer";
5496
+ import { Buffer as Buffer8 } from "buffer";
5309
5497
  import { createPublicKey, sign as cryptoSign } from "crypto";
5310
5498
  var ED25519_RAW_KEY_LENGTH = 32;
5311
5499
  var ED25519_SPKI_PREFIX_LENGTH = 12;
@@ -5323,7 +5511,7 @@ function buildPublickeyCredential(options) {
5323
5511
  return {
5324
5512
  algorithmName: "ssh-ed25519",
5325
5513
  publicKeyBlob,
5326
- sign: (data) => cryptoSign(null, Buffer9.from(data), privateKey),
5514
+ sign: (data) => cryptoSign(null, Buffer8.from(data), privateKey),
5327
5515
  type: "publickey",
5328
5516
  username
5329
5517
  };
@@ -5341,7 +5529,7 @@ function buildPublickeyCredential(options) {
5341
5529
  return {
5342
5530
  algorithmName,
5343
5531
  publicKeyBlob,
5344
- sign: (data) => cryptoSign(hash, Buffer9.from(data), privateKey),
5532
+ sign: (data) => cryptoSign(hash, Buffer8.from(data), privateKey),
5345
5533
  type: "publickey",
5346
5534
  username
5347
5535
  };
@@ -5354,7 +5542,7 @@ function buildPublickeyCredential(options) {
5354
5542
  }
5355
5543
  function base64UrlToMpint(value) {
5356
5544
  const padded = value.replace(/-/g, "+").replace(/_/g, "/");
5357
- const buffer = Buffer9.from(padded, "base64");
5545
+ const buffer = Buffer8.from(padded, "base64");
5358
5546
  return buffer;
5359
5547
  }
5360
5548
  function createInvalidKeyError(message) {
@@ -5480,7 +5668,7 @@ function encodeSshChannelClose(recipientChannel) {
5480
5668
  }
5481
5669
 
5482
5670
  // src/protocols/ssh/connection/SshSessionChannel.ts
5483
- import { Buffer as Buffer10 } from "buffer";
5671
+ import { Buffer as Buffer9 } from "buffer";
5484
5672
  var INITIAL_WINDOW_SIZE = 256 * 1024;
5485
5673
  var MAX_PACKET_SIZE = 32 * 1024;
5486
5674
  var WINDOW_REFILL_THRESHOLD = 64 * 1024;
@@ -5638,7 +5826,7 @@ var SshSessionChannel = class {
5638
5826
  this.remoteWindowRemaining,
5639
5827
  this.remoteMaxPacketSize
5640
5828
  );
5641
- const chunk = Buffer10.from(data.subarray(offset, offset + chunkSize));
5829
+ const chunk = Buffer9.from(data.subarray(offset, offset + chunkSize));
5642
5830
  this.transport.sendPayload(
5643
5831
  encodeSshChannelData({ data: chunk, recipientChannel: this.remoteChannelId })
5644
5832
  );
@@ -5900,10 +6088,10 @@ var SshConnectionManager = class {
5900
6088
  };
5901
6089
 
5902
6090
  // src/protocols/ssh/transport/SshTransportConnection.ts
5903
- import { Buffer as Buffer18 } from "buffer";
6091
+ import { Buffer as Buffer17 } from "buffer";
5904
6092
 
5905
6093
  // src/protocols/ssh/transport/SshTransportHandshake.ts
5906
- import { Buffer as Buffer16 } from "buffer";
6094
+ import { Buffer as Buffer15 } from "buffer";
5907
6095
 
5908
6096
  // src/protocols/ssh/transport/SshAlgorithmNegotiation.ts
5909
6097
  var DEFAULT_SSH_ALGORITHM_PREFERENCES = {
@@ -6065,12 +6253,12 @@ function parseSshIdentificationLine(line) {
6065
6253
  }
6066
6254
 
6067
6255
  // src/protocols/ssh/transport/SshKexInit.ts
6068
- import { Buffer as Buffer11 } from "buffer";
6256
+ import { Buffer as Buffer10 } from "buffer";
6069
6257
  import { randomBytes } from "crypto";
6070
6258
  var SSH_MSG_KEXINIT = 20;
6071
6259
  var KEXINIT_COOKIE_LENGTH = 16;
6072
6260
  function encodeSshKexInitMessage(options) {
6073
- const cookie = options.cookie === void 0 ? randomBytes(KEXINIT_COOKIE_LENGTH) : Buffer11.from(options.cookie);
6261
+ const cookie = options.cookie === void 0 ? randomBytes(KEXINIT_COOKIE_LENGTH) : Buffer10.from(options.cookie);
6074
6262
  if (cookie.length !== KEXINIT_COOKIE_LENGTH) {
6075
6263
  throw new ConfigurationError({
6076
6264
  details: { actualLength: cookie.length, expectedLength: KEXINIT_COOKIE_LENGTH },
@@ -6140,12 +6328,12 @@ function decodeSshKexInitMessage(payload) {
6140
6328
  }
6141
6329
 
6142
6330
  // src/protocols/ssh/transport/SshKexCurve25519.ts
6143
- import { Buffer as Buffer12 } from "buffer";
6331
+ import { Buffer as Buffer11 } from "buffer";
6144
6332
  import { createPublicKey as createPublicKey2, diffieHellman, generateKeyPairSync } from "crypto";
6145
6333
  var SSH_MSG_KEX_ECDH_INIT = 30;
6146
6334
  var SSH_MSG_KEX_ECDH_REPLY = 31;
6147
6335
  var X25519_PUBLIC_KEY_LENGTH = 32;
6148
- var X25519_SPKI_PREFIX = Buffer12.from("302a300506032b656e032100", "hex");
6336
+ var X25519_SPKI_PREFIX = Buffer11.from("302a300506032b656e032100", "hex");
6149
6337
  function createCurve25519Ephemeral() {
6150
6338
  const { privateKey, publicKey } = generateKeyPairSync("x25519");
6151
6339
  const encodedPublicKey = exportX25519PublicKeyRaw(publicKey);
@@ -6190,7 +6378,7 @@ function exportX25519PublicKeyRaw(publicKey) {
6190
6378
  }
6191
6379
  function importX25519PublicKeyRaw(raw) {
6192
6380
  const normalized = normalizeX25519PublicKey(raw, "server");
6193
- const der = Buffer12.concat([X25519_SPKI_PREFIX, normalized]);
6381
+ const der = Buffer11.concat([X25519_SPKI_PREFIX, normalized]);
6194
6382
  return createPublicKey2({
6195
6383
  format: "der",
6196
6384
  key: der,
@@ -6198,7 +6386,7 @@ function importX25519PublicKeyRaw(raw) {
6198
6386
  });
6199
6387
  }
6200
6388
  function normalizeX25519PublicKey(value, label) {
6201
- const key = Buffer12.from(value);
6389
+ const key = Buffer11.from(value);
6202
6390
  if (key.length !== X25519_PUBLIC_KEY_LENGTH) {
6203
6391
  throw new ConfigurationError({
6204
6392
  details: { keyLength: key.length, label },
@@ -6211,7 +6399,7 @@ function normalizeX25519PublicKey(value, label) {
6211
6399
  }
6212
6400
 
6213
6401
  // src/protocols/ssh/transport/SshKeyDerivation.ts
6214
- import { Buffer as Buffer13 } from "buffer";
6402
+ import { Buffer as Buffer12 } from "buffer";
6215
6403
  import { createHash } from "crypto";
6216
6404
  function deriveSshSessionKeys(input) {
6217
6405
  const hashAlgorithm = resolveKexHashAlgorithm(input.kexAlgorithm);
@@ -6233,7 +6421,7 @@ function deriveSshSessionKeys(input) {
6233
6421
  input.negotiatedAlgorithms.encryptionServerToClient,
6234
6422
  input.negotiatedAlgorithms.macServerToClient
6235
6423
  );
6236
- const sharedSecret = Buffer13.from(input.sharedSecret);
6424
+ const sharedSecret = Buffer12.from(input.sharedSecret);
6237
6425
  return {
6238
6426
  clientToServer: {
6239
6427
  encryptionKey: deriveMaterial(
@@ -6283,21 +6471,21 @@ function computeCurve25519ExchangeHash(input, hashAlgorithm) {
6283
6471
  }
6284
6472
  function deriveMaterial(sharedSecret, exchangeHash, sessionId, letter, length, hashAlgorithm) {
6285
6473
  if (length <= 0) {
6286
- return Buffer13.alloc(0);
6474
+ return Buffer12.alloc(0);
6287
6475
  }
6288
6476
  const result = [];
6289
6477
  const first2 = createHash(hashAlgorithm).update(
6290
6478
  new SshDataWriter().writeMpint(sharedSecret).writeBytes(exchangeHash).writeByte(letter.charCodeAt(0)).writeBytes(sessionId).toBuffer()
6291
6479
  ).digest();
6292
6480
  result.push(first2);
6293
- while (Buffer13.concat(result).length < length) {
6294
- const previous = Buffer13.concat(result);
6481
+ while (Buffer12.concat(result).length < length) {
6482
+ const previous = Buffer12.concat(result);
6295
6483
  const next = createHash(hashAlgorithm).update(
6296
6484
  new SshDataWriter().writeMpint(sharedSecret).writeBytes(exchangeHash).writeBytes(previous).toBuffer()
6297
6485
  ).digest();
6298
6486
  result.push(next);
6299
6487
  }
6300
- return Buffer13.concat(result).subarray(0, length);
6488
+ return Buffer12.concat(result).subarray(0, length);
6301
6489
  }
6302
6490
  function resolveKexHashAlgorithm(kexAlgorithm) {
6303
6491
  if (kexAlgorithm === "curve25519-sha256" || kexAlgorithm === "curve25519-sha256@libssh.org") {
@@ -6385,20 +6573,21 @@ function decodeSshNewKeysMessage(payload) {
6385
6573
  }
6386
6574
 
6387
6575
  // src/protocols/ssh/transport/SshTransportPacket.ts
6388
- import { Buffer as Buffer14 } from "buffer";
6576
+ import { Buffer as Buffer13 } from "buffer";
6389
6577
  import { randomBytes as randomBytes2 } from "crypto";
6390
6578
  var MIN_PADDING_LENGTH = 4;
6391
6579
  var MIN_PACKET_LENGTH = 1 + MIN_PADDING_LENGTH;
6580
+ var MAX_SSH_PACKET_LENGTH = 256 * 1024;
6392
6581
  function encodeSshTransportPacket(payload, options = {}) {
6393
- const body = Buffer14.from(payload);
6582
+ const body = Buffer13.from(payload);
6394
6583
  const blockSize = normalizeBlockSize(options.blockSize ?? 8);
6395
6584
  let paddingLength = MIN_PADDING_LENGTH;
6396
6585
  while ((1 + body.length + paddingLength + 4) % blockSize !== 0) {
6397
6586
  paddingLength += 1;
6398
6587
  }
6399
- const padding = options.randomPadding === false ? Buffer14.alloc(paddingLength) : randomBytes2(paddingLength);
6588
+ const padding = options.randomPadding === false ? Buffer13.alloc(paddingLength) : randomBytes2(paddingLength);
6400
6589
  const packetLength = 1 + body.length + paddingLength;
6401
- const frame = Buffer14.alloc(4 + packetLength);
6590
+ const frame = Buffer13.alloc(4 + packetLength);
6402
6591
  frame.writeUInt32BE(packetLength, 0);
6403
6592
  frame.writeUInt8(paddingLength, 4);
6404
6593
  body.copy(frame, 5);
@@ -6406,7 +6595,7 @@ function encodeSshTransportPacket(payload, options = {}) {
6406
6595
  return frame;
6407
6596
  }
6408
6597
  function decodeSshTransportPacket(frame) {
6409
- const bytes = Buffer14.from(frame);
6598
+ const bytes = Buffer13.from(frame);
6410
6599
  if (bytes.length < 4 + MIN_PACKET_LENGTH) {
6411
6600
  throw new ParseError({
6412
6601
  details: { length: bytes.length },
@@ -6460,12 +6649,20 @@ function decodeSshTransportPacket(frame) {
6460
6649
  };
6461
6650
  }
6462
6651
  var SshTransportPacketFramer = class {
6463
- pending = Buffer14.alloc(0);
6652
+ pending = Buffer13.alloc(0);
6464
6653
  push(chunk) {
6465
- this.pending = Buffer14.concat([this.pending, Buffer14.from(chunk)]);
6654
+ this.pending = Buffer13.concat([this.pending, Buffer13.from(chunk)]);
6466
6655
  const packets = [];
6467
6656
  while (this.pending.length >= 4) {
6468
6657
  const packetLength = this.pending.readUInt32BE(0);
6658
+ if (packetLength > MAX_SSH_PACKET_LENGTH) {
6659
+ throw new ParseError({
6660
+ details: { maxPacketLength: MAX_SSH_PACKET_LENGTH, packetLength },
6661
+ message: "SSH transport packet length exceeds the maximum accepted size",
6662
+ protocol: "sftp",
6663
+ retryable: false
6664
+ });
6665
+ }
6469
6666
  const frameLength = 4 + packetLength;
6470
6667
  if (this.pending.length < frameLength) {
6471
6668
  break;
@@ -6481,8 +6678,8 @@ var SshTransportPacketFramer = class {
6481
6678
  }
6482
6679
  /** Returns and clears any bytes buffered but not yet part of a complete packet. */
6483
6680
  takeRemainingBytes() {
6484
- const remaining = Buffer14.from(this.pending);
6485
- this.pending = Buffer14.alloc(0);
6681
+ const remaining = Buffer13.from(this.pending);
6682
+ this.pending = Buffer13.alloc(0);
6486
6683
  return remaining;
6487
6684
  }
6488
6685
  };
@@ -6499,10 +6696,10 @@ function normalizeBlockSize(blockSize) {
6499
6696
  }
6500
6697
 
6501
6698
  // src/protocols/ssh/transport/SshHostKeyVerification.ts
6502
- import { Buffer as Buffer15 } from "buffer";
6699
+ import { Buffer as Buffer14 } from "buffer";
6503
6700
  import { createHash as createHash2, createPublicKey as createPublicKey3, verify as cryptoVerify } from "crypto";
6504
6701
  var ED25519_RAW_KEY_LENGTH2 = 32;
6505
- var ED25519_SPKI_PREFIX = Buffer15.from("302a300506032b6570032100", "hex");
6702
+ var ED25519_SPKI_PREFIX = Buffer14.from("302a300506032b6570032100", "hex");
6506
6703
  function verifySshHostKeySignature(input) {
6507
6704
  const { algorithmName, publicKey } = parseHostKey(input.hostKeyBlob);
6508
6705
  const { signatureAlgorithm, signatureBytes } = parseSignatureBlob(input.signatureBlob);
@@ -6515,9 +6712,9 @@ function verifySshHostKeySignature(input) {
6515
6712
  });
6516
6713
  }
6517
6714
  const verified = verifySignature({
6518
- data: Buffer15.from(input.exchangeHash),
6715
+ data: Buffer14.from(input.exchangeHash),
6519
6716
  publicKey,
6520
- signature: Buffer15.from(signatureBytes),
6717
+ signature: Buffer14.from(signatureBytes),
6521
6718
  signatureAlgorithm
6522
6719
  });
6523
6720
  if (!verified) {
@@ -6546,7 +6743,7 @@ function parseHostKey(blob) {
6546
6743
  retryable: false
6547
6744
  });
6548
6745
  }
6549
- const spki = Buffer15.concat([ED25519_SPKI_PREFIX, raw]);
6746
+ const spki = Buffer14.concat([ED25519_SPKI_PREFIX, raw]);
6550
6747
  return {
6551
6748
  algorithmName,
6552
6749
  publicKey: createPublicKey3({ format: "der", key: spki, type: "spki" })
@@ -6652,37 +6849,37 @@ function verifySignature(input) {
6652
6849
  function rsaPublicKeyFromComponents(e, n) {
6653
6850
  const eDer = encodeAsn1Integer(e);
6654
6851
  const nDer = encodeAsn1Integer(n);
6655
- const rsaPublicKeyDer = encodeAsn1Sequence(Buffer15.concat([nDer, eDer]));
6656
- const bitStringContent = Buffer15.concat([Buffer15.from([0]), rsaPublicKeyDer]);
6657
- const bitString = Buffer15.concat([
6658
- Buffer15.from([3]),
6852
+ const rsaPublicKeyDer = encodeAsn1Sequence(Buffer14.concat([nDer, eDer]));
6853
+ const bitStringContent = Buffer14.concat([Buffer14.from([0]), rsaPublicKeyDer]);
6854
+ const bitString = Buffer14.concat([
6855
+ Buffer14.from([3]),
6659
6856
  encodeAsn1Length(bitStringContent.length),
6660
6857
  bitStringContent
6661
6858
  ]);
6662
- const algoId = Buffer15.from("300d06092a864886f70d010101 0500".replace(/\s+/g, ""), "hex");
6663
- const spki = encodeAsn1Sequence(Buffer15.concat([algoId, bitString]));
6859
+ const algoId = Buffer14.from("300d06092a864886f70d010101 0500".replace(/\s+/g, ""), "hex");
6860
+ const spki = encodeAsn1Sequence(Buffer14.concat([algoId, bitString]));
6664
6861
  return createPublicKey3({ format: "der", key: spki, type: "spki" });
6665
6862
  }
6666
6863
  function encodeAsn1Integer(value) {
6667
6864
  let body = value;
6668
6865
  while (body.length > 1 && body[0] === 0) body = body.subarray(1);
6669
6866
  if (body.length > 0 && (body[0] & 128) !== 0) {
6670
- body = Buffer15.concat([Buffer15.from([0]), body]);
6867
+ body = Buffer14.concat([Buffer14.from([0]), body]);
6671
6868
  }
6672
- return Buffer15.concat([Buffer15.from([2]), encodeAsn1Length(body.length), body]);
6869
+ return Buffer14.concat([Buffer14.from([2]), encodeAsn1Length(body.length), body]);
6673
6870
  }
6674
6871
  function encodeAsn1Sequence(content) {
6675
- return Buffer15.concat([Buffer15.from([48]), encodeAsn1Length(content.length), content]);
6872
+ return Buffer14.concat([Buffer14.from([48]), encodeAsn1Length(content.length), content]);
6676
6873
  }
6677
6874
  function encodeAsn1Length(length) {
6678
- if (length < 128) return Buffer15.from([length]);
6875
+ if (length < 128) return Buffer14.from([length]);
6679
6876
  const bytes = [];
6680
6877
  let n = length;
6681
6878
  while (n > 0) {
6682
6879
  bytes.unshift(n & 255);
6683
6880
  n >>>= 8;
6684
6881
  }
6685
- return Buffer15.from([128 | bytes.length, ...bytes]);
6882
+ return Buffer14.from([128 | bytes.length, ...bytes]);
6686
6883
  }
6687
6884
  var ECDSA_OID_BY_CURVE = {
6688
6885
  nistp256: "06082a8648ce3d030107",
@@ -6703,15 +6900,15 @@ function ecdsaPublicKeyFromPoint(curveIdentifier, point) {
6703
6900
  retryable: false
6704
6901
  });
6705
6902
  }
6706
- const algoIdContent = Buffer15.from(ECDSA_ALGORITHM_OID_HEX + oidHex, "hex");
6903
+ const algoIdContent = Buffer14.from(ECDSA_ALGORITHM_OID_HEX + oidHex, "hex");
6707
6904
  const algoId = encodeAsn1Sequence(algoIdContent);
6708
- const bitStringContent = Buffer15.concat([Buffer15.from([0]), point]);
6709
- const bitString = Buffer15.concat([
6710
- Buffer15.from([3]),
6905
+ const bitStringContent = Buffer14.concat([Buffer14.from([0]), point]);
6906
+ const bitString = Buffer14.concat([
6907
+ Buffer14.from([3]),
6711
6908
  encodeAsn1Length(bitStringContent.length),
6712
6909
  bitStringContent
6713
6910
  ]);
6714
- const spki = encodeAsn1Sequence(Buffer15.concat([algoId, bitString]));
6911
+ const spki = encodeAsn1Sequence(Buffer14.concat([algoId, bitString]));
6715
6912
  return createPublicKey3({ format: "der", key: spki, type: "spki" });
6716
6913
  }
6717
6914
  function sshEcdsaSignatureToDer(sshSignature) {
@@ -6720,7 +6917,7 @@ function sshEcdsaSignatureToDer(sshSignature) {
6720
6917
  const s = reader.readMpint();
6721
6918
  const rDer = encodeAsn1Integer(r);
6722
6919
  const sDer = encodeAsn1Integer(s);
6723
- return encodeAsn1Sequence(Buffer15.concat([rDer, sDer]));
6920
+ return encodeAsn1Sequence(Buffer14.concat([rDer, sDer]));
6724
6921
  }
6725
6922
 
6726
6923
  // src/protocols/ssh/transport/SshTransportHandshake.ts
@@ -6752,7 +6949,7 @@ var SshTransportHandshake = class {
6752
6949
  serverIdentification;
6753
6950
  /** Creates the first outbound bytes (client identification line). */
6754
6951
  createInitialClientBytes() {
6755
- return Buffer16.from(`${this.clientIdentificationLine}\r
6952
+ return Buffer15.from(`${this.clientIdentificationLine}\r
6756
6953
  `, "ascii");
6757
6954
  }
6758
6955
  /**
@@ -6776,7 +6973,7 @@ var SshTransportHandshake = class {
6776
6973
  }
6777
6974
  return { outbound };
6778
6975
  }
6779
- return this.pushServerBytesWithPhase(outbound, Buffer16.from(chunk));
6976
+ return this.pushServerBytesWithPhase(outbound, Buffer15.from(chunk));
6780
6977
  }
6781
6978
  getServerBannerLines() {
6782
6979
  return this.identificationLines;
@@ -6830,12 +7027,12 @@ var SshTransportHandshake = class {
6830
7027
  clientKexInitPayload: this.clientKexInitPayload,
6831
7028
  clientPublicKey: this.pendingCurve25519.publicKey,
6832
7029
  negotiatedAlgorithms,
6833
- serverHostKey: Buffer16.alloc(0),
7030
+ serverHostKey: Buffer15.alloc(0),
6834
7031
  serverIdentification: (this.serverIdentification ?? missingServerIdentificationError()).raw,
6835
- serverKexInitPayload: Buffer16.from(packet.payload),
6836
- serverPublicKey: Buffer16.alloc(0),
6837
- serverSignature: Buffer16.alloc(0),
6838
- sharedSecret: Buffer16.alloc(0)
7032
+ serverKexInitPayload: Buffer15.from(packet.payload),
7033
+ serverPublicKey: Buffer15.alloc(0),
7034
+ serverSignature: Buffer15.alloc(0),
7035
+ sharedSecret: Buffer15.alloc(0)
6839
7036
  };
6840
7037
  continue;
6841
7038
  }
@@ -6943,24 +7140,54 @@ var SshTransportHandshake = class {
6943
7140
  return { outbound };
6944
7141
  }
6945
7142
  };
7143
+ var MAX_IDENTIFICATION_LINE_BYTES = 8192;
7144
+ var MAX_PRE_IDENTIFICATION_LINES = 1024;
6946
7145
  var SshIdentificationAccumulator = class {
6947
- pending = Buffer16.alloc(0);
7146
+ pending = Buffer15.alloc(0);
7147
+ bannerLineCount = 0;
6948
7148
  push(chunk) {
6949
- this.pending = Buffer16.concat([this.pending, Buffer16.from(chunk)]);
7149
+ this.pending = Buffer15.concat([this.pending, Buffer15.from(chunk)]);
6950
7150
  const bannerLines = [];
6951
7151
  while (true) {
6952
7152
  const lfIndex = this.pending.indexOf(10);
6953
- if (lfIndex < 0) break;
7153
+ if (lfIndex < 0) {
7154
+ if (this.pending.length > MAX_IDENTIFICATION_LINE_BYTES) {
7155
+ throw new ProtocolError({
7156
+ details: { limitBytes: MAX_IDENTIFICATION_LINE_BYTES },
7157
+ message: "SSH identification line exceeds the maximum accepted length",
7158
+ protocol: "sftp",
7159
+ retryable: false
7160
+ });
7161
+ }
7162
+ break;
7163
+ }
7164
+ if (lfIndex > MAX_IDENTIFICATION_LINE_BYTES) {
7165
+ throw new ProtocolError({
7166
+ details: { limitBytes: MAX_IDENTIFICATION_LINE_BYTES },
7167
+ message: "SSH identification line exceeds the maximum accepted length",
7168
+ protocol: "sftp",
7169
+ retryable: false
7170
+ });
7171
+ }
6954
7172
  const lineText = trimLineEndings(this.pending.subarray(0, lfIndex + 1).toString("ascii"));
6955
- const remainder = Buffer16.from(this.pending.subarray(lfIndex + 1));
7173
+ const remainder = Buffer15.from(this.pending.subarray(lfIndex + 1));
6956
7174
  this.pending = remainder;
6957
7175
  if (lineText.startsWith("SSH-")) {
6958
- this.pending = Buffer16.alloc(0);
7176
+ this.pending = Buffer15.alloc(0);
6959
7177
  return { bannerLines, identLine: lineText, remainder };
6960
7178
  }
7179
+ this.bannerLineCount += 1;
7180
+ if (this.bannerLineCount > MAX_PRE_IDENTIFICATION_LINES) {
7181
+ throw new ProtocolError({
7182
+ details: { limitLines: MAX_PRE_IDENTIFICATION_LINES },
7183
+ message: "SSH server sent too many pre-identification banner lines",
7184
+ protocol: "sftp",
7185
+ retryable: false
7186
+ });
7187
+ }
6961
7188
  bannerLines.push(lineText);
6962
7189
  }
6963
- return { bannerLines, remainder: Buffer16.alloc(0) };
7190
+ return { bannerLines, remainder: Buffer15.alloc(0) };
6964
7191
  }
6965
7192
  };
6966
7193
  function trimLineEndings(value) {
@@ -6988,7 +7215,7 @@ function missingPendingKeyExchangeError() {
6988
7215
  }
6989
7216
 
6990
7217
  // src/protocols/ssh/transport/SshTransportProtection.ts
6991
- import { Buffer as Buffer17 } from "buffer";
7218
+ import { Buffer as Buffer16 } from "buffer";
6992
7219
  import {
6993
7220
  createCipheriv,
6994
7221
  createDecipheriv,
@@ -7050,7 +7277,7 @@ var SshTransportPacketProtector = class {
7050
7277
  );
7051
7278
  const encrypted = this.cipher === void 0 ? clearPacket : this.cipher.update(clearPacket);
7052
7279
  this.sequenceNumber = this.sequenceNumber + 1 >>> 0;
7053
- return Buffer17.concat([encrypted, mac]);
7280
+ return Buffer16.concat([encrypted, mac]);
7054
7281
  }
7055
7282
  };
7056
7283
  var SshTransportPacketUnprotector = class {
@@ -7076,7 +7303,7 @@ var SshTransportPacketUnprotector = class {
7076
7303
  sequenceNumber;
7077
7304
  // Streaming framing state for pushBytes()
7078
7305
  framePartialDecrypted;
7079
- framePendingRaw = Buffer17.alloc(0);
7306
+ framePendingRaw = Buffer16.alloc(0);
7080
7307
  frameRemainingNeeded;
7081
7308
  getSequenceNumber() {
7082
7309
  return this.sequenceNumber;
@@ -7086,15 +7313,23 @@ var SshTransportPacketUnprotector = class {
7086
7313
  * Maintains internal framing state across calls - pass each `data` event chunk directly.
7087
7314
  */
7088
7315
  pushBytes(chunk) {
7089
- this.framePendingRaw = Buffer17.concat([this.framePendingRaw, chunk]);
7316
+ this.framePendingRaw = Buffer16.concat([this.framePendingRaw, chunk]);
7090
7317
  const results = [];
7091
7318
  while (true) {
7092
7319
  if (this.framePartialDecrypted === void 0) {
7093
7320
  if (this.framePendingRaw.length < this.blockLength) break;
7094
7321
  const firstBlock = this.framePendingRaw.subarray(0, this.blockLength);
7095
- this.framePendingRaw = Buffer17.from(this.framePendingRaw.subarray(this.blockLength));
7096
- this.framePartialDecrypted = this.decipher ? Buffer17.from(this.decipher.update(firstBlock)) : Buffer17.from(firstBlock);
7322
+ this.framePendingRaw = Buffer16.from(this.framePendingRaw.subarray(this.blockLength));
7323
+ this.framePartialDecrypted = this.decipher ? Buffer16.from(this.decipher.update(firstBlock)) : Buffer16.from(firstBlock);
7097
7324
  const packetLength = this.framePartialDecrypted.readUInt32BE(0);
7325
+ if (packetLength > MAX_SSH_PACKET_LENGTH) {
7326
+ throw new ProtocolError({
7327
+ details: { maxPacketLength: MAX_SSH_PACKET_LENGTH, packetLength },
7328
+ message: "SSH encrypted packet length exceeds the maximum accepted size",
7329
+ protocol: "sftp",
7330
+ retryable: false
7331
+ });
7332
+ }
7098
7333
  const remaining = 4 + packetLength - this.blockLength + this.macLength;
7099
7334
  if (remaining < 0) {
7100
7335
  throw new ProtocolError({
@@ -7110,9 +7345,9 @@ var SshTransportPacketUnprotector = class {
7110
7345
  if (this.framePendingRaw.length < needed) break;
7111
7346
  const encryptedRest = this.framePendingRaw.subarray(0, needed - this.macLength);
7112
7347
  const receivedMac = this.framePendingRaw.subarray(needed - this.macLength, needed);
7113
- this.framePendingRaw = Buffer17.from(this.framePendingRaw.subarray(needed));
7114
- const decryptedRest = encryptedRest.length > 0 ? this.decipher ? Buffer17.from(this.decipher.update(encryptedRest)) : Buffer17.from(encryptedRest) : Buffer17.alloc(0);
7115
- const clearPacket = Buffer17.concat([this.framePartialDecrypted, decryptedRest]);
7348
+ this.framePendingRaw = Buffer16.from(this.framePendingRaw.subarray(needed));
7349
+ const decryptedRest = encryptedRest.length > 0 ? this.decipher ? Buffer16.from(this.decipher.update(encryptedRest)) : Buffer16.from(encryptedRest) : Buffer16.alloc(0);
7350
+ const clearPacket = Buffer16.concat([this.framePartialDecrypted, decryptedRest]);
7116
7351
  const expectedMac = computeMac(
7117
7352
  this.macAlgorithm,
7118
7353
  this.options.keys.macKey,
@@ -7135,7 +7370,7 @@ var SshTransportPacketUnprotector = class {
7135
7370
  return results;
7136
7371
  }
7137
7372
  unprotectPayload(packet) {
7138
- const frame = Buffer17.from(packet);
7373
+ const frame = Buffer16.from(packet);
7139
7374
  if (frame.length < this.macLength) {
7140
7375
  throw new ProtocolError({
7141
7376
  details: { length: frame.length, macLength: this.macLength },
@@ -7276,10 +7511,10 @@ function resolveMacLength(encryptionAlgorithm, macAlgorithm) {
7276
7511
  }
7277
7512
  function computeMac(macAlgorithm, macKey, sequence, packet, macLength) {
7278
7513
  if (macLength === 0) {
7279
- return Buffer17.alloc(0);
7514
+ return Buffer16.alloc(0);
7280
7515
  }
7281
7516
  const hashName = macAlgorithm === "hmac-sha2-512" ? "sha512" : "sha256";
7282
- const sequenceBuffer = Buffer17.alloc(4);
7517
+ const sequenceBuffer = Buffer16.alloc(4);
7283
7518
  sequenceBuffer.writeUInt32BE(sequence >>> 0, 0);
7284
7519
  return createHmac2(hashName, macKey).update(sequenceBuffer).update(packet).digest().subarray(0, macLength);
7285
7520
  }
@@ -7486,7 +7721,7 @@ var SshTransportConnection = class {
7486
7721
  */
7487
7722
  sendPayload(payload) {
7488
7723
  this.assertConnected();
7489
- const frame = this.protector.protectPayload(Buffer18.from(payload));
7724
+ const frame = this.protector.protectPayload(Buffer17.from(payload));
7490
7725
  this.socket.write(frame);
7491
7726
  this.resetKeepaliveTimer();
7492
7727
  }
@@ -7644,7 +7879,7 @@ function parseDisconnectPayload(payload) {
7644
7879
  }
7645
7880
 
7646
7881
  // src/protocols/sftp/v3/SftpSession.ts
7647
- import { Buffer as Buffer20 } from "buffer";
7882
+ import { Buffer as Buffer19 } from "buffer";
7648
7883
 
7649
7884
  // src/protocols/sftp/v3/SftpAttributes.ts
7650
7885
  var SFTP_ATTR_FLAG = {
@@ -7717,7 +7952,8 @@ function decodeSftpAttributesFromReader(reader) {
7717
7952
  }
7718
7953
 
7719
7954
  // src/protocols/sftp/v3/SftpPacket.ts
7720
- import { Buffer as Buffer19 } from "buffer";
7955
+ import { Buffer as Buffer18 } from "buffer";
7956
+ var MAX_SFTP_PACKET_LENGTH = 256 * 1024;
7721
7957
  var SFTP_PACKET_TYPE = {
7722
7958
  ATTRS: 105,
7723
7959
  CLOSE: 4,
@@ -7748,7 +7984,7 @@ var SFTP_PACKET_TYPE = {
7748
7984
  WRITE: 6
7749
7985
  };
7750
7986
  function decodeSftpPacket(frame) {
7751
- const bytes = Buffer19.from(frame);
7987
+ const bytes = Buffer18.from(frame);
7752
7988
  if (bytes.length < 5) {
7753
7989
  throw new ParseError({
7754
7990
  details: { length: bytes.length },
@@ -7771,12 +8007,19 @@ function decodeSftpPacket(frame) {
7771
8007
  };
7772
8008
  }
7773
8009
  var SftpPacketFramer = class {
7774
- pending = Buffer19.alloc(0);
8010
+ pending = Buffer18.alloc(0);
7775
8011
  push(chunk) {
7776
- this.pending = Buffer19.concat([this.pending, Buffer19.from(chunk)]);
8012
+ this.pending = Buffer18.concat([this.pending, Buffer18.from(chunk)]);
7777
8013
  const packets = [];
7778
8014
  while (this.pending.length >= 4) {
7779
8015
  const bodyLength = this.pending.readUInt32BE(0);
8016
+ if (bodyLength > MAX_SFTP_PACKET_LENGTH) {
8017
+ throw new ParseError({
8018
+ details: { bodyLength, maxPacketLength: MAX_SFTP_PACKET_LENGTH },
8019
+ message: "SFTP packet length exceeds the maximum accepted size",
8020
+ retryable: false
8021
+ });
8022
+ }
7780
8023
  const frameLength = 4 + bodyLength;
7781
8024
  if (this.pending.length < frameLength) {
7782
8025
  break;
@@ -8231,7 +8474,7 @@ var SftpSession = class {
8231
8474
  * serializes concurrent calls so byte ordering is preserved.
8232
8475
  */
8233
8476
  sendRaw(encodedMessage, requestId) {
8234
- const frame = Buffer20.alloc(4 + encodedMessage.length);
8477
+ const frame = Buffer19.alloc(4 + encodedMessage.length);
8235
8478
  frame.writeUInt32BE(encodedMessage.length, 0);
8236
8479
  encodedMessage.copy(frame, 4);
8237
8480
  this.channel.sendData(frame).catch((err) => {
@@ -8378,7 +8621,7 @@ function buildNativeSftpCapabilities(maxConcurrency) {
8378
8621
  var NATIVE_SFTP_PROVIDER_CAPABILITIES = buildNativeSftpCapabilities(
8379
8622
  NATIVE_SFTP_DEFAULT_MAX_CONCURRENCY
8380
8623
  );
8381
- function createNativeSftpProviderFactory(options = {}) {
8624
+ function createSftpProviderFactory(options = {}) {
8382
8625
  validateNativeSftpOptions(options);
8383
8626
  const capabilities = buildNativeSftpCapabilities(
8384
8627
  options.maxConcurrency ?? NATIVE_SFTP_DEFAULT_MAX_CONCURRENCY
@@ -8774,9 +9017,9 @@ function buildNativePublickeyCredential(profile, username) {
8774
9017
  const passphrase = profile.ssh?.passphrase;
8775
9018
  try {
8776
9019
  const privateKey = createPrivateKey({
8777
- key: Buffer21.isBuffer(keyMaterial) ? keyMaterial : keyMaterial,
9020
+ key: Buffer20.isBuffer(keyMaterial) ? keyMaterial : keyMaterial,
8778
9021
  ...passphrase === void 0 ? {} : {
8779
- passphrase: Buffer21.isBuffer(passphrase) ? passphrase : passphrase
9022
+ passphrase: Buffer20.isBuffer(passphrase) ? passphrase : passphrase
8780
9023
  }
8781
9024
  });
8782
9025
  return buildPublickeyCredential({ privateKey, username });
@@ -8903,12 +9146,12 @@ function normalizeNativeHostKeyPins(value) {
8903
9146
  const trimmed = pin.trim();
8904
9147
  const hex = trimmed.replace(/:/g, "");
8905
9148
  if (hex.length === 64 && /^[a-f0-9]+$/i.test(hex)) {
8906
- normalized.add(Buffer21.from(hex, "hex").toString("base64").replace(/=+$/g, ""));
9149
+ normalized.add(Buffer20.from(hex, "hex").toString("base64").replace(/=+$/g, ""));
8907
9150
  continue;
8908
9151
  }
8909
9152
  const bare = trimmed.startsWith("SHA256:") ? trimmed.slice("SHA256:".length) : trimmed;
8910
9153
  const padded = bare.length % 4 === 0 ? bare : `${bare}${"=".repeat(4 - bare.length % 4)}`;
8911
- normalized.add(Buffer21.from(padded, "base64").toString("base64").replace(/=+$/g, ""));
9154
+ normalized.add(Buffer20.from(padded, "base64").toString("base64").replace(/=+$/g, ""));
8912
9155
  }
8913
9156
  return normalized;
8914
9157
  }
@@ -8918,7 +9161,7 @@ function parseNativeKnownHosts(source) {
8918
9161
  const entries = [];
8919
9162
  let sawNonEmpty = false;
8920
9163
  for (const value of sources) {
8921
- const text = Buffer21.isBuffer(value) ? value.toString("utf8") : String(value);
9164
+ const text = Buffer20.isBuffer(value) ? value.toString("utf8") : String(value);
8922
9165
  if (text.length === 0) continue;
8923
9166
  sawNonEmpty = true;
8924
9167
  entries.push(...parseKnownHosts(text));
@@ -8991,7 +9234,7 @@ function requireNativeSftpUsername(profile) {
8991
9234
  }
8992
9235
  function resolveNativeSftpTextSecret(value) {
8993
9236
  if (value === void 0) return void 0;
8994
- const text = Buffer21.isBuffer(value) ? value.toString("utf8") : value;
9237
+ const text = Buffer20.isBuffer(value) ? value.toString("utf8") : value;
8995
9238
  if (text.length === 0) return void 0;
8996
9239
  return text;
8997
9240
  }
@@ -9065,16 +9308,16 @@ export {
9065
9308
  copyBetween,
9066
9309
  createAtomicDeployPlan,
9067
9310
  createBandwidthThrottle,
9311
+ createDefaultRetryPolicy,
9068
9312
  createLocalProviderFactory,
9069
9313
  createMemoryProviderFactory,
9070
- createNativeSftpProviderFactory,
9071
9314
  createOAuthTokenSecretSource,
9072
9315
  createPooledTransferClient,
9073
9316
  createProgressEvent,
9074
9317
  createProviderTransferExecutor,
9075
9318
  createRemoteBrowser,
9076
9319
  createRemoteManifest,
9077
- createNativeSftpProviderFactory as createSftpProviderFactory,
9320
+ createSftpProviderFactory,
9078
9321
  createSyncPlan,
9079
9322
  createTransferClient,
9080
9323
  createTransferJobsFromPlan,
@@ -9102,8 +9345,10 @@ export {
9102
9345
  parseRemoteManifest,
9103
9346
  redactCommand,
9104
9347
  redactConnectionProfile,
9348
+ redactErrorForLogging,
9105
9349
  redactObject,
9106
9350
  redactSecretSource,
9351
+ redactUrlForLogging,
9107
9352
  redactValue,
9108
9353
  resolveConnectionProfileSecrets,
9109
9354
  resolveOpenSshHost,