Skip to content
Merged
2 changes: 1 addition & 1 deletion src/lib/onboard/sandbox-provider-cleanup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ const TOLERATED_DETACH_OUTPUT_RE =

const MISSING_SANDBOX_OUTPUT_RE = /sandbox[^\n]{0,200}?(?:\bNotFound\b|\bnot\s+found\b)/i;

const ATTACHED_TO_SANDBOX_RE = /attached\s+to\s+sandbox\(\s*es?\s*\)?\s*:\s*([^"\n]+)/i;
const ATTACHED_TO_SANDBOX_RE = /attached\s+to(?:\s|│)+sandbox\(\s*es?\s*\)?\s*:\s*([^"\n]+)/i;

const MAX_WARNING_OUTPUT_CHARS = 500;

Expand Down
36 changes: 36 additions & 0 deletions test/e2e/fixtures/bedrock-runtime-leak-scan.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

export type ForbiddenLeakPattern = [name: string, value: string];

export function bedrockRuntimeForbiddenLeakPatterns(options: {
adapterToken: string;
bedrockHostname: string;
compatibleKey: string;
}): ForbiddenLeakPattern[] {
return [
["fake user key", options.compatibleKey],
["adapter token", options.adapterToken],
["AWS bearer env name", "AWS_BEARER_TOKEN_BEDROCK"],
["raw Bedrock hostname", options.bedrockHostname],
];
}

export function findForbiddenLeaks(
text: string,
label: string,
patterns: ForbiddenLeakPattern[],
): string[] {
const locations: string[] = [];
let current = label;
for (const line of text.split("\n")) {
if (line.startsWith("@@NEMOCLAW_E2E_FILE@@ ")) {
current = line.slice("@@NEMOCLAW_E2E_FILE@@ ".length);
continue;
}
for (const [name, value] of patterns) {
if (value && line.includes(value)) locations.push(`${name}: ${current}`);
}
}
return [...new Set(locations)].sort();
}
35 changes: 9 additions & 26 deletions test/e2e/live/bedrock-runtime-compatible-anthropic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ import os from "node:os";
import path from "node:path";
import type { ArtifactSink } from "../fixtures/artifacts.ts";
import { buildAvailabilityProbeEnv } from "../fixtures/availability-env.ts";
import {
bedrockRuntimeForbiddenLeakPatterns,
findForbiddenLeaks,
} from "../fixtures/bedrock-runtime-leak-scan.ts";
import {
cleanupAcquiredResource,
cleanupExistingPath,
Expand Down Expand Up @@ -1180,25 +1184,6 @@ for proc_dir in /proc/[0-9]*; do
done
`);

function findForbiddenLeaks(
text: string,
label: string,
patterns: Array<[string, string]>,
): string[] {
const locations: string[] = [];
let current = label;
for (const line of text.split("\n")) {
if (line.startsWith("@@NEMOCLAW_E2E_FILE@@ ")) {
current = line.slice("@@NEMOCLAW_E2E_FILE@@ ".length);
continue;
}
for (const [name, value] of patterns) {
if (value && line.includes(value)) locations.push(`${name}: ${current}`);
}
}
return [...new Set(locations)].sort();
}

function isPreContractEndpointValidationRateLimit(options: {
mock: MockBedrockRuntime | undefined;
onboarding: RawRunResult;
Expand Down Expand Up @@ -1255,13 +1240,11 @@ async function assertNoBedrockLeaks(options: {
redact: (text: string, extraValues?: string[]) => string;
}): Promise<void> {
const adapterToken = readAdapterToken(options.home);
const patterns: Array<[string, string]> = [
["fake user key", COMPATIBLE_KEY],
["adapter token", adapterToken],
["AWS bearer env name", "AWS_BEARER_TOKEN_BEDROCK"],
["adapter token env name", "NEMOCLAW_BEDROCK_RUNTIME_ADAPTER_TOKEN"],
["raw Bedrock hostname", BEDROCK_HOSTNAME],
];
const patterns = bedrockRuntimeForbiddenLeakPatterns({
adapterToken,
bedrockHostname: BEDROCK_HOSTNAME,
compatibleKey: COMPATIBLE_KEY,
});
const snapshot = await runRawCommand(
"openshell",
["sandbox", "exec", "-n", SANDBOX_NAME, "--", ...sandboxShellArgs(SNAPSHOT_SCRIPT)],
Expand Down
54 changes: 54 additions & 0 deletions test/e2e/support/bedrock-runtime-leak-scan.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { describe, expect, it } from "vitest";
import {
bedrockRuntimeForbiddenLeakPatterns,
findForbiddenLeaks,
} from "../fixtures/bedrock-runtime-leak-scan.ts";
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

describe("Bedrock Runtime leak scan", () => {
const patterns = bedrockRuntimeForbiddenLeakPatterns({
adapterToken: "adapter-secret-value",
bedrockHostname: "bedrock.example.test",
compatibleKey: "user-secret-value",
});

it("allows the managed provider credential name without allowing its token value", () => {
expect(
findForbiddenLeaks(
"@@NEMOCLAW_E2E_FILE@@ /proc/42/environ\nNEMOCLAW_BEDROCK_RUNTIME_ADAPTER_TOKEN=[managed]",
"sandbox snapshot",
patterns,
),
).toEqual([]);

expect(
findForbiddenLeaks(
"@@NEMOCLAW_E2E_FILE@@ /proc/42/environ\nadapter-secret-value",
"sandbox snapshot",
patterns,
),
).toEqual(["adapter token: /proc/42/environ"]);
});

it("reports other Bedrock credentials and routing details at their source", () => {
expect(
findForbiddenLeaks(
[
"@@NEMOCLAW_E2E_FILE@@ config.json",
"user-secret-value",
"AWS_BEARER_TOKEN_BEDROCK",
"@@NEMOCLAW_E2E_FILE@@ runtime.log",
"bedrock.example.test",
].join("\n"),
"sandbox snapshot",
patterns,
),
).toEqual([
"AWS bearer env name: config.json",
"fake user key: config.json",
"raw Bedrock hostname: runtime.log",
]);
});
});
8 changes: 8 additions & 0 deletions test/sandbox-provider-cleanup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,14 @@ describe("parseAttachedSandboxes", () => {
expect(parseAttachedSandboxes(output)).toEqual(["spark-nemo"]);
});

it("parses an OpenShell diagnostic wrapped with continuation markers", () => {
const output =
"Error: × code: 'The system is not in a state required for the operation's\n" +
"│ execution', message: \"provider 'compatible-endpoint' is attached to\n" +
'│ sandbox(es): e2e-diag"';
expect(parseAttachedSandboxes(output)).toEqual(["e2e-diag"]);
});

it("parses multiple sandbox names from the same diagnostic", () => {
const output = "provider 'x' is attached to sandbox(es): alpha, beta, gamma";
expect(parseAttachedSandboxes(output)).toEqual(["alpha", "beta", "gamma"]);
Expand Down
Loading