@zero-transfer/ssh 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,10 +4987,10 @@ function isMainModule(importMetaUrl) {
4799
4987
  }
4800
4988
 
4801
4989
  // src/protocols/ssh/transport/SshTransportConnection.ts
4802
- import { Buffer as Buffer16 } from "buffer";
4990
+ import { Buffer as Buffer15 } from "buffer";
4803
4991
 
4804
4992
  // src/protocols/ssh/binary/SshDataReader.ts
4805
- import { Buffer as Buffer7 } from "buffer";
4993
+ import { Buffer as Buffer6 } from "buffer";
4806
4994
  var SshDataReader = class {
4807
4995
  constructor(source) {
4808
4996
  this.source = source;
@@ -4828,18 +5016,18 @@ var SshDataReader = class {
4828
5016
  this.ensureAvailable(length, "bytes");
4829
5017
  const data = this.source.subarray(this.offset, this.offset + length);
4830
5018
  this.offset += length;
4831
- return Buffer7.from(data);
5019
+ return Buffer6.from(data);
4832
5020
  }
4833
5021
  readUint32() {
4834
5022
  this.ensureAvailable(4, "uint32");
4835
- const buffer = Buffer7.from(this.source);
5023
+ const buffer = Buffer6.from(this.source);
4836
5024
  const value = buffer.readUInt32BE(this.offset);
4837
5025
  this.offset += 4;
4838
5026
  return value;
4839
5027
  }
4840
5028
  readUint64() {
4841
5029
  this.ensureAvailable(8, "uint64");
4842
- const buffer = Buffer7.from(this.source);
5030
+ const buffer = Buffer6.from(this.source);
4843
5031
  const value = buffer.readBigUInt64BE(this.offset);
4844
5032
  this.offset += 8;
4845
5033
  return value;
@@ -4849,7 +5037,7 @@ var SshDataReader = class {
4849
5037
  this.ensureAvailable(length, "string");
4850
5038
  const data = this.source.subarray(this.offset, this.offset + length);
4851
5039
  this.offset += length;
4852
- return Buffer7.from(data);
5040
+ return Buffer6.from(data);
4853
5041
  }
4854
5042
  readUtf8String() {
4855
5043
  return this.readString().toString("utf8");
@@ -4894,7 +5082,7 @@ var SshDataReader = class {
4894
5082
  };
4895
5083
 
4896
5084
  // src/protocols/ssh/binary/SshDataWriter.ts
4897
- import { Buffer as Buffer8 } from "buffer";
5085
+ import { Buffer as Buffer7 } from "buffer";
4898
5086
  var MAX_UINT32 = 4294967295;
4899
5087
  var MAX_UINT64 = (1n << 64n) - 1n;
4900
5088
  var SshDataWriter = class {
@@ -4902,7 +5090,7 @@ var SshDataWriter = class {
4902
5090
  length = 0;
4903
5091
  writeByte(value) {
4904
5092
  this.assertByte(value, "byte");
4905
- const chunk = Buffer8.alloc(1);
5093
+ const chunk = Buffer7.alloc(1);
4906
5094
  chunk.writeUInt8(value, 0);
4907
5095
  return this.push(chunk);
4908
5096
  }
@@ -4910,7 +5098,7 @@ var SshDataWriter = class {
4910
5098
  return this.writeByte(value ? 1 : 0);
4911
5099
  }
4912
5100
  writeBytes(value) {
4913
- return this.push(Buffer8.from(value));
5101
+ return this.push(Buffer7.from(value));
4914
5102
  }
4915
5103
  writeUint32(value) {
4916
5104
  if (!Number.isInteger(value) || value < 0 || value > MAX_UINT32) {
@@ -4920,7 +5108,7 @@ var SshDataWriter = class {
4920
5108
  retryable: false
4921
5109
  });
4922
5110
  }
4923
- const chunk = Buffer8.alloc(4);
5111
+ const chunk = Buffer7.alloc(4);
4924
5112
  chunk.writeUInt32BE(value, 0);
4925
5113
  return this.push(chunk);
4926
5114
  }
@@ -4932,12 +5120,12 @@ var SshDataWriter = class {
4932
5120
  retryable: false
4933
5121
  });
4934
5122
  }
4935
- const chunk = Buffer8.alloc(8);
5123
+ const chunk = Buffer7.alloc(8);
4936
5124
  chunk.writeBigUInt64BE(value, 0);
4937
5125
  return this.push(chunk);
4938
5126
  }
4939
5127
  writeString(value, encoding = "utf8") {
4940
- const payload = typeof value === "string" ? Buffer8.from(value, encoding) : Buffer8.from(value);
5128
+ const payload = typeof value === "string" ? Buffer7.from(value, encoding) : Buffer7.from(value);
4941
5129
  this.writeUint32(payload.length);
4942
5130
  return this.push(payload);
4943
5131
  }
@@ -4959,7 +5147,7 @@ var SshDataWriter = class {
4959
5147
  return this.writeString(values.join(","), "ascii");
4960
5148
  }
4961
5149
  toBuffer() {
4962
- return Buffer8.concat(this.chunks, this.length);
5150
+ return Buffer7.concat(this.chunks, this.length);
4963
5151
  }
4964
5152
  push(chunk) {
4965
5153
  this.chunks.push(chunk);
@@ -4977,23 +5165,23 @@ var SshDataWriter = class {
4977
5165
  }
4978
5166
  };
4979
5167
  function normalizePositiveMpint(value) {
4980
- const input = Buffer8.from(value);
5168
+ const input = Buffer7.from(value);
4981
5169
  let offset = 0;
4982
5170
  while (offset < input.length && input[offset] === 0) {
4983
5171
  offset += 1;
4984
5172
  }
4985
5173
  if (offset >= input.length) {
4986
- return Buffer8.alloc(0);
5174
+ return Buffer7.alloc(0);
4987
5175
  }
4988
5176
  const stripped = input.subarray(offset);
4989
5177
  if ((stripped[0] & 128) === 128) {
4990
- return Buffer8.concat([Buffer8.from([0]), stripped]);
5178
+ return Buffer7.concat([Buffer7.from([0]), stripped]);
4991
5179
  }
4992
5180
  return stripped;
4993
5181
  }
4994
5182
 
4995
5183
  // src/protocols/ssh/transport/SshTransportHandshake.ts
4996
- import { Buffer as Buffer14 } from "buffer";
5184
+ import { Buffer as Buffer13 } from "buffer";
4997
5185
 
4998
5186
  // src/protocols/ssh/transport/SshAlgorithmNegotiation.ts
4999
5187
  var DEFAULT_SSH_ALGORITHM_PREFERENCES = {
@@ -5155,12 +5343,12 @@ function parseSshIdentificationLine(line) {
5155
5343
  }
5156
5344
 
5157
5345
  // src/protocols/ssh/transport/SshKexInit.ts
5158
- import { Buffer as Buffer9 } from "buffer";
5346
+ import { Buffer as Buffer8 } from "buffer";
5159
5347
  import { randomBytes } from "crypto";
5160
5348
  var SSH_MSG_KEXINIT = 20;
5161
5349
  var KEXINIT_COOKIE_LENGTH = 16;
5162
5350
  function encodeSshKexInitMessage(options) {
5163
- const cookie = options.cookie === void 0 ? randomBytes(KEXINIT_COOKIE_LENGTH) : Buffer9.from(options.cookie);
5351
+ const cookie = options.cookie === void 0 ? randomBytes(KEXINIT_COOKIE_LENGTH) : Buffer8.from(options.cookie);
5164
5352
  if (cookie.length !== KEXINIT_COOKIE_LENGTH) {
5165
5353
  throw new ConfigurationError({
5166
5354
  details: { actualLength: cookie.length, expectedLength: KEXINIT_COOKIE_LENGTH },
@@ -5230,12 +5418,12 @@ function decodeSshKexInitMessage(payload) {
5230
5418
  }
5231
5419
 
5232
5420
  // src/protocols/ssh/transport/SshKexCurve25519.ts
5233
- import { Buffer as Buffer10 } from "buffer";
5421
+ import { Buffer as Buffer9 } from "buffer";
5234
5422
  import { createPublicKey, diffieHellman, generateKeyPairSync } from "crypto";
5235
5423
  var SSH_MSG_KEX_ECDH_INIT = 30;
5236
5424
  var SSH_MSG_KEX_ECDH_REPLY = 31;
5237
5425
  var X25519_PUBLIC_KEY_LENGTH = 32;
5238
- var X25519_SPKI_PREFIX = Buffer10.from("302a300506032b656e032100", "hex");
5426
+ var X25519_SPKI_PREFIX = Buffer9.from("302a300506032b656e032100", "hex");
5239
5427
  function createCurve25519Ephemeral() {
5240
5428
  const { privateKey, publicKey } = generateKeyPairSync("x25519");
5241
5429
  const encodedPublicKey = exportX25519PublicKeyRaw(publicKey);
@@ -5280,7 +5468,7 @@ function exportX25519PublicKeyRaw(publicKey) {
5280
5468
  }
5281
5469
  function importX25519PublicKeyRaw(raw) {
5282
5470
  const normalized = normalizeX25519PublicKey(raw, "server");
5283
- const der = Buffer10.concat([X25519_SPKI_PREFIX, normalized]);
5471
+ const der = Buffer9.concat([X25519_SPKI_PREFIX, normalized]);
5284
5472
  return createPublicKey({
5285
5473
  format: "der",
5286
5474
  key: der,
@@ -5288,7 +5476,7 @@ function importX25519PublicKeyRaw(raw) {
5288
5476
  });
5289
5477
  }
5290
5478
  function normalizeX25519PublicKey(value, label) {
5291
- const key = Buffer10.from(value);
5479
+ const key = Buffer9.from(value);
5292
5480
  if (key.length !== X25519_PUBLIC_KEY_LENGTH) {
5293
5481
  throw new ConfigurationError({
5294
5482
  details: { keyLength: key.length, label },
@@ -5301,7 +5489,7 @@ function normalizeX25519PublicKey(value, label) {
5301
5489
  }
5302
5490
 
5303
5491
  // src/protocols/ssh/transport/SshKeyDerivation.ts
5304
- import { Buffer as Buffer11 } from "buffer";
5492
+ import { Buffer as Buffer10 } from "buffer";
5305
5493
  import { createHash } from "crypto";
5306
5494
  function deriveSshSessionKeys(input) {
5307
5495
  const hashAlgorithm = resolveKexHashAlgorithm(input.kexAlgorithm);
@@ -5323,7 +5511,7 @@ function deriveSshSessionKeys(input) {
5323
5511
  input.negotiatedAlgorithms.encryptionServerToClient,
5324
5512
  input.negotiatedAlgorithms.macServerToClient
5325
5513
  );
5326
- const sharedSecret = Buffer11.from(input.sharedSecret);
5514
+ const sharedSecret = Buffer10.from(input.sharedSecret);
5327
5515
  return {
5328
5516
  clientToServer: {
5329
5517
  encryptionKey: deriveMaterial(
@@ -5373,21 +5561,21 @@ function computeCurve25519ExchangeHash(input, hashAlgorithm) {
5373
5561
  }
5374
5562
  function deriveMaterial(sharedSecret, exchangeHash, sessionId, letter, length, hashAlgorithm) {
5375
5563
  if (length <= 0) {
5376
- return Buffer11.alloc(0);
5564
+ return Buffer10.alloc(0);
5377
5565
  }
5378
5566
  const result = [];
5379
5567
  const first2 = createHash(hashAlgorithm).update(
5380
5568
  new SshDataWriter().writeMpint(sharedSecret).writeBytes(exchangeHash).writeByte(letter.charCodeAt(0)).writeBytes(sessionId).toBuffer()
5381
5569
  ).digest();
5382
5570
  result.push(first2);
5383
- while (Buffer11.concat(result).length < length) {
5384
- const previous = Buffer11.concat(result);
5571
+ while (Buffer10.concat(result).length < length) {
5572
+ const previous = Buffer10.concat(result);
5385
5573
  const next = createHash(hashAlgorithm).update(
5386
5574
  new SshDataWriter().writeMpint(sharedSecret).writeBytes(exchangeHash).writeBytes(previous).toBuffer()
5387
5575
  ).digest();
5388
5576
  result.push(next);
5389
5577
  }
5390
- return Buffer11.concat(result).subarray(0, length);
5578
+ return Buffer10.concat(result).subarray(0, length);
5391
5579
  }
5392
5580
  function resolveKexHashAlgorithm(kexAlgorithm) {
5393
5581
  if (kexAlgorithm === "curve25519-sha256" || kexAlgorithm === "curve25519-sha256@libssh.org") {
@@ -5475,20 +5663,21 @@ function decodeSshNewKeysMessage(payload) {
5475
5663
  }
5476
5664
 
5477
5665
  // src/protocols/ssh/transport/SshTransportPacket.ts
5478
- import { Buffer as Buffer12 } from "buffer";
5666
+ import { Buffer as Buffer11 } from "buffer";
5479
5667
  import { randomBytes as randomBytes2 } from "crypto";
5480
5668
  var MIN_PADDING_LENGTH = 4;
5481
5669
  var MIN_PACKET_LENGTH = 1 + MIN_PADDING_LENGTH;
5670
+ var MAX_SSH_PACKET_LENGTH = 256 * 1024;
5482
5671
  function encodeSshTransportPacket(payload, options = {}) {
5483
- const body = Buffer12.from(payload);
5672
+ const body = Buffer11.from(payload);
5484
5673
  const blockSize = normalizeBlockSize(options.blockSize ?? 8);
5485
5674
  let paddingLength = MIN_PADDING_LENGTH;
5486
5675
  while ((1 + body.length + paddingLength + 4) % blockSize !== 0) {
5487
5676
  paddingLength += 1;
5488
5677
  }
5489
- const padding = options.randomPadding === false ? Buffer12.alloc(paddingLength) : randomBytes2(paddingLength);
5678
+ const padding = options.randomPadding === false ? Buffer11.alloc(paddingLength) : randomBytes2(paddingLength);
5490
5679
  const packetLength = 1 + body.length + paddingLength;
5491
- const frame = Buffer12.alloc(4 + packetLength);
5680
+ const frame = Buffer11.alloc(4 + packetLength);
5492
5681
  frame.writeUInt32BE(packetLength, 0);
5493
5682
  frame.writeUInt8(paddingLength, 4);
5494
5683
  body.copy(frame, 5);
@@ -5496,7 +5685,7 @@ function encodeSshTransportPacket(payload, options = {}) {
5496
5685
  return frame;
5497
5686
  }
5498
5687
  function decodeSshTransportPacket(frame) {
5499
- const bytes = Buffer12.from(frame);
5688
+ const bytes = Buffer11.from(frame);
5500
5689
  if (bytes.length < 4 + MIN_PACKET_LENGTH) {
5501
5690
  throw new ParseError({
5502
5691
  details: { length: bytes.length },
@@ -5550,12 +5739,20 @@ function decodeSshTransportPacket(frame) {
5550
5739
  };
5551
5740
  }
5552
5741
  var SshTransportPacketFramer = class {
5553
- pending = Buffer12.alloc(0);
5742
+ pending = Buffer11.alloc(0);
5554
5743
  push(chunk) {
5555
- this.pending = Buffer12.concat([this.pending, Buffer12.from(chunk)]);
5744
+ this.pending = Buffer11.concat([this.pending, Buffer11.from(chunk)]);
5556
5745
  const packets = [];
5557
5746
  while (this.pending.length >= 4) {
5558
5747
  const packetLength = this.pending.readUInt32BE(0);
5748
+ if (packetLength > MAX_SSH_PACKET_LENGTH) {
5749
+ throw new ParseError({
5750
+ details: { maxPacketLength: MAX_SSH_PACKET_LENGTH, packetLength },
5751
+ message: "SSH transport packet length exceeds the maximum accepted size",
5752
+ protocol: "sftp",
5753
+ retryable: false
5754
+ });
5755
+ }
5559
5756
  const frameLength = 4 + packetLength;
5560
5757
  if (this.pending.length < frameLength) {
5561
5758
  break;
@@ -5571,8 +5768,8 @@ var SshTransportPacketFramer = class {
5571
5768
  }
5572
5769
  /** Returns and clears any bytes buffered but not yet part of a complete packet. */
5573
5770
  takeRemainingBytes() {
5574
- const remaining = Buffer12.from(this.pending);
5575
- this.pending = Buffer12.alloc(0);
5771
+ const remaining = Buffer11.from(this.pending);
5772
+ this.pending = Buffer11.alloc(0);
5576
5773
  return remaining;
5577
5774
  }
5578
5775
  };
@@ -5589,10 +5786,10 @@ function normalizeBlockSize(blockSize) {
5589
5786
  }
5590
5787
 
5591
5788
  // src/protocols/ssh/transport/SshHostKeyVerification.ts
5592
- import { Buffer as Buffer13 } from "buffer";
5789
+ import { Buffer as Buffer12 } from "buffer";
5593
5790
  import { createHash as createHash2, createPublicKey as createPublicKey2, verify as cryptoVerify } from "crypto";
5594
5791
  var ED25519_RAW_KEY_LENGTH = 32;
5595
- var ED25519_SPKI_PREFIX = Buffer13.from("302a300506032b6570032100", "hex");
5792
+ var ED25519_SPKI_PREFIX = Buffer12.from("302a300506032b6570032100", "hex");
5596
5793
  function verifySshHostKeySignature(input) {
5597
5794
  const { algorithmName, publicKey } = parseHostKey(input.hostKeyBlob);
5598
5795
  const { signatureAlgorithm, signatureBytes } = parseSignatureBlob(input.signatureBlob);
@@ -5605,9 +5802,9 @@ function verifySshHostKeySignature(input) {
5605
5802
  });
5606
5803
  }
5607
5804
  const verified = verifySignature({
5608
- data: Buffer13.from(input.exchangeHash),
5805
+ data: Buffer12.from(input.exchangeHash),
5609
5806
  publicKey,
5610
- signature: Buffer13.from(signatureBytes),
5807
+ signature: Buffer12.from(signatureBytes),
5611
5808
  signatureAlgorithm
5612
5809
  });
5613
5810
  if (!verified) {
@@ -5636,7 +5833,7 @@ function parseHostKey(blob) {
5636
5833
  retryable: false
5637
5834
  });
5638
5835
  }
5639
- const spki = Buffer13.concat([ED25519_SPKI_PREFIX, raw]);
5836
+ const spki = Buffer12.concat([ED25519_SPKI_PREFIX, raw]);
5640
5837
  return {
5641
5838
  algorithmName,
5642
5839
  publicKey: createPublicKey2({ format: "der", key: spki, type: "spki" })
@@ -5742,37 +5939,37 @@ function verifySignature(input) {
5742
5939
  function rsaPublicKeyFromComponents(e, n) {
5743
5940
  const eDer = encodeAsn1Integer(e);
5744
5941
  const nDer = encodeAsn1Integer(n);
5745
- const rsaPublicKeyDer = encodeAsn1Sequence(Buffer13.concat([nDer, eDer]));
5746
- const bitStringContent = Buffer13.concat([Buffer13.from([0]), rsaPublicKeyDer]);
5747
- const bitString = Buffer13.concat([
5748
- Buffer13.from([3]),
5942
+ const rsaPublicKeyDer = encodeAsn1Sequence(Buffer12.concat([nDer, eDer]));
5943
+ const bitStringContent = Buffer12.concat([Buffer12.from([0]), rsaPublicKeyDer]);
5944
+ const bitString = Buffer12.concat([
5945
+ Buffer12.from([3]),
5749
5946
  encodeAsn1Length(bitStringContent.length),
5750
5947
  bitStringContent
5751
5948
  ]);
5752
- const algoId = Buffer13.from("300d06092a864886f70d010101 0500".replace(/\s+/g, ""), "hex");
5753
- const spki = encodeAsn1Sequence(Buffer13.concat([algoId, bitString]));
5949
+ const algoId = Buffer12.from("300d06092a864886f70d010101 0500".replace(/\s+/g, ""), "hex");
5950
+ const spki = encodeAsn1Sequence(Buffer12.concat([algoId, bitString]));
5754
5951
  return createPublicKey2({ format: "der", key: spki, type: "spki" });
5755
5952
  }
5756
5953
  function encodeAsn1Integer(value) {
5757
5954
  let body = value;
5758
5955
  while (body.length > 1 && body[0] === 0) body = body.subarray(1);
5759
5956
  if (body.length > 0 && (body[0] & 128) !== 0) {
5760
- body = Buffer13.concat([Buffer13.from([0]), body]);
5957
+ body = Buffer12.concat([Buffer12.from([0]), body]);
5761
5958
  }
5762
- return Buffer13.concat([Buffer13.from([2]), encodeAsn1Length(body.length), body]);
5959
+ return Buffer12.concat([Buffer12.from([2]), encodeAsn1Length(body.length), body]);
5763
5960
  }
5764
5961
  function encodeAsn1Sequence(content) {
5765
- return Buffer13.concat([Buffer13.from([48]), encodeAsn1Length(content.length), content]);
5962
+ return Buffer12.concat([Buffer12.from([48]), encodeAsn1Length(content.length), content]);
5766
5963
  }
5767
5964
  function encodeAsn1Length(length) {
5768
- if (length < 128) return Buffer13.from([length]);
5965
+ if (length < 128) return Buffer12.from([length]);
5769
5966
  const bytes = [];
5770
5967
  let n = length;
5771
5968
  while (n > 0) {
5772
5969
  bytes.unshift(n & 255);
5773
5970
  n >>>= 8;
5774
5971
  }
5775
- return Buffer13.from([128 | bytes.length, ...bytes]);
5972
+ return Buffer12.from([128 | bytes.length, ...bytes]);
5776
5973
  }
5777
5974
  var ECDSA_OID_BY_CURVE = {
5778
5975
  nistp256: "06082a8648ce3d030107",
@@ -5793,15 +5990,15 @@ function ecdsaPublicKeyFromPoint(curveIdentifier, point) {
5793
5990
  retryable: false
5794
5991
  });
5795
5992
  }
5796
- const algoIdContent = Buffer13.from(ECDSA_ALGORITHM_OID_HEX + oidHex, "hex");
5993
+ const algoIdContent = Buffer12.from(ECDSA_ALGORITHM_OID_HEX + oidHex, "hex");
5797
5994
  const algoId = encodeAsn1Sequence(algoIdContent);
5798
- const bitStringContent = Buffer13.concat([Buffer13.from([0]), point]);
5799
- const bitString = Buffer13.concat([
5800
- Buffer13.from([3]),
5995
+ const bitStringContent = Buffer12.concat([Buffer12.from([0]), point]);
5996
+ const bitString = Buffer12.concat([
5997
+ Buffer12.from([3]),
5801
5998
  encodeAsn1Length(bitStringContent.length),
5802
5999
  bitStringContent
5803
6000
  ]);
5804
- const spki = encodeAsn1Sequence(Buffer13.concat([algoId, bitString]));
6001
+ const spki = encodeAsn1Sequence(Buffer12.concat([algoId, bitString]));
5805
6002
  return createPublicKey2({ format: "der", key: spki, type: "spki" });
5806
6003
  }
5807
6004
  function sshEcdsaSignatureToDer(sshSignature) {
@@ -5810,7 +6007,7 @@ function sshEcdsaSignatureToDer(sshSignature) {
5810
6007
  const s = reader.readMpint();
5811
6008
  const rDer = encodeAsn1Integer(r);
5812
6009
  const sDer = encodeAsn1Integer(s);
5813
- return encodeAsn1Sequence(Buffer13.concat([rDer, sDer]));
6010
+ return encodeAsn1Sequence(Buffer12.concat([rDer, sDer]));
5814
6011
  }
5815
6012
 
5816
6013
  // src/protocols/ssh/transport/SshTransportHandshake.ts
@@ -5842,7 +6039,7 @@ var SshTransportHandshake = class {
5842
6039
  serverIdentification;
5843
6040
  /** Creates the first outbound bytes (client identification line). */
5844
6041
  createInitialClientBytes() {
5845
- return Buffer14.from(`${this.clientIdentificationLine}\r
6042
+ return Buffer13.from(`${this.clientIdentificationLine}\r
5846
6043
  `, "ascii");
5847
6044
  }
5848
6045
  /**
@@ -5866,7 +6063,7 @@ var SshTransportHandshake = class {
5866
6063
  }
5867
6064
  return { outbound };
5868
6065
  }
5869
- return this.pushServerBytesWithPhase(outbound, Buffer14.from(chunk));
6066
+ return this.pushServerBytesWithPhase(outbound, Buffer13.from(chunk));
5870
6067
  }
5871
6068
  getServerBannerLines() {
5872
6069
  return this.identificationLines;
@@ -5920,12 +6117,12 @@ var SshTransportHandshake = class {
5920
6117
  clientKexInitPayload: this.clientKexInitPayload,
5921
6118
  clientPublicKey: this.pendingCurve25519.publicKey,
5922
6119
  negotiatedAlgorithms,
5923
- serverHostKey: Buffer14.alloc(0),
6120
+ serverHostKey: Buffer13.alloc(0),
5924
6121
  serverIdentification: (this.serverIdentification ?? missingServerIdentificationError()).raw,
5925
- serverKexInitPayload: Buffer14.from(packet.payload),
5926
- serverPublicKey: Buffer14.alloc(0),
5927
- serverSignature: Buffer14.alloc(0),
5928
- sharedSecret: Buffer14.alloc(0)
6122
+ serverKexInitPayload: Buffer13.from(packet.payload),
6123
+ serverPublicKey: Buffer13.alloc(0),
6124
+ serverSignature: Buffer13.alloc(0),
6125
+ sharedSecret: Buffer13.alloc(0)
5929
6126
  };
5930
6127
  continue;
5931
6128
  }
@@ -6033,24 +6230,54 @@ var SshTransportHandshake = class {
6033
6230
  return { outbound };
6034
6231
  }
6035
6232
  };
6233
+ var MAX_IDENTIFICATION_LINE_BYTES = 8192;
6234
+ var MAX_PRE_IDENTIFICATION_LINES = 1024;
6036
6235
  var SshIdentificationAccumulator = class {
6037
- pending = Buffer14.alloc(0);
6236
+ pending = Buffer13.alloc(0);
6237
+ bannerLineCount = 0;
6038
6238
  push(chunk) {
6039
- this.pending = Buffer14.concat([this.pending, Buffer14.from(chunk)]);
6239
+ this.pending = Buffer13.concat([this.pending, Buffer13.from(chunk)]);
6040
6240
  const bannerLines = [];
6041
6241
  while (true) {
6042
6242
  const lfIndex = this.pending.indexOf(10);
6043
- if (lfIndex < 0) break;
6243
+ if (lfIndex < 0) {
6244
+ if (this.pending.length > MAX_IDENTIFICATION_LINE_BYTES) {
6245
+ throw new ProtocolError({
6246
+ details: { limitBytes: MAX_IDENTIFICATION_LINE_BYTES },
6247
+ message: "SSH identification line exceeds the maximum accepted length",
6248
+ protocol: "sftp",
6249
+ retryable: false
6250
+ });
6251
+ }
6252
+ break;
6253
+ }
6254
+ if (lfIndex > MAX_IDENTIFICATION_LINE_BYTES) {
6255
+ throw new ProtocolError({
6256
+ details: { limitBytes: MAX_IDENTIFICATION_LINE_BYTES },
6257
+ message: "SSH identification line exceeds the maximum accepted length",
6258
+ protocol: "sftp",
6259
+ retryable: false
6260
+ });
6261
+ }
6044
6262
  const lineText = trimLineEndings(this.pending.subarray(0, lfIndex + 1).toString("ascii"));
6045
- const remainder = Buffer14.from(this.pending.subarray(lfIndex + 1));
6263
+ const remainder = Buffer13.from(this.pending.subarray(lfIndex + 1));
6046
6264
  this.pending = remainder;
6047
6265
  if (lineText.startsWith("SSH-")) {
6048
- this.pending = Buffer14.alloc(0);
6266
+ this.pending = Buffer13.alloc(0);
6049
6267
  return { bannerLines, identLine: lineText, remainder };
6050
6268
  }
6269
+ this.bannerLineCount += 1;
6270
+ if (this.bannerLineCount > MAX_PRE_IDENTIFICATION_LINES) {
6271
+ throw new ProtocolError({
6272
+ details: { limitLines: MAX_PRE_IDENTIFICATION_LINES },
6273
+ message: "SSH server sent too many pre-identification banner lines",
6274
+ protocol: "sftp",
6275
+ retryable: false
6276
+ });
6277
+ }
6051
6278
  bannerLines.push(lineText);
6052
6279
  }
6053
- return { bannerLines, remainder: Buffer14.alloc(0) };
6280
+ return { bannerLines, remainder: Buffer13.alloc(0) };
6054
6281
  }
6055
6282
  };
6056
6283
  function trimLineEndings(value) {
@@ -6078,7 +6305,7 @@ function missingPendingKeyExchangeError() {
6078
6305
  }
6079
6306
 
6080
6307
  // src/protocols/ssh/transport/SshTransportProtection.ts
6081
- import { Buffer as Buffer15 } from "buffer";
6308
+ import { Buffer as Buffer14 } from "buffer";
6082
6309
  import {
6083
6310
  createCipheriv,
6084
6311
  createDecipheriv,
@@ -6140,7 +6367,7 @@ var SshTransportPacketProtector = class {
6140
6367
  );
6141
6368
  const encrypted = this.cipher === void 0 ? clearPacket : this.cipher.update(clearPacket);
6142
6369
  this.sequenceNumber = this.sequenceNumber + 1 >>> 0;
6143
- return Buffer15.concat([encrypted, mac]);
6370
+ return Buffer14.concat([encrypted, mac]);
6144
6371
  }
6145
6372
  };
6146
6373
  var SshTransportPacketUnprotector = class {
@@ -6166,7 +6393,7 @@ var SshTransportPacketUnprotector = class {
6166
6393
  sequenceNumber;
6167
6394
  // Streaming framing state for pushBytes()
6168
6395
  framePartialDecrypted;
6169
- framePendingRaw = Buffer15.alloc(0);
6396
+ framePendingRaw = Buffer14.alloc(0);
6170
6397
  frameRemainingNeeded;
6171
6398
  getSequenceNumber() {
6172
6399
  return this.sequenceNumber;
@@ -6176,15 +6403,23 @@ var SshTransportPacketUnprotector = class {
6176
6403
  * Maintains internal framing state across calls - pass each `data` event chunk directly.
6177
6404
  */
6178
6405
  pushBytes(chunk) {
6179
- this.framePendingRaw = Buffer15.concat([this.framePendingRaw, chunk]);
6406
+ this.framePendingRaw = Buffer14.concat([this.framePendingRaw, chunk]);
6180
6407
  const results = [];
6181
6408
  while (true) {
6182
6409
  if (this.framePartialDecrypted === void 0) {
6183
6410
  if (this.framePendingRaw.length < this.blockLength) break;
6184
6411
  const firstBlock = this.framePendingRaw.subarray(0, this.blockLength);
6185
- this.framePendingRaw = Buffer15.from(this.framePendingRaw.subarray(this.blockLength));
6186
- this.framePartialDecrypted = this.decipher ? Buffer15.from(this.decipher.update(firstBlock)) : Buffer15.from(firstBlock);
6412
+ this.framePendingRaw = Buffer14.from(this.framePendingRaw.subarray(this.blockLength));
6413
+ this.framePartialDecrypted = this.decipher ? Buffer14.from(this.decipher.update(firstBlock)) : Buffer14.from(firstBlock);
6187
6414
  const packetLength = this.framePartialDecrypted.readUInt32BE(0);
6415
+ if (packetLength > MAX_SSH_PACKET_LENGTH) {
6416
+ throw new ProtocolError({
6417
+ details: { maxPacketLength: MAX_SSH_PACKET_LENGTH, packetLength },
6418
+ message: "SSH encrypted packet length exceeds the maximum accepted size",
6419
+ protocol: "sftp",
6420
+ retryable: false
6421
+ });
6422
+ }
6188
6423
  const remaining = 4 + packetLength - this.blockLength + this.macLength;
6189
6424
  if (remaining < 0) {
6190
6425
  throw new ProtocolError({
@@ -6200,9 +6435,9 @@ var SshTransportPacketUnprotector = class {
6200
6435
  if (this.framePendingRaw.length < needed) break;
6201
6436
  const encryptedRest = this.framePendingRaw.subarray(0, needed - this.macLength);
6202
6437
  const receivedMac = this.framePendingRaw.subarray(needed - this.macLength, needed);
6203
- this.framePendingRaw = Buffer15.from(this.framePendingRaw.subarray(needed));
6204
- const decryptedRest = encryptedRest.length > 0 ? this.decipher ? Buffer15.from(this.decipher.update(encryptedRest)) : Buffer15.from(encryptedRest) : Buffer15.alloc(0);
6205
- const clearPacket = Buffer15.concat([this.framePartialDecrypted, decryptedRest]);
6438
+ this.framePendingRaw = Buffer14.from(this.framePendingRaw.subarray(needed));
6439
+ const decryptedRest = encryptedRest.length > 0 ? this.decipher ? Buffer14.from(this.decipher.update(encryptedRest)) : Buffer14.from(encryptedRest) : Buffer14.alloc(0);
6440
+ const clearPacket = Buffer14.concat([this.framePartialDecrypted, decryptedRest]);
6206
6441
  const expectedMac = computeMac(
6207
6442
  this.macAlgorithm,
6208
6443
  this.options.keys.macKey,
@@ -6225,7 +6460,7 @@ var SshTransportPacketUnprotector = class {
6225
6460
  return results;
6226
6461
  }
6227
6462
  unprotectPayload(packet) {
6228
- const frame = Buffer15.from(packet);
6463
+ const frame = Buffer14.from(packet);
6229
6464
  if (frame.length < this.macLength) {
6230
6465
  throw new ProtocolError({
6231
6466
  details: { length: frame.length, macLength: this.macLength },
@@ -6366,10 +6601,10 @@ function resolveMacLength(encryptionAlgorithm, macAlgorithm) {
6366
6601
  }
6367
6602
  function computeMac(macAlgorithm, macKey, sequence, packet, macLength) {
6368
6603
  if (macLength === 0) {
6369
- return Buffer15.alloc(0);
6604
+ return Buffer14.alloc(0);
6370
6605
  }
6371
6606
  const hashName = macAlgorithm === "hmac-sha2-512" ? "sha512" : "sha256";
6372
- const sequenceBuffer = Buffer15.alloc(4);
6607
+ const sequenceBuffer = Buffer14.alloc(4);
6373
6608
  sequenceBuffer.writeUInt32BE(sequence >>> 0, 0);
6374
6609
  return createHmac2(hashName, macKey).update(sequenceBuffer).update(packet).digest().subarray(0, macLength);
6375
6610
  }
@@ -6576,7 +6811,7 @@ var SshTransportConnection = class {
6576
6811
  */
6577
6812
  sendPayload(payload) {
6578
6813
  this.assertConnected();
6579
- const frame = this.protector.protectPayload(Buffer16.from(payload));
6814
+ const frame = this.protector.protectPayload(Buffer15.from(payload));
6580
6815
  this.socket.write(frame);
6581
6816
  this.resetKeepaliveTimer();
6582
6817
  }
@@ -7044,7 +7279,7 @@ function buildKiRequest(args) {
7044
7279
  }
7045
7280
 
7046
7281
  // src/protocols/ssh/auth/SshPublickeyCredentialBuilder.ts
7047
- import { Buffer as Buffer17 } from "buffer";
7282
+ import { Buffer as Buffer16 } from "buffer";
7048
7283
  import { createPublicKey as createPublicKey3, sign as cryptoSign } from "crypto";
7049
7284
  var ED25519_RAW_KEY_LENGTH2 = 32;
7050
7285
  var ED25519_SPKI_PREFIX_LENGTH = 12;
@@ -7062,7 +7297,7 @@ function buildPublickeyCredential(options) {
7062
7297
  return {
7063
7298
  algorithmName: "ssh-ed25519",
7064
7299
  publicKeyBlob,
7065
- sign: (data) => cryptoSign(null, Buffer17.from(data), privateKey),
7300
+ sign: (data) => cryptoSign(null, Buffer16.from(data), privateKey),
7066
7301
  type: "publickey",
7067
7302
  username
7068
7303
  };
@@ -7080,7 +7315,7 @@ function buildPublickeyCredential(options) {
7080
7315
  return {
7081
7316
  algorithmName,
7082
7317
  publicKeyBlob,
7083
- sign: (data) => cryptoSign(hash, Buffer17.from(data), privateKey),
7318
+ sign: (data) => cryptoSign(hash, Buffer16.from(data), privateKey),
7084
7319
  type: "publickey",
7085
7320
  username
7086
7321
  };
@@ -7093,7 +7328,7 @@ function buildPublickeyCredential(options) {
7093
7328
  }
7094
7329
  function base64UrlToMpint(value) {
7095
7330
  const padded = value.replace(/-/g, "+").replace(/_/g, "/");
7096
- const buffer = Buffer17.from(padded, "base64");
7331
+ const buffer = Buffer16.from(padded, "base64");
7097
7332
  return buffer;
7098
7333
  }
7099
7334
  function createInvalidKeyError(message) {
@@ -7219,7 +7454,7 @@ function encodeSshChannelClose(recipientChannel) {
7219
7454
  }
7220
7455
 
7221
7456
  // src/protocols/ssh/connection/SshSessionChannel.ts
7222
- import { Buffer as Buffer18 } from "buffer";
7457
+ import { Buffer as Buffer17 } from "buffer";
7223
7458
  var INITIAL_WINDOW_SIZE = 256 * 1024;
7224
7459
  var MAX_PACKET_SIZE = 32 * 1024;
7225
7460
  var WINDOW_REFILL_THRESHOLD = 64 * 1024;
@@ -7377,7 +7612,7 @@ var SshSessionChannel = class {
7377
7612
  this.remoteWindowRemaining,
7378
7613
  this.remoteMaxPacketSize
7379
7614
  );
7380
- const chunk = Buffer18.from(data.subarray(offset, offset + chunkSize));
7615
+ const chunk = Buffer17.from(data.subarray(offset, offset + chunkSize));
7381
7616
  this.transport.sendPayload(
7382
7617
  encodeSshChannelData({ data: chunk, recipientChannel: this.remoteChannelId })
7383
7618
  );
@@ -7756,6 +7991,7 @@ export {
7756
7991
  copyBetween,
7757
7992
  createAtomicDeployPlan,
7758
7993
  createBandwidthThrottle,
7994
+ createDefaultRetryPolicy,
7759
7995
  createLocalProviderFactory,
7760
7996
  createMemoryProviderFactory,
7761
7997
  createOAuthTokenSecretSource,
@@ -7792,8 +8028,10 @@ export {
7792
8028
  parseRemoteManifest,
7793
8029
  redactCommand,
7794
8030
  redactConnectionProfile,
8031
+ redactErrorForLogging,
7795
8032
  redactObject,
7796
8033
  redactSecretSource,
8034
+ redactUrlForLogging,
7797
8035
  redactValue,
7798
8036
  resolveConnectionProfileSecrets,
7799
8037
  resolveOpenSshHost,