History

fix(server): redact managed endpoint output

Julius Marminge committed 4 months ago

Commit bf555bd1 parent 5b770972 files · +64 −3Browse files

Unified · Split

2 files
  1. apps/server/src/cloud/ManagedEndpointRuntime.test.ts +63 −1
  2. apps/server/src/cloud/ManagedEndpointRuntime.ts +1 −2
apps/server/src/cloud/ManagedEndpointRuntime.test.ts+63−1
@@ -65,6 +65,7 @@ function makeHandle(input: { · 64 unmodified lines
6565 readonly isRunning?: () => boolean;
6666 readonly isRunningEffect?: ChildProcessSpawner.ChildProcessHandle["isRunning"];
6767 readonly exitCode?: Effect.Effect<ChildProcessSpawner.ExitCode>;
68+ readonly output?: string;
6869 }) {
6970 return ChildProcessSpawner.makeHandle({
7071 pid: ChildProcessSpawner.ProcessId(input.pid),
@@ -78,7 +79,10 @@ function makeHandle(input: { · 7 unmodified lines
7879 stdin: Sink.drain,
7980 stdout: Stream.empty,
8081 stderr: Stream.empty,
81− all: Stream.empty,
82+ all:
83+ input.output === undefined
84+ ? Stream.empty
85+ : Stream.make(new TextEncoder().encode(input.output)),
8286 getInputFd: () => Sink.drain,
8387 getOutputFd: () => Stream.empty,
8488 });
@@ -262,6 +266,64 @@ describe("CloudManagedEndpointRuntime", () => { · 177 unmodified lines
262266 }).pipe(Effect.provide(Logger.layer([logger], { mergeWithExisting: false })));
263267 });
264268
269+ it.effect("does not copy relay client output into log annotations", () => {
270+ const connectorToken = "connector-token-sentinel";
271+ const signedUrl = "https://user:password@example.com/private?token=secret#fragment";
272+ const output = `ERR failed request ${signedUrl} ${connectorToken}`;
273+ const logMessages: unknown[] = [];
274+ let resolveObserved!: () => void;
275+ const observed = new Promise<void>((resolve) => {
276+ resolveObserved = resolve;
277+ });
278+ const logger = Logger.make(({ message }) => {
279+ logMessages.push(message);
280+ if (Array.isArray(message) && message[0] === "Relay client reported a transport warning") {
281+ resolveObserved();
282+ }
283+ });
284+ const spawner = ChildProcessSpawner.make(() =>
285+ Effect.gen(function* () {
286+ const handle = makeHandle({
287+ pid: 350,
288+ output,
289+ onKill: () => undefined,
290+ });
291+ yield* Effect.addFinalizer(() => handle.kill().pipe(Effect.ignore));
292+ return handle;
293+ }),
294+ );
295+
296+ return Effect.gen(function* () {
297+ const runtime = yield* buildCloudManagedEndpointRuntime(spawner);
298+ yield* runtime.applyConfig({
299+ providerKind: "cloudflare_tunnel",
300+ connectorToken,
301+ tunnelId: "tunnel-1",
302+ });
303+ yield* Effect.promise(() => observed);
304+
305+ const warning = logMessages.find(
306+ (message) =>
307+ Array.isArray(message) && message[0] === "Relay client reported a transport warning",
308+ );
309+ expect(warning).toBeDefined();
310+ if (!Array.isArray(warning)) return;
311+ expect(warning[1]).toMatchObject({
312+ pid: 350,
313+ tunnelId: "tunnel-1",
314+ outputLength: output.length,
315+ });
316+ expect(warning[1]).not.toHaveProperty("output");
317+ const diagnosticText = Object.values(warning[1] as Record<string, unknown>)
318+ .map(String)
319+ .join("\n");
320+ expect(diagnosticText).not.toContain(connectorToken);
321+ expect(diagnosticText).not.toContain(signedUrl);
322+ expect(diagnosticText).not.toContain("user:password");
323+ expect(diagnosticText).not.toContain("token=secret");
324+ }).pipe(Effect.provide(Logger.layer([logger], { mergeWithExisting: false })));
325+ });
326+
265327 it.effect("supervises the active connector and restarts it after process exit", () =>
266328 Effect.gen(function* () {
267329 const spawned: Array<number> = [];
apps/server/src/cloud/ManagedEndpointRuntime.ts+1−2
@@ -183,12 +183,11 @@ export const make = Effect.gen(function* () { · 182 unmodified lines
183183 Stream.map((line) => line.trim()),
184184 Stream.filter((line) => line.length > 0),
185185 Stream.runForEach((line) => {
186− const output = line.replaceAll(connector.config.connectorToken, "<redacted>");
187186 const attributes = {
188187 pid: Number(connector.child.pid),
189188 tunnelId: connector.config.tunnelId,
190189 tunnelName: connector.config.tunnelName,
191− output,
190+ outputLength: line.length,
192191 };
193192 switch (classifyRelayClientOutput(line)) {
194193 case "connected":