refactor(security): share scan path helpers

This commit is contained in:
Peter Steinberger
2026-02-15 04:29:12 +00:00
parent 0241194591
commit b373461032
3 changed files with 19 additions and 32 deletions

View File

@@ -17,6 +17,7 @@ import {
} from "../infra/install-safe-path.js";
import { validateRegistryNpmSpec } from "../infra/npm-registry-spec.js";
import { runCommandWithTimeout } from "../process/exec.js";
import { extensionUsesSkippedScannerPath, isPathInside } from "../security/scan-paths.js";
import * as skillScanner from "../security/skill-scanner.js";
import { CONFIG_DIR, resolveUserPath } from "../utils.js";
@@ -60,22 +61,6 @@ function validatePluginId(pluginId: string): string | null {
return null;
}
function isPathInside(basePath: string, candidatePath: string): boolean {
const base = path.resolve(basePath);
const candidate = path.resolve(candidatePath);
const rel = path.relative(base, candidate);
return rel === "" || (!rel.startsWith(`..${path.sep}`) && rel !== ".." && !path.isAbsolute(rel));
}
function extensionUsesSkippedScannerPath(entry: string): boolean {
const segments = entry.split(/[\\/]+/).filter(Boolean);
return segments.some(
(segment) =>
segment === "node_modules" ||
(segment.startsWith(".") && segment !== "." && segment !== ".."),
);
}
async function ensureOpenClawExtensions(manifest: PackageManifest) {
const extensions = manifest[MANIFEST_KEY]?.extensions;
if (!Array.isArray(extensions)) {

View File

@@ -31,6 +31,7 @@ import {
inspectPathPermissions,
safeStat,
} from "./audit-fs.js";
import { extensionUsesSkippedScannerPath, isPathInside } from "./scan-paths.js";
import * as skillScanner from "./skill-scanner.js";
export type SecurityAuditFinding = {
@@ -62,22 +63,6 @@ function expandTilde(p: string, env: NodeJS.ProcessEnv): string | null {
return null;
}
function isPathInside(basePath: string, candidatePath: string): boolean {
const base = path.resolve(basePath);
const candidate = path.resolve(candidatePath);
const rel = path.relative(base, candidate);
return rel === "" || (!rel.startsWith(`..${path.sep}`) && rel !== ".." && !path.isAbsolute(rel));
}
function extensionUsesSkippedScannerPath(entry: string): boolean {
const segments = entry.split(/[\\/]+/).filter(Boolean);
return segments.some(
(segment) =>
segment === "node_modules" ||
(segment.startsWith(".") && segment !== "." && segment !== ".."),
);
}
async function readPluginManifestExtensions(pluginPath: string): Promise<string[]> {
const manifestPath = path.join(pluginPath, "package.json");
const raw = await fs.readFile(manifestPath, "utf-8").catch(() => "");

View File

@@ -0,0 +1,17 @@
import path from "node:path";
export function isPathInside(basePath: string, candidatePath: string): boolean {
const base = path.resolve(basePath);
const candidate = path.resolve(candidatePath);
const rel = path.relative(base, candidate);
return rel === "" || (!rel.startsWith(`..${path.sep}`) && rel !== ".." && !path.isAbsolute(rel));
}
export function extensionUsesSkippedScannerPath(entry: string): boolean {
const segments = entry.split(/[\\/]+/).filter(Boolean);
return segments.some(
(segment) =>
segment === "node_modules" ||
(segment.startsWith(".") && segment !== "." && segment !== ".."),
);
}