[codex] Extract filesystem safety primitives (#77918)

* refactor: extract filesystem safety primitives

* refactor: use fs-safe for file access helpers

* refactor: reuse fs-safe for media reads

* refactor: use fs-safe for image reads

* refactor: reuse fs-safe in qqbot media opener

* refactor: reuse fs-safe for local media checks

* refactor: consume cleaner fs-safe api

* refactor: align fs-safe json option names

* fix: preserve fs-safe migration contracts

* refactor: use fs-safe primitive subpaths

* refactor: use grouped fs-safe subpaths

* refactor: align fs-safe api usage

* refactor: adapt private state store api

* chore: refresh proof gate

* refactor: follow fs-safe json api split

* refactor: follow reduced fs-safe surface

* build: default fs-safe python helper off

* fix: preserve fs-safe plugin sdk aliases

* refactor: consolidate fs-safe usage

* refactor: unify fs-safe store usage

* refactor: trim fs-safe temp workspace usage

* refactor: hide low-level fs-safe primitives

* build: use published fs-safe package

* fix: preserve outbound recovery durability after rebase

* chore: refresh pr checks
This commit is contained in:
Peter Steinberger
2026-05-06 02:15:17 +01:00
committed by GitHub
parent 61481eb34f
commit 538605ff44
356 changed files with 4918 additions and 11913 deletions

View File

@@ -1,7 +1,7 @@
import fs from "node:fs";
import path from "node:path";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { openBoundaryFileSync } from "../infra/boundary-file-read.js";
import { openRootFileSync } from "../infra/boundary-file-read.js";
import { parseFrontmatterBlock } from "../markdown/frontmatter.js";
import { isPathInsideWithRealpath } from "../security/scan-paths.js";
import {
@@ -56,7 +56,7 @@ function stripFrontmatter(content: string): string {
function readClaudeBundleManifest(rootDir: string): Record<string, unknown> {
const manifestPath = path.join(rootDir, CLAUDE_BUNDLE_MANIFEST_RELATIVE_PATH);
const opened = openBoundaryFileSync({
const opened = openRootFileSync({
absolutePath: manifestPath,
rootPath: rootDir,
boundaryLabel: "plugin root",

View File

@@ -2,7 +2,7 @@ import fs from "node:fs";
import path from "node:path";
import { applyMergePatch } from "../config/merge-patch.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { matchBoundaryFileOpenFailure, openBoundaryFileSync } from "../infra/boundary-file-read.js";
import { matchRootFileOpenFailure, openRootFileSync } from "../infra/boundary-file-read.js";
import { isRecord } from "../utils.js";
import { normalizePluginsConfig, resolveEffectivePluginActivationState } from "./config-state.js";
import type { PluginBundleFormat } from "./manifest-types.js";
@@ -23,11 +23,11 @@ export function readBundleJsonObject(params: {
rootDir: string;
relativePath: string;
onOpenFailure?: (
failure: Extract<ReturnType<typeof openBoundaryFileSync>, { ok: false }>,
failure: Extract<ReturnType<typeof openRootFileSync>, { ok: false }>,
) => ReadBundleJsonResult;
}): ReadBundleJsonResult {
const absolutePath = path.join(params.rootDir, params.relativePath);
const opened = openBoundaryFileSync({
const opened = openRootFileSync({
absolutePath,
rootPath: params.rootDir,
boundaryLabel: "plugin root",
@@ -50,11 +50,11 @@ export function readBundleJsonObject(params: {
}
export function resolveBundleJsonOpenFailure(params: {
failure: Extract<ReturnType<typeof openBoundaryFileSync>, { ok: false }>;
failure: Extract<ReturnType<typeof openRootFileSync>, { ok: false }>;
relativePath: string;
allowMissing?: boolean;
}): ReadBundleJsonResult {
return matchBoundaryFileOpenFailure(params.failure, {
return matchRootFileOpenFailure(params.failure, {
path: () => {
if (params.allowMissing) {
return { ok: true, raw: {} };

View File

@@ -2,7 +2,7 @@ import fs from "node:fs";
import path from "node:path";
import { applyMergePatch } from "../config/merge-patch.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { openBoundaryFileSync } from "../infra/boundary-file-read.js";
import { openRootFileSync } from "../infra/boundary-file-read.js";
import { isRecord } from "../utils.js";
import {
inspectBundleServerRuntimeSupport,
@@ -65,7 +65,7 @@ function loadBundleLspConfigFile(params: {
relativePath: string;
}): BundleLspConfig {
const absolutePath = path.resolve(params.rootDir, params.relativePath);
const opened = openBoundaryFileSync({
const opened = openRootFileSync({
absolutePath,
rootPath: params.rootDir,
boundaryLabel: "plugin root",

View File

@@ -1,7 +1,7 @@
import fs from "node:fs";
import path from "node:path";
import JSON5 from "json5";
import { matchBoundaryFileOpenFailure, openBoundaryFileSync } from "../infra/boundary-file-read.js";
import { matchRootFileOpenFailure, openRootFileSync } from "../infra/boundary-file-read.js";
import {
normalizeLowercaseStringOrEmpty,
normalizeOptionalString,
@@ -98,7 +98,7 @@ function loadBundleManifestFile(params: {
allowMissing?: boolean;
}): BundleManifestFileLoadResult {
const manifestPath = path.join(params.rootDir, params.manifestRelativePath);
const opened = openBoundaryFileSync({
const opened = openRootFileSync({
absolutePath: manifestPath,
rootPath: params.rootDir,
...(params.rootRealPath !== undefined ? { rootRealPath: params.rootRealPath } : {}),
@@ -106,7 +106,7 @@ function loadBundleManifestFile(params: {
rejectHardlinks: params.rejectHardlinks,
});
if (!opened.ok) {
return matchBoundaryFileOpenFailure(opened, {
return matchRootFileOpenFailure(opened, {
path: () => {
if (params.allowMissing) {
return { ok: true, raw: {}, manifestPath };

View File

@@ -2,7 +2,7 @@ import fs from "node:fs";
import path from "node:path";
import { applyMergePatch } from "../config/merge-patch.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { openBoundaryFileSync } from "../infra/boundary-file-read.js";
import { openRootFileSync } from "../infra/boundary-file-read.js";
import { isRecord } from "../utils.js";
import {
inspectBundleServerRuntimeSupport,
@@ -168,7 +168,7 @@ function loadBundleFileBackedMcpConfig(params: {
}): BundleMcpConfig {
const rootDir = normalizeBundlePath(params.rootDir);
const absolutePath = path.resolve(rootDir, params.relativePath);
const opened = openBoundaryFileSync({
const opened = openRootFileSync({
absolutePath,
rootPath: rootDir,
boundaryLabel: "plugin root",

View File

@@ -1,6 +1,6 @@
import fs from "node:fs";
import { fileURLToPath } from "node:url";
import { openBoundaryFileSync } from "../infra/boundary-file-read.js";
import { openRootFileSync } from "../infra/boundary-file-read.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import {
withBundledPluginEnablementCompat,
@@ -277,7 +277,7 @@ export function loadBundledCapabilityRuntimeRegistry(params: {
workspaceDir: candidate.workspaceDir,
});
const opened = openBoundaryFileSync({
const opened = openRootFileSync({
absolutePath: record.source,
rootPath: record.source === candidate.source ? candidate.rootDir : repoRoot,
boundaryLabel: record.source === candidate.source ? "plugin root" : "repo root",

View File

@@ -3,6 +3,7 @@ import os from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { resolveOpenClawPackageRootSync } from "../infra/openclaw-root.js";
import { isPathInside } from "../infra/path-guards.js";
import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js";
import { resolveUserPath } from "../utils.js";
@@ -78,8 +79,7 @@ function safeRealpathSync(targetPath: string): string | null {
}
function pathContains(parentDir: string, childPath: string): boolean {
const relative = path.relative(parentDir, childPath);
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
return isPathInside(parentDir, childPath);
}
function trustedBundledPluginRootsForPackageRoot(packageRoot: string): string[] {

View File

@@ -749,7 +749,7 @@ async function verifyClawHubArchiveFiles(params: {
extractedBytes += bytes;
return extractedBytes <= DEFAULT_MAX_EXTRACTED_BYTES;
};
for (const entry of Object.values(zip.files)) {
for (const entry of Object.values(zip.files as Record<string, JSZip.JSZipObject>)) {
entryCount += 1;
if (entryCount > DEFAULT_MAX_ENTRIES) {
return buildClawHubInstallFailure(

View File

@@ -10,7 +10,7 @@ import {
import { getChannelPlugin, normalizeChannelId } from "../channels/plugins/index.js";
import { formatErrorMessage } from "../infra/errors.js";
import { expandHomePrefix } from "../infra/home-dir.js";
import { writeJsonAtomic } from "../infra/json-files.js";
import { writeJson } from "../infra/json-files.js";
import { type ConversationRef } from "../infra/outbound/session-binding-service.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import { resolveGlobalMap, resolveGlobalSingleton } from "../shared/global-singleton.js";
@@ -379,7 +379,7 @@ async function saveApprovals(file: PluginBindingApprovalsFile): Promise<void> {
const state = getPluginBindingGlobalState();
state.approvalsCache = file;
state.approvalsLoaded = true;
await writeJsonAtomic(filePath, file, {
await writeJson(filePath, file, {
mode: 0o600,
trailingNewline: true,
});

View File

@@ -1,7 +1,7 @@
import fs from "node:fs";
import path from "node:path";
import type { PluginInstallRecord } from "../config/types.plugins.js";
import { openBoundaryFileSync } from "../infra/boundary-file-read.js";
import { openRootFileSync } from "../infra/boundary-file-read.js";
import {
normalizeLowercaseStringOrEmpty,
normalizeOptionalString,
@@ -415,7 +415,7 @@ function readPackageManifest(
rootRealPath?: string,
): PackageManifest | null {
const manifestPath = path.join(dir, "package.json");
const opened = openBoundaryFileSync({
const opened = openRootFileSync({
absolutePath: manifestPath,
rootPath: dir,
...(rootRealPath !== undefined ? { rootRealPath } : {}),

View File

@@ -1,7 +1,8 @@
import "../infra/fs-safe-defaults.js";
import { createHash } from "node:crypto";
import fs from "node:fs/promises";
import path from "node:path";
import { withTempDir } from "../infra/install-source-utils.js";
import { replaceDirectoryAtomic } from "../infra/replace-file.js";
import {
createSafeNpmInstallArgs,
createSafeNpmInstallEnv,
@@ -192,34 +193,12 @@ async function replaceManagedGitRepo(params: {
stagedRepoDir: string;
persistentRepoDir: string;
}): Promise<{ ok: true } | { ok: false; error: string }> {
const parentDir = path.dirname(params.persistentRepoDir);
const backupDir = path.join(parentDir, `.repo-backup-${process.pid}-${Date.now()}`);
let backupCreated = false;
try {
await fs.mkdir(parentDir, { recursive: true });
try {
await fs.rename(params.persistentRepoDir, backupDir);
backupCreated = true;
} catch (err) {
if ((err as NodeJS.ErrnoException).code !== "ENOENT") {
throw err;
}
}
try {
await fs.rename(params.stagedRepoDir, params.persistentRepoDir);
} catch (err) {
if (backupCreated) {
await fs.rename(backupDir, params.persistentRepoDir);
backupCreated = false;
}
throw err;
}
if (backupCreated) {
await fs.rm(backupDir, { recursive: true, force: true });
}
await replaceDirectoryAtomic({
stagedDir: params.stagedRepoDir,
targetDir: params.persistentRepoDir,
backupPrefix: ".repo-backup-",
});
return { ok: true };
} catch (err) {
return {

View File

@@ -1,5 +1,5 @@
import { fileExists, readJsonFile, resolveArchiveKind } from "../infra/archive.js";
import { writeFileFromPathWithinRoot } from "../infra/fs-safe.js";
import { resolveArchiveKind } from "../infra/archive.js";
import { pathExists, root } from "../infra/fs-safe.js";
import { resolveExistingInstallPath, withExtractedArchiveRoot } from "../infra/install-flow.js";
import {
resolveInstallModeOptions,
@@ -15,6 +15,7 @@ import {
ensureInstallTargetAvailable,
resolveCanonicalInstallTarget,
} from "../infra/install-target.js";
import { readJson } from "../infra/json-files.js";
import {
finalizeNpmSpecArchiveInstall,
installFromNpmSpecArchiveWithInstaller,
@@ -40,9 +41,10 @@ export type { NpmIntegrityDrift, NpmSpecResolution };
export {
checkMinHostVersion,
root,
detectBundleManifestFormat,
ensureInstallTargetAvailable,
fileExists,
pathExists as fileExists,
finalizeNpmSpecArchiveInstall,
getPackageManifestMetadata,
installFromNpmSpecArchiveWithInstaller,
@@ -50,7 +52,7 @@ export {
isPathInside,
loadBundleManifest,
loadPluginManifest,
readJsonFile,
readJson as readJsonFile,
resolveArchiveKind,
resolveArchiveSourcePath,
resolveCanonicalInstallTarget,
@@ -66,5 +68,4 @@ export {
scanPackageInstallSource,
validateRegistryNpmSpec,
withExtractedArchiveRoot,
writeFileFromPathWithinRoot,
};

View File

@@ -1204,11 +1204,8 @@ export async function installPluginFromFile(params: {
logger.info?.(`Installing to ${preparedTarget.targetPath}`);
try {
await runtime.writeFileFromPathWithinRoot({
rootDir: extensionsDir,
relativePath: path.basename(preparedTarget.targetPath),
sourcePath: filePath,
});
const root = await runtime.root(extensionsDir);
await root.copyIn(path.basename(preparedTarget.targetPath), filePath);
} catch (err) {
return { ok: false, error: String(err) };
}

View File

@@ -1,7 +1,7 @@
import fs from "node:fs";
import path from "node:path";
import type { PluginInstallRecord } from "../config/types.plugins.js";
import { readJsonFile, readJsonFileSync } from "../infra/json-files.js";
import { tryReadJson, tryReadJsonSync } from "../infra/json-files.js";
import { resolveDefaultPluginNpmDir, validatePluginId } from "./install-paths.js";
import {
resolveInstalledPluginIndexStorePath,
@@ -163,14 +163,14 @@ function extractPluginInstallRecordsFromPersistedInstalledPluginIndex(
export async function readPersistedInstalledPluginIndexInstallRecords(
options: InstalledPluginIndexStoreOptions = {},
): Promise<Record<string, PluginInstallRecord> | null> {
const parsed = await readJsonFile<unknown>(resolveInstalledPluginIndexStorePath(options));
const parsed = await tryReadJson<unknown>(resolveInstalledPluginIndexStorePath(options));
return extractPluginInstallRecordsFromPersistedInstalledPluginIndex(parsed);
}
export function readPersistedInstalledPluginIndexInstallRecordsSync(
options: InstalledPluginIndexStoreOptions = {},
): Record<string, PluginInstallRecord> | null {
const parsed = readJsonFileSync(resolveInstalledPluginIndexStorePath(options));
const parsed = tryReadJsonSync(resolveInstalledPluginIndexStorePath(options));
return extractPluginInstallRecordsFromPersistedInstalledPluginIndex(parsed);
}

View File

@@ -1,6 +1,6 @@
import { z } from "zod";
import { saveJsonFile } from "../infra/json-file.js";
import { readJsonFile, readJsonFileSync, writeJsonAtomic } from "../infra/json-files.js";
import { tryReadJson, tryReadJsonSync, writeJson } from "../infra/json-files.js";
import { isBlockedObjectKey } from "../infra/prototype-keys.js";
import { safeParseWithSchema } from "../utils/zod-parse.js";
import { resolveCompatibilityHostVersion } from "../version.js";
@@ -161,14 +161,14 @@ function parseInstalledPluginIndex(value: unknown): InstalledPluginIndex | null
export async function readPersistedInstalledPluginIndex(
options: InstalledPluginIndexStoreOptions = {},
): Promise<InstalledPluginIndex | null> {
const parsed = await readJsonFile<unknown>(resolveInstalledPluginIndexStorePath(options));
const parsed = await tryReadJson<unknown>(resolveInstalledPluginIndexStorePath(options));
return parseInstalledPluginIndex(parsed);
}
export function readPersistedInstalledPluginIndexSync(
options: InstalledPluginIndexStoreOptions = {},
): InstalledPluginIndex | null {
const parsed = readJsonFileSync(resolveInstalledPluginIndexStorePath(options));
const parsed = tryReadJsonSync(resolveInstalledPluginIndexStorePath(options));
return parseInstalledPluginIndex(parsed);
}
@@ -177,12 +177,12 @@ export async function writePersistedInstalledPluginIndex(
options: InstalledPluginIndexStoreOptions = {},
): Promise<string> {
const filePath = resolveInstalledPluginIndexStorePath(options);
await writeJsonAtomic(
await writeJson(
filePath,
{ ...index, warning: INSTALLED_PLUGIN_INDEX_WARNING },
{
trailingNewline: true,
ensureDirMode: 0o700,
dirMode: 0o700,
mode: 0o600,
},
);

View File

@@ -9,7 +9,7 @@ import {
import type { OpenClawConfig } from "../config/types.openclaw.js";
import type { PluginInstallRecord } from "../config/types.plugins.js";
import type { GatewayRequestHandler } from "../gateway/server-methods/types.js";
import { openBoundaryFileSync } from "../infra/boundary-file-read.js";
import { openRootFileSync } from "../infra/boundary-file-read.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import {
DEFAULT_MEMORY_DREAMING_PLUGIN_ID,
@@ -2005,7 +2005,7 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
: runtimeCandidateEntry;
const moduleLoadSource = resolveCanonicalDistRuntimeSource(loadEntry.source);
const moduleRoot = resolveCanonicalDistRuntimeSource(loadEntry.rootDir);
const opened = openBoundaryFileSync({
const opened = openRootFileSync({
absolutePath: moduleLoadSource,
rootPath: moduleRoot,
boundaryLabel: "plugin root",
@@ -2096,7 +2096,7 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
const runtimeModuleRoot = resolveCanonicalDistRuntimeSource(
runtimeCandidateEntry.rootDir,
);
const runtimeOpened = openBoundaryFileSync({
const runtimeOpened = openRootFileSync({
absolutePath: runtimeModuleSource,
rootPath: runtimeModuleRoot,
boundaryLabel: "plugin root",
@@ -2677,7 +2677,7 @@ export async function loadOpenClawPluginCliRegistry(
seenIds.set(pluginId, candidate.origin);
continue;
}
const opened = openBoundaryFileSync({
const opened = openRootFileSync({
absolutePath: sourceForCliMetadata,
rootPath: pluginRoot,
boundaryLabel: "plugin root",

View File

@@ -2,7 +2,7 @@ import fs from "node:fs";
import path from "node:path";
import type { ChannelConfigRuntimeSchema } from "../channels/plugins/types.config.js";
import { MANIFEST_KEY } from "../compat/legacy-names.js";
import { matchBoundaryFileOpenFailure, openBoundaryFileSync } from "../infra/boundary-file-read.js";
import { matchRootFileOpenFailure, openRootFileSync } from "../infra/boundary-file-read.js";
import { isBlockedObjectKey } from "../infra/prototype-keys.js";
import {
normalizeModelCatalog,
@@ -1513,7 +1513,7 @@ export function loadPluginManifest(
rootRealPath?: string,
): PluginManifestLoadResult {
const manifestPath = resolvePluginManifestPath(rootDir);
const opened = openBoundaryFileSync({
const opened = openRootFileSync({
absolutePath: manifestPath,
rootPath: rootDir,
...(rootRealPath !== undefined ? { rootRealPath } : {}),
@@ -1522,7 +1522,7 @@ export function loadPluginManifest(
rejectHardlinks,
});
if (!opened.ok) {
return matchBoundaryFileOpenFailure(opened, {
return matchRootFileOpenFailure(opened, {
path: () => ({
ok: false,
error: `plugin manifest not found: ${manifestPath}`,

View File

@@ -3,6 +3,7 @@ import os from "node:os";
import path from "node:path";
import { resolveArchiveKind } from "../infra/archive.js";
import { formatErrorMessage } from "../infra/errors.js";
import { pathExists } from "../infra/fs-safe.js";
import { resolveOsHomeRelativePath } from "../infra/home-dir.js";
import { fetchWithSsrFGuard } from "../infra/net/fetch-guard.js";
import { isPathInside } from "../infra/path-guards.js";
@@ -308,15 +309,6 @@ function parseMarketplaceManifest(
};
}
async function pathExists(target: string): Promise<boolean> {
try {
await fs.access(target);
return true;
} catch {
return false;
}
}
async function readClaudeKnownMarketplaces(): Promise<Record<string, KnownMarketplaceRecord>> {
const knownPath = resolveOsHomeRelativePath(CLAUDE_KNOWN_MARKETPLACES_PATH);
if (!(await pathExists(knownPath))) {

View File

@@ -1,11 +1,11 @@
import fs from "node:fs";
import path from "node:path";
import {
matchBoundaryFileOpenFailure,
openBoundaryFile,
openBoundaryFileSync,
matchRootFileOpenFailure,
openRootFile,
openRootFileSync,
} from "../infra/boundary-file-read.js";
import { resolveBoundaryPath, resolveBoundaryPathSync } from "../infra/boundary-path.js";
import { resolveRootPath, resolveRootPathSync } from "../infra/boundary-path.js";
import { normalizeOptionalString } from "../shared/string-coerce.js";
import type { PluginDiagnostic } from "./manifest-types.js";
import { getPackageManifestMetadata, type PackageManifest } from "./manifest.js";
@@ -98,7 +98,7 @@ async function validatePackageExtensionEntry(params: {
}): Promise<ExtensionEntryValidation> {
const absolutePath = path.resolve(params.packageDir, params.entry);
try {
const resolved = await resolveBoundaryPath({
const resolved = await resolveRootPath({
absolutePath,
rootPath: params.packageDir,
boundaryLabel: "plugin package directory",
@@ -115,13 +115,13 @@ async function validatePackageExtensionEntry(params: {
};
}
const opened = await openBoundaryFile({
const opened = await openRootFile({
absolutePath,
rootPath: params.packageDir,
boundaryLabel: "plugin package directory",
});
if (!opened.ok) {
return matchBoundaryFileOpenFailure(opened, {
return matchRootFileOpenFailure(opened, {
path: () => ({ ok: false, error: `${params.label} not found: ${params.entry}` }),
io: () => ({ ok: false, error: `${params.label} unreadable: ${params.entry}` }),
validation: () => ({
@@ -326,7 +326,7 @@ function resolvePackageEntrySource(params: {
const rejectHardlinks = params.rejectHardlinks ?? true;
const candidates = [source];
const openCandidate = (absolutePath: string): string | null => {
const opened = openBoundaryFileSync({
const opened = openRootFileSync({
absolutePath,
rootPath: params.packageDir,
...(params.packageRootRealPath !== undefined
@@ -336,7 +336,7 @@ function resolvePackageEntrySource(params: {
rejectHardlinks,
});
if (!opened.ok) {
return matchBoundaryFileOpenFailure(opened, {
return matchRootFileOpenFailure(opened, {
path: () => null,
io: () => {
params.diagnostics.push({
@@ -415,7 +415,7 @@ function resolveSafePackageEntry(params: {
}
try {
resolveBoundaryPathSync({
resolveRootPathSync({
absolutePath,
rootPath: params.packageDir,
...(params.packageRootRealPath !== undefined

View File

@@ -1,33 +1,16 @@
import fs from "node:fs";
import { isPathInside as isBoundaryPathInside } from "../infra/path-guards.js";
export function isPathInside(baseDir: string, targetPath: string): boolean {
return isBoundaryPathInside(baseDir, targetPath);
}
export function safeRealpathSync(targetPath: string, cache?: Map<string, string>): string | null {
const cached = cache?.get(targetPath);
if (cached) {
return cached;
}
try {
const resolved = fs.realpathSync(targetPath);
cache?.set(targetPath, resolved);
cache?.set(resolved, resolved);
return resolved;
} catch {
return null;
}
}
export function safeStatSync(targetPath: string): fs.Stats | null {
try {
return fs.statSync(targetPath);
} catch {
return null;
}
}
export function formatPosixMode(mode: number): string {
return (mode & 0o777).toString(8).padStart(3, "0");
}
export {
isNotFoundPathError,
hasNodeErrorCode,
isNodeError,
isPathInside,
isPathInsideWithRealpath,
isSymlinkOpenError,
isWithinDir,
normalizeWindowsPathForComparison,
resolveSafeBaseDir,
resolveSafeRelativePath,
safeRealpathSync,
safeStatSync,
splitSafeRelativePath,
formatPosixMode,
} from "../infra/path-safety.js";

View File

@@ -2,8 +2,8 @@ import fs from "node:fs";
import { createRequire } from "node:module";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { openBoundaryFileSync } from "../infra/boundary-file-read.js";
import { sameFileIdentity } from "../infra/file-identity.js";
import { openRootFileSync } from "../infra/boundary-file-read.js";
import { sameFileIdentity } from "../infra/fs-safe-advanced.js";
import { resolveBundledPluginsDir } from "./bundled-dir.js";
import {
createPluginModuleLoaderCache,
@@ -129,7 +129,7 @@ export function loadBundledPluginPublicArtifactModuleSync<T extends object>(para
return cached as T;
}
const opened = openBoundaryFileSync({
const opened = openRootFileSync({
absolutePath: location.modulePath,
rootPath: location.boundaryRoot,
boundaryLabel:

View File

@@ -1,4 +1,5 @@
import path from "node:path";
import { isPathInside } from "../infra/path-guards.js";
import { shortenHomeInString } from "../utils.js";
import type { PluginRecord } from "./registry.js";
import type { PluginSourceRoots } from "./roots.js";
@@ -6,19 +7,13 @@ export { resolvePluginSourceRoots } from "./roots.js";
export type { PluginSourceRoots } from "./roots.js";
function tryRelative(root: string, filePath: string): string | null {
if (!isPathInside(root, filePath)) {
return null;
}
const rel = path.relative(root, filePath);
if (!rel || rel === ".") {
return null;
}
if (rel === "..") {
return null;
}
if (rel.startsWith(`..${path.sep}`) || rel.startsWith("../") || rel.startsWith("..\\")) {
return null;
}
if (path.isAbsolute(rel)) {
return null;
}
// Normalize to forward slashes for display (path.relative uses backslashes on Windows)
return rel.replaceAll("\\", "/");
}