| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
|
|
| |
| const INJECTION_PATTERNS = [ |
| { |
| name: "system_override", |
| pattern: |
| /\b(ignore|disregard|forget)\s+(all\s+)?(previous|prior|above|earlier)\s+(instructions?|prompts?|rules?|context)/i, |
| severity: "high", |
| }, |
| { |
| name: "role_hijack", |
| pattern: |
| /\b(you\s+are\s+now|act\s+as\s+if|pretend\s+(to\s+be|you\s+are)|from\s+now\s+on\s+you\s+are)\b/i, |
| severity: "medium", |
| }, |
| { |
| name: "system_prompt_leak", |
| pattern: |
| /\b(reveal|show|display|print|output|repeat)\s+(your\s+)?(system\s+prompt|instructions?|initial\s+prompt|hidden\s+prompt)/i, |
| severity: "high", |
| }, |
| { |
| name: "delimiter_injection", |
| pattern: /(\[SYSTEM\]|\[INST\]|<<SYS>>|<\|im_start\|>|<\|system\|>|<\|user\|>)/i, |
| severity: "high", |
| }, |
| { |
| name: "jailbreak_dan", |
| pattern: /\b(DAN|do\s+anything\s+now|jailbreak|developer\s+mode|enable\s+developer)\b/i, |
| severity: "medium", |
| }, |
| { |
| name: "encoding_evasion", |
| pattern: |
| /\b(base64\s+decode|rot13|hex\s+decode|unicode\s+escape)\b.*\b(instruction|prompt|command)\b/i, |
| severity: "medium", |
| }, |
| ]; |
|
|
| |
|
|
| |
| const PII_PATTERNS = [ |
| { |
| name: "email", |
| pattern: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g, |
| replacement: "[EMAIL_REDACTED]", |
| }, |
| { |
| name: "cpf", |
| pattern: /\b\d{3}\.\d{3}\.\d{3}-\d{2}\b/g, |
| replacement: "[CPF_REDACTED]", |
| }, |
| { |
| name: "cnpj", |
| pattern: /\b\d{2}\.\d{3}\.\d{3}\/\d{4}-\d{2}\b/g, |
| replacement: "[CNPJ_REDACTED]", |
| }, |
| { |
| name: "credit_card", |
| pattern: /\b(?:\d{4}[-\s]?){3}\d{4}\b/g, |
| replacement: "[CARD_REDACTED]", |
| }, |
| { |
| name: "phone_br", |
| pattern: /\b\(?\d{2}\)?\s?\d{4,5}-?\d{4}\b/g, |
| replacement: "[PHONE_REDACTED]", |
| }, |
| { |
| name: "ssn_us", |
| pattern: /\b\d{3}-\d{2}-\d{4}\b/g, |
| replacement: "[SSN_REDACTED]", |
| }, |
| ]; |
|
|
| |
|
|
| |
| |
| |
| |
| function getConfig() { |
| return { |
| enabled: process.env.INPUT_SANITIZER_ENABLED !== "false", |
| mode: process.env.INPUT_SANITIZER_MODE || "warn", |
| piiRedaction: process.env.PII_REDACTION_ENABLED === "true", |
| }; |
| } |
|
|
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| function extractMessageContents(body) { |
| const contents = []; |
|
|
| const messages = body.messages || body.input || []; |
| for (const msg of messages) { |
| if (typeof msg === "string") { |
| contents.push(msg); |
| } else if (typeof msg.content === "string") { |
| contents.push(msg.content); |
| } else if (Array.isArray(msg.content)) { |
| for (const part of msg.content) { |
| if (typeof part === "string") { |
| contents.push(part); |
| } else if (part.text) { |
| contents.push(part.text); |
| } |
| } |
| } |
| } |
|
|
| |
| if (typeof body.system === "string") { |
| contents.push(body.system); |
| } else if (Array.isArray(body.system)) { |
| for (const s of body.system) { |
| if (typeof s === "string") contents.push(s); |
| else if (s.text) contents.push(s.text); |
| } |
| } |
|
|
| return contents; |
| } |
|
|
| |
| |
| |
| |
| |
| function detectInjection(text) { |
| const detections = []; |
| for (const rule of INJECTION_PATTERNS) { |
| const match = text.match(rule.pattern); |
| if (match) { |
| detections.push({ |
| pattern: rule.name, |
| severity: rule.severity, |
| match: match[0].slice(0, 50), |
| }); |
| } |
| } |
| return detections; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| function processPII(text, redact = false) { |
| const detections = []; |
| let processed = text; |
|
|
| for (const rule of PII_PATTERNS) { |
| const matches = text.match(rule.pattern); |
| if (matches && matches.length > 0) { |
| detections.push({ type: rule.name, count: matches.length }); |
| if (redact) { |
| processed = processed.replace(rule.pattern, rule.replacement); |
| } |
| } |
| } |
|
|
| return { text: processed, detections }; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| export function sanitizeRequest(body, logger = console) { |
| const config = getConfig(); |
|
|
| const result = { |
| blocked: false, |
| modified: false, |
| detections: [], |
| piiDetections: [], |
| sanitizedBody: null, |
| }; |
|
|
| if (!config.enabled) return result; |
|
|
| const contents = extractMessageContents(body); |
| const fullText = contents.join("\n"); |
|
|
| |
| const injections = detectInjection(fullText); |
| if (injections.length > 0) { |
| result.detections = injections; |
|
|
| const highSeverity = injections.filter((d) => d.severity === "high"); |
| const logLevel = highSeverity.length > 0 ? "warn" : "info"; |
|
|
| if (logger[logLevel]) { |
| logger[logLevel]( |
| `[SANITIZER] Prompt injection detected: ${injections.map((d) => d.pattern).join(", ")}` |
| ); |
| } |
|
|
| if (config.mode === "block" && highSeverity.length > 0) { |
| result.blocked = true; |
| return result; |
| } |
| } |
|
|
| |
| if (config.piiRedaction) { |
| const piiResult = processPII(fullText, config.mode === "redact"); |
| result.piiDetections = piiResult.detections; |
|
|
| if (piiResult.detections.length > 0) { |
| logger.warn?.( |
| `[SANITIZER] PII detected: ${piiResult.detections.map((d) => `${d.type}(${d.count})`).join(", ")}` |
| ); |
|
|
| if (config.mode === "redact") { |
| |
| result.sanitizedBody = redactBody(body); |
| result.modified = true; |
| } |
| } |
| } |
|
|
| return result; |
| } |
|
|
| |
| |
| |
| |
| |
| function redactBody(body) { |
| const clone = JSON.parse(JSON.stringify(body)); |
| const messages = clone.messages || clone.input || []; |
|
|
| for (const msg of messages) { |
| if (typeof msg.content === "string") { |
| msg.content = processPII(msg.content, true).text; |
| } else if (Array.isArray(msg.content)) { |
| for (const part of msg.content) { |
| if (typeof part === "string") { |
| const idx = msg.content.indexOf(part); |
| msg.content[idx] = processPII(part, true).text; |
| } else if (part.text) { |
| part.text = processPII(part.text, true).text; |
| } |
| } |
| } |
| } |
|
|
| if (typeof clone.system === "string") { |
| clone.system = processPII(clone.system, true).text; |
| } |
|
|
| return clone; |
| } |
|
|
| |
|
|
| export { detectInjection, processPII, extractMessageContents, INJECTION_PATTERNS, PII_PATTERNS }; |
|
|