openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
645 lines (644 loc) • 29.6 kB
JavaScript
import { s as asFiniteNumber } from "./number-coercion-CLj0HTDM.js";
import { r as stripAnsi, s as visibleWidth } from "./ansi-DrXAcdMD.js";
import { a as asOptionalRecord } from "./record-coerce-DItp3I4t.js";
import { l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js";
import { t as formatErrorMessage } from "./errors-Db3Ymjlb.js";
import { n as sanitizeTerminalText } from "./safe-text-BGBqp1a4.js";
import { y as resolveClawHubBaseUrl } from "./clawhub-client-I4w9Lg5d.js";
import { r as theme } from "./theme-vjDs9tao.js";
import { c as fetchClawHubSkillVerification, s as fetchClawHubSkillSecurityVerdicts } from "./clawhub-skills-C9qmDBtk.js";
import { r as fetchClawHubPackageSecurity } from "./clawhub-packages-DzRBkN2F.js";
import pLimit from "p-limit";
//#region src/infra/clawhub-skill-security.ts
const MAX_SECURITY_VERDICT_BATCH_SIZE = 100;
const OWNER_QUALIFIED_FALLBACK_CONCURRENCY = 6;
const NON_SECURITY_VERIFY_REASONS = /* @__PURE__ */ new Set(["card.missing", "card_missing"]);
function normalizeOwnerHandle(ownerHandle) {
return normalizeOptionalString(ownerHandle)?.replace(/^@+/, "").toLowerCase() || void 0;
}
function normalizeTarget(target) {
const slug = target.slug.trim();
const ownerHandle = normalizeOwnerHandle(target.ownerHandle);
const version = target.version.trim();
return {
slug,
...ownerHandle ? { ownerHandle } : {},
version
};
}
function normalizeSlug(slug) {
return slug.trim().toLowerCase();
}
function targetKey(target) {
return `${target.ownerHandle ?? ""}\0${normalizeSlug(target.slug)}\0${target.version}`;
}
function legacyTargetKey(target) {
return `${normalizeSlug(target.slug)}\0${target.version}`;
}
function partitionCompatibleBatches(targets) {
const batches = [];
for (const target of targets) {
const legacyKey = legacyTargetKey(target);
let batch = batches.find((candidate) => candidate.items.length < MAX_SECURITY_VERDICT_BATCH_SIZE && !candidate.legacyKeys.has(legacyKey));
if (!batch) {
batch = {
items: [],
legacyKeys: /* @__PURE__ */ new Set()
};
batches.push(batch);
}
batch.items.push(target);
batch.legacyKeys.add(legacyKey);
}
return batches.map((batch) => batch.items);
}
function readOptionalStringField(value, field) {
return normalizeOptionalString(asOptionalRecord(value)?.[field]);
}
function readOptionalNumberField(value, field) {
return asFiniteNumber(asOptionalRecord(value)?.[field]);
}
function normalizeReason(reason) {
return normalizeOptionalString(reason)?.toLowerCase() ?? "";
}
function hasOnlyNonSecurityVerifyReasons(reasons) {
return reasons.length > 0 && reasons.every((reason) => NON_SECURITY_VERIFY_REASONS.has(normalizeReason(reason)));
}
function mapVerificationSecurity(verification, allowCleanCardOnlyPass) {
const security = asOptionalRecord(verification.security);
if (!security || Object.hasOwn(security, "passed")) return verification.security;
const status = normalizeOptionalString(security.status) ?? normalizeOptionalString(security.rawStatus);
const decisionPass = verification.ok && normalizeReason(verification.decision) === "pass";
if (!status || !decisionPass && !allowCleanCardOnlyPass) return verification.security;
return {
...security,
passed: true
};
}
function mapVerificationToVerdict(params) {
const skill = asOptionalRecord(params.verification.skill);
const publisher = asOptionalRecord(params.verification.publisher);
const versionRecord = asOptionalRecord(params.verification.version);
const pageUrl = normalizeOptionalString(params.verification.pageUrl);
const reasons = params.verification.reasons.map((reason) => normalizeOptionalString(reason)).filter((reason) => Boolean(reason));
const securityStatus = normalizeReason(readOptionalStringField(params.verification.security, "status") ?? readOptionalStringField(params.verification.security, "rawStatus"));
const cardOnlyCleanFailure = !params.verification.ok && securityStatus === "clean" && hasOnlyNonSecurityVerifyReasons(reasons);
const verifiedVersion = normalizeOptionalString(params.verification.version) ?? readOptionalStringField(versionRecord, "version");
return {
ok: cardOnlyCleanFailure ? true : params.verification.ok,
decision: cardOnlyCleanFailure ? "pass" : params.verification.decision,
reasons: cardOnlyCleanFailure ? [] : reasons,
requestedSlug: params.target.slug,
requestedOwnerHandle: params.target.ownerHandle,
requestedVersion: params.target.version,
slug: normalizeOptionalString(params.verification.slug) ?? readOptionalStringField(skill, "slug"),
version: verifiedVersion ?? (cardOnlyCleanFailure ? params.target.version : null),
displayName: normalizeOptionalString(params.verification.displayName) ?? readOptionalStringField(skill, "displayName"),
publisherHandle: normalizeOptionalString(params.verification.publisherHandle) ?? readOptionalStringField(publisher, "handle"),
publisherDisplayName: normalizeOptionalString(params.verification.publisherDisplayName) ?? readOptionalStringField(publisher, "displayName"),
createdAt: params.verification.createdAt ?? readOptionalNumberField(versionRecord, "createdAt") ?? null,
checkedAt: readOptionalNumberField(params.verification.security, "checkedAt") ?? null,
...pageUrl ? { skillUrl: pageUrl } : {},
...pageUrl ? { securityAuditUrl: `${pageUrl}/security-audit?version=${encodeURIComponent(params.target.version)}` } : {},
security: mapVerificationSecurity(params.verification, cardOnlyCleanFailure)
};
}
function identityFailure(target, message) {
return {
ok: false,
decision: "fail",
reasons: ["security.identity_mismatch"],
requestedSlug: target.slug,
...target.ownerHandle ? { requestedOwnerHandle: target.ownerHandle } : {},
requestedVersion: target.version,
slug: null,
version: null,
publisherHandle: null,
security: null,
error: {
code: "identity_mismatch",
message
}
};
}
function validateVerdictIdentity(target, item) {
const requestedSlug = normalizeOptionalString(item.requestedSlug)?.toLowerCase();
if (requestedSlug !== normalizeSlug(target.slug)) return identityFailure(target, `ClawHub returned requested slug ${requestedSlug ?? "unknown"}.`);
if (normalizeOptionalString(item.requestedVersion) !== target.version) return identityFailure(target, "ClawHub returned a different requested version.");
const requestedOwnerHandle = normalizeOwnerHandle(item.requestedOwnerHandle);
if (requestedOwnerHandle && requestedOwnerHandle !== target.ownerHandle) return identityFailure(target, `ClawHub returned requested owner ${requestedOwnerHandle}.`);
const resolvedSlug = normalizeOptionalString(item.slug)?.toLowerCase();
if (resolvedSlug && resolvedSlug !== normalizeSlug(target.slug)) return identityFailure(target, `ClawHub resolved skill ${resolvedSlug}.`);
const resolvedVersion = normalizeOptionalString(item.version);
if (resolvedVersion && resolvedVersion !== target.version) return identityFailure(target, `ClawHub resolved version ${resolvedVersion}.`);
if (item.ok && resolvedVersion !== target.version) return identityFailure(target, "ClawHub did not return the exact requested version.");
const publisherHandle = normalizeOwnerHandle(item.publisherHandle);
if (target.ownerHandle && item.version !== null && publisherHandle !== target.ownerHandle) return identityFailure(target, `ClawHub resolved publisher ${publisherHandle ?? "unknown"}.`);
return item;
}
function correlateBatchItems(targets, items) {
if (items.length !== targets.length) throw new Error(`ClawHub skill security verdict request returned ${items.length} verdicts for ${targets.length} targets.`);
const byKey = new Map(targets.map((target) => [targetKey(target), target]));
const byLegacyKey = new Map(targets.map((target) => [legacyTargetKey(target), target]));
const correlated = /* @__PURE__ */ new Map();
for (const item of items) {
const requestedSlug = normalizeOptionalString(item.requestedSlug)?.toLowerCase();
const requestedVersion = normalizeOptionalString(item.requestedVersion);
const requestedOwnerHandle = normalizeOwnerHandle(item.requestedOwnerHandle);
if (!requestedSlug || !requestedVersion) throw new Error("ClawHub skill security verdict response omitted request identity.");
const target = requestedOwnerHandle ? byKey.get(targetKey({
slug: requestedSlug,
ownerHandle: requestedOwnerHandle,
version: requestedVersion
})) : byLegacyKey.get(legacyTargetKey({
slug: requestedSlug,
version: requestedVersion
}));
if (!target || correlated.has(targetKey(target))) throw new Error("ClawHub skill security verdict response could not be correlated safely.");
correlated.set(targetKey(target), item);
}
return correlated;
}
async function resolveOwnerQualifiedFallback(params) {
return mapVerificationToVerdict({
verification: await fetchClawHubSkillVerification({
slug: params.target.slug,
ownerHandle: params.target.ownerHandle,
version: params.target.version,
baseUrl: params.baseUrl,
token: params.token,
...params.skipAuth !== void 0 ? { skipAuth: params.skipAuth } : {},
timeoutMs: params.timeoutMs
}),
target: params.target
});
}
async function fetchExactClawHubSkillSecurityVerdicts(params) {
const targets = params.items.map(normalizeTarget);
const keys = /* @__PURE__ */ new Set();
for (const target of targets) {
const key = targetKey(target);
if (keys.has(key)) throw new Error("ClawHub skill security verdict request contains duplicate targets.");
keys.add(key);
}
const resolved = /* @__PURE__ */ new Map();
for (const batch of partitionCompatibleBatches(targets)) {
const correlated = correlateBatchItems(batch, (await fetchClawHubSkillSecurityVerdicts({
items: batch,
baseUrl: params.baseUrl,
token: params.token,
...params.skipAuth !== void 0 ? { skipAuth: params.skipAuth } : {},
timeoutMs: params.timeoutMs
})).items);
const fallbackLimit = pLimit(OWNER_QUALIFIED_FALLBACK_CONCURRENCY);
const resolvedBatch = await Promise.all(batch.map(async (target) => {
const key = targetKey(target);
const item = correlated.get(key);
if (!item) throw new Error("ClawHub skill security verdict response omitted a target.");
const ownerHandle = target.ownerHandle;
return [key, validateVerdictIdentity(target, ownerHandle && item.error?.code === "skill_not_found" ? await fallbackLimit(() => resolveOwnerQualifiedFallback({
target: {
...target,
ownerHandle
},
baseUrl: params.baseUrl,
token: params.token,
...params.skipAuth !== void 0 ? { skipAuth: params.skipAuth } : {},
timeoutMs: params.timeoutMs
})) : item)];
}));
for (const [key, item] of resolvedBatch) resolved.set(key, item);
}
return targets.map((target) => {
const item = resolved.get(targetKey(target));
if (!item) throw new Error("ClawHub skill security verdict resolution omitted a target.");
return item;
});
}
//#endregion
//#region src/infra/clawhub-install-trust.ts
const CLAWHUB_TRUST_ERROR_CODE = {
CLAWHUB_SECURITY_UNAVAILABLE: "clawhub_security_unavailable",
CLAWHUB_DOWNLOAD_BLOCKED: "clawhub_download_blocked"
};
const CLAWHUB_RISK_MODERATION_STATES = /* @__PURE__ */ new Set([
"blocked",
"quarantined",
"revoked"
]);
const CLAWHUB_BLOCKING_MODERATION_STATES = /* @__PURE__ */ new Set([
"blocked",
"quarantined",
"revoked"
]);
const CLAWHUB_SAFE_MODERATION_STATES = /* @__PURE__ */ new Set(["", "approved"]);
const CLAWHUB_NON_RISK_SCAN_STATUSES = /* @__PURE__ */ new Set([
"pending",
"scan_pending",
"stale",
"stale_scan"
]);
const CLAWHUB_NON_RISK_REASONS = /* @__PURE__ */ new Set([
"pending",
"pending_scan",
"scan:pending",
"scan_pending",
"stale",
"scan:stale",
"stale_scan"
]);
function normalizeClawHubTrustToken(value) {
return normalizeOptionalString(value)?.toLowerCase() ?? "";
}
function formatClawHubTrustStatus(label, token) {
return token ? `${label} is ${token}` : `${label} is missing`;
}
function formatClawHubReasonCode(reason) {
switch (normalizeClawHubTrustToken(reason)) {
case "scan:malicious": return "malicious behavior detected";
case "static:malicious": return "malicious behavior detected";
case "payload_strings": return "suspicious payload strings";
case "security.status_not_clean": return "security status is not clean";
case "skill.not_found": return "skill was not found";
case "version.not_found": return "skill version was not found";
case "scan:pending":
case "pending_scan":
case "scan_pending": return "scan pending";
case "scan:stale":
case "stale_scan": return "scan data stale";
default: return reason;
}
}
function isPendingOrStaleTrustWarning(trust) {
return trust.pending || trust.stale;
}
function isNonRiskScanStatus(trust, scanStatus) {
return isPendingOrStaleTrustWarning(trust) && CLAWHUB_NON_RISK_SCAN_STATUSES.has(scanStatus);
}
function isNonRiskReason(trust, reason) {
return isPendingOrStaleTrustWarning(trust) && CLAWHUB_NON_RISK_REASONS.has(reason);
}
function resolveClawHubRiskReasons(trust) {
const reasons = [];
if (trust.blockedFromDownload) reasons.push("Download disabled by ClawHub for this release");
const scanStatus = normalizeClawHubTrustToken(trust.scanStatus);
if (scanStatus !== "clean" && !isNonRiskScanStatus(trust, scanStatus)) reasons.push(formatClawHubTrustStatus("security scan status", scanStatus));
const moderationState = normalizeClawHubTrustToken(trust.moderationState);
if (CLAWHUB_RISK_MODERATION_STATES.has(moderationState) || !CLAWHUB_SAFE_MODERATION_STATES.has(moderationState)) reasons.push(formatClawHubTrustStatus("moderation state", moderationState));
for (const reason of trust.reasons) {
const normalized = normalizeClawHubTrustToken(reason);
if (normalized && !isNonRiskReason(trust, normalized)) reasons.push(formatClawHubReasonCode(reason));
}
return reasons;
}
function resolveClawHubTrustStatusNotices(trust) {
const notices = [];
if (trust.pending) notices.push("security scan is pending");
if (trust.stale) notices.push("scan data is stale");
for (const reason of trust.reasons) {
const normalized = normalizeClawHubTrustToken(reason);
if (normalized && isNonRiskReason(trust, normalized)) notices.push(formatClawHubReasonCode(reason));
}
return notices;
}
function isBlockingClawHubTrust(trust) {
if (trust.blockedFromDownload) return true;
if (normalizeClawHubTrustToken(trust.scanStatus) === "malicious") return true;
if (CLAWHUB_BLOCKING_MODERATION_STATES.has(normalizeClawHubTrustToken(trust.moderationState))) return true;
return trust.reasons.some((reason) => {
const normalized = normalizeClawHubTrustToken(reason);
return normalized === "scan:malicious" || normalized === "static:malicious";
});
}
function assessClawHubTrust(trust) {
const riskReasons = resolveClawHubRiskReasons(trust);
const notices = resolveClawHubTrustStatusNotices(trust);
if (riskReasons.length === 0 && notices.length === 0) return {
disposition: "clean",
riskReasons,
notices
};
if (isBlockingClawHubTrust(trust)) return {
disposition: "blocked",
riskReasons,
notices
};
if (riskReasons.length > 0) return {
disposition: "review-required",
riskReasons,
notices
};
return {
disposition: "review-recommended",
riskReasons,
notices
};
}
function buildClawHubTrustInstallRecordFields(params) {
const scanStatus = normalizeClawHubTrustToken(params.trust.scanStatus);
const moderationState = normalizeClawHubTrustToken(params.trust.moderationState);
const reasons = params.trust.reasons.map((reason) => normalizeOptionalString(reason)).filter((reason) => Boolean(reason));
return {
clawhubTrustDisposition: params.assessment.disposition,
...scanStatus ? { clawhubTrustScanStatus: scanStatus } : {},
...moderationState ? { clawhubTrustModerationState: moderationState } : {},
...reasons.length > 0 ? { clawhubTrustReasons: reasons } : {},
...params.trust.pending ? { clawhubTrustPending: true } : {},
...params.trust.stale ? { clawhubTrustStale: true } : {},
clawhubTrustCheckedAt: params.checkedAt
};
}
function encodeClawHubPackagePath(packageName) {
return packageName.split("/").map((part) => encodeURIComponent(part).replaceAll("%40", "@")).join("/");
}
function resolveClawHubSubjectUrl(params) {
if (params.subject.kind === "skill" && params.subject.ownerHandle) return `${resolveClawHubBaseUrl(params.baseUrl)}/${encodeURIComponent(params.subject.ownerHandle)}/skills/${encodeURIComponent(params.subject.packageName)}`;
const pathRoot = params.subject.kind === "skill" ? "skills" : "plugins";
return `${resolveClawHubBaseUrl(params.baseUrl)}/${pathRoot}/${encodeClawHubPackagePath(params.subject.packageName)}`;
}
function resolveClawHubSecurityLinks(params) {
const subjectUrl = resolveClawHubSubjectUrl(params);
if (params.subject.kind === "skill") {
const resolvedSubjectUrl = normalizeOptionalString(params.links?.subject) ?? subjectUrl;
return {
subject: resolvedSubjectUrl,
security: normalizeOptionalString(params.links?.security) ?? `${resolvedSubjectUrl}/security-audit?version=${encodeURIComponent(params.version)}`
};
}
return {
subject: subjectUrl,
security: normalizeOptionalString(params.links?.security) ?? `${subjectUrl}/security-audit?version=${encodeURIComponent(params.version)}`
};
}
function wrapClawHubAuditLine(value, width) {
if (visibleWidth(value) <= width) return [value];
const words = value.split(/\s+/u).filter(Boolean);
const lines = [];
let line = "";
for (const word of words) {
const next = line ? `${line} ${word}` : word;
if (line && visibleWidth(next) > width) {
lines.push(line);
line = word;
} else line = next;
}
return line ? [...lines, line] : lines;
}
function renderClawHubAuditBox(lines) {
const columns = Math.max(72, Math.min(process.stdout.columns ?? 88, 104));
const innerWidth = Math.max(54, Math.min(columns - 4, 88));
const title = "─ ClawHub Security Audit ";
const borderWidth = innerWidth + 2;
return [
`╭${title}${"─".repeat(Math.max(0, borderWidth - visibleWidth(title)))}╮`,
...lines.flatMap((line) => {
if (!line) return [`│ ${" ".repeat(innerWidth)} │`];
return wrapClawHubAuditLine(line, innerWidth).map((wrapped) => `│ ${wrapped}${" ".repeat(Math.max(0, innerWidth - visibleWidth(wrapped)))} │`);
}),
`╰${"─".repeat(borderWidth)}╯`
].join("\n");
}
function formatClawHubSecurityAudit(params) {
const links = resolveClawHubSecurityLinks({
baseUrl: params.baseUrl,
subject: params.subject,
version: params.version,
links: params.links
});
return renderClawHubAuditBox([
formatClawHubSubjectReleaseLabel(params.subject, params.version),
"",
`Outcome: ${params.assessment.disposition === "blocked" ? theme.error("Blocked") : params.assessment.disposition === "clean" ? theme.success("Safe") : theme.warn("Review")}`,
"",
"Overview:",
...params.overview.split("\n").map((line) => sanitizeTerminalText(line)),
"",
`Details: ${sanitizeTerminalText(links.security)}`
]);
}
function formatClawHubReleaseLabel(packageName, version) {
return `${sanitizeTerminalText(packageName)}@${sanitizeTerminalText(version)}`;
}
function formatClawHubSubjectPackageName(subject) {
return subject.kind === "skill" && subject.ownerHandle ? `@${subject.ownerHandle}/${subject.packageName}` : subject.packageName;
}
function formatClawHubSubjectReleaseLabel(subject, version) {
return formatClawHubReleaseLabel(formatClawHubSubjectPackageName(subject), version);
}
function validateClawHubSecurityIdentity(params) {
const packageLabel = params.packageLabel ?? params.packageName;
const responsePackageName = normalizeOptionalString(params.security.package?.name);
if (responsePackageName !== params.packageName) return {
ok: false,
error: `ClawHub release trust check for "${formatClawHubReleaseLabel(packageLabel, params.version)}" returned package "${sanitizeTerminalText(responsePackageName ?? "unknown")}".`,
code: CLAWHUB_TRUST_ERROR_CODE.CLAWHUB_SECURITY_UNAVAILABLE,
version: params.version
};
const responseVersion = normalizeOptionalString(params.security.release?.version);
if (responseVersion !== params.version) return {
ok: false,
error: `ClawHub release trust check for "${formatClawHubReleaseLabel(packageLabel, params.version)}" returned version "${sanitizeTerminalText(responseVersion ?? "unknown")}".`,
code: CLAWHUB_TRUST_ERROR_CODE.CLAWHUB_SECURITY_UNAVAILABLE,
version: params.version
};
return null;
}
function readSkillVerdictSecurityStatus(item) {
if (!item.security || typeof item.security !== "object") return;
const security = item.security;
if (typeof security.status === "string") return security.status;
return typeof security.rawStatus === "string" ? security.rawStatus : void 0;
}
function readSkillVerdictSecurityPassed(item) {
if (!item.security || typeof item.security !== "object") return;
const passed = item.security.passed;
return typeof passed === "boolean" ? passed : void 0;
}
function hasUsablePassingSkillVerdictSecurity(item) {
return Boolean(readSkillVerdictSecurityStatus(item)) && readSkillVerdictSecurityPassed(item) === true;
}
function hasSkillVerdictSecurityError(item) {
return Boolean(item.error?.code || item.error?.message || item.version === null);
}
function isSkillVerdictPendingReason(reason) {
const normalized = normalizeClawHubTrustToken(reason);
return normalized === "pending" || normalized === "pending_scan" || normalized === "scan_pending";
}
function isSkillVerdictStaleReason(reason) {
const normalized = normalizeClawHubTrustToken(reason);
return normalized === "stale" || normalized === "scan:stale" || normalized === "stale_scan";
}
function isSkillVerdictBlockingReason(reason) {
const normalized = normalizeClawHubTrustToken(reason);
return normalized.includes("malicious") || normalized.includes("malware") || normalized.endsWith("_blocked") || normalized.endsWith(".blocked") || normalized === "blocked";
}
function mapSkillSecurityVerdictToPackageSecurity(params) {
const responseSlug = normalizeOptionalString(params.item.slug ?? params.item.requestedSlug);
if (responseSlug !== params.packageName) throw new Error(`ClawHub skill trust check for "${formatClawHubReleaseLabel(params.packageName, params.version)}" returned skill "${sanitizeTerminalText(responseSlug ?? "unknown")}".`);
const responsePublisher = normalizeOptionalString(params.item.publisherHandle);
if (params.ownerHandle && responsePublisher !== params.ownerHandle) throw new Error(`ClawHub skill trust check for "${formatClawHubReleaseLabel(params.packageName, params.version)}" returned publisher "${sanitizeTerminalText(responsePublisher ?? "unknown")}", expected "${sanitizeTerminalText(params.ownerHandle)}".`);
const responseVersion = normalizeOptionalString(params.item.version);
if (responseVersion !== params.version) {
const reason = params.item.error?.message ? `: ${sanitizeTerminalText(params.item.error.message)}` : "";
throw new Error(`ClawHub skill trust check for "${formatClawHubReleaseLabel(params.packageName, params.version)}" returned version "${sanitizeTerminalText(responseVersion ?? "unknown")}"${reason}.`);
}
if (hasSkillVerdictSecurityError(params.item)) {
const reason = params.item.error?.message ? `: ${sanitizeTerminalText(params.item.error.message)}` : "";
throw new Error(`ClawHub skill trust check for "${formatClawHubReleaseLabel(params.packageName, params.version)}" did not return a usable security verdict${reason}.`);
}
const decision = normalizeClawHubTrustToken(params.item.decision);
if (params.item.ok && decision === "pass" && !hasUsablePassingSkillVerdictSecurity(params.item)) throw new Error(`ClawHub skill trust check for "${formatClawHubReleaseLabel(params.packageName, params.version)}" did not return a usable security verdict.`);
const securityStatus = normalizeClawHubTrustToken(readSkillVerdictSecurityStatus(params.item));
const securityPassed = readSkillVerdictSecurityPassed(params.item);
const reasons = params.item.reasons.map((reason) => normalizeOptionalString(reason)).filter((reason) => Boolean(reason));
const securityPassedAllowsInstall = securityPassed ?? true;
const verdictPassed = params.item.ok && decision === "pass" && securityPassedAllowsInstall;
const scanStatus = verdictPassed ? securityStatus || "clean" : securityStatus && securityStatus !== "clean" ? securityStatus : "suspicious";
if (!verdictPassed && reasons.length === 0) reasons.push(decision ? `decision:${decision}` : "decision:fail");
const hasBlockingReason = reasons.some(isSkillVerdictBlockingReason);
const displayName = normalizeOptionalString(params.item.displayName);
const overview = params.item.overview;
if (typeof overview !== "string" || !overview.trim()) throw new Error(`ClawHub skill trust check for "${formatClawHubReleaseLabel(params.packageName, params.version)}" did not return an audit overview.`);
const securityAuditUrl = normalizeOptionalString(params.item.securityAuditUrl);
if (!securityAuditUrl) throw new Error(`ClawHub skill trust check for "${formatClawHubReleaseLabel(params.packageName, params.version)}" did not return a security audit URL.`);
return {
package: {
name: params.packageName,
family: "skill",
...displayName ? { displayName } : {}
},
release: { version: params.version },
overview,
securityAuditUrl,
trust: {
scanStatus,
moderationState: null,
blockedFromDownload: decision === "blocked" || securityStatus === "malicious" || hasBlockingReason,
reasons,
pending: securityStatus === "pending" || reasons.some(isSkillVerdictPendingReason),
stale: securityStatus === "stale" || reasons.some(isSkillVerdictStaleReason)
}
};
}
function resolveSkillSecurityLinks(item) {
const subject = normalizeOptionalString(item.skillUrl);
const security = normalizeOptionalString(item.securityAuditUrl);
if (!subject && !security) return;
return {
...subject ? { subject } : {},
...security ? { security } : {}
};
}
async function fetchClawHubSubjectSecurity(params) {
if (params.subject.kind === "plugin") {
const security = await fetchClawHubPackageSecurity({
name: params.subject.packageName,
version: params.version,
baseUrl: params.baseUrl,
token: params.token,
timeoutMs: params.timeoutMs
});
return {
security,
links: { security: security.securityAuditUrl }
};
}
const [item] = await fetchExactClawHubSkillSecurityVerdicts({
items: [{
slug: params.subject.packageName,
...params.subject.ownerHandle ? { ownerHandle: params.subject.ownerHandle } : {},
version: params.version
}],
baseUrl: params.baseUrl,
token: params.token,
timeoutMs: params.timeoutMs
});
if (!item) throw new Error(`ClawHub skill trust check for "${formatClawHubReleaseLabel(params.subject.packageName, params.version)}" returned no verdict.`);
return {
security: mapSkillSecurityVerdictToPackageSecurity({
item,
packageName: params.subject.packageName,
...params.subject.ownerHandle ? { ownerHandle: params.subject.ownerHandle } : {},
version: params.version
}),
links: resolveSkillSecurityLinks(item)
};
}
async function checkClawHubPackageTrust(params) {
let trust;
let overview;
let warningLinks;
const packageLabel = formatClawHubSubjectPackageName(params.subject);
const releaseLabel = formatClawHubSubjectReleaseLabel(params.subject, params.version);
try {
const fetchedSecurity = await fetchClawHubSubjectSecurity({
subject: params.subject,
version: params.version,
baseUrl: params.baseUrl,
token: params.token,
timeoutMs: params.timeoutMs
});
const identityFailure = validateClawHubSecurityIdentity({
security: fetchedSecurity.security,
packageName: params.subject.packageName,
packageLabel,
version: params.version
});
if (identityFailure) return identityFailure;
trust = fetchedSecurity.security.trust;
overview = fetchedSecurity.security.overview;
warningLinks = fetchedSecurity.links;
} catch (error) {
return {
ok: false,
error: `ClawHub release trust check failed for "${releaseLabel}": ${sanitizeTerminalText(formatErrorMessage(error))}`,
code: CLAWHUB_TRUST_ERROR_CODE.CLAWHUB_SECURITY_UNAVAILABLE,
version: params.version
};
}
const assessment = assessClawHubTrust(trust);
const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
const acceptTrust = (warning) => ({
ok: true,
trustInstallRecordFields: buildClawHubTrustInstallRecordFields({
trust,
assessment,
checkedAt
}),
...warning ? { warning } : {}
});
const terminalAudit = formatClawHubSecurityAudit({
baseUrl: params.baseUrl,
subject: params.subject,
version: params.version,
overview,
assessment,
links: warningLinks
});
const audit = stripAnsi(formatClawHubSecurityAudit({
baseUrl: params.baseUrl,
subject: params.subject,
version: params.version,
overview,
assessment,
links: warningLinks
}));
if (assessment.disposition === "clean") params.logger?.info?.(terminalAudit);
else params.logger?.warn?.(terminalAudit);
if (assessment.disposition === "blocked") return {
ok: false,
error: `ClawHub blocked this release; ${params.mode === "update" ? "update" : "install"} was not started.`,
code: CLAWHUB_TRUST_ERROR_CODE.CLAWHUB_DOWNLOAD_BLOCKED,
warning: audit,
version: params.version
};
if (params.mode !== "update" && params.confirmInstall && !await params.confirmInstall()) return {
ok: false,
error: "Install cancelled.",
warning: audit,
version: params.version
};
return acceptTrust(assessment.disposition === "clean" ? void 0 : audit);
}
//#endregion
export { checkClawHubPackageTrust as n, fetchExactClawHubSkillSecurityVerdicts as r, CLAWHUB_TRUST_ERROR_CODE as t };