From 8ed2330a3cf7747a6cf657fc5289cfc9bb52183e Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Mon, 13 Oct 2025 15:49:42 +0200 Subject: [PATCH 01/30] Allow the safe-chain to act as a regular http proxy too (besides the CONNECT tunneling implementation) --- .../src/registryProxy/plainHttpProxy.js | 58 +++++++++++++++++++ .../src/registryProxy/registryProxy.js | 9 +-- 2 files changed, 60 insertions(+), 7 deletions(-) create mode 100644 packages/safe-chain/src/registryProxy/plainHttpProxy.js diff --git a/packages/safe-chain/src/registryProxy/plainHttpProxy.js b/packages/safe-chain/src/registryProxy/plainHttpProxy.js new file mode 100644 index 0000000..68a2362 --- /dev/null +++ b/packages/safe-chain/src/registryProxy/plainHttpProxy.js @@ -0,0 +1,58 @@ +import * as http from "http"; +import * as https from "https"; + +export function handleHttpProxyRequest(req, res) { + const url = new URL(req.url); + + let protocol; + if (url.protocol === "http:") { + protocol = http; + } else if (url.protocol === "https:") { + protocol = https; + } else { + res.writeHead(502); + res.end(`Bad Gateway: Unsupported protocol ${url.protocol}`); + return; + } + + const proxyRequest = protocol + .request( + req.url, + { method: req.method, headers: req.headers }, + (proxyRes) => { + res.writeHead(proxyRes.statusCode, proxyRes.headers); + proxyRes.pipe(res); + + proxyRes.on("error", () => { + // Stream error while piping response + // Response headers already sent, can't send error status + }); + } + ) + .on("error", (err) => { + res.writeHead(502); + res.end(`Bad Gateway: ${err.message}`); + }); + + req.on("error", () => { + // Client request stream error + // Abort the proxy request + proxyRequest.destroy(); + }); + + res.on("error", () => { + // Client response stream error (client disconnected) + // Clean up proxy streams + proxyRequest.destroy(); + }); + + res.on("close", () => { + // Client disconnected + // Abort the proxy request to avoid unnecessary work + if (!res.writableEnded) { + proxyRequest.destroy(); + } + }); + + req.pipe(proxyRequest); +} diff --git a/packages/safe-chain/src/registryProxy/registryProxy.js b/packages/safe-chain/src/registryProxy/registryProxy.js index 3558673..2895753 100644 --- a/packages/safe-chain/src/registryProxy/registryProxy.js +++ b/packages/safe-chain/src/registryProxy/registryProxy.js @@ -1,6 +1,7 @@ import * as http from "http"; import { tunnelRequest } from "./tunnelRequestHandler.js"; import { mitmConnect } from "./mitmRequestHandler.js"; +import { handleHttpProxyRequest } from "./plainHttpProxy.js"; import { getCaCertPath } from "./certUtils.js"; import { auditChanges } from "../scanning/audit/index.js"; import { knownRegistries, parsePackageFromUrl } from "./parsePackageFromUrl.js"; @@ -54,13 +55,7 @@ export function mergeSafeChainProxyEnvironmentVariables(env) { } function createProxyServer() { - const server = http.createServer((_, res) => { - res.writeHead(400, "Bad Request"); - res.write( - "Safe-chain proxy: Direct http not supported. Only CONNECT requests are allowed." - ); - res.end(); - }); + const server = http.createServer(handleHttpProxyRequest); return server; } From d2c155afeea4738370eacc8839a90108d16d4607 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Tue, 14 Oct 2025 12:55:56 +0200 Subject: [PATCH 02/30] Add e2e test for registry over http --- test/e2e/DockerTestContainer.js | 20 +++++++++++++++++ test/e2e/safe-chain-proxy.e2e.spec.js | 31 +++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/test/e2e/DockerTestContainer.js b/test/e2e/DockerTestContainer.js index 483f03a..1a817eb 100644 --- a/test/e2e/DockerTestContainer.js +++ b/test/e2e/DockerTestContainer.js @@ -60,6 +60,26 @@ export class DockerTestContainer { } } + dockerExec(command, daemon = false) { + if (!this.isRunning) { + throw new Error("Container is not running"); + } + + try { + const dockerExecCommand = `docker exec ${daemon ? "-d " : " "}${ + this.containerName + } bash -c "${command}"`; + const output = execSync(dockerExecCommand, { + encoding: "utf-8", + stdio: "pipe", + timeout: 10000, + }); + return output; + } catch (error) { + throw new Error(`Failed to execute command: ${error.message}`); + } + } + async openShell(shell) { let ptyProcess = pty.spawn( "docker", diff --git a/test/e2e/safe-chain-proxy.e2e.spec.js b/test/e2e/safe-chain-proxy.e2e.spec.js index 6abbb0f..3efd2aa 100644 --- a/test/e2e/safe-chain-proxy.e2e.spec.js +++ b/test/e2e/safe-chain-proxy.e2e.spec.js @@ -57,4 +57,35 @@ describe("E2E: Safe chain proxy", () => { "Proxy log does not contain expected entries" ); }); + + it(`safe-chain proxy allows to request through a local http registry`, async () => { + // Start a local npm registry (verdaccio) inside the container + container.dockerExec("npx -y verdaccio", true); + + // Wait for verdaccio to be ready (max 30 seconds) + for (let i = 0; i < 60; i++) { + await new Promise((resolve) => setTimeout(resolve, 500)); + try { + const curlOutput = container.dockerExec( + "curl -I http://localhost:4873/" + ); + if (curlOutput.includes("200 OK")) { + break; + } + } catch { + // ignore, this means docker exec returned -1 and verdaccio is not yet ready + } + } + + const shell = await container.openShell("bash"); + const result = await shell.runCommand( + "npm --registry http://localhost:4873 install react" + ); + + // Check if the installation was successful + assert( + result.output.includes("added"), + "npm install did not complete successfully, output: " + result.output + ); + }); }); From f4933b08d00f82dc6089ce26d3c0c64aca937a8d Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Tue, 14 Oct 2025 13:15:14 +0200 Subject: [PATCH 03/30] Add log to diagnose e2e tests --- test/e2e/DockerTestContainer.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/e2e/DockerTestContainer.js b/test/e2e/DockerTestContainer.js index 1a817eb..196afdb 100644 --- a/test/e2e/DockerTestContainer.js +++ b/test/e2e/DockerTestContainer.js @@ -116,6 +116,8 @@ export class DockerTestContainer { const timeout = setTimeout(() => { // Fallback in case the command doesn't finish in a reasonable time + // oxlint-disable-next-line no-console - having this log in CI helps diagnose issues + console.log("Command timeout reached"); resolve({ allData, output: parseShellOutput(allData), command }); ptyProcess.removeListener("data", handleInput); }, 10000); From 2968960b41f691141e3b915443e794cb1086e902 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Tue, 14 Oct 2025 13:22:58 +0200 Subject: [PATCH 04/30] Cleanup registryProxy, increase timeout on DockerTestContainer --- packages/safe-chain/src/registryProxy/registryProxy.js | 8 ++++++-- test/e2e/DockerTestContainer.js | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/safe-chain/src/registryProxy/registryProxy.js b/packages/safe-chain/src/registryProxy/registryProxy.js index 2895753..d548999 100644 --- a/packages/safe-chain/src/registryProxy/registryProxy.js +++ b/packages/safe-chain/src/registryProxy/registryProxy.js @@ -16,7 +16,6 @@ const state = { export function createSafeChainProxy() { const server = createProxyServer(); - server.on("connect", handleConnect); return { startServer: () => startServer(server), @@ -55,7 +54,12 @@ export function mergeSafeChainProxyEnvironmentVariables(env) { } function createProxyServer() { - const server = http.createServer(handleHttpProxyRequest); + const server = http.createServer( + handleHttpProxyRequest // This handles plain HTTP requests + ); + + // This handles HTTPS requests via the CONNECT method + server.on("connect", handleConnect); return server; } diff --git a/test/e2e/DockerTestContainer.js b/test/e2e/DockerTestContainer.js index 196afdb..45b66d0 100644 --- a/test/e2e/DockerTestContainer.js +++ b/test/e2e/DockerTestContainer.js @@ -120,7 +120,7 @@ export class DockerTestContainer { console.log("Command timeout reached"); resolve({ allData, output: parseShellOutput(allData), command }); ptyProcess.removeListener("data", handleInput); - }, 10000); + }, 20000); function handleInput(data) { allData.push(data); From b6c31e1a5a1168eff0f3a40ac479c61d7aaf0ca4 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Tue, 14 Oct 2025 13:30:06 +0200 Subject: [PATCH 05/30] Increase time to start verdaccio --- test/e2e/safe-chain-proxy.e2e.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/e2e/safe-chain-proxy.e2e.spec.js b/test/e2e/safe-chain-proxy.e2e.spec.js index 3efd2aa..8a62052 100644 --- a/test/e2e/safe-chain-proxy.e2e.spec.js +++ b/test/e2e/safe-chain-proxy.e2e.spec.js @@ -62,8 +62,8 @@ describe("E2E: Safe chain proxy", () => { // Start a local npm registry (verdaccio) inside the container container.dockerExec("npx -y verdaccio", true); - // Wait for verdaccio to be ready (max 30 seconds) - for (let i = 0; i < 60; i++) { + // Wait for verdaccio to be ready (max 60 seconds) + for (let i = 0; i < 120; i++) { await new Promise((resolve) => setTimeout(resolve, 500)); try { const curlOutput = container.dockerExec( From c50eac977bbdfa371852e21f20674d1b50f52ce7 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Tue, 14 Oct 2025 13:34:47 +0200 Subject: [PATCH 06/30] Throw when verdaccio did not start --- test/e2e/safe-chain-proxy.e2e.spec.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/e2e/safe-chain-proxy.e2e.spec.js b/test/e2e/safe-chain-proxy.e2e.spec.js index 8a62052..11d01f7 100644 --- a/test/e2e/safe-chain-proxy.e2e.spec.js +++ b/test/e2e/safe-chain-proxy.e2e.spec.js @@ -62,6 +62,7 @@ describe("E2E: Safe chain proxy", () => { // Start a local npm registry (verdaccio) inside the container container.dockerExec("npx -y verdaccio", true); + let verdaccioStarted = false; // Wait for verdaccio to be ready (max 60 seconds) for (let i = 0; i < 120; i++) { await new Promise((resolve) => setTimeout(resolve, 500)); @@ -70,12 +71,16 @@ describe("E2E: Safe chain proxy", () => { "curl -I http://localhost:4873/" ); if (curlOutput.includes("200 OK")) { + verdaccioStarted = true; break; } } catch { // ignore, this means docker exec returned -1 and verdaccio is not yet ready } } + if (!verdaccioStarted) { + throw new Error("Verdaccio did not start in time"); + } const shell = await container.openShell("bash"); const result = await shell.runCommand( From 37585e80735f5c06192bb462cb9052936d087769 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Tue, 14 Oct 2025 13:44:49 +0200 Subject: [PATCH 07/30] Add more logs, handle verdaccio not starting better --- test/e2e/safe-chain-proxy.e2e.spec.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/e2e/safe-chain-proxy.e2e.spec.js b/test/e2e/safe-chain-proxy.e2e.spec.js index 11d01f7..12929df 100644 --- a/test/e2e/safe-chain-proxy.e2e.spec.js +++ b/test/e2e/safe-chain-proxy.e2e.spec.js @@ -72,6 +72,7 @@ describe("E2E: Safe chain proxy", () => { ); if (curlOutput.includes("200 OK")) { verdaccioStarted = true; + console.log("Verdaccio started, after " + i * 500 + "ms"); break; } } catch { @@ -79,10 +80,10 @@ describe("E2E: Safe chain proxy", () => { } } if (!verdaccioStarted) { - throw new Error("Verdaccio did not start in time"); + assert.fail("Verdaccio did not start in time"); } - const shell = await container.openShell("bash"); + const shell = await container.openShell("zsh"); const result = await shell.runCommand( "npm --registry http://localhost:4873 install react" ); From f655e8cfcb786754b81e4d1b5f4b022894e1b128 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Tue, 14 Oct 2025 13:52:28 +0200 Subject: [PATCH 08/30] Change command to install through registry. --- test/e2e/safe-chain-proxy.e2e.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/e2e/safe-chain-proxy.e2e.spec.js b/test/e2e/safe-chain-proxy.e2e.spec.js index 12929df..363787f 100644 --- a/test/e2e/safe-chain-proxy.e2e.spec.js +++ b/test/e2e/safe-chain-proxy.e2e.spec.js @@ -83,9 +83,9 @@ describe("E2E: Safe chain proxy", () => { assert.fail("Verdaccio did not start in time"); } - const shell = await container.openShell("zsh"); + const shell = await container.openShell("bash"); const result = await shell.runCommand( - "npm --registry http://localhost:4873 install react" + "npm install lodash --registry=http://localhost:4873" ); // Check if the installation was successful From 35beeb55b09ff9822a6bfccdd50ab73e2bd894c6 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Tue, 14 Oct 2025 14:10:23 +0200 Subject: [PATCH 09/30] Curl url with npm package --- test/e2e/safe-chain-proxy.e2e.spec.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/e2e/safe-chain-proxy.e2e.spec.js b/test/e2e/safe-chain-proxy.e2e.spec.js index 363787f..be0d6ea 100644 --- a/test/e2e/safe-chain-proxy.e2e.spec.js +++ b/test/e2e/safe-chain-proxy.e2e.spec.js @@ -68,7 +68,7 @@ describe("E2E: Safe chain proxy", () => { await new Promise((resolve) => setTimeout(resolve, 500)); try { const curlOutput = container.dockerExec( - "curl -I http://localhost:4873/" + "curl -I http://localhost:4873/lodash" ); if (curlOutput.includes("200 OK")) { verdaccioStarted = true; @@ -88,6 +88,8 @@ describe("E2E: Safe chain proxy", () => { "npm install lodash --registry=http://localhost:4873" ); + console.log("NPM install output:", result.output); + // Check if the installation was successful assert( result.output.includes("added"), From a2d05b0cf057cedd93bde112a3f2ee082900274f Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Tue, 14 Oct 2025 14:18:33 +0200 Subject: [PATCH 10/30] More logs --- packages/safe-chain/src/registryProxy/plainHttpProxy.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/safe-chain/src/registryProxy/plainHttpProxy.js b/packages/safe-chain/src/registryProxy/plainHttpProxy.js index 68a2362..507f518 100644 --- a/packages/safe-chain/src/registryProxy/plainHttpProxy.js +++ b/packages/safe-chain/src/registryProxy/plainHttpProxy.js @@ -1,8 +1,10 @@ import * as http from "http"; import * as https from "https"; +// oxlint-disable no-console - just for testing, remove afterwards export function handleHttpProxyRequest(req, res) { const url = new URL(req.url); + console.log(`Proxying request to: ${req.url}`); let protocol; if (url.protocol === "http:") { @@ -23,7 +25,8 @@ export function handleHttpProxyRequest(req, res) { res.writeHead(proxyRes.statusCode, proxyRes.headers); proxyRes.pipe(res); - proxyRes.on("error", () => { + proxyRes.on("error", (err) => { + console.log("Error in proxy response stream:", err); // Stream error while piping response // Response headers already sent, can't send error status }); @@ -35,18 +38,21 @@ export function handleHttpProxyRequest(req, res) { }); req.on("error", () => { + console.log("Error in client request stream"); // Client request stream error // Abort the proxy request proxyRequest.destroy(); }); res.on("error", () => { + console.log("Error in client response stream"); // Client response stream error (client disconnected) // Clean up proxy streams proxyRequest.destroy(); }); res.on("close", () => { + console.log("Client response stream closed"); // Client disconnected // Abort the proxy request to avoid unnecessary work if (!res.writableEnded) { From ee82134c19bad03b75c411929d38ebe052677c05 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Tue, 14 Oct 2025 14:54:58 +0200 Subject: [PATCH 11/30] Proxyres on close and end --- .../src/registryProxy/plainHttpProxy.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/safe-chain/src/registryProxy/plainHttpProxy.js b/packages/safe-chain/src/registryProxy/plainHttpProxy.js index 507f518..214ad0f 100644 --- a/packages/safe-chain/src/registryProxy/plainHttpProxy.js +++ b/packages/safe-chain/src/registryProxy/plainHttpProxy.js @@ -30,6 +30,22 @@ export function handleHttpProxyRequest(req, res) { // Stream error while piping response // Response headers already sent, can't send error status }); + + proxyRes.on("close", () => { + console.log("Proxy response stream closed"); + // Clean up if the proxy response stream closes + if (!res.writableEnded) { + res.end(); + } + }); + + proxyRes.on("end", () => { + console.log("Proxy response stream ended"); + // End of proxy response + if (!res.writableEnded) { + res.end(); + } + }); } ) .on("error", (err) => { From daf69964f2891ea878e6c255ae69e3ede6d3fd51 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Tue, 14 Oct 2025 15:00:00 +0200 Subject: [PATCH 12/30] Test without safe-chain --- test/e2e/safe-chain-proxy.e2e.spec.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/e2e/safe-chain-proxy.e2e.spec.js b/test/e2e/safe-chain-proxy.e2e.spec.js index be0d6ea..0e34db0 100644 --- a/test/e2e/safe-chain-proxy.e2e.spec.js +++ b/test/e2e/safe-chain-proxy.e2e.spec.js @@ -62,6 +62,9 @@ describe("E2E: Safe chain proxy", () => { // Start a local npm registry (verdaccio) inside the container container.dockerExec("npx -y verdaccio", true); + const shell1 = await container.openShell("bash"); + await shell1.runCommand("safe-chain teardown"); + let verdaccioStarted = false; // Wait for verdaccio to be ready (max 60 seconds) for (let i = 0; i < 120; i++) { From bfe5820d0fbef2492ddac593acaa1352e748ea12 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Tue, 14 Oct 2025 15:16:57 +0200 Subject: [PATCH 13/30] Log even more --- test/e2e/safe-chain-proxy.e2e.spec.js | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/test/e2e/safe-chain-proxy.e2e.spec.js b/test/e2e/safe-chain-proxy.e2e.spec.js index 0e34db0..8e602ca 100644 --- a/test/e2e/safe-chain-proxy.e2e.spec.js +++ b/test/e2e/safe-chain-proxy.e2e.spec.js @@ -59,14 +59,21 @@ describe("E2E: Safe chain proxy", () => { }); it(`safe-chain proxy allows to request through a local http registry`, async () => { + const configShell = await container.openShell("bash"); + await configShell.runCommand("touch ~/.verdaccio-config.yaml"); + await configShell.runCommand("echo 'log:' >> ~/.verdaccio-config.yaml"); + await configShell.runCommand( + "echo ' type: file' >> ~/.verdaccio-config.yaml" + ); + await configShell.runCommand( + "echo ' path: /verdaccio.log' >> ~/.verdaccio-config.yaml" + ); + // Start a local npm registry (verdaccio) inside the container - container.dockerExec("npx -y verdaccio", true); - - const shell1 = await container.openShell("bash"); - await shell1.runCommand("safe-chain teardown"); + container.dockerExec("npx -y verdaccio -c ~/.verdaccio-config.yaml", true); + // Polling until verdaccio is ready (max 60 seconds) let verdaccioStarted = false; - // Wait for verdaccio to be ready (max 60 seconds) for (let i = 0; i < 120; i++) { await new Promise((resolve) => setTimeout(resolve, 500)); try { @@ -75,7 +82,7 @@ describe("E2E: Safe chain proxy", () => { ); if (curlOutput.includes("200 OK")) { verdaccioStarted = true; - console.log("Verdaccio started, after " + i * 500 + "ms"); + console.log("Verdaccio started, after " + i * 500 + "ms", curlOutput); break; } } catch { @@ -93,6 +100,13 @@ describe("E2E: Safe chain proxy", () => { console.log("NPM install output:", result.output); + const verdaccioLog = await container.openShell("bash"); + const { output: logOutput } = await verdaccioLog.runCommand( + "cat /verdaccio.log" + ); + + console.log("Verdaccio log output:", logOutput); + // Check if the installation was successful assert( result.output.includes("added"), From dfdce18c8ddcc2b04abf477f1bbd013aa00d0591 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Tue, 14 Oct 2025 15:23:40 +0200 Subject: [PATCH 14/30] Fix config --- test/e2e/safe-chain-proxy.e2e.spec.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/e2e/safe-chain-proxy.e2e.spec.js b/test/e2e/safe-chain-proxy.e2e.spec.js index 8e602ca..a6aadd8 100644 --- a/test/e2e/safe-chain-proxy.e2e.spec.js +++ b/test/e2e/safe-chain-proxy.e2e.spec.js @@ -61,12 +61,14 @@ describe("E2E: Safe chain proxy", () => { it(`safe-chain proxy allows to request through a local http registry`, async () => { const configShell = await container.openShell("bash"); await configShell.runCommand("touch ~/.verdaccio-config.yaml"); - await configShell.runCommand("echo 'log:' >> ~/.verdaccio-config.yaml"); + // verdaccio.yaml + // storage: ./storage + // log: { type: file, path: ./verdaccio.log, level: info } await configShell.runCommand( - "echo ' type: file' >> ~/.verdaccio-config.yaml" + "echo 'log: { type: file, path: /verdaccio.log, level: info }' >> ~/.verdaccio-config.yaml" ); await configShell.runCommand( - "echo ' path: /verdaccio.log' >> ~/.verdaccio-config.yaml" + "echo 'storage: ./storage' >> ~/.verdaccio-config.yaml" ); // Start a local npm registry (verdaccio) inside the container From 4c76242d443d3324c94b371deebefa423885c5e9 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Tue, 14 Oct 2025 15:25:10 +0200 Subject: [PATCH 15/30] More config --- test/e2e/safe-chain-proxy.e2e.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/safe-chain-proxy.e2e.spec.js b/test/e2e/safe-chain-proxy.e2e.spec.js index a6aadd8..8b2d56b 100644 --- a/test/e2e/safe-chain-proxy.e2e.spec.js +++ b/test/e2e/safe-chain-proxy.e2e.spec.js @@ -65,7 +65,7 @@ describe("E2E: Safe chain proxy", () => { // storage: ./storage // log: { type: file, path: ./verdaccio.log, level: info } await configShell.runCommand( - "echo 'log: { type: file, path: /verdaccio.log, level: info }' >> ~/.verdaccio-config.yaml" + "echo 'log: { type: file, path: /verdaccio.log, level: trace, colors: false }' >> ~/.verdaccio-config.yaml" ); await configShell.runCommand( "echo 'storage: ./storage' >> ~/.verdaccio-config.yaml" From b794b293d136d02f4ec908936edabfd705fc5c41 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Tue, 14 Oct 2025 15:32:13 +0200 Subject: [PATCH 16/30] Fix config --- test/e2e/safe-chain-proxy.e2e.spec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/e2e/safe-chain-proxy.e2e.spec.js b/test/e2e/safe-chain-proxy.e2e.spec.js index 8b2d56b..51d5dd4 100644 --- a/test/e2e/safe-chain-proxy.e2e.spec.js +++ b/test/e2e/safe-chain-proxy.e2e.spec.js @@ -65,14 +65,14 @@ describe("E2E: Safe chain proxy", () => { // storage: ./storage // log: { type: file, path: ./verdaccio.log, level: info } await configShell.runCommand( - "echo 'log: { type: file, path: /verdaccio.log, level: trace, colors: false }' >> ~/.verdaccio-config.yaml" + "echo 'storage: ./storage' >> ~/verdaccio-config.yaml" ); await configShell.runCommand( - "echo 'storage: ./storage' >> ~/.verdaccio-config.yaml" + "echo 'log: { type: file, path: /verdaccio.log, level: trace }' >> ~/verdaccio-config.yaml" ); // Start a local npm registry (verdaccio) inside the container - container.dockerExec("npx -y verdaccio -c ~/.verdaccio-config.yaml", true); + container.dockerExec("npx -y verdaccio -c ~/verdaccio-config.yaml", true); // Polling until verdaccio is ready (max 60 seconds) let verdaccioStarted = false; From 23bce71356fd68aa003e5032c76ba3626f6b9fbc Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Tue, 14 Oct 2025 15:40:08 +0200 Subject: [PATCH 17/30] Fix config 2 --- test/e2e/safe-chain-proxy.e2e.spec.js | 32 +++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/test/e2e/safe-chain-proxy.e2e.spec.js b/test/e2e/safe-chain-proxy.e2e.spec.js index 51d5dd4..42eadfb 100644 --- a/test/e2e/safe-chain-proxy.e2e.spec.js +++ b/test/e2e/safe-chain-proxy.e2e.spec.js @@ -62,13 +62,37 @@ describe("E2E: Safe chain proxy", () => { const configShell = await container.openShell("bash"); await configShell.runCommand("touch ~/.verdaccio-config.yaml"); // verdaccio.yaml - // storage: ./storage - // log: { type: file, path: ./verdaccio.log, level: info } + /* +storage: ./storage +uplinks: + npmjs: + url: https://registry.npmjs.org/ +packages: + "**": + access: $all + proxy: npmjs +log: { type: file, path: ./verdaccio.log, level: trace, colors: false } + */ await configShell.runCommand( - "echo 'storage: ./storage' >> ~/verdaccio-config.yaml" + `echo 'storage: ./storage' >> ~/.verdaccio-config.yaml` + ); + await configShell.runCommand(`echo 'uplinks:' >> ~/.verdaccio-config.yaml`); + await configShell.runCommand(`echo ' npmjs:' >> ~/.verdaccio-config.yaml`); + await configShell.runCommand( + `echo ' url: https://registry.npmjs.org/' >> ~/.verdaccio-config.yaml` ); await configShell.runCommand( - "echo 'log: { type: file, path: /verdaccio.log, level: trace }' >> ~/verdaccio-config.yaml" + `echo 'packages:' >> ~/.verdaccio-config.yaml` + ); + await configShell.runCommand(`echo ' "**":' >> ~/.verdaccio-config.yaml`); + await configShell.runCommand( + `echo ' access: $all' >> ~/.verdaccio-config.yaml` + ); + await configShell.runCommand( + `echo ' proxy: npmjs' >> ~/.verdaccio-config.yaml` + ); + await configShell.runCommand( + `echo 'log: { type: file, path: ./verdaccio.log, level: trace, colors: false }' >> ~/.verdaccio-config.yaml` ); // Start a local npm registry (verdaccio) inside the container From 7ae4d3bc8d817253ea9b30c712c3c035d76b7ba6 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Tue, 14 Oct 2025 15:59:43 +0200 Subject: [PATCH 18/30] Try some more config --- test/e2e/safe-chain-proxy.e2e.spec.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/e2e/safe-chain-proxy.e2e.spec.js b/test/e2e/safe-chain-proxy.e2e.spec.js index 42eadfb..a81224e 100644 --- a/test/e2e/safe-chain-proxy.e2e.spec.js +++ b/test/e2e/safe-chain-proxy.e2e.spec.js @@ -76,6 +76,16 @@ log: { type: file, path: ./verdaccio.log, level: trace, colors: false } await configShell.runCommand( `echo 'storage: ./storage' >> ~/.verdaccio-config.yaml` ); + await configShell.runCommand(`echo 'auth:' >> ~/.verdaccio-config.yaml`); + await configShell.runCommand( + `echo ' htpasswd:' >> ~/.verdaccio-config.yaml` + ); + await configShell.runCommand( + `echo ' file: ./htpasswd' >> ~/.verdaccio-config.yaml` + ); + await configShell.runCommand( + `echo ' max_users: 100' >> ~/.verdaccio-config.yaml` + ); await configShell.runCommand(`echo 'uplinks:' >> ~/.verdaccio-config.yaml`); await configShell.runCommand(`echo ' npmjs:' >> ~/.verdaccio-config.yaml`); await configShell.runCommand( From 93223fe64012c1870574e60747d64284ec9a8e4d Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Tue, 14 Oct 2025 16:00:31 +0200 Subject: [PATCH 19/30] Try more config --- test/e2e/safe-chain-proxy.e2e.spec.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/e2e/safe-chain-proxy.e2e.spec.js b/test/e2e/safe-chain-proxy.e2e.spec.js index a81224e..dbc6522 100644 --- a/test/e2e/safe-chain-proxy.e2e.spec.js +++ b/test/e2e/safe-chain-proxy.e2e.spec.js @@ -106,7 +106,10 @@ log: { type: file, path: ./verdaccio.log, level: trace, colors: false } ); // Start a local npm registry (verdaccio) inside the container - container.dockerExec("npx -y verdaccio -c ~/verdaccio-config.yaml", true); + container.dockerExec( + "npx -y verdaccio --listen 4873 -c ~/verdaccio-config.yaml", + true + ); // Polling until verdaccio is ready (max 60 seconds) let verdaccioStarted = false; From d35a4ca3572cb20e7cf14103143fa7ca50f7ffc0 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Tue, 14 Oct 2025 16:05:39 +0200 Subject: [PATCH 20/30] Change config location --- test/e2e/safe-chain-proxy.e2e.spec.js | 36 +++++++++++++-------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/test/e2e/safe-chain-proxy.e2e.spec.js b/test/e2e/safe-chain-proxy.e2e.spec.js index dbc6522..a1ffe53 100644 --- a/test/e2e/safe-chain-proxy.e2e.spec.js +++ b/test/e2e/safe-chain-proxy.e2e.spec.js @@ -60,7 +60,7 @@ describe("E2E: Safe chain proxy", () => { it(`safe-chain proxy allows to request through a local http registry`, async () => { const configShell = await container.openShell("bash"); - await configShell.runCommand("touch ~/.verdaccio-config.yaml"); + await configShell.runCommand("touch /.verdaccio-config.yaml"); // verdaccio.yaml /* storage: ./storage @@ -74,40 +74,38 @@ packages: log: { type: file, path: ./verdaccio.log, level: trace, colors: false } */ await configShell.runCommand( - `echo 'storage: ./storage' >> ~/.verdaccio-config.yaml` + `echo 'storage: ./storage' >> /.verdaccio-config.yaml` ); - await configShell.runCommand(`echo 'auth:' >> ~/.verdaccio-config.yaml`); + await configShell.runCommand(`echo 'auth:' >> /.verdaccio-config.yaml`); await configShell.runCommand( - `echo ' htpasswd:' >> ~/.verdaccio-config.yaml` + `echo ' htpasswd:' >> /.verdaccio-config.yaml` ); await configShell.runCommand( - `echo ' file: ./htpasswd' >> ~/.verdaccio-config.yaml` + `echo ' file: ./htpasswd' >> /.verdaccio-config.yaml` ); await configShell.runCommand( - `echo ' max_users: 100' >> ~/.verdaccio-config.yaml` + `echo ' max_users: 100' >> /.verdaccio-config.yaml` ); - await configShell.runCommand(`echo 'uplinks:' >> ~/.verdaccio-config.yaml`); - await configShell.runCommand(`echo ' npmjs:' >> ~/.verdaccio-config.yaml`); + await configShell.runCommand(`echo 'uplinks:' >> /.verdaccio-config.yaml`); + await configShell.runCommand(`echo ' npmjs:' >> /.verdaccio-config.yaml`); await configShell.runCommand( - `echo ' url: https://registry.npmjs.org/' >> ~/.verdaccio-config.yaml` + `echo ' url: https://registry.npmjs.org/' >> /.verdaccio-config.yaml` + ); + await configShell.runCommand(`echo 'packages:' >> /.verdaccio-config.yaml`); + await configShell.runCommand(`echo ' "**":' >> /.verdaccio-config.yaml`); + await configShell.runCommand( + `echo ' access: $all' >> /.verdaccio-config.yaml` ); await configShell.runCommand( - `echo 'packages:' >> ~/.verdaccio-config.yaml` - ); - await configShell.runCommand(`echo ' "**":' >> ~/.verdaccio-config.yaml`); - await configShell.runCommand( - `echo ' access: $all' >> ~/.verdaccio-config.yaml` + `echo ' proxy: npmjs' >> /.verdaccio-config.yaml` ); await configShell.runCommand( - `echo ' proxy: npmjs' >> ~/.verdaccio-config.yaml` - ); - await configShell.runCommand( - `echo 'log: { type: file, path: ./verdaccio.log, level: trace, colors: false }' >> ~/.verdaccio-config.yaml` + `echo 'log: { type: file, path: ./verdaccio.log, level: trace, colors: false }' >> /.verdaccio-config.yaml` ); // Start a local npm registry (verdaccio) inside the container container.dockerExec( - "npx -y verdaccio --listen 4873 -c ~/verdaccio-config.yaml", + "npx -y verdaccio --listen 4873 -c /verdaccio-config.yaml", true ); From b567016ddd0023260736a7edad5b2c4f5efdd11c Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Tue, 14 Oct 2025 16:11:34 +0200 Subject: [PATCH 21/30] Simplify test --- test/e2e/safe-chain-proxy.e2e.spec.js | 103 +++++++++++++------------- 1 file changed, 50 insertions(+), 53 deletions(-) diff --git a/test/e2e/safe-chain-proxy.e2e.spec.js b/test/e2e/safe-chain-proxy.e2e.spec.js index a1ffe53..0c21f88 100644 --- a/test/e2e/safe-chain-proxy.e2e.spec.js +++ b/test/e2e/safe-chain-proxy.e2e.spec.js @@ -60,58 +60,55 @@ describe("E2E: Safe chain proxy", () => { it(`safe-chain proxy allows to request through a local http registry`, async () => { const configShell = await container.openShell("bash"); - await configShell.runCommand("touch /.verdaccio-config.yaml"); - // verdaccio.yaml - /* -storage: ./storage -uplinks: - npmjs: - url: https://registry.npmjs.org/ -packages: - "**": - access: $all - proxy: npmjs -log: { type: file, path: ./verdaccio.log, level: trace, colors: false } - */ - await configShell.runCommand( - `echo 'storage: ./storage' >> /.verdaccio-config.yaml` - ); - await configShell.runCommand(`echo 'auth:' >> /.verdaccio-config.yaml`); - await configShell.runCommand( - `echo ' htpasswd:' >> /.verdaccio-config.yaml` - ); - await configShell.runCommand( - `echo ' file: ./htpasswd' >> /.verdaccio-config.yaml` - ); - await configShell.runCommand( - `echo ' max_users: 100' >> /.verdaccio-config.yaml` - ); - await configShell.runCommand(`echo 'uplinks:' >> /.verdaccio-config.yaml`); - await configShell.runCommand(`echo ' npmjs:' >> /.verdaccio-config.yaml`); - await configShell.runCommand( - `echo ' url: https://registry.npmjs.org/' >> /.verdaccio-config.yaml` - ); - await configShell.runCommand(`echo 'packages:' >> /.verdaccio-config.yaml`); - await configShell.runCommand(`echo ' "**":' >> /.verdaccio-config.yaml`); - await configShell.runCommand( - `echo ' access: $all' >> /.verdaccio-config.yaml` - ); - await configShell.runCommand( - `echo ' proxy: npmjs' >> /.verdaccio-config.yaml` - ); - await configShell.runCommand( - `echo 'log: { type: file, path: ./verdaccio.log, level: trace, colors: false }' >> /.verdaccio-config.yaml` - ); + // await configShell.runCommand("touch /.verdaccio-config.yaml"); + // // verdaccio.yaml + // /* + // storage: ./storage + // uplinks: + // npmjs: + // url: https://registry.npmjs.org/ + // packages: + // "**": + // access: $all + // proxy: npmjs + // log: { type: file, path: ./verdaccio.log, level: trace, colors: false } + // */ + // await configShell.runCommand( + // `echo 'storage: ./storage' >> /.verdaccio-config.yaml` + // ); + // await configShell.runCommand(`echo 'auth:' >> /.verdaccio-config.yaml`); + // await configShell.runCommand( + // `echo ' htpasswd:' >> /.verdaccio-config.yaml` + // ); + // await configShell.runCommand( + // `echo ' file: ./htpasswd' >> /.verdaccio-config.yaml` + // ); + // await configShell.runCommand( + // `echo ' max_users: 100' >> /.verdaccio-config.yaml` + // ); + // await configShell.runCommand(`echo 'uplinks:' >> /.verdaccio-config.yaml`); + // await configShell.runCommand(`echo ' npmjs:' >> /.verdaccio-config.yaml`); + // await configShell.runCommand( + // `echo ' url: https://registry.npmjs.org/' >> /.verdaccio-config.yaml` + // ); + // await configShell.runCommand(`echo 'packages:' >> /.verdaccio-config.yaml`); + // await configShell.runCommand(`echo ' "**":' >> /.verdaccio-config.yaml`); + // await configShell.runCommand( + // `echo ' access: $all' >> /.verdaccio-config.yaml` + // ); + // await configShell.runCommand( + // `echo ' proxy: npmjs' >> /.verdaccio-config.yaml` + // ); + // await configShell.runCommand( + // `echo 'log: { type: file, path: ./verdaccio.log, level: trace, colors: false }' >> /.verdaccio-config.yaml` + // ); // Start a local npm registry (verdaccio) inside the container - container.dockerExec( - "npx -y verdaccio --listen 4873 -c /verdaccio-config.yaml", - true - ); + container.dockerExec("npx -y verdaccio --listen 4873", true); - // Polling until verdaccio is ready (max 60 seconds) + // Polling until verdaccio is ready (max 30 seconds) let verdaccioStarted = false; - for (let i = 0; i < 120; i++) { + for (let i = 0; i < 30; i++) { await new Promise((resolve) => setTimeout(resolve, 500)); try { const curlOutput = container.dockerExec( @@ -137,12 +134,12 @@ log: { type: file, path: ./verdaccio.log, level: trace, colors: false } console.log("NPM install output:", result.output); - const verdaccioLog = await container.openShell("bash"); - const { output: logOutput } = await verdaccioLog.runCommand( - "cat /verdaccio.log" - ); + // const verdaccioLog = await container.openShell("bash"); + // const { output: logOutput } = await verdaccioLog.runCommand( + // "cat /verdaccio.log" + // ); - console.log("Verdaccio log output:", logOutput); + // console.log("Verdaccio log output:", logOutput); // Check if the installation was successful assert( From 24bda852d0e168a5b24fe27b09a7cfbc4451bb8f Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Wed, 15 Oct 2025 07:42:16 +0200 Subject: [PATCH 22/30] Redo test - start simple --- test/e2e/safe-chain-proxy.e2e.spec.js | 79 ++++++--------------------- 1 file changed, 17 insertions(+), 62 deletions(-) diff --git a/test/e2e/safe-chain-proxy.e2e.spec.js b/test/e2e/safe-chain-proxy.e2e.spec.js index 0c21f88..518390c 100644 --- a/test/e2e/safe-chain-proxy.e2e.spec.js +++ b/test/e2e/safe-chain-proxy.e2e.spec.js @@ -59,51 +59,6 @@ describe("E2E: Safe chain proxy", () => { }); it(`safe-chain proxy allows to request through a local http registry`, async () => { - const configShell = await container.openShell("bash"); - // await configShell.runCommand("touch /.verdaccio-config.yaml"); - // // verdaccio.yaml - // /* - // storage: ./storage - // uplinks: - // npmjs: - // url: https://registry.npmjs.org/ - // packages: - // "**": - // access: $all - // proxy: npmjs - // log: { type: file, path: ./verdaccio.log, level: trace, colors: false } - // */ - // await configShell.runCommand( - // `echo 'storage: ./storage' >> /.verdaccio-config.yaml` - // ); - // await configShell.runCommand(`echo 'auth:' >> /.verdaccio-config.yaml`); - // await configShell.runCommand( - // `echo ' htpasswd:' >> /.verdaccio-config.yaml` - // ); - // await configShell.runCommand( - // `echo ' file: ./htpasswd' >> /.verdaccio-config.yaml` - // ); - // await configShell.runCommand( - // `echo ' max_users: 100' >> /.verdaccio-config.yaml` - // ); - // await configShell.runCommand(`echo 'uplinks:' >> /.verdaccio-config.yaml`); - // await configShell.runCommand(`echo ' npmjs:' >> /.verdaccio-config.yaml`); - // await configShell.runCommand( - // `echo ' url: https://registry.npmjs.org/' >> /.verdaccio-config.yaml` - // ); - // await configShell.runCommand(`echo 'packages:' >> /.verdaccio-config.yaml`); - // await configShell.runCommand(`echo ' "**":' >> /.verdaccio-config.yaml`); - // await configShell.runCommand( - // `echo ' access: $all' >> /.verdaccio-config.yaml` - // ); - // await configShell.runCommand( - // `echo ' proxy: npmjs' >> /.verdaccio-config.yaml` - // ); - // await configShell.runCommand( - // `echo 'log: { type: file, path: ./verdaccio.log, level: trace, colors: false }' >> /.verdaccio-config.yaml` - // ); - - // Start a local npm registry (verdaccio) inside the container container.dockerExec("npx -y verdaccio --listen 4873", true); // Polling until verdaccio is ready (max 30 seconds) @@ -112,7 +67,7 @@ describe("E2E: Safe chain proxy", () => { await new Promise((resolve) => setTimeout(resolve, 500)); try { const curlOutput = container.dockerExec( - "curl -I http://localhost:4873/lodash" + "curl -I http://localhost:4873/lodash/-/lodash-4.17.21.tgz" ); if (curlOutput.includes("200 OK")) { verdaccioStarted = true; @@ -127,24 +82,24 @@ describe("E2E: Safe chain proxy", () => { assert.fail("Verdaccio did not start in time"); } - const shell = await container.openShell("bash"); - const result = await shell.runCommand( - "npm install lodash --registry=http://localhost:4873" - ); - - console.log("NPM install output:", result.output); - - // const verdaccioLog = await container.openShell("bash"); - // const { output: logOutput } = await verdaccioLog.runCommand( - // "cat /verdaccio.log" + // const shell = await container.openShell("bash"); + // const result = await shell.runCommand( + // "npm install lodash --registry=http://localhost:4873" // ); - // console.log("Verdaccio log output:", logOutput); + // console.log("NPM install output:", result.output); - // Check if the installation was successful - assert( - result.output.includes("added"), - "npm install did not complete successfully, output: " + result.output - ); + // // const verdaccioLog = await container.openShell("bash"); + // // const { output: logOutput } = await verdaccioLog.runCommand( + // // "cat /verdaccio.log" + // // ); + + // // console.log("Verdaccio log output:", logOutput); + + // // Check if the installation was successful + // assert( + // result.output.includes("added"), + // "npm install did not complete successfully, output: " + result.output + // ); }); }); From b4f7d845631e9b52c53b888827c0d70aaed07fdd Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Wed, 15 Oct 2025 07:50:13 +0200 Subject: [PATCH 23/30] Run npm install command --- test/e2e/package.json | 2 +- test/e2e/safe-chain-proxy.e2e.spec.js | 22 ++++++++-------------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/test/e2e/package.json b/test/e2e/package.json index 9217808..b34fd0b 100644 --- a/test/e2e/package.json +++ b/test/e2e/package.json @@ -4,7 +4,7 @@ "version": "1.0.0", "description": "End-to-end tests for the Aikido Safe Chain", "scripts": { - "test": "node --test --test-concurrency=1 **/*.spec.js" + "test": "node --test --test-concurrency=1 **/safe-chain-proxy.e2e.spec.js" }, "keywords": [], "author": "Aikido Security", diff --git a/test/e2e/safe-chain-proxy.e2e.spec.js b/test/e2e/safe-chain-proxy.e2e.spec.js index 518390c..fb4b61d 100644 --- a/test/e2e/safe-chain-proxy.e2e.spec.js +++ b/test/e2e/safe-chain-proxy.e2e.spec.js @@ -63,7 +63,7 @@ describe("E2E: Safe chain proxy", () => { // Polling until verdaccio is ready (max 30 seconds) let verdaccioStarted = false; - for (let i = 0; i < 30; i++) { + for (let i = 0; i < 60; i++) { await new Promise((resolve) => setTimeout(resolve, 500)); try { const curlOutput = container.dockerExec( @@ -71,7 +71,10 @@ describe("E2E: Safe chain proxy", () => { ); if (curlOutput.includes("200 OK")) { verdaccioStarted = true; - console.log("Verdaccio started, after " + i * 500 + "ms", curlOutput); + console.log( + "Verdaccio started, after " + i * 500 + "ms\n", + curlOutput + ); break; } } catch { @@ -82,19 +85,10 @@ describe("E2E: Safe chain proxy", () => { assert.fail("Verdaccio did not start in time"); } - // const shell = await container.openShell("bash"); - // const result = await shell.runCommand( - // "npm install lodash --registry=http://localhost:4873" - // ); + const shell = await container.openShell("bash"); + const result = await shell.runCommand("npm install lodash"); - // console.log("NPM install output:", result.output); - - // // const verdaccioLog = await container.openShell("bash"); - // // const { output: logOutput } = await verdaccioLog.runCommand( - // // "cat /verdaccio.log" - // // ); - - // // console.log("Verdaccio log output:", logOutput); + console.log("NPM install output:\n", result.output); // // Check if the installation was successful // assert( From 1a8d58889c410c82b4f9e51a1f28f2b221b10a9a Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Wed, 15 Oct 2025 07:50:56 +0200 Subject: [PATCH 24/30] Try again --- test/e2e/safe-chain-proxy.e2e.spec.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/e2e/safe-chain-proxy.e2e.spec.js b/test/e2e/safe-chain-proxy.e2e.spec.js index fb4b61d..c475fdf 100644 --- a/test/e2e/safe-chain-proxy.e2e.spec.js +++ b/test/e2e/safe-chain-proxy.e2e.spec.js @@ -90,6 +90,12 @@ describe("E2E: Safe chain proxy", () => { console.log("NPM install output:\n", result.output); + const curlOutput = container.dockerExec( + "curl -I http://localhost:4873/lodash/-/lodash-4.17.21.tgz" + ); + + console.log("Curl output:\n", curlOutput); + // // Check if the installation was successful // assert( // result.output.includes("added"), From 1f2d4e86c7de83c5ca1b00e63b0467fa3c2dce4d Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Wed, 15 Oct 2025 07:54:35 +0200 Subject: [PATCH 25/30] Add registry to localhost again --- test/e2e/safe-chain-proxy.e2e.spec.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/e2e/safe-chain-proxy.e2e.spec.js b/test/e2e/safe-chain-proxy.e2e.spec.js index c475fdf..276d5e8 100644 --- a/test/e2e/safe-chain-proxy.e2e.spec.js +++ b/test/e2e/safe-chain-proxy.e2e.spec.js @@ -86,7 +86,9 @@ describe("E2E: Safe chain proxy", () => { } const shell = await container.openShell("bash"); - const result = await shell.runCommand("npm install lodash"); + const result = await shell.runCommand( + "npm install lodash --registry http://localhost:4873" + ); console.log("NPM install output:\n", result.output); From 3aec4737550a4c3e3cb6fce989173836c578f65b Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Wed, 15 Oct 2025 08:50:13 +0200 Subject: [PATCH 26/30] Without safe-chain --- test/e2e/safe-chain-proxy.e2e.spec.js | 58 +++++++++++++-------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/test/e2e/safe-chain-proxy.e2e.spec.js b/test/e2e/safe-chain-proxy.e2e.spec.js index 276d5e8..ffa1e79 100644 --- a/test/e2e/safe-chain-proxy.e2e.spec.js +++ b/test/e2e/safe-chain-proxy.e2e.spec.js @@ -14,8 +14,8 @@ describe("E2E: Safe chain proxy", () => { container = new DockerTestContainer(); await container.start(); - const installationShell = await container.openShell("zsh"); - await installationShell.runCommand("safe-chain setup"); + // const installationShell = await container.openShell("zsh"); + // await installationShell.runCommand("safe-chain setup"); }); afterEach(async () => { @@ -26,37 +26,37 @@ describe("E2E: Safe chain proxy", () => { } }); - it(`safe-chain proxy respects upstream proxy settings`, async () => { - // Configure and start a proxy inside the container - const proxy = await container.openShell("zsh"); - await proxy.runCommand( - `echo 'BasicAuth user password' >> /etc/tinyproxy/tinyproxy.conf` - ); - await proxy.runCommand("tinyproxy"); + // it(`safe-chain proxy respects upstream proxy settings`, async () => { + // // Configure and start a proxy inside the container + // const proxy = await container.openShell("zsh"); + // await proxy.runCommand( + // `echo 'BasicAuth user password' >> /etc/tinyproxy/tinyproxy.conf` + // ); + // await proxy.runCommand("tinyproxy"); - const shell = await container.openShell("zsh"); - await shell.runCommand( - 'export HTTPS_PROXY="http://user:password@localhost:8888"' - ); - const { output } = await shell.runCommand("npm install axios"); + // const shell = await container.openShell("zsh"); + // await shell.runCommand( + // 'export HTTPS_PROXY="http://user:password@localhost:8888"' + // ); + // const { output } = await shell.runCommand("npm install axios"); - // Check if the installation was successful - assert( - output.includes("added") || output.includes("up to date"), - "npm install did not complete successfully" - ); + // // Check if the installation was successful + // assert( + // output.includes("added") || output.includes("up to date"), + // "npm install did not complete successfully" + // ); - const proxyLog = await container.openShell("zsh"); - const { output: logOutput } = await proxyLog.runCommand( - "cat /var/log/tinyproxy/tinyproxy.log" - ); + // const proxyLog = await container.openShell("zsh"); + // const { output: logOutput } = await proxyLog.runCommand( + // "cat /var/log/tinyproxy/tinyproxy.log" + // ); - // Check if the proxy log contains entries for the npm install - assert( - logOutput.includes("CONNECT registry.npmjs.org:443"), - "Proxy log does not contain expected entries" - ); - }); + // // Check if the proxy log contains entries for the npm install + // assert( + // logOutput.includes("CONNECT registry.npmjs.org:443"), + // "Proxy log does not contain expected entries" + // ); + // }); it(`safe-chain proxy allows to request through a local http registry`, async () => { container.dockerExec("npx -y verdaccio --listen 4873", true); From 056a1963e3be19837e0b99d0e7f8b60670f323c5 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Wed, 15 Oct 2025 09:18:11 +0200 Subject: [PATCH 27/30] Remove test again --- test/e2e/safe-chain-proxy.e2e.spec.js | 95 +++++++-------------------- 1 file changed, 24 insertions(+), 71 deletions(-) diff --git a/test/e2e/safe-chain-proxy.e2e.spec.js b/test/e2e/safe-chain-proxy.e2e.spec.js index ffa1e79..22a7038 100644 --- a/test/e2e/safe-chain-proxy.e2e.spec.js +++ b/test/e2e/safe-chain-proxy.e2e.spec.js @@ -26,82 +26,35 @@ describe("E2E: Safe chain proxy", () => { } }); - // it(`safe-chain proxy respects upstream proxy settings`, async () => { - // // Configure and start a proxy inside the container - // const proxy = await container.openShell("zsh"); - // await proxy.runCommand( - // `echo 'BasicAuth user password' >> /etc/tinyproxy/tinyproxy.conf` - // ); - // await proxy.runCommand("tinyproxy"); + it(`safe-chain proxy respects upstream proxy settings`, async () => { + // Configure and start a proxy inside the container + const proxy = await container.openShell("zsh"); + await proxy.runCommand( + `echo 'BasicAuth user password' >> /etc/tinyproxy/tinyproxy.conf` + ); + await proxy.runCommand("tinyproxy"); - // const shell = await container.openShell("zsh"); - // await shell.runCommand( - // 'export HTTPS_PROXY="http://user:password@localhost:8888"' - // ); - // const { output } = await shell.runCommand("npm install axios"); + const shell = await container.openShell("zsh"); + await shell.runCommand( + 'export HTTPS_PROXY="http://user:password@localhost:8888"' + ); + const { output } = await shell.runCommand("npm install axios"); - // // Check if the installation was successful - // assert( - // output.includes("added") || output.includes("up to date"), - // "npm install did not complete successfully" - // ); - - // const proxyLog = await container.openShell("zsh"); - // const { output: logOutput } = await proxyLog.runCommand( - // "cat /var/log/tinyproxy/tinyproxy.log" - // ); - - // // Check if the proxy log contains entries for the npm install - // assert( - // logOutput.includes("CONNECT registry.npmjs.org:443"), - // "Proxy log does not contain expected entries" - // ); - // }); - - it(`safe-chain proxy allows to request through a local http registry`, async () => { - container.dockerExec("npx -y verdaccio --listen 4873", true); - - // Polling until verdaccio is ready (max 30 seconds) - let verdaccioStarted = false; - for (let i = 0; i < 60; i++) { - await new Promise((resolve) => setTimeout(resolve, 500)); - try { - const curlOutput = container.dockerExec( - "curl -I http://localhost:4873/lodash/-/lodash-4.17.21.tgz" - ); - if (curlOutput.includes("200 OK")) { - verdaccioStarted = true; - console.log( - "Verdaccio started, after " + i * 500 + "ms\n", - curlOutput - ); - break; - } - } catch { - // ignore, this means docker exec returned -1 and verdaccio is not yet ready - } - } - if (!verdaccioStarted) { - assert.fail("Verdaccio did not start in time"); - } - - const shell = await container.openShell("bash"); - const result = await shell.runCommand( - "npm install lodash --registry http://localhost:4873" + // Check if the installation was successful + assert( + output.includes("added") || output.includes("up to date"), + "npm install did not complete successfully" ); - console.log("NPM install output:\n", result.output); - - const curlOutput = container.dockerExec( - "curl -I http://localhost:4873/lodash/-/lodash-4.17.21.tgz" + const proxyLog = await container.openShell("zsh"); + const { output: logOutput } = await proxyLog.runCommand( + "cat /var/log/tinyproxy/tinyproxy.log" ); - console.log("Curl output:\n", curlOutput); - - // // Check if the installation was successful - // assert( - // result.output.includes("added"), - // "npm install did not complete successfully, output: " + result.output - // ); + // Check if the proxy log contains entries for the npm install + assert( + logOutput.includes("CONNECT registry.npmjs.org:443"), + "Proxy log does not contain expected entries" + ); }); }); From fce7550609f217ad7c814e2a224854db93efea41 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Wed, 15 Oct 2025 09:21:23 +0200 Subject: [PATCH 28/30] Cleanup debugging code from test again --- .../src/registryProxy/plainHttpProxy.js | 20 ------------------- test/e2e/DockerTestContainer.js | 2 +- test/e2e/package.json | 2 +- test/e2e/safe-chain-proxy.e2e.spec.js | 4 ++-- 4 files changed, 4 insertions(+), 24 deletions(-) diff --git a/packages/safe-chain/src/registryProxy/plainHttpProxy.js b/packages/safe-chain/src/registryProxy/plainHttpProxy.js index 214ad0f..2cd5f24 100644 --- a/packages/safe-chain/src/registryProxy/plainHttpProxy.js +++ b/packages/safe-chain/src/registryProxy/plainHttpProxy.js @@ -1,10 +1,8 @@ import * as http from "http"; import * as https from "https"; -// oxlint-disable no-console - just for testing, remove afterwards export function handleHttpProxyRequest(req, res) { const url = new URL(req.url); - console.log(`Proxying request to: ${req.url}`); let protocol; if (url.protocol === "http:") { @@ -25,27 +23,12 @@ export function handleHttpProxyRequest(req, res) { res.writeHead(proxyRes.statusCode, proxyRes.headers); proxyRes.pipe(res); - proxyRes.on("error", (err) => { - console.log("Error in proxy response stream:", err); - // Stream error while piping response - // Response headers already sent, can't send error status - }); - proxyRes.on("close", () => { - console.log("Proxy response stream closed"); // Clean up if the proxy response stream closes if (!res.writableEnded) { res.end(); } }); - - proxyRes.on("end", () => { - console.log("Proxy response stream ended"); - // End of proxy response - if (!res.writableEnded) { - res.end(); - } - }); } ) .on("error", (err) => { @@ -54,21 +37,18 @@ export function handleHttpProxyRequest(req, res) { }); req.on("error", () => { - console.log("Error in client request stream"); // Client request stream error // Abort the proxy request proxyRequest.destroy(); }); res.on("error", () => { - console.log("Error in client response stream"); // Client response stream error (client disconnected) // Clean up proxy streams proxyRequest.destroy(); }); res.on("close", () => { - console.log("Client response stream closed"); // Client disconnected // Abort the proxy request to avoid unnecessary work if (!res.writableEnded) { diff --git a/test/e2e/DockerTestContainer.js b/test/e2e/DockerTestContainer.js index 45b66d0..ec1af3c 100644 --- a/test/e2e/DockerTestContainer.js +++ b/test/e2e/DockerTestContainer.js @@ -120,7 +120,7 @@ export class DockerTestContainer { console.log("Command timeout reached"); resolve({ allData, output: parseShellOutput(allData), command }); ptyProcess.removeListener("data", handleInput); - }, 20000); + }, 15000); function handleInput(data) { allData.push(data); diff --git a/test/e2e/package.json b/test/e2e/package.json index b34fd0b..9217808 100644 --- a/test/e2e/package.json +++ b/test/e2e/package.json @@ -4,7 +4,7 @@ "version": "1.0.0", "description": "End-to-end tests for the Aikido Safe Chain", "scripts": { - "test": "node --test --test-concurrency=1 **/safe-chain-proxy.e2e.spec.js" + "test": "node --test --test-concurrency=1 **/*.spec.js" }, "keywords": [], "author": "Aikido Security", diff --git a/test/e2e/safe-chain-proxy.e2e.spec.js b/test/e2e/safe-chain-proxy.e2e.spec.js index 22a7038..6abbb0f 100644 --- a/test/e2e/safe-chain-proxy.e2e.spec.js +++ b/test/e2e/safe-chain-proxy.e2e.spec.js @@ -14,8 +14,8 @@ describe("E2E: Safe chain proxy", () => { container = new DockerTestContainer(); await container.start(); - // const installationShell = await container.openShell("zsh"); - // await installationShell.runCommand("safe-chain setup"); + const installationShell = await container.openShell("zsh"); + await installationShell.runCommand("safe-chain setup"); }); afterEach(async () => { From 37ef3e187b83a0b39b05160a73a15eaba582aba8 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Wed, 15 Oct 2025 09:25:24 +0200 Subject: [PATCH 29/30] Further cleanup --- .../safe-chain/src/registryProxy/plainHttpProxy.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/safe-chain/src/registryProxy/plainHttpProxy.js b/packages/safe-chain/src/registryProxy/plainHttpProxy.js index 2cd5f24..29b7fe1 100644 --- a/packages/safe-chain/src/registryProxy/plainHttpProxy.js +++ b/packages/safe-chain/src/registryProxy/plainHttpProxy.js @@ -23,9 +23,17 @@ export function handleHttpProxyRequest(req, res) { res.writeHead(proxyRes.statusCode, proxyRes.headers); proxyRes.pipe(res); + proxyRes.on("error", () => { + // Proxy response stream error + // Clean up client response stream + if (res.writable) { + res.end(); + } + }); + proxyRes.on("close", () => { // Clean up if the proxy response stream closes - if (!res.writableEnded) { + if (res.writable) { res.end(); } }); @@ -51,9 +59,7 @@ export function handleHttpProxyRequest(req, res) { res.on("close", () => { // Client disconnected // Abort the proxy request to avoid unnecessary work - if (!res.writableEnded) { - proxyRequest.destroy(); - } + proxyRequest.destroy(); }); req.pipe(proxyRequest); From 05354ba2f0be2f1eb1e4ac5ae37f418a2d67262a Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Wed, 15 Oct 2025 11:56:03 +0200 Subject: [PATCH 30/30] Add some more comments on why http / https is handled in different code paths --- packages/safe-chain/src/registryProxy/plainHttpProxy.js | 3 +++ packages/safe-chain/src/registryProxy/registryProxy.js | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/safe-chain/src/registryProxy/plainHttpProxy.js b/packages/safe-chain/src/registryProxy/plainHttpProxy.js index 29b7fe1..e337b44 100644 --- a/packages/safe-chain/src/registryProxy/plainHttpProxy.js +++ b/packages/safe-chain/src/registryProxy/plainHttpProxy.js @@ -4,6 +4,9 @@ import * as https from "https"; export function handleHttpProxyRequest(req, res) { const url = new URL(req.url); + // The protocol for the plainHttpProxy should usually only be http: + // but when the client for some reason sends an https: request directly + // instead of using the CONNECT method, we should handle it gracefully. let protocol; if (url.protocol === "http:") { protocol = http; diff --git a/packages/safe-chain/src/registryProxy/registryProxy.js b/packages/safe-chain/src/registryProxy/registryProxy.js index d548999..b0e8dd1 100644 --- a/packages/safe-chain/src/registryProxy/registryProxy.js +++ b/packages/safe-chain/src/registryProxy/registryProxy.js @@ -55,7 +55,10 @@ export function mergeSafeChainProxyEnvironmentVariables(env) { function createProxyServer() { const server = http.createServer( - handleHttpProxyRequest // This handles plain HTTP requests + // This handles direct HTTP requests (non-CONNECT requests) + // This is normally http-only traffic, but we also handle + // https for clients that don't properly use CONNECT + handleHttpProxyRequest ); // This handles HTTPS requests via the CONNECT method