openclaw/src/secrets/exec-resolution-policy.ts
Josh Avant 0ffcc308f2
Secrets: gate exec dry-run and preflight resolution behind --allow-exec (#49417)
* Secrets: gate exec dry-run resolution behind --allow-exec

* Secrets: fix dry-run completeness and skipped exec audit semantics

* Secrets: require --allow-exec for exec-containing apply writes

* Docs: align secrets exec consent behavior

* Changelog: note secrets exec consent gating
2026-03-17 23:24:34 -05:00

42 lines
1.5 KiB
TypeScript

import type { OpenClawConfig } from "../config/config.js";
import type { SecretRef } from "../config/types.secrets.js";
import { formatExecSecretRefIdValidationMessage, isValidExecSecretRefId } from "./ref-contract.js";
export function selectRefsForExecPolicy(params: { refs: SecretRef[]; allowExec: boolean }): {
refsToResolve: SecretRef[];
skippedExecRefs: SecretRef[];
} {
const refsToResolve: SecretRef[] = [];
const skippedExecRefs: SecretRef[] = [];
for (const ref of params.refs) {
if (ref.source === "exec" && !params.allowExec) {
skippedExecRefs.push(ref);
continue;
}
refsToResolve.push(ref);
}
return { refsToResolve, skippedExecRefs };
}
export function getSkippedExecRefStaticError(params: {
ref: SecretRef;
config: OpenClawConfig;
}): string | null {
const id = params.ref.id.trim();
const refLabel = `${params.ref.source}:${params.ref.provider}:${id}`;
if (!id) {
return "Error: Secret reference id is empty.";
}
if (!isValidExecSecretRefId(id)) {
return `Error: ${formatExecSecretRefIdValidationMessage()} (ref: ${refLabel}).`;
}
const providerConfig = params.config.secrets?.providers?.[params.ref.provider];
if (!providerConfig) {
return `Error: Secret provider "${params.ref.provider}" is not configured (ref: ${refLabel}).`;
}
if (providerConfig.source !== params.ref.source) {
return `Error: Secret provider "${params.ref.provider}" has source "${providerConfig.source}" but ref requests "${params.ref.source}".`;
}
return null;
}