openclaw/scripts/check-no-random-messaging-tmp.mts
Peter Steinberger e0ed92a7ca
fix(browser): report native hosts with missing runtime targets (#131907)
* fix(browser): flag native hosts with unavailable runtime targets

Validate the registered interpreter and native entry before reporting readiness. Keep valid OpenClaw registrations owned when targets are missing or unsafe so install, doctor repair, and uninstall remain available.

Share target validation with installation, keep unused browser warnings nonblocking, and document the read-only filesystem readiness snapshot.

Refs #131837: Chrome native-host status stays ready after its runtime is removed.

* test(browser): separate installer layout coverage

* test(crabbox): use canonical PID liveness

* fix(ci): classify plugin fixtures in temp guard
2026-08-28 13:13:28 -07:00

95 lines
3.1 KiB
TypeScript

#!/usr/bin/env node
// Blocks host-random tmpdir usage in messaging/channel runtime sources.
import ts from "typescript";
import { runCallsiteGuard } from "./lib/callsite-guard.mts";
import { classifyBundledExtensionSourcePath } from "./lib/extension-source-classifier.mts";
import {
collectCallExpressionLines,
runAsScript,
unwrapExpression,
} from "./lib/ts-guard-utils.mts";
/**
* Source roots scanned for unsafe messaging tmpdir usage.
*/
export const messagingTmpdirGuardSourceRoots = [
"src/channels",
"src/infra/outbound",
"src/line",
"src/media",
"src/media-understanding",
"extensions",
];
function collectOsTmpdirImports(sourceFile: ts.SourceFile) {
const osModuleSpecifiers = new Set(["node:os", "os"]);
const osNamespaceOrDefault = new Set<string>();
const namedTmpdir = new Set<string>();
for (const statement of sourceFile.statements) {
if (!ts.isImportDeclaration(statement)) {
continue;
}
if (!statement.importClause || !ts.isStringLiteral(statement.moduleSpecifier)) {
continue;
}
if (!osModuleSpecifiers.has(statement.moduleSpecifier.text)) {
continue;
}
const clause = statement.importClause;
if (clause.name) {
osNamespaceOrDefault.add(clause.name.text);
}
if (!clause.namedBindings) {
continue;
}
if (ts.isNamespaceImport(clause.namedBindings)) {
osNamespaceOrDefault.add(clause.namedBindings.name.text);
continue;
}
for (const element of clause.namedBindings.elements) {
if ((element.propertyName?.text ?? element.name.text) === "tmpdir") {
namedTmpdir.add(element.name.text);
}
}
}
return { osNamespaceOrDefault, namedTmpdir };
}
/**
* Finds `os.tmpdir()` or imported `tmpdir()` call lines in source.
*/
export function findMessagingTmpdirCallLines(content: string, fileName = "source.ts"): number[] {
const sourceFile = ts.createSourceFile(fileName, content, ts.ScriptTarget.Latest, true);
const { osNamespaceOrDefault, namedTmpdir } = collectOsTmpdirImports(sourceFile);
return collectCallExpressionLines(ts, sourceFile, (node) => {
const callee = unwrapExpression(node.expression);
if (
ts.isPropertyAccessExpression(callee) &&
callee.name.text === "tmpdir" &&
ts.isIdentifier(callee.expression) &&
osNamespaceOrDefault.has(callee.expression.text)
) {
return callee;
}
return ts.isIdentifier(callee) && namedTmpdir.has(callee.text) ? callee : null;
});
}
/**
* Runs the messaging tmpdir guard.
*/
export async function main() {
await runCallsiteGuard({
importMetaUrl: import.meta.url,
sourceRoots: messagingTmpdirGuardSourceRoots,
skipRelativePath: (relPath) =>
relPath.startsWith("extensions/") && classifyBundledExtensionSourcePath(relPath).isTestLike,
findCallLines: findMessagingTmpdirCallLines,
header: "Found os.tmpdir()/tmpdir() usage in messaging/channel runtime sources:",
footer:
"Use resolvePreferredOpenClawTmpDir() or plugin-sdk temp helpers instead of host tmp defaults.",
sortViolations: false,
});
}
runAsScript(import.meta.url, main);