| |
| |
| |
| |
| |
| |
| |
|
|
|
|
|
|
|
|
|
|
| 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 };
|
|
|