mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-12 18:06:11 +02:00
Invoke the public Playwright JavaScript entry directly for installed Pi tests so pnpm shim NODE_PATH injection cannot cross the closed controller boundary. Add a credential-free health/UI startup proof and a pinned published Daytona plugin fixture path without production runtime overrides. Co-Authored-By: Paperclip <noreply@paperclip.ing>
73 lines
6.2 KiB
TypeScript
73 lines
6.2 KiB
TypeScript
/** Public plugin fixture admission; the complete installed graph is audited separately. */
|
|
import { constants } from "node:fs";
|
|
import { lstat, open, realpath } from "node:fs/promises";
|
|
import { createHash } from "node:crypto";
|
|
import { findPackageJSON } from "node:module";
|
|
import { isAbsolute, join, resolve } from "node:path";
|
|
import { pathToFileURL } from "node:url";
|
|
import { assertInstalledCliSelection, usesInstalledCli } from "./installed-cli.js";
|
|
import type { MatrixExecution } from "./types.js";
|
|
|
|
export const installedDaytonaPluginKeys = ["PAPERCLIP_RUNNER_E2E_INSTALLED_DAYTONA_PLUGIN", "PAPERCLIP_RUNNER_E2E_INSTALLED_DAYTONA_PLUGIN_AUTHORITY", "PAPERCLIP_RUNNER_E2E_INSTALLED_DAYTONA_PLUGIN_AUTHORITY_SHA256"] as const;
|
|
type PackagePin = { root: string; packageSha256: string; entry: string; entrySha256: string };
|
|
export interface InstalledDaytonaPluginAuthority {
|
|
schema: "paperclip.e2e.installed-daytona-plugin/v1";
|
|
graphRoot: string;
|
|
plugin: PackagePin & { manifestSha256: string; workerSha256: string };
|
|
sdk: PackagePin;
|
|
shared: PackagePin;
|
|
daytona: PackagePin;
|
|
}
|
|
const digest = (bytes: Buffer) => createHash("sha256").update(bytes).digest("hex");
|
|
async function canonical(path: string) {
|
|
if (typeof path !== "string" || !isAbsolute(path) || resolve(path) !== path || await realpath(path) !== path) throw new Error("Installed Daytona plugin path must be canonical and unlinked");
|
|
}
|
|
async function pinned(path: string, expected: string, maximum = 32 * 1024 * 1024) {
|
|
if (!/^[a-f0-9]{64}$/.test(expected)) throw new Error("Installed Daytona plugin requires a SHA-256 pin");
|
|
await canonical(path);
|
|
const handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
try {
|
|
const before = await handle.stat({ bigint: true });
|
|
if (!before.isFile() || before.size < 1n || before.size > BigInt(maximum) || before.nlink !== 1n) throw new Error("Installed Daytona plugin requires a bounded unlinked regular file");
|
|
const bytes = await handle.readFile(); const after = await handle.stat({ bigint: true }); const named = await lstat(path, { bigint: true });
|
|
if (before.dev !== after.dev || before.ino !== after.ino || before.size !== after.size || before.mtimeNs !== after.mtimeNs || before.ctimeNs !== after.ctimeNs || named.dev !== before.dev || named.ino !== before.ino || bytes.length !== Number(before.size) || digest(bytes) !== expected) throw new Error("Installed Daytona plugin bytes differ from reviewed pins");
|
|
return bytes;
|
|
} finally { await handle.close(); }
|
|
}
|
|
function compiledEntry(manifest: any): string | undefined {
|
|
const exported = manifest.exports?.["."];
|
|
return typeof exported === "string" ? exported : (typeof exported?.import === "string" ? exported.import : exported?.import?.default) ?? manifest.main;
|
|
}
|
|
export async function verifyInstalledDaytonaPlugin(env: NodeJS.ProcessEnv, executions: readonly MatrixExecution[]) {
|
|
const selected = installedDaytonaPluginKeys.some(key => env[key] !== undefined);
|
|
const required = usesInstalledCli(env) && executions.some(e => e.environment.id === "daytona");
|
|
if (!selected && !required) return undefined;
|
|
assertInstalledCliSelection(env, executions);
|
|
if (!usesInstalledCli(env) || !installedDaytonaPluginKeys.every(key => Boolean(env[key])) || executions.some(e => e.environment.id !== "daytona")) throw new Error("Installed Daytona plugin requires complete pins and installed Pi Daytona selection");
|
|
const authorityPath = env.PAPERCLIP_RUNNER_E2E_INSTALLED_DAYTONA_PLUGIN_AUTHORITY!;
|
|
const authoritySha256 = env.PAPERCLIP_RUNNER_E2E_INSTALLED_DAYTONA_PLUGIN_AUTHORITY_SHA256!;
|
|
const a: InstalledDaytonaPluginAuthority = JSON.parse((await pinned(authorityPath, authoritySha256, 65536)).toString());
|
|
if (a.schema !== "paperclip.e2e.installed-daytona-plugin/v1" || a.plugin?.root !== env.PAPERCLIP_RUNNER_E2E_INSTALLED_DAYTONA_PLUGIN) throw new Error("Installed Daytona plugin authority differs");
|
|
await canonical(a.graphRoot);
|
|
async function packageAt(pin: PackagePin, name: string) {
|
|
await canonical(pin.root);
|
|
if (pin.root !== join(a.graphRoot, "node_modules", name)) throw new Error("Installed Daytona plugin package escapes its reviewed graph");
|
|
const manifest = JSON.parse((await pinned(join(pin.root, "package.json"), pin.packageSha256, 65536)).toString());
|
|
if (manifest.name !== name || typeof manifest.version !== "string" || !/^\d+\.\d+\.\d+(?:[-+][a-zA-Z0-9.-]+)?$/.test(manifest.version) || pin.entry !== (name === "@daytonaio/sdk" ? "./esm/index.js" : "./dist/index.js") || compiledEntry(manifest) !== pin.entry) throw new Error("Installed Daytona plugin requires published compiled package exports");
|
|
await pinned(join(pin.root, pin.entry), pin.entrySha256);
|
|
return manifest;
|
|
}
|
|
const plugin = await packageAt(a.plugin, "@paperclipai/plugin-daytona");
|
|
const sdk = await packageAt(a.sdk, "@paperclipai/plugin-sdk");
|
|
const shared = await packageAt(a.shared, "@paperclipai/shared");
|
|
const daytona = await packageAt(a.daytona, "@daytonaio/sdk");
|
|
if (plugin.paperclipPlugin?.manifest !== "./dist/manifest.js" || plugin.paperclipPlugin?.worker !== "./dist/worker.js" || plugin.dependencies?.["@paperclipai/plugin-sdk"] !== sdk.version || sdk.dependencies?.["@paperclipai/shared"] !== shared.version || plugin.dependencies?.["@daytonaio/sdk"] !== "0.203.0" || daytona.version !== "0.203.0") throw new Error("Installed Daytona plugin public dependency identity differs");
|
|
await pinned(join(a.plugin.root, "dist/manifest.js"), a.plugin.manifestSha256);
|
|
await pinned(join(a.plugin.root, "dist/worker.js"), a.plugin.workerSha256);
|
|
for (const [name, parent, pin] of [["@paperclipai/plugin-sdk", join(a.plugin.root, "dist/worker.js"), a.sdk], ["@paperclipai/shared", join(a.sdk.root, a.sdk.entry), a.shared], ["@daytonaio/sdk", join(a.plugin.root, "dist/worker.js"), a.daytona]] as const) {
|
|
const found = findPackageJSON(name, pathToFileURL(parent));
|
|
if (!found || await realpath(found) !== join(pin.root, "package.json")) throw new Error("Installed Daytona plugin resolves a foreign dependency");
|
|
}
|
|
return { schema: a.schema, authorityPath, authoritySha256, packageRoot: a.plugin.root, graphRoot: a.graphRoot, fullGraphVerified: false, completeGraphAuthorityRequired: true, sourceFallback: false };
|
|
}
|