mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-07 22:51:23 +00:00
fix(security): block workspace hardlink alias escapes
This commit is contained in:
38
src/infra/hardlink-guards.ts
Normal file
38
src/infra/hardlink-guards.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import { isNotFoundPathError } from "./path-guards.js";
|
||||
|
||||
export async function assertNoHardlinkedFinalPath(params: {
|
||||
filePath: string;
|
||||
root: string;
|
||||
boundaryLabel: string;
|
||||
allowFinalHardlink?: boolean;
|
||||
}): Promise<void> {
|
||||
if (params.allowFinalHardlink) {
|
||||
return;
|
||||
}
|
||||
let stat: Awaited<ReturnType<typeof fs.stat>>;
|
||||
try {
|
||||
stat = await fs.stat(params.filePath);
|
||||
} catch (err) {
|
||||
if (isNotFoundPathError(err)) {
|
||||
return;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
if (!stat.isFile()) {
|
||||
return;
|
||||
}
|
||||
if (stat.nlink > 1) {
|
||||
throw new Error(
|
||||
`Hardlinked path is not allowed under ${params.boundaryLabel} (${shortPath(params.root)}): ${shortPath(params.filePath)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function shortPath(value: string) {
|
||||
if (value.startsWith(os.homedir())) {
|
||||
return `~${value.slice(os.homedir().length)}`;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
Reference in New Issue
Block a user