-
Notifications
You must be signed in to change notification settings - Fork 3.1k
fix(e2e): respect Launchable gateway ownership and retain command evidence #11494
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
2545d08
a545c56
0bf3920
9151cb6
7e78b70
a4f8f62
caa309d
7ca376d
cb6de7f
7fc3eb8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -131,10 +131,14 @@ | |
| async writeText(relativePath: string, text: string): Promise<string> { | ||
| const target = this.pathFor(relativePath); | ||
| await fs.mkdir(path.dirname(target), { recursive: true }); | ||
| await fs.writeFile(target, redactString(text, this.redactionValues), "utf8"); | ||
| await fs.writeFile(target, this.redact(text), "utf8"); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reviewed against commit 2545d08. This is the existing E2E evidence-writing boundary, not a new download or execution path. The two reported inference callers already serialize response JSON into test-selected artifact filenames. This change delegates to ArtifactSink.redact(), which calls the same redactString(text, this.redactionValues) used before the change. ArtifactSink.pathFor() still rejects absolute paths and traversal outside the artifact root; response content does not select the destination path. No execution of response content is introduced. The focused gateway/redaction support run passed all 63 tests after the CI repair. I am retaining the warning as reviewed evidence without suppressing or dismissing the security rule. |
||
| return target; | ||
| } | ||
|
|
||
| redact(text: string): string { | ||
| return redactString(text, this.redactionValues); | ||
| } | ||
|
|
||
| async writeJson(relativePath: string, value: unknown): Promise<string> { | ||
| return this.writeText(relativePath, `${JSON.stringify(value, null, 2)}\n`); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| import { DEFAULT_GATEWAY_PORT, parsePort } from "../../../src/lib/core/ports.ts"; | ||
| import { resolveGatewayName } from "../../../src/lib/onboard/gateway-binding/identity.ts"; | ||
| import { loadGatewayManagementDeclaration } from "../../../src/lib/onboard/gateway-management.ts"; | ||
|
|
||
| /** Both eager cleanup and registered teardown must respect the same gateway owner. */ | ||
| export async function withOwnedFullE2eGateway( | ||
| gateway: { owned: boolean }, | ||
| cleanup: () => unknown, | ||
| ): Promise<void> { | ||
| if (gateway.owned) await cleanup(); | ||
| } | ||
|
|
||
| /** Resolve the platform declaration before the test can register destructive cleanup. */ | ||
| export function fullE2eGateway(preinstalled: boolean, env: NodeJS.ProcessEnv = process.env) { | ||
| if (!preinstalled) { | ||
| const port = parsePort("NEMOCLAW_GATEWAY_PORT", DEFAULT_GATEWAY_PORT, env); | ||
| return { owned: true, env: { OPENSHELL_GATEWAY: resolveGatewayName(port) } }; | ||
| } | ||
| const declarationPath = | ||
| env.NEMOCLAW_GATEWAY_MANAGEMENT?.trim() || "/etc/nemoclaw/gateway-management.json"; | ||
| const loaded = loadGatewayManagementDeclaration({ | ||
| env: { ...env, NEMOCLAW_GATEWAY_MANAGEMENT: declarationPath }, | ||
| }); | ||
| if (!loaded.ok) throw new Error(`Launchable gateway declaration: ${loaded.reason}`); | ||
| if (loaded.declaration?.mode !== "externally-supervised" || !loaded.declaration.endpoint) { | ||
| throw new Error("The preinstalled Launchable requires an externally supervised gateway"); | ||
| } | ||
| const endpoint = new URL(loaded.declaration.endpoint); | ||
| const port = parsePort("NEMOCLAW_GATEWAY_PORT", DEFAULT_GATEWAY_PORT, { | ||
| NEMOCLAW_GATEWAY_PORT: endpoint.port || (endpoint.protocol === "https:" ? "443" : "80"), | ||
| }); | ||
| if (parsePort("NEMOCLAW_GATEWAY_PORT", port, env) !== port) { | ||
| throw new Error("Launchable gateway port conflicts with its declaration"); | ||
| } | ||
| return { | ||
| owned: false, | ||
| env: { | ||
| OPENSHELL_GATEWAY: resolveGatewayName(port), | ||
| NEMOCLAW_GATEWAY_MANAGEMENT: declarationPath, | ||
| NEMOCLAW_GATEWAY_PORT: String(port), | ||
| }, | ||
| }; | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.