UNPKG

@sellable/install

Version:

One-command installer for Sellable MCP in Claude Code, Codex, and Hermes

118 lines (108 loc) • 4.01 kB
import { createHash } from "node:crypto"; export const SOUL_ARTIFACT_MAX_BYTES = 64 * 1024; export const SOUL_EFFECTIVE_MAX_CHARACTERS = 20_000; const SECRET_PATTERNS = Object.freeze([ /(?:^|[^A-Za-z0-9])(?:xapp|xoxb|xoxa|sat)[_-][A-Za-z0-9_-]+/i, /sk_lf_[A-Za-z0-9_-]{8,}/i, /asec\.v1\./i, /wkey\.v1\./i, /authorization[_-]?code/i, /access[_-]?token/i, /refresh[_-]?token/i, /\bctok_[0-9a-f]{32}\b/i, /"(?:connectHandle|handle|callbackNonce|nonce|connectLinkUrl|connectToken)"\s*:/i, ]); const sha256 = (bytes) => createHash("sha256").update(bytes).digest("hex"); export class SoulArtifactValidationError extends Error { constructor(code) { super(code); this.name = "SoulArtifactValidationError"; this.code = code; } } function invalid(code) { return Object.freeze({ ok: false, code }); } function decodeUtf8(input) { if (typeof input === "string") { const bytes = Buffer.from(input, "utf8"); // Node replaces unpaired UTF-16 surrogates during UTF-8 encoding. Reject // them so the raw hash always describes the exact caller-visible text. if (bytes.toString("utf8") !== input) return invalid("soul_utf8_invalid"); return { ok: true, content: input, bytes }; } if (!Buffer.isBuffer(input) && !(input instanceof Uint8Array)) { return invalid("soul_content_invalid"); } const bytes = Buffer.from(input); try { const content = new TextDecoder("utf-8", { fatal: true }).decode(bytes); return { ok: true, content, bytes }; } catch { return invalid("soul_utf8_invalid"); } } /** * The one portable SOUL content contract used by server compilation and by * the installed Machine bridge. Results are intentionally redacted: invalid * content never appears in messages, details, logs, or receipts. */ export function validateSoulArtifact( input, { enforceEffectiveLimit = true } = {} ) { const decoded = decodeUtf8(input); if (!decoded.ok) return decoded; const { bytes, content } = decoded; if (bytes.byteLength < 1) return invalid("soul_empty"); if (bytes.byteLength > SOUL_ARTIFACT_MAX_BYTES) { return invalid("soul_bytes_exceeded"); } if (content.charCodeAt(0) === 0xfeff) return invalid("soul_bom_rejected"); if (content.includes("\0")) return invalid("soul_nul_rejected"); if (!content.trim()) return invalid("soul_empty"); // Existing published SOUL artifacts conventionally end in one LF. Hermes // strips that LF, so accept exactly that established representation while // rejecting every other trim delta (leading space, trailing spaces/CR, or // multiple terminal whitespace). Raw and effective hashes remain distinct. const effectiveContent = content.endsWith("\n") ? content.slice(0, -1) : content; if ( !effectiveContent || effectiveContent.trim() !== effectiveContent || (content !== effectiveContent && content !== `${effectiveContent}\n`) ) { return invalid("soul_trim_changes_effective_content"); } const effectiveBytes = Buffer.from(effectiveContent, "utf8"); const characterCount = Array.from(effectiveContent).length; if (enforceEffectiveLimit && characterCount > SOUL_EFFECTIVE_MAX_CHARACTERS) { return invalid("soul_effective_limit_exceeded"); } if (SECRET_PATTERNS.some((pattern) => pattern.test(content))) { return invalid("soul_secret_material_rejected"); } return Object.freeze({ ok: true, content, bytes, byteLength: bytes.byteLength, characterCount, rawFileSha256: sha256(bytes), effectiveSha256: sha256(effectiveBytes), effectiveByteLength: effectiveBytes.byteLength, }); } export function assertSoulArtifact(input, options) { const result = validateSoulArtifact(input, options); if (!result.ok) throw new SoulArtifactValidationError(result.code); return result; } export function containsSoulSecretMaterial(input) { const decoded = decodeUtf8(input); return ( decoded.ok && SECRET_PATTERNS.some((pattern) => pattern.test(decoded.content)) ); }