mirror of
https://github.com/NVIDIA/NemoClaw.git
synced 2026-07-03 03:37:16 +00:00
## Summary Probe-time curl spawns embedded the literal API key as an argv element (`-H "Authorization: Bearer ..."`, `-H "x-api-key: ..."`, `?key=...` in URL), exposing it to host `ps auxww` on container runtimes that share `/proc` with the host (k3s/containerd, Docker Desktop on WSL2). Route every credential through a 0600 `curl --config` tmpfile so the secret never reaches argv. ## Related Issue Fixes #5966 ## Changes - New `src/lib/adapters/http/auth-config.ts` helper that writes a 0600 curl config tmpfile carrying `header = "..."` or `url-query = "..."` entries; returns the `--config <path>` argv pair plus a `cleanup()` for `finally`. - `provider-models.ts`: `fetchNvidiaEndpointModels`, `fetchOpenAiLikeModels`, `fetchAnthropicModels` route Bearer / x-api-key / query-param credentials via the new helper. - `onboard-probes.ts`: `probeResponsesToolCalling`, `probeChatCompletionsToolCalling`, `probeOpenAiLikeEndpoint` (Responses + Chat Completions + streaming + doubled-timeout retry), `probeAnthropicEndpoint` — all argv sites swapped to `--config`; bearer/x-api-key/`?key=` literals removed from URL and argv. Single `authConfig` per probe scope, cleaned up in `finally`. - Defence-in-depth in `curl-args.ts`: `validateCurlProbeArgs` refuses inline `Authorization:` / `x-api-key:` / `x-goog-api-key:` `-H` values and refuses URLs with `?key=` / `?api_key=` / `?apikey=` / `?token=` / `?access_token=` query parameters. Trusted `--config` route remains the only legal credential channel. - Tests updated to invert "argv contains Bearer literal" → "argv contains `--config <path>` and the file is mode 0600 carrying the expected header/url-query entry; literal API key never appears in argv". New behavioural coverage for Anthropic. Existing onboard-smoke harness now reads the config file mid-probe to keep its auth assertion. ## Type of Change - [x] Code change (feature, bug fix, or refactor) - [ ] Code change with doc updates - [ ] Doc only (prose changes, no code sample modifications) - [ ] Doc only (includes code sample changes) ## Quality Gates - [x] Tests added or updated for changed behavior - [ ] Existing tests cover changed behavior — justification: - [ ] Tests not applicable — justification: - [ ] Docs updated for user-facing behavior changes - [x] Docs not applicable — justification: internal probe transport; no user-visible behaviour change - [x] Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging) - [ ] Sensitive-path review completed or maintainer-approved waiver recorded — reviewer/approval link/justification: - [ ] Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue: ## Verification - [x] PR description includes the DCO sign-off declaration and every commit appears as `Verified` in GitHub - [x] Git hooks passed during commit and push, or `npx prek run --from-ref main --to-ref HEAD` passes - [x] Targeted tests pass for changed behavior - [ ] Full `npm test` passes (broad runtime changes only) - [x] Quality Gates section completed with required justifications or waivers - [x] No secrets, API keys, or credentials committed - [ ] `npm run docs` builds without warnings (doc changes only) - [ ] Doc pages follow the style guide (doc changes only) - [ ] New doc pages include SPDX header and frontmatter (new pages only) --- Signed-off-by: Tinson Lai <tinsonl@nvidia.com> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added curl probe auth via temporary on-disk config for Bearer, API key, and OpenAI-like “bearer vs query-param” modes. * **Bug Fixes** * Reduced credential exposure by rejecting inline auth headers and secret-bearing query parameters; curl requests now use `--config` with safe cleanup. * Improved onboarding/provider probing and standardized retry behavior for timeout and retriable HTTP failures. * **Tests** * Expanded coverage for auth config generation/escaping, curl-arg validation, onboarding flows, and tracing redaction. * **Chores** * Increased Node heap limit for integration subprocesses during Vitest. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Tinson Lai <tinsonl@nvidia.com> Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com> Co-authored-by: Prekshi Vyas <prekshiv@nvidia.com> Co-authored-by: Prekshi Vyas <34834085+prekshivyas@users.noreply.github.com>
156 lines
6.2 KiB
TypeScript
156 lines
6.2 KiB
TypeScript
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
import path from "node:path";
|
|
|
|
import { defineConfig } from "vitest/config";
|
|
|
|
import {
|
|
shouldRunBranchValidationE2E,
|
|
shouldRunLiveE2E,
|
|
} from "./test/e2e/fixtures/live-project-gate.ts";
|
|
import { resolveE2ERetryCount } from "./test/helpers/e2e-retries";
|
|
import { testTimeout } from "./test/helpers/timeouts";
|
|
|
|
const isGithubActions = process.env.GITHUB_ACTIONS === "true";
|
|
const isCi = isGithubActions || process.env.CI === "true" || process.env.CI === "1";
|
|
const LIVE_E2E_PROJECT_TIMEOUT_MS = 30 * 60 * 1000;
|
|
const runLiveE2E = shouldRunLiveE2E();
|
|
const runBranchValidationE2E = shouldRunBranchValidationE2E();
|
|
const e2eRetryCount = resolveE2ERetryCount();
|
|
const sourceRequireHook = path.resolve("test/helpers/onboard-script-mocks.cjs");
|
|
const sourceNodeOptions = [process.env.NODE_OPTIONS, `--require=${sourceRequireHook}`]
|
|
.filter(Boolean)
|
|
.join(" ");
|
|
|
|
export default defineConfig({
|
|
test: {
|
|
env: {
|
|
NEMOCLAW_DISABLE_GATEWAY_DRIFT_PREFLIGHT: "1",
|
|
},
|
|
// CI logs are easiest to scan when test chatter stays quiet and failures
|
|
// surface as GitHub annotations at the relevant file and line.
|
|
reporters: isGithubActions ? ["github-actions"] : ["default"],
|
|
silent: isCi,
|
|
hideSkippedTests: isCi,
|
|
projects: [
|
|
{
|
|
test: {
|
|
name: "cli",
|
|
testTimeout: testTimeout(),
|
|
setupFiles: ["test/helpers/onboard-script-mocks.cjs"],
|
|
include: ["src/**/*.test.ts"],
|
|
exclude: ["**/node_modules/**", "**/.claude/**"],
|
|
},
|
|
},
|
|
{
|
|
test: {
|
|
name: "integration",
|
|
// Source-backed process fixtures can exceed the unit-test budget
|
|
// when several coverage shards transpile and spawn them concurrently.
|
|
testTimeout: testTimeout(15_000),
|
|
setupFiles: ["test/helpers/onboard-script-mocks.cjs"],
|
|
// Integration fixtures often spawn short Node programs. Keep those
|
|
// programs on the same source graph as their parent test process.
|
|
// The integration suite shells out heavily, and stacking multiple
|
|
// forks of the require-hook transpile cache on the 7 GiB ubuntu
|
|
// runner reliably exhausts physical RAM when coverage is on.
|
|
// Disable file parallelism for the integration project so the test
|
|
// files run serially against a single worker (vitest 4 dropped
|
|
// poolOptions.forks.singleFork; fileParallelism: false is the
|
|
// documented replacement).
|
|
fileParallelism: false,
|
|
env: { NODE_OPTIONS: sourceNodeOptions },
|
|
include: ["test/**/*.test.{js,ts}"],
|
|
exclude: [
|
|
"**/node_modules/**",
|
|
"**/.claude/**",
|
|
"test/e2e/**",
|
|
"test/e2e/live/**",
|
|
"test/e2e/support/**",
|
|
"test/package-contract/**",
|
|
"test/install-express-prompt.test.ts",
|
|
"test/install-preflight.test.ts",
|
|
"test/install-preflight-docker-bootstrap.test.ts",
|
|
"test/install-openshell-version-check.test.ts",
|
|
],
|
|
},
|
|
},
|
|
{
|
|
test: {
|
|
name: "installer-integration",
|
|
include: [
|
|
"test/install-express-prompt.test.ts",
|
|
"test/install-preflight.test.ts",
|
|
"test/install-preflight-docker-bootstrap.test.ts",
|
|
"test/install-openshell-version-check.test.ts",
|
|
],
|
|
// Slow tests that spawn real bash install.sh processes. Explicit
|
|
// project selection keeps them out of the fast source-test command.
|
|
},
|
|
},
|
|
{
|
|
test: {
|
|
name: "package-contract",
|
|
include: ["test/package-contract/**/*.test.ts"],
|
|
},
|
|
},
|
|
{
|
|
test: {
|
|
name: "plugin",
|
|
include: ["nemoclaw/src/**/*.test.ts"],
|
|
},
|
|
},
|
|
{
|
|
test: {
|
|
// Fast tests for the E2E fixture/support layer. Vitest remains the
|
|
// only harness; this project does not define a separate runner.
|
|
name: "e2e-support",
|
|
testTimeout: testTimeout(),
|
|
include: ["test/e2e/support/**/*.test.ts"],
|
|
},
|
|
},
|
|
{
|
|
test: {
|
|
name: "e2e-live",
|
|
testTimeout: testTimeout(LIVE_E2E_PROJECT_TIMEOUT_MS),
|
|
// Vitest counts retries after the initial failure. In CI the default
|
|
// value of 2 gives live E2Es up to three total attempts while keeping
|
|
// local opt-in runs single-shot unless NEMOCLAW_E2E_RETRIES is set.
|
|
retry: e2eRetryCount,
|
|
include: runLiveE2E ? ["test/e2e/live/**/*.test.ts"] : [],
|
|
// Live E2E tests are opt-in because they install, onboard, and
|
|
// mutate real NemoClaw/OpenShell state. Run explicitly with:
|
|
// NEMOCLAW_RUN_LIVE_E2E=1 npx vitest run --project e2e-live
|
|
},
|
|
},
|
|
{
|
|
test: {
|
|
name: "e2e-branch-validation",
|
|
retry: e2eRetryCount,
|
|
include: runBranchValidationE2E ? ["test/e2e/brev-e2e.test.ts"] : [],
|
|
// Branch validation E2E: rsyncs the branch over a Brev instance
|
|
// provisioned from the published NemoClaw launchable image and
|
|
// runs the selected test suites. Only run when explicitly enabled:
|
|
// NEMOCLAW_RUN_BRANCH_VALIDATION_E2E=1 npx vitest run --project e2e-branch-validation
|
|
//
|
|
// Override the project-root `silent: isCi` setting — diagnostic
|
|
// output from createBrevInstance / waitForSsh / waitForLaunchableReady
|
|
// is essential for debugging Brev provisioning timing and the
|
|
// overall suite runs in a single `describe` block, so there's no
|
|
// test chatter to suppress anyway.
|
|
// Gate on a workflow-owned sentinel or Brev auth env. Historically
|
|
// this used BREV_API_TOKEN (short-lived refresh token); newer
|
|
// workflows authenticate with BREV_API_KEY + BREV_ORG_ID before
|
|
// invoking Vitest.
|
|
},
|
|
},
|
|
],
|
|
coverage: {
|
|
provider: "v8",
|
|
include: ["src/**/*.ts", "bin/**/*.js", "nemoclaw/src/**/*.ts"],
|
|
exclude: ["**/*.test.ts", "dist/**"],
|
|
reporter: ["text-summary", "json-summary"],
|
|
},
|
|
},
|
|
});
|