openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
851 lines (850 loc) • 34.7 kB
JavaScript
import "./src-vebZIeLe.js";
import { t as expectDefined } from "./expect-CyE8FADM.js";
import { c as isRecord } from "./record-coerce-DItp3I4t.js";
import { l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js";
import { y as uniqueStrings } from "./string-normalization-DsCfAx8q.js";
import { c as resolveUserPath } from "./home-dir-BPhrG-aM.js";
import { i as getOrCreatePromise } from "./lazy-promise-DGqyc4Y4.js";
import { n as isPathInside } from "./path-safety-Bi0ppMWC.js";
import { t as FsSafeError, v as readSecureFile } from "./fs-safe-B6pvPGnf.js";
import { r as isPathInside$1 } from "./path-guards-Cp-mGr3-.js";
import "./utils-P__uGsPB.js";
import { p as isValidEnvSecretRefId } from "./types.secrets-kC0nOetj.js";
import { t as formatErrorMessage } from "./errors-Db3Ymjlb.js";
import { a as isBuiltInDefaultSecretProviderRef, c as isValidSecretProviderAlias, d as resolveSecretRefProviderSourceMismatch, f as secretRefKey, i as formatExecSecretRefIdValidationMessage, n as SINGLE_VALUE_FILE_REF_ID, o as isValidExecSecretRefId, s as isValidFileSecretRefId } from "./ref-contract-D92DqQ-r.js";
import { l as normalizePluginsConfig } from "./config-state-BkU1frVq.js";
import { t as shouldRejectHardlinkedPluginFiles } from "./hardlink-policy-CslDqyMD.js";
import { n as loadPluginManifestRegistryCore } from "./manifest-registry-DCCgYk7q.js";
import { n as getCurrentPluginMetadataSnapshot } from "./current-plugin-metadata-snapshot-CmSX4G3W.js";
import { n as isActivatedManifestOwner } from "./manifest-owner-policy-D98oU3cV.js";
import { n as normalizePositiveInt, r as normalizePositiveTimerMs, t as isNonEmptyString } from "./shared-CeAc-MzA.js";
import { r as runCommandWithTimeout } from "./exec-BIE-3oLG.js";
import { c as refResolutionError, o as isSecretResolutionError, s as providerResolutionError } from "./resolve-errors-C2XRFrol.js";
import { t as runTasksWithConcurrency } from "./run-with-concurrency-Dtu208ef.js";
import { a as inspectPathPermissions, o as safeStat } from "./permissions-BhjKuixU.js";
import { n as readJsonPointer } from "./json-pointer-CAhyFpd7.js";
import { t as SECRET_STORE_VALUE_MAX_BYTES } from "./secret-store-validation-error-Bzzf_1MN.js";
import { c as readSecretStoreValue } from "./secret-store-CC1e6gjb.js";
import fs from "node:fs";
import path from "node:path";
//#region src/secrets/provider-integrations.ts
/** Materializes trusted plugin secret-provider integrations into exec provider configs. */
const NODE_COMMAND_PLACEHOLDER = "${node}";
const PLUGIN_INTEGRATION_PROVIDER_ID_MAX_LENGTH = 128;
function resolvePluginRelativePath(value, pluginRoot) {
const resolved = path.resolve(pluginRoot, value);
return isPathInside$1(pluginRoot, resolved) ? resolved : void 0;
}
function isPluginRelativeEntrypoint(value) {
return value.startsWith("./");
}
function resolveArg(arg, pluginRoot) {
if (!arg.startsWith("./") && !arg.startsWith("../")) return arg;
return resolvePluginRelativePath(arg, pluginRoot);
}
function withNodeCommandTrustedDir(command, pluginRoot) {
return command === NODE_COMMAND_PLACEHOLDER ? [.../* @__PURE__ */ new Set([path.dirname(process.execPath), pluginRoot])] : [pluginRoot];
}
function isSecurePosixPathStat(stat) {
if (process.platform === "win32") return true;
if ((stat.mode & 18) !== 0) return false;
if (typeof process.getuid !== "function" || typeof stat.uid !== "number") return true;
const uid = process.getuid();
return stat.uid === uid || stat.uid === 0;
}
function pathSegmentsBetween(rootDir, targetDir) {
if (!isPathInside$1(rootDir, targetDir)) return;
const relative = path.relative(rootDir, targetDir);
if (relative === "") return [];
return relative.split(path.sep).filter(Boolean);
}
function isSecurePluginEntrypointPath(params) {
if (process.platform === "win32") return true;
const originalSegments = pathSegmentsBetween(path.resolve(params.pluginRoot), path.dirname(path.resolve(params.resolvedEntrypoint)));
const realpathSegments = pathSegmentsBetween(params.pluginRootRealpath, path.dirname(params.entrypointRealpath));
if (!originalSegments || !realpathSegments) return false;
let originalDir = path.resolve(params.pluginRoot);
for (const [index, segment] of ["", ...originalSegments].entries()) {
if (segment) originalDir = path.join(originalDir, segment);
const stat = fs.lstatSync(originalDir);
if (index === 0 && stat.isSymbolicLink()) continue;
if (!stat.isDirectory() || stat.isSymbolicLink() || !isSecurePosixPathStat(stat)) return false;
}
let realpathDir = params.pluginRootRealpath;
for (const segment of ["", ...realpathSegments]) {
if (segment) realpathDir = path.join(realpathDir, segment);
const stat = fs.lstatSync(realpathDir);
if (!stat.isDirectory() || !isSecurePosixPathStat(stat)) return false;
}
return true;
}
function resolveNodeEntrypointArg(params) {
const entrypoint = params.integration.args?.[0];
if (!entrypoint || !isPluginRelativeEntrypoint(entrypoint)) return;
let pluginRootRealpath;
try {
pluginRootRealpath = fs.realpathSync(params.pluginRoot);
} catch {
return;
}
const resolved = resolvePluginRelativePath(entrypoint, params.pluginRoot);
if (!resolved) return;
let stat;
try {
stat = fs.lstatSync(resolved);
} catch {
return;
}
if (!stat.isFile() || stat.isSymbolicLink()) return;
if (params.rejectHardlinks && stat.nlink > 1) return;
if (!isSecurePosixPathStat(stat)) return;
try {
const realpath = fs.realpathSync(resolved);
if (!isPathInside$1(pluginRootRealpath, realpath)) return;
if (!isSecurePluginEntrypointPath({
pluginRoot: params.pluginRoot,
pluginRootRealpath,
resolvedEntrypoint: resolved,
entrypointRealpath: realpath
})) return;
return realpath;
} catch {
return;
}
}
function materializeExecProviderConfig(integration, record, env) {
const pluginRoot = record.rootDir;
if (integration.command !== NODE_COMMAND_PLACEHOLDER) return;
const nodeEntrypoint = resolveNodeEntrypointArg({
integration,
pluginRoot,
rejectHardlinks: shouldRejectHardlinkedPluginFiles({
origin: record.origin,
rootDir: pluginRoot,
env
})
});
if (!nodeEntrypoint) return;
const args = integration.args?.map((arg, index) => nodeEntrypoint && index === 0 ? nodeEntrypoint : resolveArg(arg, pluginRoot)).filter((arg) => arg !== void 0);
if (integration.args && args?.length !== integration.args.length) return;
const trustedDirs = withNodeCommandTrustedDir(integration.command, pluginRoot);
return {
source: "exec",
command: process.execPath,
...args ? { args } : {},
...integration.timeoutMs !== void 0 ? { timeoutMs: integration.timeoutMs } : {},
...integration.noOutputTimeoutMs !== void 0 ? { noOutputTimeoutMs: integration.noOutputTimeoutMs } : {},
...integration.maxOutputBytes !== void 0 ? { maxOutputBytes: integration.maxOutputBytes } : {},
...integration.jsonOnly === false ? { jsonOnly: false } : {},
...integration.env ? { env: integration.env } : {},
...integration.passEnv ? { passEnv: integration.passEnv } : {},
trustedDirs
};
}
function canExposeSecretProviderIntegrations(params) {
if (params.record.origin !== "bundled" && params.record.origin !== "global") return false;
return isActivatedManifestOwner({
plugin: params.record,
normalizedConfig: params.normalizedConfig,
rootConfig: params.config
});
}
function integrationDisplayName(record, integrationId, integration) {
return normalizeOptionalString(integration.displayName) ?? normalizeOptionalString(record.name) ?? integrationId;
}
function createPluginIntegrationProviderConfig(params) {
return {
source: "exec",
pluginIntegration: {
pluginId: params.pluginId,
integrationId: params.integrationId
}
};
}
function isValidPluginIntegrationProviderId(value) {
return value.length > 0 && value.length <= PLUGIN_INTEGRATION_PROVIDER_ID_MAX_LENGTH;
}
/** Narrows a secret provider config to the plugin-integration exec shape. */
function isPluginIntegrationSecretProviderConfig(value) {
return typeof value === "object" && value !== null && "source" in value && value.source === "exec" && "pluginIntegration" in value && typeof value.pluginIntegration === "object" && value.pluginIntegration !== null && "pluginId" in value.pluginIntegration && typeof value.pluginIntegration.pluginId === "string" && value.pluginIntegration.pluginId.trim().length > 0 && "integrationId" in value.pluginIntegration && typeof value.pluginIntegration.integrationId === "string" && value.pluginIntegration.integrationId.trim().length > 0;
}
/** Materializes an active trusted plugin secret-provider integration into an exec provider. */
/** Resolves a trusted plugin secret-provider integration into executable provider config. */
function resolveSecretProviderIntegrationConfig(params) {
const config = params.config ?? {};
const normalizedConfig = normalizePluginsConfig(config.plugins);
const env = params.env ?? process.env;
const { pluginId, integrationId } = params.providerConfig.pluginIntegration;
if (!isValidSecretProviderAlias(params.providerAlias)) return {
ok: false,
reason: `provider alias "${params.providerAlias}" is invalid`
};
const record = params.manifestRegistry.plugins.find((candidate) => candidate.id === pluginId);
if (!record) return {
ok: false,
reason: `plugin "${pluginId}" is not installed`
};
if (!canExposeSecretProviderIntegrations({
record,
normalizedConfig,
config
})) return {
ok: false,
reason: `plugin "${pluginId}" is not active or is not from a trusted install origin`
};
const integration = record.secretProviderIntegrations?.[integrationId];
if (!integration) return {
ok: false,
reason: `plugin "${record.id}" does not declare secret provider integration "${integrationId}"`
};
const materialized = materializeExecProviderConfig(integration, record, env);
if (!materialized) return {
ok: false,
reason: `plugin "${record.id}" integration "${integrationId}" could not be materialized`
};
return {
ok: true,
providerConfig: materialized
};
}
/** Lists plugin secret-provider presets available to interactive configure flows. */
function listSecretProviderIntegrationPresets(params) {
const presets = [];
const config = params.config ?? {};
const normalizedConfig = normalizePluginsConfig(config.plugins);
const env = params.env ?? process.env;
for (const record of params.manifestRegistry.plugins) {
if (!canExposeSecretProviderIntegrations({
record,
normalizedConfig,
config
})) continue;
for (const [integrationId, integration] of Object.entries(record.secretProviderIntegrations ?? {})) {
const providerAlias = normalizeOptionalString(integration.providerAlias) ?? integrationId;
if (!isValidSecretProviderAlias(providerAlias) || !isValidPluginIntegrationProviderId(record.id) || !isValidPluginIntegrationProviderId(integrationId)) continue;
if (!materializeExecProviderConfig(integration, record, env)) continue;
presets.push({
id: integrationId,
pluginId: record.id,
providerAlias,
displayName: integrationDisplayName(record, integrationId, integration),
...integration.description ? { description: integration.description } : {},
providerConfig: createPluginIntegrationProviderConfig({
pluginId: record.id,
integrationId
})
});
}
}
return presets.toSorted((left, right) => `${left.displayName}:${left.providerAlias}`.localeCompare(`${right.displayName}:${right.providerAlias}`));
}
//#endregion
//#region src/secrets/exec-provider-path-validation.ts
/** Checks the same command-path trust boundary before validation, writes, and execution. */
async function assertSecureExecCommandPath(params) {
const commandPath = resolveUserPath(params.command);
if (!commandPath) throw new Error(`${params.label} must be an absolute path.`);
const stat = await safeStat(commandPath);
if (!stat.ok) throw new Error(`${params.label} is not readable: ${commandPath}`);
if (stat.isDir) throw new Error(`${params.label} must be a file: ${commandPath}`);
if (stat.isSymlink) throw new Error(`${params.label} must not be a symlink: ${commandPath}`);
if (params.trustedDirs && params.trustedDirs.length > 0) {
if (!params.trustedDirs.map((entry) => resolveUserPath(entry)).some((dir) => isPathInside(dir, commandPath))) throw new Error(`${params.label} is outside trustedDirs: ${commandPath}`);
}
const perms = await inspectPathPermissions(commandPath);
if (!perms.ok) throw new Error(`${params.label} permissions could not be verified: ${commandPath}`);
if (perms.worldWritable || perms.groupWritable) throw new Error(`${params.label} permissions are too open: ${commandPath}`);
if (process.platform === "win32" && perms.source === "unknown") throw new FsSafeError("permission-unverified", `${params.label} ACL verification unavailable on Windows for ${commandPath}. Move the command to a path whose ACLs OpenClaw can verify; there is no provider-level bypass.`);
if (process.platform !== "win32" && typeof process.getuid === "function" && stat.uid != null) {
const uid = process.getuid();
if (stat.uid !== uid) throw new Error(`${params.label} must be owned by the current user (uid=${uid}): ${commandPath}`);
}
return commandPath;
}
//#endregion
//#region src/secrets/resolve-store.ts
const STORE_SECRET_REF_BATCH_MAX_BYTES = 512 * SECRET_STORE_VALUE_MAX_BYTES;
function resolveStoreRefs(params) {
const resolved = /* @__PURE__ */ new Map();
let resolvedBytes = 0;
for (const ref of params.refs) {
const result = readSecretStoreValue({
scope: { kind: "team" },
name: ref.id,
database: params.database
});
if (!result.ok) {
if (result.error.code === "SECRET_STORE_NOT_FOUND") throw refResolutionError({
code: "SECRET_REF_NOT_FOUND",
source: "store",
provider: params.providerName,
refId: ref.id,
message: result.error.message
});
if (result.error.code === "SECRET_STORE_INVALID_NAME") throw refResolutionError({
code: "SECRET_REF_INVALID",
source: "store",
provider: params.providerName,
refId: ref.id,
message: result.error.message
});
throw providerResolutionError({
code: "SECRET_PROVIDER_UNAVAILABLE",
source: "store",
provider: params.providerName,
message: result.error.message,
cause: result.error.cause
});
}
resolvedBytes += Buffer.byteLength(result.value, "utf8");
if (resolvedBytes > STORE_SECRET_REF_BATCH_MAX_BYTES) throw providerResolutionError({
code: "SECRET_PROVIDER_INVALID",
source: "store",
provider: params.providerName,
message: `Store provider "${params.providerName}" exceeded its ${STORE_SECRET_REF_BATCH_MAX_BYTES}-byte batch limit.`
});
resolved.set(ref.id, result.value);
}
return resolved;
}
//#endregion
//#region src/secrets/resolve.ts
/** Resolves SecretRef values from env, file, exec, and store secret providers. */
const DEFAULT_PROVIDER_CONCURRENCY = 4;
const DEFAULT_MAX_REFS_PER_PROVIDER = 512;
const DEFAULT_MAX_BATCH_BYTES = 262144;
const DEFAULT_FILE_MAX_BYTES = 1048576;
const DEFAULT_FILE_TIMEOUT_MS = 5e3;
const DEFAULT_EXEC_TIMEOUT_MS = 5e3;
const DEFAULT_EXEC_MAX_OUTPUT_BYTES = 1048576;
const SAFE_EXEC_ERROR_CODES = /* @__PURE__ */ new Set(["AMBIGUOUS_DUPLICATE_KEY", "NOT_FOUND"]);
function throwUnknownProviderResolutionError(params) {
if (isSecretResolutionError(params.err)) throw params.err;
const isWindowsPathSecurityFailure = process.platform === "win32" && (params.source === "file" || params.source === "exec") && params.err instanceof FsSafeError && params.err.code === "permission-unverified";
throw providerResolutionError({
...isWindowsPathSecurityFailure ? { code: "SECRET_PROVIDER_PATH_SECURITY_UNVERIFIABLE" } : {},
source: params.source,
provider: params.provider,
message: formatErrorMessage(params.err),
cause: params.err
});
}
function resolveResolutionLimits() {
return {
maxProviderConcurrency: DEFAULT_PROVIDER_CONCURRENCY,
maxRefsPerProvider: DEFAULT_MAX_REFS_PER_PROVIDER,
maxBatchBytes: DEFAULT_MAX_BATCH_BYTES
};
}
function toProviderKey(source, provider) {
return `${source}:${provider}`;
}
function resolveConfiguredProvider(params) {
const { ref, config } = params;
const providerConfig = config.secrets?.providers?.[ref.provider];
if (isBuiltInDefaultSecretProviderRef(config, ref)) {
if (ref.source === "env") return { source: "env" };
if (ref.source === "store") return { source: "store" };
}
if (!providerConfig) throw providerResolutionError({
code: "SECRET_PROVIDER_NOT_CONFIGURED",
source: ref.source,
provider: ref.provider,
message: `Secret provider "${ref.provider}" is not configured (ref: ${ref.source}:${ref.provider}:${ref.id}).`
});
const configuredSource = resolveSecretRefProviderSourceMismatch(config, ref);
if (configuredSource) throw providerResolutionError({
code: "SECRET_PROVIDER_INVALID",
source: ref.source,
provider: ref.provider,
message: `Secret provider "${ref.provider}" has source "${configuredSource}" but ref requests "${ref.source}".`
});
if (isPluginIntegrationSecretProviderConfig(providerConfig)) {
const resolved = resolveSecretProviderIntegrationConfig({
manifestRegistry: params.manifestRegistry ?? getCurrentPluginMetadataSnapshot({
config,
env: params.env,
allowWorkspaceScopedSnapshot: true
})?.manifestRegistry ?? loadPluginManifestRegistryCore({
config,
env: params.env
}),
providerAlias: ref.provider,
providerConfig,
config,
env: params.env
});
if (!resolved.ok) throw providerResolutionError({
source: ref.source,
provider: ref.provider,
message: `Secret provider "${ref.provider}" plugin integration is unavailable: ${resolved.reason}.`
});
return resolved.providerConfig;
}
return providerConfig;
}
async function readFileProviderPayload(params) {
const cacheKey = params.providerName;
const cache = params.cache;
const read = async () => {
const filePath = resolveUserPath(params.providerConfig.path);
const timeoutMs = normalizePositiveTimerMs(params.providerConfig.timeoutMs, DEFAULT_FILE_TIMEOUT_MS);
const maxBytes = normalizePositiveInt(params.providerConfig.maxBytes, DEFAULT_FILE_MAX_BYTES);
try {
const { buffer: payload } = await readSecureFile({
filePath,
label: `secrets.providers.${params.providerName}.path`,
io: {
maxBytes,
timeoutMs
},
permissions: { allowInsecure: false }
});
const text = payload.toString("utf8").replace(/^\uFEFF/, "");
if (params.providerConfig.mode === "singleValue") return text.replace(/\r?\n$/, "");
const parsed = JSON.parse(text);
if (!isRecord(parsed)) throw new Error(`File provider "${params.providerName}" payload is not a JSON object.`);
return parsed;
} catch (error) {
if (error instanceof FsSafeError && error.code === "timeout") throw new Error(`File provider "${params.providerName}" timed out after ${timeoutMs}ms.`, { cause: error });
throw error;
}
};
if (!cache) return await read();
cache.filePayloadByProvider ??= /* @__PURE__ */ new Map();
return await getOrCreatePromise(cache.filePayloadByProvider, cacheKey, read);
}
async function resolveEnvRefs(params) {
const resolved = /* @__PURE__ */ new Map();
const allowlist = params.providerConfig.allowlist ? new Set(params.providerConfig.allowlist) : null;
for (const ref of params.refs) {
if (allowlist && !allowlist.has(ref.id)) throw refResolutionError({
code: "SECRET_REF_POLICY_DENIED",
source: "env",
provider: params.providerName,
refId: ref.id,
message: `Environment variable "${ref.id}" is not allowlisted in secrets.providers.${params.providerName}.allowlist.`
});
const envValue = params.env[ref.id];
if (!isNonEmptyString(envValue)) throw refResolutionError({
code: "SECRET_REF_NOT_FOUND",
source: "env",
provider: params.providerName,
refId: ref.id,
message: `Environment variable "${ref.id}" is missing or empty.`
});
resolved.set(ref.id, envValue);
}
return resolved;
}
async function resolveFileRefs(params) {
let payload;
try {
payload = await readFileProviderPayload({
providerName: params.providerName,
providerConfig: params.providerConfig,
cache: params.cache
});
} catch (err) {
throwUnknownProviderResolutionError({
source: "file",
provider: params.providerName,
err
});
}
const mode = params.providerConfig.mode ?? "json";
const resolved = /* @__PURE__ */ new Map();
if (mode === "singleValue") {
for (const ref of params.refs) {
if (ref.id !== "value") throw refResolutionError({
code: "SECRET_REF_INVALID",
source: "file",
provider: params.providerName,
refId: ref.id,
message: `singleValue file provider "${params.providerName}" expects ref id "${SINGLE_VALUE_FILE_REF_ID}".`
});
resolved.set(ref.id, payload);
}
return resolved;
}
for (const ref of params.refs) try {
resolved.set(ref.id, readJsonPointer(payload, ref.id, { onMissing: "throw" }));
} catch (err) {
throw refResolutionError({
code: "SECRET_REF_NOT_FOUND",
source: "file",
provider: params.providerName,
refId: ref.id,
message: formatErrorMessage(err),
cause: err
});
}
return resolved;
}
function parseExecValues(params) {
const trimmed = params.stdout.trim();
if (!trimmed) throw providerResolutionError({
source: "exec",
provider: params.providerName,
message: `Exec provider "${params.providerName}" returned empty stdout.`
});
let parsed;
if (!params.jsonOnly && params.ids.length === 1) try {
parsed = JSON.parse(trimmed);
} catch {
return { [expectDefined(params.ids[0], "ids entry at 0")]: trimmed };
}
else try {
parsed = JSON.parse(trimmed);
} catch {
throw providerResolutionError({
source: "exec",
provider: params.providerName,
message: `Exec provider "${params.providerName}" returned invalid JSON.`
});
}
if (!isRecord(parsed)) {
if (!params.jsonOnly && params.ids.length === 1 && typeof parsed === "string") return { [expectDefined(params.ids[0], "ids entry at 0")]: parsed };
throw providerResolutionError({
source: "exec",
provider: params.providerName,
message: `Exec provider "${params.providerName}" response must be an object.`
});
}
if (parsed.protocolVersion !== 1) throw providerResolutionError({
source: "exec",
provider: params.providerName,
message: `Exec provider "${params.providerName}" protocolVersion must be 1.`
});
const responseValues = parsed.values;
if (!isRecord(responseValues)) throw providerResolutionError({
source: "exec",
provider: params.providerName,
message: `Exec provider "${params.providerName}" response missing "values".`
});
const responseErrors = isRecord(parsed.errors) ? parsed.errors : null;
const out = {};
for (const id of params.ids) {
if (responseErrors && Object.hasOwn(responseErrors, id)) {
const entry = responseErrors[id];
const code = isRecord(entry) && typeof entry.code === "string" ? entry.code : null;
const safeCode = code && SAFE_EXEC_ERROR_CODES.has(code) ? code : null;
throw refResolutionError({
code: safeCode === "NOT_FOUND" ? "SECRET_REF_NOT_FOUND" : "SECRET_REF_PROVIDER_ERROR",
source: "exec",
provider: params.providerName,
refId: id,
message: `Exec provider "${params.providerName}" failed for id "${id}"${safeCode ? ` (${safeCode})` : ""}.`
});
}
if (!Object.hasOwn(responseValues, id)) throw refResolutionError({
code: "SECRET_REF_NOT_FOUND",
source: "exec",
provider: params.providerName,
refId: id,
message: `Exec provider "${params.providerName}" response missing id "${id}".`
});
out[id] = responseValues[id];
}
return out;
}
async function resolveExecRefs(params) {
const ids = uniqueStrings(params.refs.map((ref) => ref.id));
if (ids.length > params.limits.maxRefsPerProvider) throw providerResolutionError({
code: "SECRET_PROVIDER_INVALID",
source: "exec",
provider: params.providerName,
message: `Exec provider "${params.providerName}" exceeded maxRefsPerProvider (${params.limits.maxRefsPerProvider}).`
});
let secureCommandPath;
try {
secureCommandPath = await assertSecureExecCommandPath({
command: params.providerConfig.command,
label: `secrets.providers.${params.providerName}.command`,
trustedDirs: params.providerConfig.trustedDirs
});
} catch (err) {
throwUnknownProviderResolutionError({
source: "exec",
provider: params.providerName,
err
});
}
const input = JSON.stringify({
protocolVersion: 1,
provider: params.providerName,
ids
});
if (Buffer.byteLength(input, "utf8") > params.limits.maxBatchBytes) throw providerResolutionError({
code: "SECRET_PROVIDER_INVALID",
source: "exec",
provider: params.providerName,
message: `Exec provider "${params.providerName}" request exceeded maxBatchBytes (${params.limits.maxBatchBytes}).`
});
const childEnv = {};
for (const key of params.providerConfig.passEnv ?? []) {
const value = params.env[key];
if (value !== void 0) childEnv[key] = value;
}
for (const [key, value] of Object.entries(params.providerConfig.env ?? {})) childEnv[key] = value;
const timeoutMs = normalizePositiveTimerMs(params.providerConfig.timeoutMs, DEFAULT_EXEC_TIMEOUT_MS);
const noOutputTimeoutMs = normalizePositiveTimerMs(params.providerConfig.noOutputTimeoutMs, timeoutMs);
const maxOutputBytes = normalizePositiveInt(params.providerConfig.maxOutputBytes, DEFAULT_EXEC_MAX_OUTPUT_BYTES);
const jsonOnly = params.providerConfig.jsonOnly ?? true;
let result;
try {
result = await runCommandWithTimeout([secureCommandPath, ...params.providerConfig.args ?? []], {
baseEnv: {},
cwd: path.dirname(secureCommandPath),
env: childEnv,
input,
killProcessTree: true,
maxCombinedOutputBytes: maxOutputBytes,
maxOutputBytes,
noOutputTimeoutMs,
outputCapture: "head",
terminateOnOutputLimit: true,
timeoutMs
});
} catch (err) {
throwUnknownProviderResolutionError({
source: "exec",
provider: params.providerName,
err
});
}
if (result.termination === "timeout") throw providerResolutionError({
source: "exec",
provider: params.providerName,
message: `Exec provider "${params.providerName}" timed out after ${timeoutMs}ms.`
});
if (result.termination === "no-output-timeout") throw providerResolutionError({
source: "exec",
provider: params.providerName,
message: `Exec provider "${params.providerName}" produced no output for ${noOutputTimeoutMs}ms.`
});
if (result.outputLimitExceeded) throw providerResolutionError({
source: "exec",
provider: params.providerName,
message: `Exec provider output exceeded maxOutputBytes (${maxOutputBytes}).`
});
if (result.code !== 0) throw providerResolutionError({
source: "exec",
provider: params.providerName,
message: `Exec provider "${params.providerName}" exited with code ${String(result.code)}.`
});
let values;
try {
values = parseExecValues({
providerName: params.providerName,
ids,
stdout: result.stdout,
jsonOnly
});
} catch (err) {
throwUnknownProviderResolutionError({
source: "exec",
provider: params.providerName,
err
});
}
const resolved = /* @__PURE__ */ new Map();
for (const id of ids) resolved.set(id, values[id]);
return resolved;
}
async function resolveProviderRefs(params) {
try {
if (params.providerConfig.source === "env") return await resolveEnvRefs({
refs: params.refs,
providerName: params.providerName,
providerConfig: params.providerConfig,
env: params.options.env ?? process.env
});
if (params.providerConfig.source === "file") return await resolveFileRefs({
refs: params.refs,
providerName: params.providerName,
providerConfig: params.providerConfig,
cache: params.options.cache
});
if (params.providerConfig.source === "store") return resolveStoreRefs({
refs: params.refs,
providerName: params.providerName,
database: { env: params.options.env ?? process.env }
});
if (params.providerConfig.source === "exec") {
if (isPluginIntegrationSecretProviderConfig(params.providerConfig)) throw providerResolutionError({
source: params.source,
provider: params.providerName,
message: `Secret provider "${params.providerName}" plugin integration was not materialized before exec resolution.`
});
return await resolveExecRefs({
refs: params.refs,
providerName: params.providerName,
providerConfig: params.providerConfig,
env: params.options.env ?? process.env,
limits: params.limits
});
}
throw providerResolutionError({
source: params.source,
provider: params.providerName,
message: `Unsupported secret provider source "${String(params.providerConfig.source)}".`
});
} catch (err) {
return throwUnknownProviderResolutionError({
source: params.source,
provider: params.providerName,
err
});
}
}
function normalizeAndGroupSecretRefs(refs) {
if (refs.length === 0) return [];
const uniqueRefs = /* @__PURE__ */ new Map();
for (const ref of refs) {
const id = ref.id.trim();
if (!id) throw new Error("Secret reference id is empty.");
if (!isValidSecretProviderAlias(ref.provider)) throw new Error(`Secret reference provider must match /^[a-z][a-z0-9_-]{0,63}$/ (ref: ${ref.source}:${ref.provider}:${id}).`);
if (ref.source === "env" && !isValidEnvSecretRefId(id)) throw new Error(`Env secret reference id must match /^[A-Z][A-Z0-9_]{0,127}$/ (ref: ${ref.source}:${ref.provider}:${id}).`);
if (ref.source === "file" && !isValidFileSecretRefId(id)) throw new Error(`File secret reference id must be an absolute JSON pointer or "value" (ref: ${ref.source}:${ref.provider}:${id}).`);
if (ref.source === "store" && !isValidEnvSecretRefId(id)) throw new Error(`Store secret reference id must match /^[A-Z][A-Z0-9_]{0,127}$/ (ref: ${ref.source}:${ref.provider}:${id}).`);
if (ref.source === "exec" && !isValidExecSecretRefId(id)) throw new Error(`${formatExecSecretRefIdValidationMessage()} (ref: ${ref.source}:${ref.provider}:${id}).`);
uniqueRefs.set(secretRefKey(ref), {
...ref,
id
});
}
const grouped = /* @__PURE__ */ new Map();
for (const ref of uniqueRefs.values()) {
const key = toProviderKey(ref.source, ref.provider);
const existing = grouped.get(key);
if (existing) {
existing.refs.push(ref);
continue;
}
grouped.set(key, {
source: ref.source,
providerName: ref.provider,
refs: [ref]
});
}
return [...grouped.values()];
}
function createProviderResolutionTasks(params) {
return params.groups.map((group) => async () => {
if (group.refs.length > params.limits.maxRefsPerProvider) throw providerResolutionError({
code: "SECRET_PROVIDER_INVALID",
source: group.source,
provider: group.providerName,
message: `Secret provider "${group.providerName}" exceeded maxRefsPerProvider (${params.limits.maxRefsPerProvider}).`
});
const providerConfig = resolveConfiguredProvider({
ref: expectDefined(group.refs[0], "refs entry at 0"),
config: params.options.config,
env: params.options.env ?? process.env,
manifestRegistry: params.options.manifestRegistry
});
const values = await resolveProviderRefs({
refs: group.refs,
source: group.source,
providerName: group.providerName,
providerConfig,
options: params.options,
limits: params.limits
});
for (const ref of group.refs) if (!values.has(ref.id)) throw refResolutionError({
code: "SECRET_REF_PROVIDER_CONTRACT",
source: group.source,
provider: group.providerName,
refId: ref.id,
message: `Secret provider "${group.providerName}" did not return id "${ref.id}".`
});
return {
group,
values
};
});
}
async function resolveSecretRefProviderGroups(params) {
const groups = normalizeAndGroupSecretRefs(params.refs);
const limits = resolveResolutionLimits();
const errorsByIndex = /* @__PURE__ */ new Map();
const taskResults = await runTasksWithConcurrency({
tasks: createProviderResolutionTasks({
groups,
options: params.options,
limits
}),
limit: limits.maxProviderConcurrency,
errorMode: params.errorMode,
onTaskError: (error, index) => {
errorsByIndex.set(index, error);
}
});
const resolved = /* @__PURE__ */ new Map();
for (const result of taskResults.results) {
if (!result) continue;
for (const ref of result.group.refs) resolved.set(secretRefKey(ref), result.values.get(ref.id));
}
const failures = [];
for (const [index, group] of groups.entries()) if (errorsByIndex.has(index)) failures.push({
group,
error: errorsByIndex.get(index)
});
return {
resolved,
failures,
hasError: taskResults.hasError,
firstError: taskResults.firstError
};
}
/** Resolves a batch of SecretRefs, grouped by provider for bounded provider concurrency. */
async function resolveSecretRefValues(refs, options) {
const result = await resolveSecretRefProviderGroups({
refs,
options,
errorMode: "stop"
});
if (result.hasError) throw result.firstError;
return result.resolved;
}
/** Internal owner-isolation resolver that preserves one provider call per batch. */
async function resolveSecretRefValuesSettledByProvider(refs, options) {
const result = await resolveSecretRefProviderGroups({
refs,
options,
errorMode: "continue"
});
return {
resolved: result.resolved,
failures: result.failures
};
}
/** Resolves one SecretRef, using the optional shared runtime cache. */
/** Resolves one SecretRef to an unknown value using configured provider state. */
async function resolveSecretRefValue(ref, options) {
const cache = options.cache;
const key = secretRefKey(ref);
const resolve = async () => {
const resolved = await resolveSecretRefValues([ref], options);
if (!resolved.has(key)) throw refResolutionError({
code: "SECRET_REF_PROVIDER_CONTRACT",
source: ref.source,
provider: ref.provider,
refId: ref.id,
message: `Secret reference "${key}" resolved to no value.`
});
return resolved.get(key);
};
if (!cache) return await resolve();
cache.resolvedByRefKey ??= /* @__PURE__ */ new Map();
return await getOrCreatePromise(cache.resolvedByRefKey, key, resolve);
}
/** Resolves one SecretRef and requires a non-empty string result. */
async function resolveSecretRefString(ref, options) {
const resolved = await resolveSecretRefValue(ref, options);
if (!isNonEmptyString(resolved)) throw new Error(`Secret reference "${ref.source}:${ref.provider}:${ref.id}" resolved to a non-string or empty value.`);
return resolved;
}
//#endregion
export { assertSecureExecCommandPath as a, resolveSecretProviderIntegrationConfig as c, resolveSecretRefValuesSettledByProvider as i, resolveSecretRefValue as n, isPluginIntegrationSecretProviderConfig as o, resolveSecretRefValues as r, listSecretProviderIntegrationPresets as s, resolveSecretRefString as t };