@zero-transfer/ssh 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.cjs CHANGED
@@ -70,6 +70,7 @@ __export(ssh_exports, {
70
70
  copyBetween: () => copyBetween,
71
71
  createAtomicDeployPlan: () => createAtomicDeployPlan,
72
72
  createBandwidthThrottle: () => createBandwidthThrottle,
73
+ createDefaultRetryPolicy: () => createDefaultRetryPolicy,
73
74
  createLocalProviderFactory: () => createLocalProviderFactory,
74
75
  createMemoryProviderFactory: () => createMemoryProviderFactory,
75
76
  createOAuthTokenSecretSource: () => createOAuthTokenSecretSource,
@@ -106,8 +107,10 @@ __export(ssh_exports, {
106
107
  parseRemoteManifest: () => parseRemoteManifest,
107
108
  redactCommand: () => redactCommand,
108
109
  redactConnectionProfile: () => redactConnectionProfile,
110
+ redactErrorForLogging: () => redactErrorForLogging,
109
111
  redactObject: () => redactObject,
110
112
  redactSecretSource: () => redactSecretSource,
113
+ redactUrlForLogging: () => redactUrlForLogging,
111
114
  redactValue: () => redactValue,
112
115
  resolveConnectionProfileSecrets: () => resolveConnectionProfileSecrets,
113
116
  resolveOpenSshHost: () => resolveOpenSshHost,
@@ -129,6 +132,68 @@ module.exports = __toCommonJS(ssh_exports);
129
132
  // src/client/ZeroTransfer.ts
130
133
  var import_node_events = require("events");
131
134
 
135
+ // src/logging/redaction.ts
136
+ var REDACTED = "[REDACTED]";
137
+ var SENSITIVE_KEY_PATTERN = /(?:password|passphrase|privatekey|token|secret|username|user)$/i;
138
+ var SECRET_COMMAND_PATTERN = /^(PASS|USER|ACCT)\s+(.+)$/i;
139
+ var URL_KEY_PATTERN = /(?:url|uri|href)$/i;
140
+ function isSensitiveKey(key) {
141
+ return SENSITIVE_KEY_PATTERN.test(key.replace(/[_-]/g, ""));
142
+ }
143
+ function redactCommand(command) {
144
+ return command.replace(SECRET_COMMAND_PATTERN, (_fullMatch, commandName) => {
145
+ return `${commandName.toUpperCase()} ${REDACTED}`;
146
+ });
147
+ }
148
+ function redactValue(value) {
149
+ if (typeof value === "string") {
150
+ return redactCommand(value);
151
+ }
152
+ if (Array.isArray(value)) {
153
+ return value.map((item) => redactValue(item));
154
+ }
155
+ if (value !== null && typeof value === "object") {
156
+ return redactObject(value);
157
+ }
158
+ return value;
159
+ }
160
+ function redactObject(input) {
161
+ return Object.fromEntries(
162
+ Object.entries(input).map(([key, value]) => {
163
+ if (isSensitiveKey(key)) {
164
+ return [key, REDACTED];
165
+ }
166
+ if (URL_KEY_PATTERN.test(key) && typeof value === "string") {
167
+ return [key, redactUrlForLogging(value)];
168
+ }
169
+ return [key, redactValue(value)];
170
+ })
171
+ );
172
+ }
173
+ function redactUrlForLogging(url) {
174
+ let parsed;
175
+ try {
176
+ parsed = typeof url === "string" ? new URL(url) : url;
177
+ } catch {
178
+ return REDACTED;
179
+ }
180
+ const origin = parsed.host.length > 0 ? `${parsed.protocol}//${parsed.host}` : parsed.protocol;
181
+ const query = parsed.search.length > 0 ? `?${REDACTED}` : "";
182
+ return `${origin}${parsed.pathname}${query}`;
183
+ }
184
+ function redactErrorForLogging(error) {
185
+ if (error !== null && typeof error === "object") {
186
+ const candidate = error;
187
+ if (typeof candidate.toJSON === "function") {
188
+ return redactObject(candidate.toJSON());
189
+ }
190
+ }
191
+ if (error instanceof Error) {
192
+ return redactObject({ message: error.message, name: error.name });
193
+ }
194
+ return { message: redactValue(typeof error === "string" ? error : String(error)) };
195
+ }
196
+
132
197
  // src/errors/ZeroTransferError.ts
133
198
  var ZeroTransferError = class extends Error {
134
199
  /** Stable machine-readable error code. */
@@ -170,6 +235,11 @@ var ZeroTransferError = class extends Error {
170
235
  /**
171
236
  * Serializes the error into a plain object suitable for logs or API responses.
172
237
  *
238
+ * `details` and `command` are passed through secret redaction so serialized
239
+ * errors never leak credentials, signed URLs, or raw protocol commands. The
240
+ * live {@link ZeroTransferError.details | details} property stays unredacted
241
+ * for programmatic consumers.
242
+ *
173
243
  * @returns A JSON-safe object containing public structured error fields.
174
244
  */
175
245
  toJSON() {
@@ -179,12 +249,12 @@ var ZeroTransferError = class extends Error {
179
249
  message: this.message,
180
250
  protocol: this.protocol,
181
251
  host: this.host,
182
- command: this.command,
252
+ command: this.command === void 0 ? void 0 : redactCommand(this.command),
183
253
  ftpCode: this.ftpCode,
184
254
  sftpCode: this.sftpCode,
185
255
  path: this.path,
186
256
  retryable: this.retryable,
187
- details: this.details
257
+ details: this.details === void 0 ? void 0 : redactObject(this.details)
188
258
  };
189
259
  }
190
260
  };
@@ -707,15 +777,20 @@ var ProviderRegistry = class {
707
777
  var TransferClient = class {
708
778
  /** Provider registry used by this client. */
709
779
  registry;
780
+ /** Execution defaults applied when call sites omit their own values. */
781
+ defaults;
710
782
  logger;
711
783
  /**
712
784
  * Creates a transfer client without opening any provider connections.
713
785
  *
714
- * @param options - Optional registry, provider factories, and logger.
786
+ * @param options - Optional registry, provider factories, logger, and execution defaults.
715
787
  */
716
788
  constructor(options = {}) {
717
789
  this.registry = options.registry ?? new ProviderRegistry();
718
790
  this.logger = options.logger ?? noopLogger;
791
+ if (options.defaults !== void 0) {
792
+ this.defaults = { ...options.defaults };
793
+ }
719
794
  for (const provider of options.providers ?? []) {
720
795
  this.registry.register(provider);
721
796
  }
@@ -1288,18 +1363,25 @@ var TransferEngine = class {
1288
1363
  for (let attemptNumber = 1; attemptNumber <= maxAttempts; attemptNumber += 1) {
1289
1364
  this.throwIfAborted(abortScope.signal, job);
1290
1365
  const attemptStartedAt = this.now();
1366
+ const attemptScope = createAttemptScope(
1367
+ abortScope.signal,
1368
+ options.timeout,
1369
+ job,
1370
+ attemptNumber
1371
+ );
1291
1372
  const context = this.createExecutionContext(
1292
1373
  job,
1293
1374
  attemptNumber,
1294
1375
  attemptStartedAt,
1295
1376
  options,
1296
- abortScope.signal,
1377
+ attemptScope.signal,
1297
1378
  (bytesTransferred) => {
1298
1379
  latestBytesTransferred = bytesTransferred;
1299
- }
1380
+ },
1381
+ attemptScope.notifyProgress
1300
1382
  );
1301
1383
  try {
1302
- const result = await runExecutor(executor, context, abortScope.signal, job);
1384
+ const result = await runExecutor(executor, context, attemptScope.signal, job);
1303
1385
  context.throwIfAborted();
1304
1386
  latestBytesTransferred = result.bytesTransferred;
1305
1387
  const completedAt = this.now();
@@ -1317,16 +1399,27 @@ var TransferEngine = class {
1317
1399
  summarizeError(error)
1318
1400
  );
1319
1401
  attempts.push(attempt);
1320
- if (error instanceof AbortError || error instanceof TimeoutError) {
1402
+ if (error instanceof AbortError || abortScope.signal?.aborted === true) {
1321
1403
  throw error;
1322
1404
  }
1323
- const retryInput = { attempt: attemptNumber, error, job };
1405
+ const retryInput = {
1406
+ attempt: attemptNumber,
1407
+ elapsedMs: Math.max(0, completedAt.getTime() - startedAt.getTime()),
1408
+ error,
1409
+ job
1410
+ };
1324
1411
  const shouldRetry = attemptNumber < maxAttempts && (options.retry?.shouldRetry?.(retryInput) ?? isRetryable(error));
1325
1412
  if (shouldRetry) {
1326
1413
  options.retry?.onRetry?.(retryInput);
1414
+ const delayMs = normalizeDelayMs(options.retry?.getDelayMs?.(retryInput));
1415
+ if (delayMs > 0) {
1416
+ await sleepWithAbort(delayMs, abortScope.signal, job);
1417
+ }
1327
1418
  continue;
1328
1419
  }
1329
1420
  throw createTransferFailure(job, error, attempts);
1421
+ } finally {
1422
+ attemptScope.dispose();
1330
1423
  }
1331
1424
  }
1332
1425
  throw createTransferFailure(job, void 0, attempts);
@@ -1334,12 +1427,13 @@ var TransferEngine = class {
1334
1427
  abortScope.dispose();
1335
1428
  }
1336
1429
  }
1337
- createExecutionContext(job, attempt, startedAt, options, signal, updateBytesTransferred) {
1430
+ createExecutionContext(job, attempt, startedAt, options, signal, updateBytesTransferred, notifyProgress) {
1338
1431
  const context = {
1339
1432
  attempt,
1340
1433
  job,
1341
1434
  reportProgress: (bytesTransferred, totalBytes) => {
1342
1435
  this.throwIfAborted(signal, job);
1436
+ notifyProgress();
1343
1437
  updateBytesTransferred(bytesTransferred);
1344
1438
  const progressInput = {
1345
1439
  bytesTransferred,
@@ -1408,6 +1502,96 @@ function createAbortScope(parentSignal, timeout, job) {
1408
1502
  signal: controller.signal
1409
1503
  };
1410
1504
  }
1505
+ function createAttemptScope(parentSignal, timeout, job, attempt) {
1506
+ const attemptTimeoutMs = normalizeTimeoutMs(timeout?.attemptTimeoutMs);
1507
+ const stallTimeoutMs = normalizeTimeoutMs(timeout?.stallTimeoutMs);
1508
+ if (attemptTimeoutMs === void 0 && stallTimeoutMs === void 0) {
1509
+ const scope = {
1510
+ dispose: () => void 0,
1511
+ notifyProgress: () => void 0
1512
+ };
1513
+ if (parentSignal !== void 0) scope.signal = parentSignal;
1514
+ return scope;
1515
+ }
1516
+ const controller = new AbortController();
1517
+ const retryable = timeout?.retryable ?? true;
1518
+ const abortFromParent = () => controller.abort(parentSignal?.reason);
1519
+ if (parentSignal?.aborted === true) {
1520
+ abortFromParent();
1521
+ } else {
1522
+ parentSignal?.addEventListener("abort", abortFromParent, { once: true });
1523
+ }
1524
+ const attemptTimer = attemptTimeoutMs === void 0 ? void 0 : setTimeout(() => {
1525
+ controller.abort(
1526
+ new TimeoutError({
1527
+ details: { attempt, attemptTimeoutMs, jobId: job.id, operation: job.operation },
1528
+ message: `Transfer attempt ${String(attempt)} timed out after ${String(attemptTimeoutMs)}ms: ${job.id}`,
1529
+ retryable
1530
+ })
1531
+ );
1532
+ }, attemptTimeoutMs);
1533
+ let stallTimer;
1534
+ const armStallWatchdog = () => {
1535
+ if (stallTimeoutMs === void 0 || controller.signal.aborted) return;
1536
+ if (stallTimer !== void 0) clearTimeout(stallTimer);
1537
+ stallTimer = setTimeout(() => {
1538
+ controller.abort(
1539
+ new TimeoutError({
1540
+ details: { attempt, jobId: job.id, operation: job.operation, stallTimeoutMs },
1541
+ message: `Transfer attempt ${String(attempt)} stalled (no progress for ${String(stallTimeoutMs)}ms): ${job.id}`,
1542
+ retryable
1543
+ })
1544
+ );
1545
+ }, stallTimeoutMs);
1546
+ };
1547
+ armStallWatchdog();
1548
+ return {
1549
+ dispose: () => {
1550
+ if (attemptTimer !== void 0) clearTimeout(attemptTimer);
1551
+ if (stallTimer !== void 0) clearTimeout(stallTimer);
1552
+ parentSignal?.removeEventListener("abort", abortFromParent);
1553
+ },
1554
+ notifyProgress: armStallWatchdog,
1555
+ signal: controller.signal
1556
+ };
1557
+ }
1558
+ function sleepWithAbort(delayMs, signal, job) {
1559
+ return new Promise((resolve, reject) => {
1560
+ if (signal === void 0) {
1561
+ setTimeout(resolve, delayMs);
1562
+ return;
1563
+ }
1564
+ if (signal.aborted) {
1565
+ reject(toAbortFailure(signal, job));
1566
+ return;
1567
+ }
1568
+ const rejectAbort = () => {
1569
+ clearTimeout(timer);
1570
+ reject(toAbortFailure(signal, job));
1571
+ };
1572
+ const timer = setTimeout(() => {
1573
+ signal.removeEventListener("abort", rejectAbort);
1574
+ resolve();
1575
+ }, delayMs);
1576
+ signal.addEventListener("abort", rejectAbort, { once: true });
1577
+ });
1578
+ }
1579
+ function toAbortFailure(signal, job) {
1580
+ if (signal.reason instanceof ZeroTransferError) {
1581
+ return signal.reason;
1582
+ }
1583
+ return new AbortError({
1584
+ details: { jobId: job.id, operation: job.operation },
1585
+ message: `Transfer job aborted: ${job.id}`,
1586
+ retryable: false
1587
+ });
1588
+ }
1589
+ function normalizeDelayMs(value) {
1590
+ if (value === void 0 || !Number.isFinite(value) || value <= 0) {
1591
+ return 0;
1592
+ }
1593
+ return Math.floor(value);
1594
+ }
1411
1595
  function normalizeTimeoutMs(value) {
1412
1596
  if (value === void 0 || !Number.isFinite(value) || value <= 0) {
1413
1597
  return void 0;
@@ -1576,7 +1760,7 @@ async function runRoute(options) {
1576
1760
  const executor = createProviderTransferExecutor({
1577
1761
  resolveSession: ({ role }) => sessions.get(role)
1578
1762
  });
1579
- return await engine.execute(job, executor, buildExecuteOptions(options));
1763
+ return await engine.execute(job, executor, buildExecuteOptions(options, client));
1580
1764
  } finally {
1581
1765
  if (destinationSession !== void 0) {
1582
1766
  await destinationSession.disconnect();
@@ -1613,12 +1797,14 @@ function defaultJobId(route, now) {
1613
1797
  const timestamp = (now?.() ?? /* @__PURE__ */ new Date()).getTime();
1614
1798
  return `route:${route.id}:${timestamp.toString(36)}`;
1615
1799
  }
1616
- function buildExecuteOptions(options) {
1800
+ function buildExecuteOptions(options, client) {
1617
1801
  const execute = {};
1802
+ const retry = options.retry ?? client.defaults?.retry;
1803
+ const timeout = options.timeout ?? client.defaults?.timeout;
1618
1804
  if (options.signal !== void 0) execute.signal = options.signal;
1619
- if (options.retry !== void 0) execute.retry = options.retry;
1805
+ if (retry !== void 0) execute.retry = retry;
1620
1806
  if (options.onProgress !== void 0) execute.onProgress = options.onProgress;
1621
- if (options.timeout !== void 0) execute.timeout = options.timeout;
1807
+ if (timeout !== void 0) execute.timeout = timeout;
1622
1808
  if (options.bandwidthLimit !== void 0) execute.bandwidthLimit = options.bandwidthLimit;
1623
1809
  return execute;
1624
1810
  }
@@ -1675,41 +1861,6 @@ function defaultRouteSuffix(source, destination) {
1675
1861
  return `${source}->${destination}`;
1676
1862
  }
1677
1863
 
1678
- // src/logging/redaction.ts
1679
- var REDACTED = "[REDACTED]";
1680
- var SENSITIVE_KEY_PATTERN = /(?:password|passphrase|privatekey|token|secret|username|user)$/i;
1681
- var SECRET_COMMAND_PATTERN = /^(PASS|USER|ACCT)\s+(.+)$/i;
1682
- function isSensitiveKey(key) {
1683
- return SENSITIVE_KEY_PATTERN.test(key.replace(/[_-]/g, ""));
1684
- }
1685
- function redactCommand(command) {
1686
- return command.replace(SECRET_COMMAND_PATTERN, (_fullMatch, commandName) => {
1687
- return `${commandName.toUpperCase()} ${REDACTED}`;
1688
- });
1689
- }
1690
- function redactValue(value) {
1691
- if (typeof value === "string") {
1692
- return redactCommand(value);
1693
- }
1694
- if (Array.isArray(value)) {
1695
- return value.map((item) => redactValue(item));
1696
- }
1697
- if (value !== null && typeof value === "object") {
1698
- return redactObject(value);
1699
- }
1700
- return value;
1701
- }
1702
- function redactObject(input) {
1703
- return Object.fromEntries(
1704
- Object.entries(input).map(([key, value]) => {
1705
- if (isSensitiveKey(key)) {
1706
- return [key, REDACTED];
1707
- }
1708
- return [key, redactValue(value)];
1709
- })
1710
- );
1711
- }
1712
-
1713
1864
  // src/profiles/SecretSource.ts
1714
1865
  var import_node_buffer2 = require("buffer");
1715
1866
  var import_promises = require("fs/promises");
@@ -2081,11 +2232,11 @@ var import_promises2 = require("fs/promises");
2081
2232
  var import_node_path2 = __toESM(require("path"));
2082
2233
 
2083
2234
  // src/utils/path.ts
2084
- var UNSAFE_FTP_ARGUMENT_PATTERN = /[\r\n]/;
2235
+ var UNSAFE_FTP_ARGUMENT_PATTERN = /[\r\n\0]/;
2085
2236
  function assertSafeFtpArgument(value, label = "path") {
2086
2237
  if (UNSAFE_FTP_ARGUMENT_PATTERN.test(value)) {
2087
2238
  throw new ConfigurationError({
2088
- message: `Unsafe FTP ${label}: CR and LF characters are not allowed`,
2239
+ message: `Unsafe FTP ${label}: CR, LF, and NUL characters are not allowed`,
2089
2240
  retryable: false,
2090
2241
  details: {
2091
2242
  label
@@ -3387,7 +3538,6 @@ function expandAlgorithms(values) {
3387
3538
  }
3388
3539
 
3389
3540
  // src/profiles/importers/FileZillaImporter.ts
3390
- var import_node_buffer5 = require("buffer");
3391
3541
  function importFileZillaSites(xml) {
3392
3542
  const events = tokenizeXml(xml);
3393
3543
  if (events.length === 0) {
@@ -3403,7 +3553,6 @@ function importFileZillaSites(xml) {
3403
3553
  const folderNamePending = [];
3404
3554
  let inServer = false;
3405
3555
  let serverFields = {};
3406
- let serverPasswordEncoding;
3407
3556
  let activeTag;
3408
3557
  let captureFolderName = false;
3409
3558
  for (const event of events) {
@@ -3416,13 +3565,9 @@ function importFileZillaSites(xml) {
3416
3565
  if (event.name === "Server") {
3417
3566
  inServer = true;
3418
3567
  serverFields = {};
3419
- serverPasswordEncoding = void 0;
3420
3568
  continue;
3421
3569
  }
3422
3570
  activeTag = event.name;
3423
- if (event.name === "Pass" && inServer) {
3424
- serverPasswordEncoding = event.attributes["encoding"];
3425
- }
3426
3571
  if (event.name === "Name" && !inServer && folderNamePending.length > 0) {
3427
3572
  captureFolderName = true;
3428
3573
  }
@@ -3448,7 +3593,7 @@ function importFileZillaSites(xml) {
3448
3593
  }
3449
3594
  if (event.name === "Server") {
3450
3595
  const folder = folderStack.filter((segment) => segment !== "");
3451
- const result = buildSiteFromFields(serverFields, serverPasswordEncoding);
3596
+ const result = buildSiteFromFields(serverFields);
3452
3597
  if (result.kind === "site") {
3453
3598
  sites.push({ ...result.site, folder });
3454
3599
  } else {
@@ -3460,7 +3605,6 @@ function importFileZillaSites(xml) {
3460
3605
  }
3461
3606
  inServer = false;
3462
3607
  serverFields = {};
3463
- serverPasswordEncoding = void 0;
3464
3608
  activeTag = void 0;
3465
3609
  continue;
3466
3610
  }
@@ -3469,7 +3613,7 @@ function importFileZillaSites(xml) {
3469
3613
  }
3470
3614
  return { sites, skipped };
3471
3615
  }
3472
- function buildSiteFromFields(fields, passwordEncoding) {
3616
+ function buildSiteFromFields(fields) {
3473
3617
  const name = (fields["Name"] ?? fields["Host"] ?? "Untitled").trim();
3474
3618
  const host = (fields["Host"] ?? "").trim();
3475
3619
  if (host === "") return { kind: "skipped", name };
@@ -3488,18 +3632,9 @@ function buildSiteFromFields(fields, passwordEncoding) {
3488
3632
  }
3489
3633
  const user = fields["User"]?.trim();
3490
3634
  if (user !== void 0 && user !== "") profile.username = { value: user };
3491
- let password;
3492
3635
  const rawPass = fields["Pass"];
3493
- if (rawPass !== void 0 && rawPass !== "") {
3494
- if (passwordEncoding === "base64") {
3495
- password = import_node_buffer5.Buffer.from(rawPass, "base64").toString("utf8");
3496
- } else {
3497
- password = rawPass;
3498
- }
3499
- if (password !== void 0 && password !== "") profile.password = { value: password };
3500
- }
3501
- const site = { name, profile };
3502
- if (password !== void 0) site.password = password;
3636
+ const hasStoredPassword = rawPass !== void 0 && rawPass !== "";
3637
+ const site = { hasStoredPassword, name, profile };
3503
3638
  const logonText = fields["Logontype"];
3504
3639
  if (logonText !== void 0) {
3505
3640
  const logonType = Number.parseInt(logonText.trim(), 10);
@@ -3742,6 +3877,62 @@ function mapFtp550(details) {
3742
3877
  return new PermissionDeniedError(details);
3743
3878
  }
3744
3879
 
3880
+ // src/transfers/createDefaultRetryPolicy.ts
3881
+ var DEFAULT_MAX_ATTEMPTS = 4;
3882
+ var DEFAULT_BASE_DELAY_MS = 250;
3883
+ var DEFAULT_MAX_DELAY_MS = 3e4;
3884
+ var DEFAULT_MAX_ELAPSED_MS = 3e5;
3885
+ function createDefaultRetryPolicy(options = {}) {
3886
+ const maxAttempts = normalizePositiveInteger(options.maxAttempts, DEFAULT_MAX_ATTEMPTS);
3887
+ const baseDelayMs = normalizeNonNegative(options.baseDelayMs, DEFAULT_BASE_DELAY_MS);
3888
+ const maxDelayMs = normalizeNonNegative(options.maxDelayMs, DEFAULT_MAX_DELAY_MS);
3889
+ const maxElapsedMs = normalizeNonNegative(options.maxElapsedMs, DEFAULT_MAX_ELAPSED_MS);
3890
+ const random = options.random ?? Math.random;
3891
+ return {
3892
+ getDelayMs(input) {
3893
+ const retryAfterMs = readRetryAfterMs(input.error);
3894
+ if (retryAfterMs !== void 0) {
3895
+ return retryAfterMs;
3896
+ }
3897
+ const exponentialMs = baseDelayMs * 2 ** (input.attempt - 1);
3898
+ const cappedMs = Math.min(maxDelayMs, exponentialMs);
3899
+ return Math.floor(random() * cappedMs);
3900
+ },
3901
+ maxAttempts,
3902
+ shouldRetry(input) {
3903
+ if (!(input.error instanceof ZeroTransferError) || !input.error.retryable) {
3904
+ return false;
3905
+ }
3906
+ if (input.elapsedMs >= maxElapsedMs) {
3907
+ return false;
3908
+ }
3909
+ const retryAfterMs = readRetryAfterMs(input.error);
3910
+ if (retryAfterMs !== void 0 && input.elapsedMs + retryAfterMs > maxElapsedMs) {
3911
+ return false;
3912
+ }
3913
+ return true;
3914
+ }
3915
+ };
3916
+ }
3917
+ function readRetryAfterMs(error) {
3918
+ if (!(error instanceof ZeroTransferError)) return void 0;
3919
+ const value = error.details?.["retryAfterMs"];
3920
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return void 0;
3921
+ return Math.floor(value);
3922
+ }
3923
+ function normalizePositiveInteger(value, fallback) {
3924
+ if (value === void 0 || !Number.isFinite(value) || value < 1) {
3925
+ return fallback;
3926
+ }
3927
+ return Math.floor(value);
3928
+ }
3929
+ function normalizeNonNegative(value, fallback) {
3930
+ if (value === void 0 || !Number.isFinite(value) || value < 0) {
3931
+ return fallback;
3932
+ }
3933
+ return Math.floor(value);
3934
+ }
3935
+
3745
3936
  // src/transfers/TransferPlan.ts
3746
3937
  function createTransferPlan(input) {
3747
3938
  const plan = {
@@ -3839,8 +4030,8 @@ var TransferQueue = class {
3839
4030
  this.concurrency = normalizeConcurrency(options.concurrency);
3840
4031
  this.defaultExecutor = options.executor;
3841
4032
  this.resolveExecutor = options.resolveExecutor;
3842
- this.retry = options.retry;
3843
- this.timeout = options.timeout;
4033
+ this.retry = options.retry ?? options.client?.defaults?.retry;
4034
+ this.timeout = options.timeout ?? options.client?.defaults?.timeout;
3844
4035
  this.bandwidthLimit = options.bandwidthLimit;
3845
4036
  this.onProgress = options.onProgress;
3846
4037
  this.onReceipt = options.onReceipt;
@@ -4917,10 +5108,10 @@ function isMainModule(importMetaUrl) {
4917
5108
  }
4918
5109
 
4919
5110
  // src/protocols/ssh/transport/SshTransportConnection.ts
4920
- var import_node_buffer15 = require("buffer");
5111
+ var import_node_buffer14 = require("buffer");
4921
5112
 
4922
5113
  // src/protocols/ssh/binary/SshDataReader.ts
4923
- var import_node_buffer6 = require("buffer");
5114
+ var import_node_buffer5 = require("buffer");
4924
5115
  var SshDataReader = class {
4925
5116
  constructor(source) {
4926
5117
  this.source = source;
@@ -4946,18 +5137,18 @@ var SshDataReader = class {
4946
5137
  this.ensureAvailable(length, "bytes");
4947
5138
  const data = this.source.subarray(this.offset, this.offset + length);
4948
5139
  this.offset += length;
4949
- return import_node_buffer6.Buffer.from(data);
5140
+ return import_node_buffer5.Buffer.from(data);
4950
5141
  }
4951
5142
  readUint32() {
4952
5143
  this.ensureAvailable(4, "uint32");
4953
- const buffer = import_node_buffer6.Buffer.from(this.source);
5144
+ const buffer = import_node_buffer5.Buffer.from(this.source);
4954
5145
  const value = buffer.readUInt32BE(this.offset);
4955
5146
  this.offset += 4;
4956
5147
  return value;
4957
5148
  }
4958
5149
  readUint64() {
4959
5150
  this.ensureAvailable(8, "uint64");
4960
- const buffer = import_node_buffer6.Buffer.from(this.source);
5151
+ const buffer = import_node_buffer5.Buffer.from(this.source);
4961
5152
  const value = buffer.readBigUInt64BE(this.offset);
4962
5153
  this.offset += 8;
4963
5154
  return value;
@@ -4967,7 +5158,7 @@ var SshDataReader = class {
4967
5158
  this.ensureAvailable(length, "string");
4968
5159
  const data = this.source.subarray(this.offset, this.offset + length);
4969
5160
  this.offset += length;
4970
- return import_node_buffer6.Buffer.from(data);
5161
+ return import_node_buffer5.Buffer.from(data);
4971
5162
  }
4972
5163
  readUtf8String() {
4973
5164
  return this.readString().toString("utf8");
@@ -5012,7 +5203,7 @@ var SshDataReader = class {
5012
5203
  };
5013
5204
 
5014
5205
  // src/protocols/ssh/binary/SshDataWriter.ts
5015
- var import_node_buffer7 = require("buffer");
5206
+ var import_node_buffer6 = require("buffer");
5016
5207
  var MAX_UINT32 = 4294967295;
5017
5208
  var MAX_UINT64 = (1n << 64n) - 1n;
5018
5209
  var SshDataWriter = class {
@@ -5020,7 +5211,7 @@ var SshDataWriter = class {
5020
5211
  length = 0;
5021
5212
  writeByte(value) {
5022
5213
  this.assertByte(value, "byte");
5023
- const chunk = import_node_buffer7.Buffer.alloc(1);
5214
+ const chunk = import_node_buffer6.Buffer.alloc(1);
5024
5215
  chunk.writeUInt8(value, 0);
5025
5216
  return this.push(chunk);
5026
5217
  }
@@ -5028,7 +5219,7 @@ var SshDataWriter = class {
5028
5219
  return this.writeByte(value ? 1 : 0);
5029
5220
  }
5030
5221
  writeBytes(value) {
5031
- return this.push(import_node_buffer7.Buffer.from(value));
5222
+ return this.push(import_node_buffer6.Buffer.from(value));
5032
5223
  }
5033
5224
  writeUint32(value) {
5034
5225
  if (!Number.isInteger(value) || value < 0 || value > MAX_UINT32) {
@@ -5038,7 +5229,7 @@ var SshDataWriter = class {
5038
5229
  retryable: false
5039
5230
  });
5040
5231
  }
5041
- const chunk = import_node_buffer7.Buffer.alloc(4);
5232
+ const chunk = import_node_buffer6.Buffer.alloc(4);
5042
5233
  chunk.writeUInt32BE(value, 0);
5043
5234
  return this.push(chunk);
5044
5235
  }
@@ -5050,12 +5241,12 @@ var SshDataWriter = class {
5050
5241
  retryable: false
5051
5242
  });
5052
5243
  }
5053
- const chunk = import_node_buffer7.Buffer.alloc(8);
5244
+ const chunk = import_node_buffer6.Buffer.alloc(8);
5054
5245
  chunk.writeBigUInt64BE(value, 0);
5055
5246
  return this.push(chunk);
5056
5247
  }
5057
5248
  writeString(value, encoding = "utf8") {
5058
- const payload = typeof value === "string" ? import_node_buffer7.Buffer.from(value, encoding) : import_node_buffer7.Buffer.from(value);
5249
+ const payload = typeof value === "string" ? import_node_buffer6.Buffer.from(value, encoding) : import_node_buffer6.Buffer.from(value);
5059
5250
  this.writeUint32(payload.length);
5060
5251
  return this.push(payload);
5061
5252
  }
@@ -5077,7 +5268,7 @@ var SshDataWriter = class {
5077
5268
  return this.writeString(values.join(","), "ascii");
5078
5269
  }
5079
5270
  toBuffer() {
5080
- return import_node_buffer7.Buffer.concat(this.chunks, this.length);
5271
+ return import_node_buffer6.Buffer.concat(this.chunks, this.length);
5081
5272
  }
5082
5273
  push(chunk) {
5083
5274
  this.chunks.push(chunk);
@@ -5095,23 +5286,23 @@ var SshDataWriter = class {
5095
5286
  }
5096
5287
  };
5097
5288
  function normalizePositiveMpint(value) {
5098
- const input = import_node_buffer7.Buffer.from(value);
5289
+ const input = import_node_buffer6.Buffer.from(value);
5099
5290
  let offset = 0;
5100
5291
  while (offset < input.length && input[offset] === 0) {
5101
5292
  offset += 1;
5102
5293
  }
5103
5294
  if (offset >= input.length) {
5104
- return import_node_buffer7.Buffer.alloc(0);
5295
+ return import_node_buffer6.Buffer.alloc(0);
5105
5296
  }
5106
5297
  const stripped = input.subarray(offset);
5107
5298
  if ((stripped[0] & 128) === 128) {
5108
- return import_node_buffer7.Buffer.concat([import_node_buffer7.Buffer.from([0]), stripped]);
5299
+ return import_node_buffer6.Buffer.concat([import_node_buffer6.Buffer.from([0]), stripped]);
5109
5300
  }
5110
5301
  return stripped;
5111
5302
  }
5112
5303
 
5113
5304
  // src/protocols/ssh/transport/SshTransportHandshake.ts
5114
- var import_node_buffer13 = require("buffer");
5305
+ var import_node_buffer12 = require("buffer");
5115
5306
 
5116
5307
  // src/protocols/ssh/transport/SshAlgorithmNegotiation.ts
5117
5308
  var DEFAULT_SSH_ALGORITHM_PREFERENCES = {
@@ -5273,12 +5464,12 @@ function parseSshIdentificationLine(line) {
5273
5464
  }
5274
5465
 
5275
5466
  // src/protocols/ssh/transport/SshKexInit.ts
5276
- var import_node_buffer8 = require("buffer");
5467
+ var import_node_buffer7 = require("buffer");
5277
5468
  var import_node_crypto2 = require("crypto");
5278
5469
  var SSH_MSG_KEXINIT = 20;
5279
5470
  var KEXINIT_COOKIE_LENGTH = 16;
5280
5471
  function encodeSshKexInitMessage(options) {
5281
- const cookie = options.cookie === void 0 ? (0, import_node_crypto2.randomBytes)(KEXINIT_COOKIE_LENGTH) : import_node_buffer8.Buffer.from(options.cookie);
5472
+ const cookie = options.cookie === void 0 ? (0, import_node_crypto2.randomBytes)(KEXINIT_COOKIE_LENGTH) : import_node_buffer7.Buffer.from(options.cookie);
5282
5473
  if (cookie.length !== KEXINIT_COOKIE_LENGTH) {
5283
5474
  throw new ConfigurationError({
5284
5475
  details: { actualLength: cookie.length, expectedLength: KEXINIT_COOKIE_LENGTH },
@@ -5348,12 +5539,12 @@ function decodeSshKexInitMessage(payload) {
5348
5539
  }
5349
5540
 
5350
5541
  // src/protocols/ssh/transport/SshKexCurve25519.ts
5351
- var import_node_buffer9 = require("buffer");
5542
+ var import_node_buffer8 = require("buffer");
5352
5543
  var import_node_crypto3 = require("crypto");
5353
5544
  var SSH_MSG_KEX_ECDH_INIT = 30;
5354
5545
  var SSH_MSG_KEX_ECDH_REPLY = 31;
5355
5546
  var X25519_PUBLIC_KEY_LENGTH = 32;
5356
- var X25519_SPKI_PREFIX = import_node_buffer9.Buffer.from("302a300506032b656e032100", "hex");
5547
+ var X25519_SPKI_PREFIX = import_node_buffer8.Buffer.from("302a300506032b656e032100", "hex");
5357
5548
  function createCurve25519Ephemeral() {
5358
5549
  const { privateKey, publicKey } = (0, import_node_crypto3.generateKeyPairSync)("x25519");
5359
5550
  const encodedPublicKey = exportX25519PublicKeyRaw(publicKey);
@@ -5398,7 +5589,7 @@ function exportX25519PublicKeyRaw(publicKey) {
5398
5589
  }
5399
5590
  function importX25519PublicKeyRaw(raw) {
5400
5591
  const normalized = normalizeX25519PublicKey(raw, "server");
5401
- const der = import_node_buffer9.Buffer.concat([X25519_SPKI_PREFIX, normalized]);
5592
+ const der = import_node_buffer8.Buffer.concat([X25519_SPKI_PREFIX, normalized]);
5402
5593
  return (0, import_node_crypto3.createPublicKey)({
5403
5594
  format: "der",
5404
5595
  key: der,
@@ -5406,7 +5597,7 @@ function importX25519PublicKeyRaw(raw) {
5406
5597
  });
5407
5598
  }
5408
5599
  function normalizeX25519PublicKey(value, label) {
5409
- const key = import_node_buffer9.Buffer.from(value);
5600
+ const key = import_node_buffer8.Buffer.from(value);
5410
5601
  if (key.length !== X25519_PUBLIC_KEY_LENGTH) {
5411
5602
  throw new ConfigurationError({
5412
5603
  details: { keyLength: key.length, label },
@@ -5419,7 +5610,7 @@ function normalizeX25519PublicKey(value, label) {
5419
5610
  }
5420
5611
 
5421
5612
  // src/protocols/ssh/transport/SshKeyDerivation.ts
5422
- var import_node_buffer10 = require("buffer");
5613
+ var import_node_buffer9 = require("buffer");
5423
5614
  var import_node_crypto4 = require("crypto");
5424
5615
  function deriveSshSessionKeys(input) {
5425
5616
  const hashAlgorithm = resolveKexHashAlgorithm(input.kexAlgorithm);
@@ -5441,7 +5632,7 @@ function deriveSshSessionKeys(input) {
5441
5632
  input.negotiatedAlgorithms.encryptionServerToClient,
5442
5633
  input.negotiatedAlgorithms.macServerToClient
5443
5634
  );
5444
- const sharedSecret = import_node_buffer10.Buffer.from(input.sharedSecret);
5635
+ const sharedSecret = import_node_buffer9.Buffer.from(input.sharedSecret);
5445
5636
  return {
5446
5637
  clientToServer: {
5447
5638
  encryptionKey: deriveMaterial(
@@ -5491,21 +5682,21 @@ function computeCurve25519ExchangeHash(input, hashAlgorithm) {
5491
5682
  }
5492
5683
  function deriveMaterial(sharedSecret, exchangeHash, sessionId, letter, length, hashAlgorithm) {
5493
5684
  if (length <= 0) {
5494
- return import_node_buffer10.Buffer.alloc(0);
5685
+ return import_node_buffer9.Buffer.alloc(0);
5495
5686
  }
5496
5687
  const result = [];
5497
5688
  const first2 = (0, import_node_crypto4.createHash)(hashAlgorithm).update(
5498
5689
  new SshDataWriter().writeMpint(sharedSecret).writeBytes(exchangeHash).writeByte(letter.charCodeAt(0)).writeBytes(sessionId).toBuffer()
5499
5690
  ).digest();
5500
5691
  result.push(first2);
5501
- while (import_node_buffer10.Buffer.concat(result).length < length) {
5502
- const previous = import_node_buffer10.Buffer.concat(result);
5692
+ while (import_node_buffer9.Buffer.concat(result).length < length) {
5693
+ const previous = import_node_buffer9.Buffer.concat(result);
5503
5694
  const next = (0, import_node_crypto4.createHash)(hashAlgorithm).update(
5504
5695
  new SshDataWriter().writeMpint(sharedSecret).writeBytes(exchangeHash).writeBytes(previous).toBuffer()
5505
5696
  ).digest();
5506
5697
  result.push(next);
5507
5698
  }
5508
- return import_node_buffer10.Buffer.concat(result).subarray(0, length);
5699
+ return import_node_buffer9.Buffer.concat(result).subarray(0, length);
5509
5700
  }
5510
5701
  function resolveKexHashAlgorithm(kexAlgorithm) {
5511
5702
  if (kexAlgorithm === "curve25519-sha256" || kexAlgorithm === "curve25519-sha256@libssh.org") {
@@ -5593,20 +5784,21 @@ function decodeSshNewKeysMessage(payload) {
5593
5784
  }
5594
5785
 
5595
5786
  // src/protocols/ssh/transport/SshTransportPacket.ts
5596
- var import_node_buffer11 = require("buffer");
5787
+ var import_node_buffer10 = require("buffer");
5597
5788
  var import_node_crypto5 = require("crypto");
5598
5789
  var MIN_PADDING_LENGTH = 4;
5599
5790
  var MIN_PACKET_LENGTH = 1 + MIN_PADDING_LENGTH;
5791
+ var MAX_SSH_PACKET_LENGTH = 256 * 1024;
5600
5792
  function encodeSshTransportPacket(payload, options = {}) {
5601
- const body = import_node_buffer11.Buffer.from(payload);
5793
+ const body = import_node_buffer10.Buffer.from(payload);
5602
5794
  const blockSize = normalizeBlockSize(options.blockSize ?? 8);
5603
5795
  let paddingLength = MIN_PADDING_LENGTH;
5604
5796
  while ((1 + body.length + paddingLength + 4) % blockSize !== 0) {
5605
5797
  paddingLength += 1;
5606
5798
  }
5607
- const padding = options.randomPadding === false ? import_node_buffer11.Buffer.alloc(paddingLength) : (0, import_node_crypto5.randomBytes)(paddingLength);
5799
+ const padding = options.randomPadding === false ? import_node_buffer10.Buffer.alloc(paddingLength) : (0, import_node_crypto5.randomBytes)(paddingLength);
5608
5800
  const packetLength = 1 + body.length + paddingLength;
5609
- const frame = import_node_buffer11.Buffer.alloc(4 + packetLength);
5801
+ const frame = import_node_buffer10.Buffer.alloc(4 + packetLength);
5610
5802
  frame.writeUInt32BE(packetLength, 0);
5611
5803
  frame.writeUInt8(paddingLength, 4);
5612
5804
  body.copy(frame, 5);
@@ -5614,7 +5806,7 @@ function encodeSshTransportPacket(payload, options = {}) {
5614
5806
  return frame;
5615
5807
  }
5616
5808
  function decodeSshTransportPacket(frame) {
5617
- const bytes = import_node_buffer11.Buffer.from(frame);
5809
+ const bytes = import_node_buffer10.Buffer.from(frame);
5618
5810
  if (bytes.length < 4 + MIN_PACKET_LENGTH) {
5619
5811
  throw new ParseError({
5620
5812
  details: { length: bytes.length },
@@ -5668,12 +5860,20 @@ function decodeSshTransportPacket(frame) {
5668
5860
  };
5669
5861
  }
5670
5862
  var SshTransportPacketFramer = class {
5671
- pending = import_node_buffer11.Buffer.alloc(0);
5863
+ pending = import_node_buffer10.Buffer.alloc(0);
5672
5864
  push(chunk) {
5673
- this.pending = import_node_buffer11.Buffer.concat([this.pending, import_node_buffer11.Buffer.from(chunk)]);
5865
+ this.pending = import_node_buffer10.Buffer.concat([this.pending, import_node_buffer10.Buffer.from(chunk)]);
5674
5866
  const packets = [];
5675
5867
  while (this.pending.length >= 4) {
5676
5868
  const packetLength = this.pending.readUInt32BE(0);
5869
+ if (packetLength > MAX_SSH_PACKET_LENGTH) {
5870
+ throw new ParseError({
5871
+ details: { maxPacketLength: MAX_SSH_PACKET_LENGTH, packetLength },
5872
+ message: "SSH transport packet length exceeds the maximum accepted size",
5873
+ protocol: "sftp",
5874
+ retryable: false
5875
+ });
5876
+ }
5677
5877
  const frameLength = 4 + packetLength;
5678
5878
  if (this.pending.length < frameLength) {
5679
5879
  break;
@@ -5689,8 +5889,8 @@ var SshTransportPacketFramer = class {
5689
5889
  }
5690
5890
  /** Returns and clears any bytes buffered but not yet part of a complete packet. */
5691
5891
  takeRemainingBytes() {
5692
- const remaining = import_node_buffer11.Buffer.from(this.pending);
5693
- this.pending = import_node_buffer11.Buffer.alloc(0);
5892
+ const remaining = import_node_buffer10.Buffer.from(this.pending);
5893
+ this.pending = import_node_buffer10.Buffer.alloc(0);
5694
5894
  return remaining;
5695
5895
  }
5696
5896
  };
@@ -5707,10 +5907,10 @@ function normalizeBlockSize(blockSize) {
5707
5907
  }
5708
5908
 
5709
5909
  // src/protocols/ssh/transport/SshHostKeyVerification.ts
5710
- var import_node_buffer12 = require("buffer");
5910
+ var import_node_buffer11 = require("buffer");
5711
5911
  var import_node_crypto6 = require("crypto");
5712
5912
  var ED25519_RAW_KEY_LENGTH = 32;
5713
- var ED25519_SPKI_PREFIX = import_node_buffer12.Buffer.from("302a300506032b6570032100", "hex");
5913
+ var ED25519_SPKI_PREFIX = import_node_buffer11.Buffer.from("302a300506032b6570032100", "hex");
5714
5914
  function verifySshHostKeySignature(input) {
5715
5915
  const { algorithmName, publicKey } = parseHostKey(input.hostKeyBlob);
5716
5916
  const { signatureAlgorithm, signatureBytes } = parseSignatureBlob(input.signatureBlob);
@@ -5723,9 +5923,9 @@ function verifySshHostKeySignature(input) {
5723
5923
  });
5724
5924
  }
5725
5925
  const verified = verifySignature({
5726
- data: import_node_buffer12.Buffer.from(input.exchangeHash),
5926
+ data: import_node_buffer11.Buffer.from(input.exchangeHash),
5727
5927
  publicKey,
5728
- signature: import_node_buffer12.Buffer.from(signatureBytes),
5928
+ signature: import_node_buffer11.Buffer.from(signatureBytes),
5729
5929
  signatureAlgorithm
5730
5930
  });
5731
5931
  if (!verified) {
@@ -5754,7 +5954,7 @@ function parseHostKey(blob) {
5754
5954
  retryable: false
5755
5955
  });
5756
5956
  }
5757
- const spki = import_node_buffer12.Buffer.concat([ED25519_SPKI_PREFIX, raw]);
5957
+ const spki = import_node_buffer11.Buffer.concat([ED25519_SPKI_PREFIX, raw]);
5758
5958
  return {
5759
5959
  algorithmName,
5760
5960
  publicKey: (0, import_node_crypto6.createPublicKey)({ format: "der", key: spki, type: "spki" })
@@ -5860,37 +6060,37 @@ function verifySignature(input) {
5860
6060
  function rsaPublicKeyFromComponents(e, n) {
5861
6061
  const eDer = encodeAsn1Integer(e);
5862
6062
  const nDer = encodeAsn1Integer(n);
5863
- const rsaPublicKeyDer = encodeAsn1Sequence(import_node_buffer12.Buffer.concat([nDer, eDer]));
5864
- const bitStringContent = import_node_buffer12.Buffer.concat([import_node_buffer12.Buffer.from([0]), rsaPublicKeyDer]);
5865
- const bitString = import_node_buffer12.Buffer.concat([
5866
- import_node_buffer12.Buffer.from([3]),
6063
+ const rsaPublicKeyDer = encodeAsn1Sequence(import_node_buffer11.Buffer.concat([nDer, eDer]));
6064
+ const bitStringContent = import_node_buffer11.Buffer.concat([import_node_buffer11.Buffer.from([0]), rsaPublicKeyDer]);
6065
+ const bitString = import_node_buffer11.Buffer.concat([
6066
+ import_node_buffer11.Buffer.from([3]),
5867
6067
  encodeAsn1Length(bitStringContent.length),
5868
6068
  bitStringContent
5869
6069
  ]);
5870
- const algoId = import_node_buffer12.Buffer.from("300d06092a864886f70d010101 0500".replace(/\s+/g, ""), "hex");
5871
- const spki = encodeAsn1Sequence(import_node_buffer12.Buffer.concat([algoId, bitString]));
6070
+ const algoId = import_node_buffer11.Buffer.from("300d06092a864886f70d010101 0500".replace(/\s+/g, ""), "hex");
6071
+ const spki = encodeAsn1Sequence(import_node_buffer11.Buffer.concat([algoId, bitString]));
5872
6072
  return (0, import_node_crypto6.createPublicKey)({ format: "der", key: spki, type: "spki" });
5873
6073
  }
5874
6074
  function encodeAsn1Integer(value) {
5875
6075
  let body = value;
5876
6076
  while (body.length > 1 && body[0] === 0) body = body.subarray(1);
5877
6077
  if (body.length > 0 && (body[0] & 128) !== 0) {
5878
- body = import_node_buffer12.Buffer.concat([import_node_buffer12.Buffer.from([0]), body]);
6078
+ body = import_node_buffer11.Buffer.concat([import_node_buffer11.Buffer.from([0]), body]);
5879
6079
  }
5880
- return import_node_buffer12.Buffer.concat([import_node_buffer12.Buffer.from([2]), encodeAsn1Length(body.length), body]);
6080
+ return import_node_buffer11.Buffer.concat([import_node_buffer11.Buffer.from([2]), encodeAsn1Length(body.length), body]);
5881
6081
  }
5882
6082
  function encodeAsn1Sequence(content) {
5883
- return import_node_buffer12.Buffer.concat([import_node_buffer12.Buffer.from([48]), encodeAsn1Length(content.length), content]);
6083
+ return import_node_buffer11.Buffer.concat([import_node_buffer11.Buffer.from([48]), encodeAsn1Length(content.length), content]);
5884
6084
  }
5885
6085
  function encodeAsn1Length(length) {
5886
- if (length < 128) return import_node_buffer12.Buffer.from([length]);
6086
+ if (length < 128) return import_node_buffer11.Buffer.from([length]);
5887
6087
  const bytes = [];
5888
6088
  let n = length;
5889
6089
  while (n > 0) {
5890
6090
  bytes.unshift(n & 255);
5891
6091
  n >>>= 8;
5892
6092
  }
5893
- return import_node_buffer12.Buffer.from([128 | bytes.length, ...bytes]);
6093
+ return import_node_buffer11.Buffer.from([128 | bytes.length, ...bytes]);
5894
6094
  }
5895
6095
  var ECDSA_OID_BY_CURVE = {
5896
6096
  nistp256: "06082a8648ce3d030107",
@@ -5911,15 +6111,15 @@ function ecdsaPublicKeyFromPoint(curveIdentifier, point) {
5911
6111
  retryable: false
5912
6112
  });
5913
6113
  }
5914
- const algoIdContent = import_node_buffer12.Buffer.from(ECDSA_ALGORITHM_OID_HEX + oidHex, "hex");
6114
+ const algoIdContent = import_node_buffer11.Buffer.from(ECDSA_ALGORITHM_OID_HEX + oidHex, "hex");
5915
6115
  const algoId = encodeAsn1Sequence(algoIdContent);
5916
- const bitStringContent = import_node_buffer12.Buffer.concat([import_node_buffer12.Buffer.from([0]), point]);
5917
- const bitString = import_node_buffer12.Buffer.concat([
5918
- import_node_buffer12.Buffer.from([3]),
6116
+ const bitStringContent = import_node_buffer11.Buffer.concat([import_node_buffer11.Buffer.from([0]), point]);
6117
+ const bitString = import_node_buffer11.Buffer.concat([
6118
+ import_node_buffer11.Buffer.from([3]),
5919
6119
  encodeAsn1Length(bitStringContent.length),
5920
6120
  bitStringContent
5921
6121
  ]);
5922
- const spki = encodeAsn1Sequence(import_node_buffer12.Buffer.concat([algoId, bitString]));
6122
+ const spki = encodeAsn1Sequence(import_node_buffer11.Buffer.concat([algoId, bitString]));
5923
6123
  return (0, import_node_crypto6.createPublicKey)({ format: "der", key: spki, type: "spki" });
5924
6124
  }
5925
6125
  function sshEcdsaSignatureToDer(sshSignature) {
@@ -5928,7 +6128,7 @@ function sshEcdsaSignatureToDer(sshSignature) {
5928
6128
  const s = reader.readMpint();
5929
6129
  const rDer = encodeAsn1Integer(r);
5930
6130
  const sDer = encodeAsn1Integer(s);
5931
- return encodeAsn1Sequence(import_node_buffer12.Buffer.concat([rDer, sDer]));
6131
+ return encodeAsn1Sequence(import_node_buffer11.Buffer.concat([rDer, sDer]));
5932
6132
  }
5933
6133
 
5934
6134
  // src/protocols/ssh/transport/SshTransportHandshake.ts
@@ -5960,7 +6160,7 @@ var SshTransportHandshake = class {
5960
6160
  serverIdentification;
5961
6161
  /** Creates the first outbound bytes (client identification line). */
5962
6162
  createInitialClientBytes() {
5963
- return import_node_buffer13.Buffer.from(`${this.clientIdentificationLine}\r
6163
+ return import_node_buffer12.Buffer.from(`${this.clientIdentificationLine}\r
5964
6164
  `, "ascii");
5965
6165
  }
5966
6166
  /**
@@ -5984,7 +6184,7 @@ var SshTransportHandshake = class {
5984
6184
  }
5985
6185
  return { outbound };
5986
6186
  }
5987
- return this.pushServerBytesWithPhase(outbound, import_node_buffer13.Buffer.from(chunk));
6187
+ return this.pushServerBytesWithPhase(outbound, import_node_buffer12.Buffer.from(chunk));
5988
6188
  }
5989
6189
  getServerBannerLines() {
5990
6190
  return this.identificationLines;
@@ -6038,12 +6238,12 @@ var SshTransportHandshake = class {
6038
6238
  clientKexInitPayload: this.clientKexInitPayload,
6039
6239
  clientPublicKey: this.pendingCurve25519.publicKey,
6040
6240
  negotiatedAlgorithms,
6041
- serverHostKey: import_node_buffer13.Buffer.alloc(0),
6241
+ serverHostKey: import_node_buffer12.Buffer.alloc(0),
6042
6242
  serverIdentification: (this.serverIdentification ?? missingServerIdentificationError()).raw,
6043
- serverKexInitPayload: import_node_buffer13.Buffer.from(packet.payload),
6044
- serverPublicKey: import_node_buffer13.Buffer.alloc(0),
6045
- serverSignature: import_node_buffer13.Buffer.alloc(0),
6046
- sharedSecret: import_node_buffer13.Buffer.alloc(0)
6243
+ serverKexInitPayload: import_node_buffer12.Buffer.from(packet.payload),
6244
+ serverPublicKey: import_node_buffer12.Buffer.alloc(0),
6245
+ serverSignature: import_node_buffer12.Buffer.alloc(0),
6246
+ sharedSecret: import_node_buffer12.Buffer.alloc(0)
6047
6247
  };
6048
6248
  continue;
6049
6249
  }
@@ -6151,24 +6351,54 @@ var SshTransportHandshake = class {
6151
6351
  return { outbound };
6152
6352
  }
6153
6353
  };
6354
+ var MAX_IDENTIFICATION_LINE_BYTES = 8192;
6355
+ var MAX_PRE_IDENTIFICATION_LINES = 1024;
6154
6356
  var SshIdentificationAccumulator = class {
6155
- pending = import_node_buffer13.Buffer.alloc(0);
6357
+ pending = import_node_buffer12.Buffer.alloc(0);
6358
+ bannerLineCount = 0;
6156
6359
  push(chunk) {
6157
- this.pending = import_node_buffer13.Buffer.concat([this.pending, import_node_buffer13.Buffer.from(chunk)]);
6360
+ this.pending = import_node_buffer12.Buffer.concat([this.pending, import_node_buffer12.Buffer.from(chunk)]);
6158
6361
  const bannerLines = [];
6159
6362
  while (true) {
6160
6363
  const lfIndex = this.pending.indexOf(10);
6161
- if (lfIndex < 0) break;
6364
+ if (lfIndex < 0) {
6365
+ if (this.pending.length > MAX_IDENTIFICATION_LINE_BYTES) {
6366
+ throw new ProtocolError({
6367
+ details: { limitBytes: MAX_IDENTIFICATION_LINE_BYTES },
6368
+ message: "SSH identification line exceeds the maximum accepted length",
6369
+ protocol: "sftp",
6370
+ retryable: false
6371
+ });
6372
+ }
6373
+ break;
6374
+ }
6375
+ if (lfIndex > MAX_IDENTIFICATION_LINE_BYTES) {
6376
+ throw new ProtocolError({
6377
+ details: { limitBytes: MAX_IDENTIFICATION_LINE_BYTES },
6378
+ message: "SSH identification line exceeds the maximum accepted length",
6379
+ protocol: "sftp",
6380
+ retryable: false
6381
+ });
6382
+ }
6162
6383
  const lineText = trimLineEndings(this.pending.subarray(0, lfIndex + 1).toString("ascii"));
6163
- const remainder = import_node_buffer13.Buffer.from(this.pending.subarray(lfIndex + 1));
6384
+ const remainder = import_node_buffer12.Buffer.from(this.pending.subarray(lfIndex + 1));
6164
6385
  this.pending = remainder;
6165
6386
  if (lineText.startsWith("SSH-")) {
6166
- this.pending = import_node_buffer13.Buffer.alloc(0);
6387
+ this.pending = import_node_buffer12.Buffer.alloc(0);
6167
6388
  return { bannerLines, identLine: lineText, remainder };
6168
6389
  }
6390
+ this.bannerLineCount += 1;
6391
+ if (this.bannerLineCount > MAX_PRE_IDENTIFICATION_LINES) {
6392
+ throw new ProtocolError({
6393
+ details: { limitLines: MAX_PRE_IDENTIFICATION_LINES },
6394
+ message: "SSH server sent too many pre-identification banner lines",
6395
+ protocol: "sftp",
6396
+ retryable: false
6397
+ });
6398
+ }
6169
6399
  bannerLines.push(lineText);
6170
6400
  }
6171
- return { bannerLines, remainder: import_node_buffer13.Buffer.alloc(0) };
6401
+ return { bannerLines, remainder: import_node_buffer12.Buffer.alloc(0) };
6172
6402
  }
6173
6403
  };
6174
6404
  function trimLineEndings(value) {
@@ -6196,7 +6426,7 @@ function missingPendingKeyExchangeError() {
6196
6426
  }
6197
6427
 
6198
6428
  // src/protocols/ssh/transport/SshTransportProtection.ts
6199
- var import_node_buffer14 = require("buffer");
6429
+ var import_node_buffer13 = require("buffer");
6200
6430
  var import_node_crypto7 = require("crypto");
6201
6431
  function createSshTransportProtectionContext(input) {
6202
6432
  return {
@@ -6253,7 +6483,7 @@ var SshTransportPacketProtector = class {
6253
6483
  );
6254
6484
  const encrypted = this.cipher === void 0 ? clearPacket : this.cipher.update(clearPacket);
6255
6485
  this.sequenceNumber = this.sequenceNumber + 1 >>> 0;
6256
- return import_node_buffer14.Buffer.concat([encrypted, mac]);
6486
+ return import_node_buffer13.Buffer.concat([encrypted, mac]);
6257
6487
  }
6258
6488
  };
6259
6489
  var SshTransportPacketUnprotector = class {
@@ -6279,7 +6509,7 @@ var SshTransportPacketUnprotector = class {
6279
6509
  sequenceNumber;
6280
6510
  // Streaming framing state for pushBytes()
6281
6511
  framePartialDecrypted;
6282
- framePendingRaw = import_node_buffer14.Buffer.alloc(0);
6512
+ framePendingRaw = import_node_buffer13.Buffer.alloc(0);
6283
6513
  frameRemainingNeeded;
6284
6514
  getSequenceNumber() {
6285
6515
  return this.sequenceNumber;
@@ -6289,15 +6519,23 @@ var SshTransportPacketUnprotector = class {
6289
6519
  * Maintains internal framing state across calls - pass each `data` event chunk directly.
6290
6520
  */
6291
6521
  pushBytes(chunk) {
6292
- this.framePendingRaw = import_node_buffer14.Buffer.concat([this.framePendingRaw, chunk]);
6522
+ this.framePendingRaw = import_node_buffer13.Buffer.concat([this.framePendingRaw, chunk]);
6293
6523
  const results = [];
6294
6524
  while (true) {
6295
6525
  if (this.framePartialDecrypted === void 0) {
6296
6526
  if (this.framePendingRaw.length < this.blockLength) break;
6297
6527
  const firstBlock = this.framePendingRaw.subarray(0, this.blockLength);
6298
- this.framePendingRaw = import_node_buffer14.Buffer.from(this.framePendingRaw.subarray(this.blockLength));
6299
- this.framePartialDecrypted = this.decipher ? import_node_buffer14.Buffer.from(this.decipher.update(firstBlock)) : import_node_buffer14.Buffer.from(firstBlock);
6528
+ this.framePendingRaw = import_node_buffer13.Buffer.from(this.framePendingRaw.subarray(this.blockLength));
6529
+ this.framePartialDecrypted = this.decipher ? import_node_buffer13.Buffer.from(this.decipher.update(firstBlock)) : import_node_buffer13.Buffer.from(firstBlock);
6300
6530
  const packetLength = this.framePartialDecrypted.readUInt32BE(0);
6531
+ if (packetLength > MAX_SSH_PACKET_LENGTH) {
6532
+ throw new ProtocolError({
6533
+ details: { maxPacketLength: MAX_SSH_PACKET_LENGTH, packetLength },
6534
+ message: "SSH encrypted packet length exceeds the maximum accepted size",
6535
+ protocol: "sftp",
6536
+ retryable: false
6537
+ });
6538
+ }
6301
6539
  const remaining = 4 + packetLength - this.blockLength + this.macLength;
6302
6540
  if (remaining < 0) {
6303
6541
  throw new ProtocolError({
@@ -6313,9 +6551,9 @@ var SshTransportPacketUnprotector = class {
6313
6551
  if (this.framePendingRaw.length < needed) break;
6314
6552
  const encryptedRest = this.framePendingRaw.subarray(0, needed - this.macLength);
6315
6553
  const receivedMac = this.framePendingRaw.subarray(needed - this.macLength, needed);
6316
- this.framePendingRaw = import_node_buffer14.Buffer.from(this.framePendingRaw.subarray(needed));
6317
- const decryptedRest = encryptedRest.length > 0 ? this.decipher ? import_node_buffer14.Buffer.from(this.decipher.update(encryptedRest)) : import_node_buffer14.Buffer.from(encryptedRest) : import_node_buffer14.Buffer.alloc(0);
6318
- const clearPacket = import_node_buffer14.Buffer.concat([this.framePartialDecrypted, decryptedRest]);
6554
+ this.framePendingRaw = import_node_buffer13.Buffer.from(this.framePendingRaw.subarray(needed));
6555
+ const decryptedRest = encryptedRest.length > 0 ? this.decipher ? import_node_buffer13.Buffer.from(this.decipher.update(encryptedRest)) : import_node_buffer13.Buffer.from(encryptedRest) : import_node_buffer13.Buffer.alloc(0);
6556
+ const clearPacket = import_node_buffer13.Buffer.concat([this.framePartialDecrypted, decryptedRest]);
6319
6557
  const expectedMac = computeMac(
6320
6558
  this.macAlgorithm,
6321
6559
  this.options.keys.macKey,
@@ -6338,7 +6576,7 @@ var SshTransportPacketUnprotector = class {
6338
6576
  return results;
6339
6577
  }
6340
6578
  unprotectPayload(packet) {
6341
- const frame = import_node_buffer14.Buffer.from(packet);
6579
+ const frame = import_node_buffer13.Buffer.from(packet);
6342
6580
  if (frame.length < this.macLength) {
6343
6581
  throw new ProtocolError({
6344
6582
  details: { length: frame.length, macLength: this.macLength },
@@ -6479,10 +6717,10 @@ function resolveMacLength(encryptionAlgorithm, macAlgorithm) {
6479
6717
  }
6480
6718
  function computeMac(macAlgorithm, macKey, sequence, packet, macLength) {
6481
6719
  if (macLength === 0) {
6482
- return import_node_buffer14.Buffer.alloc(0);
6720
+ return import_node_buffer13.Buffer.alloc(0);
6483
6721
  }
6484
6722
  const hashName = macAlgorithm === "hmac-sha2-512" ? "sha512" : "sha256";
6485
- const sequenceBuffer = import_node_buffer14.Buffer.alloc(4);
6723
+ const sequenceBuffer = import_node_buffer13.Buffer.alloc(4);
6486
6724
  sequenceBuffer.writeUInt32BE(sequence >>> 0, 0);
6487
6725
  return (0, import_node_crypto7.createHmac)(hashName, macKey).update(sequenceBuffer).update(packet).digest().subarray(0, macLength);
6488
6726
  }
@@ -6689,7 +6927,7 @@ var SshTransportConnection = class {
6689
6927
  */
6690
6928
  sendPayload(payload) {
6691
6929
  this.assertConnected();
6692
- const frame = this.protector.protectPayload(import_node_buffer15.Buffer.from(payload));
6930
+ const frame = this.protector.protectPayload(import_node_buffer14.Buffer.from(payload));
6693
6931
  this.socket.write(frame);
6694
6932
  this.resetKeepaliveTimer();
6695
6933
  }
@@ -7157,7 +7395,7 @@ function buildKiRequest(args) {
7157
7395
  }
7158
7396
 
7159
7397
  // src/protocols/ssh/auth/SshPublickeyCredentialBuilder.ts
7160
- var import_node_buffer16 = require("buffer");
7398
+ var import_node_buffer15 = require("buffer");
7161
7399
  var import_node_crypto8 = require("crypto");
7162
7400
  var ED25519_RAW_KEY_LENGTH2 = 32;
7163
7401
  var ED25519_SPKI_PREFIX_LENGTH = 12;
@@ -7175,7 +7413,7 @@ function buildPublickeyCredential(options) {
7175
7413
  return {
7176
7414
  algorithmName: "ssh-ed25519",
7177
7415
  publicKeyBlob,
7178
- sign: (data) => (0, import_node_crypto8.sign)(null, import_node_buffer16.Buffer.from(data), privateKey),
7416
+ sign: (data) => (0, import_node_crypto8.sign)(null, import_node_buffer15.Buffer.from(data), privateKey),
7179
7417
  type: "publickey",
7180
7418
  username
7181
7419
  };
@@ -7193,7 +7431,7 @@ function buildPublickeyCredential(options) {
7193
7431
  return {
7194
7432
  algorithmName,
7195
7433
  publicKeyBlob,
7196
- sign: (data) => (0, import_node_crypto8.sign)(hash, import_node_buffer16.Buffer.from(data), privateKey),
7434
+ sign: (data) => (0, import_node_crypto8.sign)(hash, import_node_buffer15.Buffer.from(data), privateKey),
7197
7435
  type: "publickey",
7198
7436
  username
7199
7437
  };
@@ -7206,7 +7444,7 @@ function buildPublickeyCredential(options) {
7206
7444
  }
7207
7445
  function base64UrlToMpint(value) {
7208
7446
  const padded = value.replace(/-/g, "+").replace(/_/g, "/");
7209
- const buffer = import_node_buffer16.Buffer.from(padded, "base64");
7447
+ const buffer = import_node_buffer15.Buffer.from(padded, "base64");
7210
7448
  return buffer;
7211
7449
  }
7212
7450
  function createInvalidKeyError(message) {
@@ -7332,7 +7570,7 @@ function encodeSshChannelClose(recipientChannel) {
7332
7570
  }
7333
7571
 
7334
7572
  // src/protocols/ssh/connection/SshSessionChannel.ts
7335
- var import_node_buffer17 = require("buffer");
7573
+ var import_node_buffer16 = require("buffer");
7336
7574
  var INITIAL_WINDOW_SIZE = 256 * 1024;
7337
7575
  var MAX_PACKET_SIZE = 32 * 1024;
7338
7576
  var WINDOW_REFILL_THRESHOLD = 64 * 1024;
@@ -7490,7 +7728,7 @@ var SshSessionChannel = class {
7490
7728
  this.remoteWindowRemaining,
7491
7729
  this.remoteMaxPacketSize
7492
7730
  );
7493
- const chunk = import_node_buffer17.Buffer.from(data.subarray(offset, offset + chunkSize));
7731
+ const chunk = import_node_buffer16.Buffer.from(data.subarray(offset, offset + chunkSize));
7494
7732
  this.transport.sendPayload(
7495
7733
  encodeSshChannelData({ data: chunk, recipientChannel: this.remoteChannelId })
7496
7734
  );
@@ -7870,6 +8108,7 @@ function openTcpSocket(host, port, timeoutMs) {
7870
8108
  copyBetween,
7871
8109
  createAtomicDeployPlan,
7872
8110
  createBandwidthThrottle,
8111
+ createDefaultRetryPolicy,
7873
8112
  createLocalProviderFactory,
7874
8113
  createMemoryProviderFactory,
7875
8114
  createOAuthTokenSecretSource,
@@ -7906,8 +8145,10 @@ function openTcpSocket(host, port, timeoutMs) {
7906
8145
  parseRemoteManifest,
7907
8146
  redactCommand,
7908
8147
  redactConnectionProfile,
8148
+ redactErrorForLogging,
7909
8149
  redactObject,
7910
8150
  redactSecretSource,
8151
+ redactUrlForLogging,
7911
8152
  redactValue,
7912
8153
  resolveConnectionProfileSecrets,
7913
8154
  resolveOpenSshHost,