Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/actions/ci-build-typecheck/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ runs:
shell: bash
run: npm run typecheck:cli

- name: Typecheck plugin
- name: Typecheck plugin tests
shell: bash
run: npm --prefix nemoclaw run typecheck
run: npm --prefix nemoclaw exec -- tsc --noEmit -p nemoclaw/tsconfig.test.json

- name: Typecheck JavaScript config
shell: bash
Expand Down
5 changes: 5 additions & 0 deletions ci/source-shape-test-budget.json
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,11 @@
"test": "provides the package token only to trusted main dependency installation",
"category": "security"
},
{
"file": "test/automation/pull-requests/pr-workflow-contract.test.ts",
"test": "keeps %s plugin test typechecking after the trusted production build",
"category": "security"
},
{
"file": "test/automation/pull-requests/pr-workflow-contract.test.ts",
"test": "passes only the base-packaged SDK archive to pull request dependency jobs",
Expand Down
5 changes: 5 additions & 0 deletions src/lib/onboard/wsl-docker-desktop-gpu.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ describe("createArm64ContainerGpuProver (#4565)", () => {
platform: "linux",
arch: "arm64",
env: {},
resolveRuntimeProvider: () => proofProvider("docker"),
runProof,
log: () => undefined,
});
Expand All @@ -195,6 +196,7 @@ describe("createArm64ContainerGpuProver (#4565)", () => {
platform: "linux",
arch: "arm64",
env: {},
resolveRuntimeProvider: () => proofProvider("docker"),
runProof,
log: () => undefined,
});
Expand Down Expand Up @@ -709,6 +711,7 @@ describe("createArm64ContainerGpuProver (#4565)", () => {
platform: "linux",
arch: "arm64",
env: {},
resolveRuntimeProvider: () => proofProvider("docker"),
runProof: () => passingProof,
log: (message) => logs.push(message),
});
Expand Down Expand Up @@ -743,6 +746,7 @@ describe("createArm64ContainerGpuProver (#4565)", () => {
const prover = createArm64ContainerGpuProver({
platform: "linux",
arch: "arm64",
resolveRuntimeProvider: () => proofProvider("docker"),
runProof: () => failing,
log: () => undefined,
});
Expand All @@ -761,6 +765,7 @@ describe("createArm64ContainerGpuProver (#4565)", () => {
const prover = createArm64ContainerGpuProver({
platform: "linux",
arch: "arm64",
resolveRuntimeProvider: () => proofProvider("docker"),
runProof: () => execFormatFailure,
log: (message) => logs.push(message),
});
Expand Down
17 changes: 17 additions & 0 deletions test/automation/pull-requests/pr-workflow-contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,23 @@ describe("pull request and main workflow contracts", () => {
);
});

// source-shape-contract: security -- The trusted split must retain test-config coverage after compiling candidate production code
it.each([
["pull request", prWorkflow],
["main", mainWorkflow],
] as const)(
"keeps %s plugin test typechecking after the trusted production build",
(_name, workflow) => {
expect([workflow.jobs["build-typecheck"].needs].flat()).toContain("compile-artifacts");
expect(requiredStep(sharedActions.buildTypecheck, "Typecheck plugin tests").run).toBe(
"npm --prefix nemoclaw exec -- tsc --noEmit -p nemoclaw/tsconfig.test.json",
);
expect(stepRuns(sharedActions.buildTypecheck)).not.toContain(
"npm --prefix nemoclaw run typecheck",
);
},
);

// source-shape-contract: security -- The PR workflow must select an exact base-controlled package run before publishing its archive internally
it("passes only the base-packaged SDK archive to pull request dependency jobs", () => {
const packageJob = prWorkflow.jobs["openshell-sdk-package"];
Expand Down
67 changes: 4 additions & 63 deletions test/credentials/credential-rotation.test.ts
Original file line number Diff line number Diff line change
@@ -1,71 +1,12 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { createRequire } from "node:module";
import { beforeEach, describe, expect, it, vi } from "vitest";

const require = createRequire(import.meta.url);

type ModuleProperty = string | number | boolean | Function | object | null | undefined;
type ModuleRecord = { [key: string]: ModuleProperty };

type MessagingProvider = {
name: string;
envKey: string;
token: string | null;
};

type CredentialRotationInternals = {
hashCredential: (value: string | null | undefined) => string | null;
detectMessagingCredentialRotation: (
sandboxName: string,
providers: MessagingProvider[],
) => { changed: boolean; changedProviders: string[] };
};

function isObjectRecord(value: unknown): value is ModuleRecord {
return typeof value === "object" && value !== null && !Array.isArray(value);
}

function isCredentialRotationInternals(value: unknown): value is CredentialRotationInternals {
return (
isObjectRecord(value) &&
typeof value.hashCredential === "function" &&
typeof value.detectMessagingCredentialRotation === "function"
);
}

function isRegistryModule(value: unknown): value is typeof import("../../src/lib/state/registry.js") {
return isObjectRecord(value) && typeof value.getSandbox === "function";
}

function loadCredentialRotationInternals(): CredentialRotationInternals {
const loaded: unknown = require("../../src/lib/onboard.js");
if (!isCredentialRotationInternals(loaded)) {
throw new Error("Expected onboard internals to expose credential rotation helpers");
}
return loaded;
}

function loadRegistryModule(): typeof import("../../src/lib/state/registry.js") {
const loaded: unknown = require("../../src/lib/state/registry.js");
if (!isRegistryModule(loaded)) {
throw new Error("Expected registry module to expose getSandbox");
}
return loaded;
}
import { describe, expect, it, vi } from "vitest";
import { detectMessagingCredentialRotation } from "../../src/lib/onboard/messaging-credentials";
import { hashCredential } from "../../src/lib/security/credential-hash";
import * as registry from "../../src/lib/state/registry";

describe("credential rotation detection", () => {
let hashCredential: CredentialRotationInternals["hashCredential"];
let detectMessagingCredentialRotation: CredentialRotationInternals["detectMessagingCredentialRotation"];
let registry: typeof import("../../src/lib/state/registry.js");

beforeEach(() => {
// Fresh imports to avoid cross-test contamination
({ hashCredential, detectMessagingCredentialRotation } = loadCredentialRotationInternals());
registry = loadRegistryModule();
});

function hashCredentialOrThrow(value: string): string {
const hash = hashCredential(value);
expect(hash).not.toBeNull();
Expand Down
Loading