@stackmemoryai/stackmemory
Version:
Lossless, project-scoped memory for AI coding tools. Durable context across sessions with 56 MCP tools, FTS5 search, conductor orchestrator, loop/watch monitoring, snapshot capture, pre-flight overlap checks, Claude/Codex/OpenCode wrappers, Linear sync, a
113 lines (112 loc) • 4.15 kB
JavaScript
import { fileURLToPath as __fileURLToPath } from 'url';
import { dirname as __pathDirname } from 'path';
const __filename = __fileURLToPath(import.meta.url);
const __dirname = __pathDirname(__filename);
const SENSITIVE_PATTERNS = [
// API keys — common prefixes
{ pattern: /\bsk-[a-zA-Z0-9]{20,}/, label: "API key (sk-)" },
{ pattern: /\bsk-ant-[a-zA-Z0-9-]{20,}/, label: "Anthropic key" },
{ pattern: /\bAKIA[A-Z0-9]{12,}/, label: "AWS access key" },
{ pattern: /\bghp_[a-zA-Z0-9]{30,}/, label: "GitHub PAT" },
{ pattern: /\bgho_[a-zA-Z0-9]{30,}/, label: "GitHub OAuth" },
{ pattern: /\bghs_[a-zA-Z0-9]{30,}/, label: "GitHub App" },
{ pattern: /\bglpat-[a-zA-Z0-9_-]{20,}/, label: "GitLab PAT" },
{ pattern: /\bnpm_[a-zA-Z0-9]{30,}/, label: "npm token" },
{ pattern: /\bxox[bpsar]-[a-zA-Z0-9-]{10,}/, label: "Slack token" },
{ pattern: /\blin_api_[a-zA-Z0-9]{20,}/, label: "Linear API key" },
{ pattern: /\blin_oauth_[a-zA-Z0-9]{20,}/, label: "Linear OAuth" },
{ pattern: /\bSG\.[a-zA-Z0-9_-]{20,}/, label: "SendGrid key" },
{ pattern: /\brk_live_[a-zA-Z0-9]{20,}/, label: "Stripe key" },
{ pattern: /\bsk_(?:live|test)_[a-zA-Z0-9]{20,}/, label: "Stripe secret" },
{ pattern: /\bwhsec_[a-zA-Z0-9]{20,}/, label: "Webhook secret" },
// Private keys and certificates
{
pattern: /-----BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY-----/,
label: "Private key (PEM)"
},
{ pattern: /-----BEGIN\s+CERTIFICATE-----/, label: "Certificate (PEM)" },
{
pattern: /-----BEGIN\s+(?:EC\s+)?PRIVATE\s+KEY-----/,
label: "EC private key"
},
// JWT tokens (header.payload.signature format)
{
pattern: /\beyJ[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}/,
label: "JWT token"
},
{ pattern: /\bBearer\s+eyJ[a-zA-Z0-9_-]{10,}/, label: "Bearer JWT" },
// Connection strings with credentials
{
pattern: /(?:mysql|postgres|postgresql|mongodb|redis):\/\/[^:]+:[^@]+@/,
label: "DB connection string"
},
// Generic high-entropy secrets (base64 blocks in config-like context)
{
pattern: /(?:password|secret|token|api[_-]?key|auth[_-]?token)\s*[:=]\s*["'][^"']{8,}["']/i,
label: "Credential assignment"
},
{
pattern: /(?:PASSWORD|SECRET|TOKEN|API_KEY|AUTH_TOKEN)\s*=\s*\S{8,}/,
label: "Env var credential"
}
];
const APPROVED_PROVIDERS = /* @__PURE__ */ new Set(["anthropic", "anthropic-batch"]);
function checkString(text) {
const matches = [];
for (const { pattern, label } of SENSITIVE_PATTERNS) {
if (pattern.test(text)) {
matches.push(label);
}
}
return { sensitive: matches.length > 0, matches };
}
function detectSensitiveContent(task, context) {
const allMatches = [];
const taskResult = checkString(task);
allMatches.push(...taskResult.matches);
if (context) {
for (const value of Object.values(context)) {
if (typeof value === "string") {
const r = checkString(value);
allMatches.push(...r.matches);
} else if (Array.isArray(value)) {
for (const item of value) {
if (typeof item === "string") {
const r = checkString(item);
allMatches.push(...r.matches);
}
}
} else if (value && typeof value === "object") {
for (const nested of Object.values(value)) {
if (typeof nested === "string") {
const r = checkString(nested);
allMatches.push(...r.matches);
}
}
}
}
}
const unique = [...new Set(allMatches)];
return { sensitive: unique.length > 0, matches: unique };
}
function isApprovedProvider(provider) {
return APPROVED_PROVIDERS.has(provider);
}
function shouldBlockProvider(provider, task, context) {
if (isApprovedProvider(provider)) {
return { blocked: false };
}
const check = detectSensitiveContent(task, context);
if (!check.sensitive) {
return { blocked: false };
}
return {
blocked: true,
reason: `Sensitive content detected (${check.matches.join(", ")}). Blocked routing to ${provider}; using approved provider instead.`
};
}
export {
detectSensitiveContent,
isApprovedProvider,
shouldBlockProvider
};