From 94b3a62e102a55104848426a86457c3a8eea3c08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yuniel=20Acosta=20P=C3=A9rez?= <33158051+yacosta738@users.noreply.github.com> Date: Fri, 5 Jun 2026 14:36:46 +0200 Subject: [PATCH 1/5] fix(security): prevent shell injection in rook launcher - Replace execSync with spawnSync to prevent shell injection - Cache platform detection and exe suffix to avoid redundant calls - Remove unreachable return null statement Security improvements: - Arguments are now passed as array to spawnSync, not concatenated - No shell interpolation of user-provided arguments - Explicit exit code handling --- apps/rook/npm/rook/lib/index.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/apps/rook/npm/rook/lib/index.js b/apps/rook/npm/rook/lib/index.js index 3ce2707f..249c88cf 100644 --- a/apps/rook/npm/rook/lib/index.js +++ b/apps/rook/npm/rook/lib/index.js @@ -4,7 +4,7 @@ * Rook launcher - finds the platform-specific binary and executes it */ -const { execSync } = require('node:child_process'); +const { execSync, spawnSync } = require('node:child_process'); const path = require('node:path'); const os = require('node:os'); @@ -27,14 +27,17 @@ function getPlatform() { } function findBinary() { + const osPlatform = os.platform(); + const exeSuffix = osPlatform === 'win32' ? '.exe' : ''; + // Try platform-specific optional dependency first const platformPkg = `${PKG}-${getPlatform()}`; try { - const binaryPath = require.resolve(`${platformPkg}/bin/rook${os.platform() === 'win32' ? '.exe' : ''}`); + const binaryPath = require.resolve(`${platformPkg}/bin/rook${exeSuffix}`); return binaryPath; } catch { // Fall back to PATH lookup - const binaryName = `rook${os.platform() === 'win32' ? '.exe' : ''}`; + const binaryName = `rook${exeSuffix}`; try { const globalPath = execSync(`npm root -g`, { encoding: 'utf8' }).trim(); const globalBinary = path.join(globalPath, platformPkg, 'bin', binaryName); @@ -50,16 +53,21 @@ function findBinary() { } } } - return null; } try { const binaryPath = findBinary(); const args = process.argv.slice(2); - execSync(`"${binaryPath}" ${args.join(' ')}`, { + const result = spawnSync(binaryPath, args, { stdio: 'inherit', cwd: process.cwd() }); + + if (result.error) { + throw result.error; + } + + process.exit(result.status || 0); } catch (error) { console.error('Rook error:', error.message); process.exit(1); From abd13c86c64e815a84d995c462f8ef0556c36607 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 5 Jun 2026 15:34:51 +0200 Subject: [PATCH 2/5] fix(e2e): use strong password for seed-admin to pass validation The ADMIN_PASSWORD must satisfy is_strong_password() requirements: - At least 12 characters - At least one uppercase letter - At least one lowercase letter - At least one digit - At least one special character Changed from '***' to 'Admin123!234' which meets all requirements. This fixes the E2E workflow failure where seed-admin command was rejecting the weak password with exit code 1. --- dev/e2e/run-api-keys-e2e.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/e2e/run-api-keys-e2e.sh b/dev/e2e/run-api-keys-e2e.sh index 7d4fcec0..6e75096a 100755 --- a/dev/e2e/run-api-keys-e2e.sh +++ b/dev/e2e/run-api-keys-e2e.sh @@ -32,7 +32,7 @@ export API_PORT=8081 export API_TARGET="http://127.0.0.1:${API_PORT}" export API_BASE_URL="${API_TARGET}" TEST_CONFIG="${REPO_ROOT}/dev/test-configs/rook-api-keys-test.toml" -ADMIN_PASSWORD="Admin123456-" +ADMIN_PASSWORD="Admin123!234" DASHBOARD_DIR="${REPO_ROOT}/apps/rook/dashboard" # Colors From dba9e964654b738aa5dcb6595bd9303cd6a0d099 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yuniel=20Acosta=20P=C3=A9rez?= <33158051+yacosta738@users.noreply.github.com> Date: Fri, 5 Jun 2026 18:52:09 +0200 Subject: [PATCH 3/5] fix: handle signal termination and remove hard-coded credentials - Fix spawnSync exit handling to correctly fail on signal termination - Remove hard-coded ADMIN_PASSWORD from e2e script - Generate random password or read from environment - Remove password from log output to prevent credential leaks --- apps/rook/npm/rook/lib/index.js | 11 ++++++++++- dev/e2e/run-api-keys-e2e.sh | 5 +++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/apps/rook/npm/rook/lib/index.js b/apps/rook/npm/rook/lib/index.js index 249c88cf..5b3eabc4 100644 --- a/apps/rook/npm/rook/lib/index.js +++ b/apps/rook/npm/rook/lib/index.js @@ -67,7 +67,16 @@ try { throw result.error; } - process.exit(result.status || 0); + // Handle signal termination: if status is null, the child was killed by a signal + if (result.status !== null) { + process.exit(result.status); + } else if (result.signal) { + // Exit with non-zero code to reflect signal termination + // Convention: 128 + signal number, but we don't have the signal number here + process.exit(1); + } else { + process.exit(0); + } } catch (error) { console.error('Rook error:', error.message); process.exit(1); diff --git a/dev/e2e/run-api-keys-e2e.sh b/dev/e2e/run-api-keys-e2e.sh index 6e75096a..2eb6e850 100755 --- a/dev/e2e/run-api-keys-e2e.sh +++ b/dev/e2e/run-api-keys-e2e.sh @@ -32,7 +32,8 @@ export API_PORT=8081 export API_TARGET="http://127.0.0.1:${API_PORT}" export API_BASE_URL="${API_TARGET}" TEST_CONFIG="${REPO_ROOT}/dev/test-configs/rook-api-keys-test.toml" -ADMIN_PASSWORD="Admin123!234" +# Read admin password from environment or generate a random one +ADMIN_PASSWORD="${ADMIN_PASSWORD:-$(openssl rand -base64 16)}" DASHBOARD_DIR="${REPO_ROOT}/apps/rook/dashboard" # Colors @@ -97,7 +98,7 @@ docker exec "$CONTAINER_NAME" /usr/local/bin/rook seed-admin --config /app/rook. log_info "Container ready!" log_info " API: http://localhost:$API_PORT" -log_info " Admin: admin / $ADMIN_PASSWORD" +log_info " Admin credentials configured (use ADMIN_PASSWORD env var to override)" log_info "" if [ "$MODE" = "--test" ]; then From 2b87b25857e89cf4dbdbaa0a218291d7f9595577 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yuniel=20Acosta=20P=C3=A9rez?= <33158051+yacosta738@users.noreply.github.com> Date: Fri, 5 Jun 2026 19:21:18 +0200 Subject: [PATCH 4/5] fix(e2e): generate password satisfying strong requirements E2E tests were hanging at 'Seeding admin password' because openssl rand -base64 does not reliably produce passwords with uppercase, digit, and symbol characters. Changed ADMIN_PASSWORD generation to 'Admin$(openssl rand -hex 8)!' which guarantees: uppercase (A), lowercase (d,m,i,n + hex a-f), digit (hex 0-9), and symbol (!). Also removed output redirection from seed-admin command so errors are visible instead of silently failing. --- dev/e2e/run-api-keys-e2e.sh | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/dev/e2e/run-api-keys-e2e.sh b/dev/e2e/run-api-keys-e2e.sh index 2eb6e850..60e99589 100755 --- a/dev/e2e/run-api-keys-e2e.sh +++ b/dev/e2e/run-api-keys-e2e.sh @@ -32,8 +32,15 @@ export API_PORT=8081 export API_TARGET="http://127.0.0.1:${API_PORT}" export API_BASE_URL="${API_TARGET}" TEST_CONFIG="${REPO_ROOT}/dev/test-configs/rook-api-keys-test.toml" -# Read admin password from environment or generate a random one -ADMIN_PASSWORD="${ADMIN_PASSWORD:-$(openssl rand -base64 16)}" +# Read admin password from environment or generate a strong random one +# Password must satisfy is_strong_password() requirements: +# - At least 12 characters +# - At least one uppercase letter, lowercase letter, digit, and special character +if [ -z "$ADMIN_PASSWORD" ]; then + # Generate: "Admin" + 8 random hex chars + "!" + # This guarantees: uppercase (A), lowercase (d,m,i,n + hex a-f), digits (hex 0-9), symbol (!) + ADMIN_PASSWORD="Admin$(openssl rand -hex 8)!" +fi DASHBOARD_DIR="${REPO_ROOT}/apps/rook/dashboard" # Colors @@ -94,7 +101,12 @@ for i in {1..30}; do done log_info "Seeding admin password..." -docker exec "$CONTAINER_NAME" /usr/local/bin/rook seed-admin --config /app/rook.toml "$ADMIN_PASSWORD" > /dev/null 2>&1 +if ! docker exec "$CONTAINER_NAME" /usr/local/bin/rook seed-admin --config /app/rook.toml "$ADMIN_PASSWORD"; then + log_error "Failed to seed admin password" + docker logs "$CONTAINER_NAME" | tail -20 + cleanup + exit 1 +fi log_info "Container ready!" log_info " API: http://localhost:$API_PORT" From c586773d2b3aad6ab3a0259d478730fa2e7c98fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yuniel=20Acosta=20P=C3=A9rez?= <33158051+yacosta738@users.noreply.github.com> Date: Fri, 5 Jun 2026 19:34:39 +0200 Subject: [PATCH 5/5] fix(routes): update telemetry routes to Axum 0.8 syntax Axum 0.7+ requires path parameters to use {param} instead of :param. Changed /api/telemetry/:provider routes to use {provider} syntax to prevent router panic on startup. --- crates/infrastructure/transport-axum/src/routes.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/infrastructure/transport-axum/src/routes.rs b/crates/infrastructure/transport-axum/src/routes.rs index 78a7f429..9b14b63b 100644 --- a/crates/infrastructure/transport-axum/src/routes.rs +++ b/crates/infrastructure/transport-axum/src/routes.rs @@ -48,9 +48,9 @@ pub fn router( .route("/health", get(health_check)) // Telemetry endpoints .route("/api/telemetry/summary", get(telemetry_summary)) - .route("/api/telemetry/:provider", get(telemetry_provider)) + .route("/api/telemetry/{provider}", get(telemetry_provider)) .route( - "/api/telemetry/:provider/latency", + "/api/telemetry/{provider}/latency", get(telemetry_latency_distribution), ) // First-run bootstrap endpoints