test: add codex npm plugin Docker live proof

This commit is contained in:
Peter Steinberger
2026-05-02 20:08:07 +01:00
parent 66db189962
commit 23ac9ccfd5
13 changed files with 738 additions and 6 deletions

View File

@@ -127,6 +127,40 @@ describe("host-hook fixture plugin contract", () => {
);
});
it("allows the official npm Codex plugin to keep /codex command ownership", () => {
const { config, registry } = createPluginRegistryFixture();
const codexRoot = path.join("/tmp", ".openclaw", "npm", "node_modules", "@openclaw", "codex");
registerTestPlugin({
registry,
config,
record: createPluginRecord({
id: "codex",
name: "Codex",
origin: "global",
rootDir: codexRoot,
source: path.join(codexRoot, "index.ts"),
}),
register(api) {
api.registerCommand({
name: "codex",
description: "Official npm Codex command",
ownership: "reserved",
handler: async () => ({ text: "ok" }),
});
},
});
expect(registry.registry.commands.map((entry) => entry.command.name)).toEqual(["codex"]);
expect(registry.registry.diagnostics).not.toEqual(
expect.arrayContaining([
expect.objectContaining({
pluginId: "codex",
message: expect.stringContaining("only bundled plugins can claim reserved command"),
}),
]),
);
});
it("rejects reserved command ownership for non-reserved bundled command names", () => {
const { config, registry } = createPluginRegistryFixture();
registerTestPlugin({

View File

@@ -601,7 +601,7 @@ function logTrustedSourceLinkedOfficialInstall(params: {
targetLabel: string;
}) {
params.logger.warn?.(
`WARNING: ${params.targetLabel} allowed because it is an official source-linked ClawHub package: ${buildCriticalDetails({ findings: params.findings })}`,
`WARNING: ${params.targetLabel} allowed because it is an official OpenClaw package: ${buildCriticalDetails({ findings: params.findings })}`,
);
}

View File

@@ -260,6 +260,44 @@ describe("installPluginFromNpmSpec", () => {
});
});
it("allows the official Codex npm plugin to spawn its managed app-server", async () => {
const npmRoot = path.join(suiteTempRootTracker.makeTempDir(), "npm");
const warnings: string[] = [];
mockNpmViewAndInstall({
spec: "@openclaw/codex@beta",
packageName: "@openclaw/codex",
version: "2026.5.1-beta.1",
pluginId: "codex",
npmRoot,
indexJs: `import { spawn } from "node:child_process";\nspawn("codex", ["app-server"]);`,
});
const result = await installPluginFromNpmSpec({
spec: "@openclaw/codex@beta",
npmDir: npmRoot,
logger: {
info: () => {},
warn: (msg: string) => warnings.push(msg),
},
});
expect(result.ok).toBe(true);
if (!result.ok) {
return;
}
expect(result.pluginId).toBe("codex");
expect(
warnings.some((warning) =>
warning.includes("allowed because it is an official OpenClaw package"),
),
).toBe(true);
expectNpmInstallIntoRoot({
calls: runCommandWithTimeoutMock.mock.calls,
npmRoot,
spec: "@openclaw/codex@beta",
});
});
it("rejects non-registry npm specs", async () => {
const result = await installPluginFromNpmSpec({ spec: "github:evil/evil" });
expect(result.ok).toBe(false);

View File

@@ -773,7 +773,7 @@ describe("installPluginFromArchive", () => {
expect(result.ok).toBe(true);
expect(
warnings.some((warning) =>
warning.includes("allowed because it is an official source-linked ClawHub package"),
warning.includes("allowed because it is an official OpenClaw package"),
),
).toBe(true);
});
@@ -1935,7 +1935,7 @@ describe("installPluginFromArchive", () => {
expect(result.ok).toBe(true);
expect(
warnings.some((warning) =>
warning.includes("allowed because it is an official source-linked ClawHub package"),
warning.includes("allowed because it is an official OpenClaw package"),
),
).toBe(true);
});

View File

@@ -110,6 +110,7 @@ type PluginInstallPolicyRequest = {
};
const defaultLogger: PluginInstallLogger = {};
const TRUSTED_OFFICIAL_NPM_PLUGIN_PACKAGES = new Map([["@openclaw/codex", "codex"]]);
function ensureOpenClawExtensions(params: { manifest: PackageManifest }):
| {
@@ -185,6 +186,26 @@ function hasPackageRuntimeDependencies(manifest: PackageManifest): boolean {
);
}
function isTrustedOfficialNpmPluginInstall(params: {
installPolicyRequest?: PluginInstallPolicyRequest;
packageName: string;
pluginId: string;
}): boolean {
if (params.installPolicyRequest?.kind !== "plugin-npm") {
return false;
}
const requested = parseRegistryNpmSpec(params.installPolicyRequest.requestedSpecifier ?? "");
if (!requested) {
return false;
}
const expectedPluginId = TRUSTED_OFFICIAL_NPM_PLUGIN_PACKAGES.get(requested.name);
return (
expectedPluginId !== undefined &&
params.packageName === requested.name &&
params.pluginId === expectedPluginId
);
}
function buildBlockedInstallResult(params: {
blocked: NonNullable<NonNullable<InstallSecurityScanResult>["blocked"]>;
}): Extract<InstallPluginResult, { ok: false }> {
@@ -697,12 +718,19 @@ async function validatePackagePluginInstallSource(params: {
const scanMode = params.resolveEffectiveMode
? await params.resolveEffectiveMode(pluginId)
: params.mode;
const trustedOfficialInstall =
params.trustedSourceLinkedOfficialInstall ||
isTrustedOfficialNpmPluginInstall({
installPolicyRequest: params.installPolicyRequest,
packageName: pkgName,
pluginId,
});
const scanResult = await runInstallSourceScan({
subject: `Plugin "${pluginId}"`,
scan: async () =>
await params.runtime.scanPackageInstallSource({
dangerouslyForceUnsafeInstall: params.dangerouslyForceUnsafeInstall,
trustedSourceLinkedOfficialInstall: params.trustedSourceLinkedOfficialInstall,
trustedSourceLinkedOfficialInstall: trustedOfficialInstall,
packageDir: params.packageDir,
pluginId,
logger: params.logger,

View File

@@ -250,6 +250,23 @@ export function resolvePluginPath(input: string, rootDir: string | undefined): s
return rootDir ? path.resolve(rootDir, trimmed) : resolveUserPath(input);
}
function isOfficialNpmCodexPluginRecord(record: Pick<PluginRecord, "id" | "rootDir" | "source">) {
if (record.id !== "codex") {
return false;
}
const sourcePath = path
.normalize(record.rootDir ?? record.source)
.split(path.sep)
.join("/");
return sourcePath.includes("/node_modules/@openclaw/codex");
}
function canClaimReservedCommandOwnership(
record: Pick<PluginRecord, "id" | "origin" | "rootDir" | "source">,
) {
return record.origin === "bundled" || isOfficialNpmCodexPluginRecord(record);
}
const ACTIVE_PLUGIN_HOOK_REGISTRATIONS_KEY = Symbol.for("openclaw.activePluginHookRegistrations");
const activePluginHookRegistrations = resolveGlobalSingleton<
Map<string, Array<{ event: string; handler: Parameters<typeof registerInternalHook>[1] }>>
@@ -1428,7 +1445,7 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) {
return;
}
const allowReservedCommandNames = command.ownership === "reserved";
if (allowReservedCommandNames && record.origin !== "bundled") {
if (allowReservedCommandNames && !canClaimReservedCommandOwnership(record)) {
pushDiagnostic({
level: "error",
pluginId: record.id,