mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 23:08:28 +00:00
fix: use Windows ACLs for security audit
This commit is contained in:
@@ -10,6 +10,8 @@ import { resolveDefaultAgentId } from "../agents/agent-scope.js";
|
||||
import { INCLUDE_KEY, MAX_INCLUDE_DEPTH } from "../config/includes.js";
|
||||
import { normalizeAgentId } from "../routing/session-key.js";
|
||||
import { readChannelAllowFromStore } from "../pairing/pairing-store.js";
|
||||
import { runExec } from "../process/exec.js";
|
||||
import { createIcaclsResetCommand, formatIcaclsResetCommand, type ExecFn } from "./windows-acl.js";
|
||||
|
||||
export type SecurityFixChmodAction = {
|
||||
kind: "chmod";
|
||||
@@ -20,13 +22,24 @@ export type SecurityFixChmodAction = {
|
||||
error?: string;
|
||||
};
|
||||
|
||||
export type SecurityFixIcaclsAction = {
|
||||
kind: "icacls";
|
||||
path: string;
|
||||
command: string;
|
||||
ok: boolean;
|
||||
skipped?: string;
|
||||
error?: string;
|
||||
};
|
||||
|
||||
export type SecurityFixAction = SecurityFixChmodAction | SecurityFixIcaclsAction;
|
||||
|
||||
export type SecurityFixResult = {
|
||||
ok: boolean;
|
||||
stateDir: string;
|
||||
configPath: string;
|
||||
configWritten: boolean;
|
||||
changes: string[];
|
||||
actions: SecurityFixChmodAction[];
|
||||
actions: SecurityFixAction[];
|
||||
errors: string[];
|
||||
};
|
||||
|
||||
@@ -97,6 +110,82 @@ async function safeChmod(params: {
|
||||
}
|
||||
}
|
||||
|
||||
async function safeAclReset(params: {
|
||||
path: string;
|
||||
require: "dir" | "file";
|
||||
env: NodeJS.ProcessEnv;
|
||||
exec?: ExecFn;
|
||||
}): Promise<SecurityFixIcaclsAction> {
|
||||
const display = formatIcaclsResetCommand(params.path, {
|
||||
isDir: params.require === "dir",
|
||||
env: params.env,
|
||||
});
|
||||
try {
|
||||
const st = await fs.lstat(params.path);
|
||||
if (st.isSymbolicLink()) {
|
||||
return {
|
||||
kind: "icacls",
|
||||
path: params.path,
|
||||
command: display,
|
||||
ok: false,
|
||||
skipped: "symlink",
|
||||
};
|
||||
}
|
||||
if (params.require === "dir" && !st.isDirectory()) {
|
||||
return {
|
||||
kind: "icacls",
|
||||
path: params.path,
|
||||
command: display,
|
||||
ok: false,
|
||||
skipped: "not-a-directory",
|
||||
};
|
||||
}
|
||||
if (params.require === "file" && !st.isFile()) {
|
||||
return {
|
||||
kind: "icacls",
|
||||
path: params.path,
|
||||
command: display,
|
||||
ok: false,
|
||||
skipped: "not-a-file",
|
||||
};
|
||||
}
|
||||
const cmd = createIcaclsResetCommand(params.path, {
|
||||
isDir: st.isDirectory(),
|
||||
env: params.env,
|
||||
});
|
||||
if (!cmd) {
|
||||
return {
|
||||
kind: "icacls",
|
||||
path: params.path,
|
||||
command: display,
|
||||
ok: false,
|
||||
skipped: "missing-user",
|
||||
};
|
||||
}
|
||||
const exec = params.exec ?? runExec;
|
||||
await exec(cmd.command, cmd.args);
|
||||
return { kind: "icacls", path: params.path, command: cmd.display, ok: true };
|
||||
} catch (err) {
|
||||
const code = (err as { code?: string }).code;
|
||||
if (code === "ENOENT") {
|
||||
return {
|
||||
kind: "icacls",
|
||||
path: params.path,
|
||||
command: display,
|
||||
ok: false,
|
||||
skipped: "missing",
|
||||
};
|
||||
}
|
||||
return {
|
||||
kind: "icacls",
|
||||
path: params.path,
|
||||
command: display,
|
||||
ok: false,
|
||||
error: String(err),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function setGroupPolicyAllowlist(params: {
|
||||
cfg: ClawdbotConfig;
|
||||
channel: string;
|
||||
@@ -261,7 +350,12 @@ async function chmodCredentialsAndAgentState(params: {
|
||||
env: NodeJS.ProcessEnv;
|
||||
stateDir: string;
|
||||
cfg: ClawdbotConfig;
|
||||
actions: SecurityFixChmodAction[];
|
||||
actions: SecurityFixAction[];
|
||||
applyPerms: (params: {
|
||||
path: string;
|
||||
mode: number;
|
||||
require: "dir" | "file";
|
||||
}) => Promise<SecurityFixAction>;
|
||||
}): Promise<void> {
|
||||
const credsDir = resolveOAuthDir(params.env, params.stateDir);
|
||||
params.actions.push(await safeChmod({ path: credsDir, mode: 0o700, require: "dir" }));
|
||||
@@ -294,18 +388,20 @@ async function chmodCredentialsAndAgentState(params: {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
params.actions.push(await safeChmod({ path: agentRoot, mode: 0o700, require: "dir" }));
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
params.actions.push(await safeChmod({ path: agentDir, mode: 0o700, require: "dir" }));
|
||||
params.actions.push(await params.applyPerms({ path: agentDir, mode: 0o700, require: "dir" }));
|
||||
|
||||
const authPath = path.join(agentDir, "auth-profiles.json");
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
params.actions.push(await safeChmod({ path: authPath, mode: 0o600, require: "file" }));
|
||||
params.actions.push(await params.applyPerms({ path: authPath, mode: 0o600, require: "file" }));
|
||||
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
params.actions.push(await safeChmod({ path: sessionsDir, mode: 0o700, require: "dir" }));
|
||||
params.actions.push(
|
||||
await params.applyPerms({ path: sessionsDir, mode: 0o700, require: "dir" }),
|
||||
);
|
||||
|
||||
const storePath = path.join(sessionsDir, "sessions.json");
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
params.actions.push(await safeChmod({ path: storePath, mode: 0o600, require: "file" }));
|
||||
params.actions.push(await params.applyPerms({ path: storePath, mode: 0o600, require: "file" }));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,11 +409,16 @@ export async function fixSecurityFootguns(opts?: {
|
||||
env?: NodeJS.ProcessEnv;
|
||||
stateDir?: string;
|
||||
configPath?: string;
|
||||
platform?: NodeJS.Platform;
|
||||
exec?: ExecFn;
|
||||
}): Promise<SecurityFixResult> {
|
||||
const env = opts?.env ?? process.env;
|
||||
const platform = opts?.platform ?? process.platform;
|
||||
const exec = opts?.exec ?? runExec;
|
||||
const isWindows = platform === "win32";
|
||||
const stateDir = opts?.stateDir ?? resolveStateDir(env);
|
||||
const configPath = opts?.configPath ?? resolveConfigPath(env, stateDir);
|
||||
const actions: SecurityFixChmodAction[] = [];
|
||||
const actions: SecurityFixAction[] = [];
|
||||
const errors: string[] = [];
|
||||
|
||||
const io = createConfigIO({ env, configPath });
|
||||
@@ -352,8 +453,13 @@ export async function fixSecurityFootguns(opts?: {
|
||||
}
|
||||
}
|
||||
|
||||
actions.push(await safeChmod({ path: stateDir, mode: 0o700, require: "dir" }));
|
||||
actions.push(await safeChmod({ path: configPath, mode: 0o600, require: "file" }));
|
||||
const applyPerms = (params: { path: string; mode: number; require: "dir" | "file" }) =>
|
||||
isWindows
|
||||
? safeAclReset({ path: params.path, require: params.require, env, exec })
|
||||
: safeChmod({ path: params.path, mode: params.mode, require: params.require });
|
||||
|
||||
actions.push(await applyPerms({ path: stateDir, mode: 0o700, require: "dir" }));
|
||||
actions.push(await applyPerms({ path: configPath, mode: 0o600, require: "file" }));
|
||||
|
||||
if (snap.exists) {
|
||||
const includePaths = await collectIncludePathsRecursive({
|
||||
@@ -362,15 +468,19 @@ export async function fixSecurityFootguns(opts?: {
|
||||
}).catch(() => []);
|
||||
for (const p of includePaths) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
actions.push(await safeChmod({ path: p, mode: 0o600, require: "file" }));
|
||||
actions.push(await applyPerms({ path: p, mode: 0o600, require: "file" }));
|
||||
}
|
||||
}
|
||||
|
||||
await chmodCredentialsAndAgentState({ env, stateDir, cfg: snap.config ?? {}, actions }).catch(
|
||||
(err) => {
|
||||
errors.push(`chmodCredentialsAndAgentState failed: ${String(err)}`);
|
||||
},
|
||||
);
|
||||
await chmodCredentialsAndAgentState({
|
||||
env,
|
||||
stateDir,
|
||||
cfg: snap.config ?? {},
|
||||
actions,
|
||||
applyPerms,
|
||||
}).catch((err) => {
|
||||
errors.push(`chmodCredentialsAndAgentState failed: ${String(err)}`);
|
||||
});
|
||||
|
||||
return {
|
||||
ok: errors.length === 0,
|
||||
|
||||
Reference in New Issue
Block a user