import { i as formatErrorMessage } from "./errors-CSrrtEb1.js";
import { t as formatCliCommand } from "./command-format-BORwwHyH.js";
import { i as resolveChannelDefaultAccountId } from "./helpers-D5_OgjA3.js";
import { n as isDangerousNameMatchingEnabled } from "./dangerous-name-matching-CaqzORY3.js";
import { r as resolveDmAllowState } from "./dm-policy-shared-CdgbYWpM.js";
import { t as inspectReadOnlyChannelAccount } from "./read-only-account-inspect-Cvfcd3V3.js";
import { n as hasResolvedCredentialValue, t as hasConfiguredUnavailableCredentialStatus } from "./account-snapshot-fields-B6kpkQ0w.js";
//#region src/security/audit-channel.ts
function classifyChannelWarningSeverity(message) {
	const s = message.toLowerCase();
	if (s.includes("dms: open") || s.includes("grouppolicy=\"open\"") || s.includes("dmpolicy=\"open\"")) return "critical";
	if (s.includes("allows any") || s.includes("anyone can dm") || s.includes("public")) return "critical";
	if (s.includes("locked") || s.includes("disabled")) return "info";
	return "warn";
}
function dedupeFindings(findings) {
	const seen = /* @__PURE__ */ new Set();
	const out = [];
	for (const finding of findings) {
		const key = [
			finding.checkId,
			finding.severity,
			finding.title,
			finding.detail ?? "",
			finding.remediation ?? ""
		].join("\n");
		if (seen.has(key)) continue;
		seen.add(key);
		out.push(finding);
	}
	return out;
}
function hasExplicitProviderAccountConfig(cfg, provider, accountId) {
	const channel = cfg.channels?.[provider];
	if (!channel || typeof channel !== "object") return false;
	const accounts = channel.accounts;
	if (!accounts || typeof accounts !== "object") return false;
	return Object.hasOwn(accounts, accountId);
}
function formatChannelAccountNote(params) {
	return params.orderedAccountIds.length > 1 || params.hasExplicitAccountPath ? ` (account: ${params.accountId})` : "";
}
async function collectChannelSecurityFindings$1(params) {
	const findings = [];
	const sourceConfig = params.sourceConfig ?? params.cfg;
	const inspectChannelAccount = async (plugin, cfg, accountId) => {
		if (plugin.config.inspectAccount) return await plugin.config.inspectAccount(cfg, accountId);
		return await inspectReadOnlyChannelAccount({
			channelId: plugin.id,
			cfg,
			accountId
		});
	};
	const asAccountRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
	const resolveChannelAuditAccount = async (plugin, accountId) => {
		const diagnostics = [];
		const sourceInspectedAccount = await inspectChannelAccount(plugin, sourceConfig, accountId);
		const resolvedInspectedAccount = await inspectChannelAccount(plugin, params.cfg, accountId);
		const sourceInspection = sourceInspectedAccount;
		const resolvedInspection = resolvedInspectedAccount;
		let resolvedAccount = resolvedInspectedAccount;
		if (!resolvedAccount) try {
			resolvedAccount = plugin.config.resolveAccount(params.cfg, accountId);
		} catch (error) {
			diagnostics.push(`${plugin.id}:${accountId}: failed to resolve account (${formatErrorMessage(error)}).`);
		}
		if (!resolvedAccount && sourceInspectedAccount) resolvedAccount = sourceInspectedAccount;
		if (!resolvedAccount) return {
			account: {},
			enabled: false,
			configured: false,
			diagnostics
		};
		const useSourceUnavailableAccount = Boolean(sourceInspectedAccount && hasConfiguredUnavailableCredentialStatus(sourceInspectedAccount) && (!hasResolvedCredentialValue(resolvedAccount) || sourceInspection?.configured === true && resolvedInspection?.configured === false));
		const account = useSourceUnavailableAccount ? sourceInspectedAccount : resolvedAccount;
		const selectedInspection = useSourceUnavailableAccount ? sourceInspection : resolvedInspection;
		const accountRecord = asAccountRecord(account);
		let enabled = typeof selectedInspection?.enabled === "boolean" ? selectedInspection.enabled : typeof accountRecord?.enabled === "boolean" ? accountRecord.enabled : true;
		if (typeof selectedInspection?.enabled !== "boolean" && typeof accountRecord?.enabled !== "boolean" && plugin.config.isEnabled) try {
			enabled = plugin.config.isEnabled(account, params.cfg);
		} catch (error) {
			enabled = false;
			diagnostics.push(`${plugin.id}:${accountId}: failed to evaluate enabled state (${formatErrorMessage(error)}).`);
		}
		let configured = typeof selectedInspection?.configured === "boolean" ? selectedInspection.configured : typeof accountRecord?.configured === "boolean" ? accountRecord.configured : true;
		if (typeof selectedInspection?.configured !== "boolean" && typeof accountRecord?.configured !== "boolean" && plugin.config.isConfigured) try {
			configured = await plugin.config.isConfigured(account, params.cfg);
		} catch (error) {
			configured = false;
			diagnostics.push(`${plugin.id}:${accountId}: failed to evaluate configured state (${formatErrorMessage(error)}).`);
		}
		return {
			account,
			enabled,
			configured,
			diagnostics
		};
	};
	const warnDmPolicy = async (input) => {
		const policyPath = input.policyPath ?? `${input.allowFromPath}policy`;
		const { hasWildcard, isMultiUserDm } = await resolveDmAllowState({
			provider: input.provider,
			accountId: input.accountId,
			allowFrom: input.allowFrom,
			normalizeEntry: input.normalizeEntry
		});
		const dmScope = params.cfg.session?.dmScope ?? "main";
		if (input.dmPolicy === "open") {
			const allowFromKey = `${input.allowFromPath}allowFrom`;
			findings.push({
				checkId: `channels.${input.provider}.dm.open`,
				severity: "critical",
				title: `${input.label} DMs are open`,
				detail: `${policyPath}="open" allows anyone to DM the bot.`,
				remediation: `Use pairing/allowlist; if you really need open DMs, ensure ${allowFromKey} includes "*".`
			});
			if (!hasWildcard) findings.push({
				checkId: `channels.${input.provider}.dm.open_invalid`,
				severity: "warn",
				title: `${input.label} DM config looks inconsistent`,
				detail: `"open" requires ${allowFromKey} to include "*".`
			});
		}
		if (input.dmPolicy === "disabled") {
			findings.push({
				checkId: `channels.${input.provider}.dm.disabled`,
				severity: "info",
				title: `${input.label} DMs are disabled`,
				detail: `${policyPath}="disabled" ignores inbound DMs.`
			});
			return;
		}
		if (dmScope === "main" && isMultiUserDm) findings.push({
			checkId: `channels.${input.provider}.dm.scope_main_multiuser`,
			severity: "warn",
			title: `${input.label} DMs share the main session`,
			detail: "Multiple DM senders currently share the main session, which can leak context across users.",
			remediation: "Run: " + formatCliCommand("openclaw config set session.dmScope \"per-channel-peer\"") + " (or \"per-account-channel-peer\" for multi-account channels) to isolate DM sessions per sender."
		});
	};
	for (const plugin of params.plugins) {
		if (!plugin.security) continue;
		const accountIds = plugin.config.listAccountIds(sourceConfig);
		const defaultAccountId = resolveChannelDefaultAccountId({
			plugin,
			cfg: sourceConfig,
			accountIds
		});
		const orderedAccountIds = Array.from(new Set([defaultAccountId, ...accountIds]));
		for (const accountId of orderedAccountIds) {
			const hasExplicitAccountPath = hasExplicitProviderAccountConfig(sourceConfig, plugin.id, accountId);
			const { account, enabled, configured, diagnostics } = await resolveChannelAuditAccount(plugin, accountId);
			for (const diagnostic of diagnostics) findings.push({
				checkId: `channels.${plugin.id}.account.read_only_resolution`,
				severity: "warn",
				title: `${plugin.meta.label ?? plugin.id} account could not be fully resolved`,
				detail: diagnostic,
				remediation: "Ensure referenced secrets are available in this shell or run with a running gateway snapshot so security audit can inspect the full channel configuration."
			});
			if (!enabled) continue;
			if (!configured) continue;
			const accountConfig = account?.config;
			if (isDangerousNameMatchingEnabled(accountConfig)) {
				const accountNote = formatChannelAccountNote({
					orderedAccountIds,
					hasExplicitAccountPath,
					accountId
				});
				findings.push({
					checkId: `channels.${plugin.id}.allowFrom.dangerous_name_matching_enabled`,
					severity: "info",
					title: `${plugin.meta.label ?? plugin.id} dangerous name matching is enabled${accountNote}`,
					detail: "dangerouslyAllowNameMatching=true re-enables mutable name/email/tag matching for sender authorization. This is a break-glass compatibility mode, not a hardened default.",
					remediation: "Prefer stable sender IDs in allowlists, then disable dangerouslyAllowNameMatching."
				});
			}
			const dmPolicy = plugin.security.resolveDmPolicy?.({
				cfg: params.cfg,
				accountId,
				account
			});
			if (dmPolicy) await warnDmPolicy({
				label: plugin.meta.label ?? plugin.id,
				provider: plugin.id,
				accountId,
				dmPolicy: dmPolicy.policy,
				allowFrom: dmPolicy.allowFrom,
				policyPath: dmPolicy.policyPath,
				allowFromPath: dmPolicy.allowFromPath,
				normalizeEntry: dmPolicy.normalizeEntry
			});
			if (plugin.security.collectWarnings) {
				const warnings = await plugin.security.collectWarnings({
					cfg: params.cfg,
					accountId,
					account
				});
				for (const message of warnings ?? []) {
					const trimmed = message.trim();
					if (!trimmed) continue;
					findings.push({
						checkId: `channels.${plugin.id}.warning.${findings.length + 1}`,
						severity: classifyChannelWarningSeverity(trimmed),
						title: `${plugin.meta.label ?? plugin.id} security warning`,
						detail: trimmed.replace(/^-\s*/, "")
					});
				}
			}
			if (plugin.security.collectAuditFindings) {
				const auditFindings = await plugin.security.collectAuditFindings({
					cfg: params.cfg,
					sourceConfig,
					accountId,
					account,
					orderedAccountIds,
					hasExplicitAccountPath
				});
				for (const finding of auditFindings ?? []) findings.push(finding);
			}
		}
	}
	return dedupeFindings(findings);
}
//#endregion
//#region src/security/audit-channel.collect.runtime.ts
function collectChannelSecurityFindings(...args) {
	return collectChannelSecurityFindings$1(...args);
}
//#endregion
export { collectChannelSecurityFindings };
