|
1 | 1 | import { spawn } from "node:child_process"; |
2 | 2 | import type { SshParsedTarget } from "./ssh-tunnel.js"; |
3 | 3 | |
| 4 | +export const SSH_CONFIG_OUTPUT_MAX_CHARS = 64 * 1024; |
| 5 | + |
4 | 6 | export type SshResolvedConfig = { |
5 | 7 | user?: string; |
6 | 8 | host?: string; |
7 | 9 | port?: number; |
8 | 10 | identityFiles: string[]; |
9 | 11 | }; |
10 | 12 | |
| 13 | +type AppendSshConfigOutputResult = { ok: true; value: string } | { ok: false; reason: "too-large" }; |
| 14 | + |
11 | 15 | function parsePort(value: string | undefined): number | undefined { |
12 | 16 | if (!value) { |
13 | 17 | return undefined; |
@@ -54,6 +58,18 @@ export function parseSshConfigOutput(output: string): SshResolvedConfig {
|
54 | 58 | return result; |
55 | 59 | } |
56 | 60 | |
| 61 | +export function appendSshConfigOutput( |
| 62 | +current: string, |
| 63 | +chunk: unknown, |
| 64 | +maxChars = SSH_CONFIG_OUTPUT_MAX_CHARS, |
| 65 | +): AppendSshConfigOutputResult { |
| 66 | +const next = current + String(chunk); |
| 67 | +if (next.length > maxChars) { |
| 68 | +return { ok: false, reason: "too-large" }; |
| 69 | +} |
| 70 | +return { ok: true, value: next }; |
| 71 | +} |
| 72 | + |
57 | 73 | export async function resolveSshConfig( |
58 | 74 | target: SshParsedTarget, |
59 | 75 | opts: { identity?: string; timeoutMs?: number } = {}, |
@@ -75,9 +91,16 @@ export async function resolveSshConfig(
|
75 | 91 | stdio: ["ignore", "pipe", "ignore"], |
76 | 92 | }); |
77 | 93 | let stdout = ""; |
| 94 | +let outputTooLarge = false; |
78 | 95 | child.stdout?.setEncoding("utf8"); |
79 | 96 | child.stdout?.on("data", (chunk) => { |
80 | | -stdout += String(chunk); |
| 97 | +const appended = appendSshConfigOutput(stdout, chunk); |
| 98 | +if (!appended.ok) { |
| 99 | +outputTooLarge = true; |
| 100 | +child.kill("SIGKILL"); |
| 101 | +return; |
| 102 | +} |
| 103 | +stdout = appended.value; |
81 | 104 | }); |
82 | 105 | |
83 | 106 | const timeoutMs = Math.max(200, opts.timeoutMs ?? 800); |
@@ -95,7 +118,7 @@ export async function resolveSshConfig(
|
95 | 118 | }); |
96 | 119 | child.once("exit", (code) => { |
97 | 120 | clearTimeout(timer); |
98 | | -if (code !== 0 || !stdout.trim()) { |
| 121 | +if (outputTooLarge || code !== 0 || !stdout.trim()) { |
99 | 122 | resolve(null); |
100 | 123 | return; |
101 | 124 | } |
|