@zero-transfer/classic 0.4.7 → 0.4.8

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