mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-11 03:16:03 +00:00
* fix(security): route temp workspaces through private OpenClaw temp root (#101224) * test(infra): skip POSIX mode proof on Windows * chore: align release changelog before main sync --------- Co-authored-by: Peter Steinberger <steipete@gmail.com> Co-authored-by: Peter Steinberger <peter@steipete.me>
This commit is contained in:
@@ -1385,7 +1385,6 @@ export async function downloadClawHubPackageArchive(params: {
|
||||
const target = await createTempDownloadTarget({
|
||||
prefix: "openclaw-clawhub-clawpack",
|
||||
fileName: npmTarballName,
|
||||
tmpDir: os.tmpdir(),
|
||||
});
|
||||
await fs.writeFile(target.path, bytes);
|
||||
return {
|
||||
@@ -1428,7 +1427,6 @@ export async function downloadClawHubPackageArchive(params: {
|
||||
const target = await createTempDownloadTarget({
|
||||
prefix: "openclaw-clawhub-package",
|
||||
fileName: `${params.name}.zip`,
|
||||
tmpDir: os.tmpdir(),
|
||||
});
|
||||
await fs.writeFile(target.path, bytes);
|
||||
return {
|
||||
@@ -1475,7 +1473,6 @@ export async function downloadClawHubSkillArchive(params: {
|
||||
const target = await createTempDownloadTarget({
|
||||
prefix: "openclaw-clawhub-skill",
|
||||
fileName: `${params.slug}.zip`,
|
||||
tmpDir: os.tmpdir(),
|
||||
});
|
||||
await fs.writeFile(target.path, bytes);
|
||||
return {
|
||||
@@ -1518,7 +1515,6 @@ export async function downloadClawHubSkillArchiveUrl(params: {
|
||||
const target = await createTempDownloadTarget({
|
||||
prefix: "openclaw-clawhub-skill",
|
||||
fileName: "skill.zip",
|
||||
tmpDir: os.tmpdir(),
|
||||
});
|
||||
await fs.writeFile(target.path, bytes);
|
||||
return {
|
||||
@@ -1555,7 +1551,6 @@ export async function downloadClawHubGitHubSkillArchive(params: {
|
||||
const target = await createTempDownloadTarget({
|
||||
prefix: "openclaw-clawhub-github-skill",
|
||||
fileName: `${params.commit}.zip`,
|
||||
tmpDir: os.tmpdir(),
|
||||
});
|
||||
await fs.writeFile(target.path, bytes);
|
||||
return {
|
||||
|
||||
57
src/infra/install-source-utils.chmod.test.ts
Normal file
57
src/infra/install-source-utils.chmod.test.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { useAutoCleanupTempDirTracker } from "../../test/helpers/temp-dir.js";
|
||||
|
||||
const resolvePreferredOpenClawTmpDirMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("./tmp-openclaw-dir.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("./tmp-openclaw-dir.js")>();
|
||||
return {
|
||||
...actual,
|
||||
resolvePreferredOpenClawTmpDir: resolvePreferredOpenClawTmpDirMock,
|
||||
};
|
||||
});
|
||||
|
||||
import { withTempDir } from "./install-source-utils.js";
|
||||
|
||||
describe("withTempDir private root", () => {
|
||||
const tempDirs = useAutoCleanupTempDirTracker(afterEach);
|
||||
|
||||
it.runIf(process.platform !== "win32")(
|
||||
"preserves parent temp root permissions when using private OpenClaw temp root",
|
||||
async () => {
|
||||
const mockParentRoot = tempDirs.make("openclaw-chmod-test-");
|
||||
const mockOpenClawDir = path.join(mockParentRoot, "openclaw");
|
||||
|
||||
await fs.mkdir(mockOpenClawDir, { recursive: true });
|
||||
await fs.chmod(mockParentRoot, 0o1777);
|
||||
const canonicalOpenClawDir = await fs.realpath(mockOpenClawDir);
|
||||
|
||||
resolvePreferredOpenClawTmpDirMock.mockReturnValue(mockOpenClawDir);
|
||||
|
||||
let observedDir = "";
|
||||
const value = await withTempDir("openclaw-test-", async (tmpDir) => {
|
||||
observedDir = tmpDir;
|
||||
expect(path.dirname(tmpDir)).toBe(canonicalOpenClawDir);
|
||||
await fs.writeFile(path.join(tmpDir, "marker.txt"), "ok");
|
||||
return "done";
|
||||
});
|
||||
|
||||
expect(value).toBe("done");
|
||||
|
||||
await expect(
|
||||
fs.stat(observedDir).then(
|
||||
() => true,
|
||||
() => false,
|
||||
),
|
||||
).resolves.toBe(false);
|
||||
|
||||
const privateRootStat = await fs.stat(mockOpenClawDir);
|
||||
expect(privateRootStat.mode & 0o7777).toBe(0o700);
|
||||
|
||||
const parentStat = await fs.stat(mockParentRoot);
|
||||
expect(parentStat.mode & 0o7777).toBe(0o1777);
|
||||
},
|
||||
);
|
||||
});
|
||||
@@ -1,6 +1,5 @@
|
||||
// Resolves and packages install sources for plugin installs.
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { isRecord } from "@openclaw/normalization-core/record-coerce";
|
||||
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
|
||||
@@ -11,6 +10,7 @@ import { resolveArchiveKind } from "./archive.js";
|
||||
import { pathExists } from "./fs-safe.js";
|
||||
import { applyNpmFreshnessBypassEnv, type NpmProjectInstallEnvOptions } from "./npm-install-env.js";
|
||||
import { withTempWorkspace } from "./private-temp-workspace.js";
|
||||
import { resolvePreferredOpenClawTmpDir } from "./tmp-openclaw-dir.js";
|
||||
|
||||
/** Metadata npm reports when resolving a registry spec or packed archive. */
|
||||
export type NpmSpecResolution = {
|
||||
@@ -141,7 +141,7 @@ export async function withTempDir<T>(
|
||||
fn: (tmpDir: string) => Promise<T>,
|
||||
options?: { rootDir?: string },
|
||||
): Promise<T> {
|
||||
const rootDir = options?.rootDir ?? os.tmpdir();
|
||||
const rootDir = options?.rootDir ?? resolvePreferredOpenClawTmpDir();
|
||||
return await withTempWorkspace({ rootDir, prefix }, async (tmp) => fn(tmp.dir));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user