claude-code-templates
Version:
CLI tool to setup Claude Code configurations with framework-specific commands, automation hooks and MCP Servers for your projects
450 lines (409 loc) • 14.2 kB
JavaScript
const BaseValidator = require('../BaseValidator');
/**
* SemanticValidator - Validates component content for security threats
*
* Detects:
* - Prompt injection attacks
* - Jailbreak attempts
* - Instruction override attempts
* - Self-modification requests
* - Credential harvesting patterns
* - Malicious command execution patterns
* - Role manipulation
*/
class SemanticValidator extends BaseValidator {
constructor() {
super();
// Dangerous patterns for prompt injection and jailbreaks
this.DANGEROUS_PATTERNS = [
{
pattern: /ignore\s+(all\s+)?(previous|prior|earlier|above)\s+(instructions?|prompts?|rules?|commands?)/gi,
code: 'SEM_E001',
message: 'Jailbreak pattern detected: Attempt to ignore previous instructions',
severity: 'critical'
},
{
pattern: /(system\s+prompt|developer\s+instructions?|hidden\s+prompt|internal\s+instructions?)/gi,
code: 'SEM_E002',
message: 'Prompt injection detected: Reference to system/developer instructions',
severity: 'critical'
},
{
pattern: /you\s+are\s+now\s+(a|an)\s+/gi,
code: 'SEM_E003',
message: 'Role manipulation detected: Attempt to redefine AI role',
severity: 'high'
},
{
pattern: /execute\s+the\s+following\s+(code|command|script)/gi,
code: 'SEM_E004',
message: 'Command execution attempt detected',
severity: 'critical'
},
{
pattern: /\b(fetch|retrieve|get|extract|obtain|steal|harvest|capture|collect)\s+(the\s+)?(user['']?s?\s+)?(auth\s+)?(token|key|password|credential|secret|api[\s_-]?key)/gi,
code: 'SEM_E005',
message: 'Credential harvesting pattern detected',
severity: 'critical'
},
{
pattern: /(open|spawn|exec|run)\s+(a\s+)?(shell|terminal|bash|cmd|powershell)/gi,
code: 'SEM_E006',
message: 'Shell access attempt detected',
severity: 'critical'
},
{
pattern: /(disable|bypass|override|skip)\s+(security|safety|filter|protection|validation)/gi,
code: 'SEM_E007',
message: 'Security bypass attempt detected',
severity: 'critical'
},
{
pattern: /always\s+(obey|follow|comply\s+with|execute)\s+the\s+user/gi,
code: 'SEM_E008',
message: 'Unconditional obedience instruction detected',
severity: 'high'
},
{
pattern: /(forget|disregard|remove)\s+(everything|all\s+previous|prior\s+context)/gi,
code: 'SEM_E009',
message: 'Context manipulation attempt detected',
severity: 'high'
},
{
pattern: /modify\s+your\s+(own\s+)?(code|behavior|instructions?|rules?)/gi,
code: 'SEM_E010',
message: 'Self-modification request detected',
severity: 'high'
}
];
// Suspicious patterns (warnings, not errors)
this.SUSPICIOUS_PATTERNS = [
{
pattern: /\bpretend\s+(you\s+are|to\s+be)\b/gi,
code: 'SEM_W001',
message: 'Suspicious instruction: Role pretending detected',
severity: 'medium'
},
{
pattern: /\b(jailbreak|DAN|ChatGPT\s+Developer\s+Mode)\b/gi,
code: 'SEM_W002',
message: 'Known jailbreak terminology detected',
severity: 'medium'
},
{
pattern: /output\s+raw\s+(code|text|data)/gi,
code: 'SEM_W003',
message: 'Raw output request (potential data exfiltration)',
severity: 'low'
},
{
pattern: /(repeat|echo)\s+after\s+me/gi,
code: 'SEM_W004',
message: 'Repetition instruction (potential prompt leakage)',
severity: 'low'
}
];
// Sensitive data patterns
this.SENSITIVE_DATA_PATTERNS = [
{
pattern: /(?:password|passwd|pwd)\s*[:=]\s*[^\s]+/gi,
code: 'SEM_E011',
message: 'Hardcoded password detected',
severity: 'critical'
},
{
pattern: /(?:api[_-]?key|apikey)\s*[:=]\s*['"]?[a-zA-Z0-9]{20,}['"]?/gi,
code: 'SEM_E012',
message: 'Hardcoded API key detected',
severity: 'critical'
},
{
pattern: /(?:secret|token)\s*[:=]\s*['"]?[a-zA-Z0-9]{20,}['"]?/gi,
code: 'SEM_E013',
message: 'Hardcoded secret/token detected',
severity: 'critical'
}
];
}
/**
* Validate component semantics and content
* @param {object} component - Component data
* @param {string} component.content - Raw markdown content
* @param {string} component.path - File path
* @param {string} component.type - Component type
* @param {object} options - Validation options
* @param {boolean} options.strict - Enable strict mode (warnings become errors)
* @returns {Promise<object>} Validation results
*/
async validate(component, options = {}) {
this.reset();
const { content, path, type } = component;
const { strict = false } = options;
if (!content) {
this.addError('SEM_E001', 'Component content is empty or missing', { path });
return this.getResults();
}
// 1. Check for dangerous patterns
this.checkDangerousPatterns(content, path);
// 2. Check for suspicious patterns
this.checkSuspiciousPatterns(content, path, strict);
// 3. Check for sensitive data
this.checkSensitiveData(content, path);
// 4. Check for HTML/Script injection in markdown
this.checkHtmlInjection(content, path);
// 5. Context-specific validation based on component type
if (type === 'agent') {
this.validateAgentContent(content, path);
} else if (type === 'command') {
this.validateCommandContent(content, path);
}
return this.getResults();
}
/**
* Check for dangerous patterns
*/
checkDangerousPatterns(content, path) {
for (const { pattern, code, message, severity } of this.DANGEROUS_PATTERNS) {
const matches = content.matchAll(pattern);
const matchArray = Array.from(matches);
if (matchArray.length > 0) {
const contexts = matchArray.map(m => {
const lineInfo = this.getLineFromIndex(content, m.index);
return {
text: m[0],
index: m.index,
line: lineInfo.line,
column: lineInfo.column,
position: lineInfo.position,
lineText: lineInfo.lineText,
context: this.getContext(content, m.index, 50)
};
});
this.addError(code, message, {
path,
severity,
matches: contexts.length,
examples: contexts.slice(0, 3) // Show first 3 matches
});
}
}
}
/**
* Check for suspicious patterns
*/
checkSuspiciousPatterns(content, path, strict) {
for (const { pattern, code, message, severity } of this.SUSPICIOUS_PATTERNS) {
const matches = content.matchAll(pattern);
const matchArray = Array.from(matches);
if (matchArray.length > 0) {
const contexts = matchArray.map(m => {
const lineInfo = this.getLineFromIndex(content, m.index);
return {
text: m[0],
index: m.index,
line: lineInfo.line,
column: lineInfo.column,
position: lineInfo.position,
lineText: lineInfo.lineText,
context: this.getContext(content, m.index, 50)
};
});
if (strict) {
this.addError(code, message + ' (strict mode)', {
path,
severity,
matches: contexts.length,
examples: contexts.slice(0, 3)
});
} else {
this.addWarning(code, message, {
path,
severity,
matches: contexts.length,
examples: contexts.slice(0, 3)
});
}
}
}
}
/**
* Check for sensitive data (passwords, API keys, etc.)
*/
checkSensitiveData(content, path) {
for (const { pattern, code, message, severity } of this.SENSITIVE_DATA_PATTERNS) {
const matches = content.matchAll(pattern);
const matchArray = Array.from(matches);
if (matchArray.length > 0) {
const contexts = matchArray.map(m => {
const lineInfo = this.getLineFromIndex(content, m.index);
return {
text: m[0].replace(/[:=].*/, ':=<REDACTED>'), // Redact the value
index: m.index,
line: lineInfo.line,
column: lineInfo.column,
position: lineInfo.position,
lineText: lineInfo.lineText.replace(/[:=].*/, ':=<REDACTED>') // Redact in line text too
};
});
this.addError(code, message, {
path,
severity,
matches: contexts.length,
examples: contexts.slice(0, 3)
});
}
}
}
/**
* Check for HTML/Script injection attempts
*/
checkHtmlInjection(content, path) {
const dangerousTags = [
{ tag: '<script', code: 'SEM_E014', message: '<script> tag detected (XSS risk)' },
{ tag: '<iframe', code: 'SEM_E015', message: '<iframe> tag detected (injection risk)' },
{ tag: 'javascript:', code: 'SEM_E016', message: 'javascript: protocol detected (XSS risk)' },
{ tag: 'onclick=', code: 'SEM_E017', message: 'Inline event handler detected (XSS risk)' },
{ tag: 'onerror=', code: 'SEM_E018', message: 'onerror handler detected (XSS risk)' }
];
for (const { tag, code, message } of dangerousTags) {
const lowerContent = content.toLowerCase();
if (lowerContent.includes(tag.toLowerCase())) {
const index = lowerContent.indexOf(tag.toLowerCase());
const lineInfo = this.getLineFromIndex(content, index);
this.addError(code, message, {
path,
severity: 'critical',
line: lineInfo.line,
column: lineInfo.column,
position: lineInfo.position,
lineText: lineInfo.lineText,
context: this.getContext(content, index, 50)
});
}
}
}
/**
* Validate agent-specific content
*/
validateAgentContent(content, path) {
// Check for overly permissive instructions
const overlyPermissivePatterns = [
{
pattern: /do\s+anything\s+(the\s+)?user\s+(asks|wants|requests)/gi,
warning: 'Overly permissive instruction: "do anything user asks"'
},
{
pattern: /no\s+limitations?/gi,
warning: 'Overly permissive instruction: "no limitations"'
},
{
pattern: /unrestricted\s+access/gi,
warning: 'Overly permissive instruction: "unrestricted access"'
}
];
for (const { pattern, warning } of overlyPermissivePatterns) {
const matches = content.matchAll(pattern);
const matchArray = Array.from(matches);
if (matchArray.length > 0) {
const firstMatch = matchArray[0];
const lineInfo = this.getLineFromIndex(content, firstMatch.index);
this.addWarning('SEM_W005', warning, {
path,
line: lineInfo.line,
column: lineInfo.column,
position: lineInfo.position,
lineText: lineInfo.lineText
});
}
}
}
/**
* Validate command-specific content
*/
validateCommandContent(content, path) {
// Check for dangerous command patterns
const dangerousCommands = [
{
pattern: /rm\s+-rf\s+\//gi,
message: 'Dangerous command: rm -rf /'
},
{
pattern: /:(){ :|:& };:/gi,
message: 'Fork bomb detected'
},
{
pattern: /dd\s+if=.*of=\/dev\/(sd|hd)/gi,
message: 'Dangerous disk operation detected'
}
];
for (const { pattern, message } of dangerousCommands) {
const matches = content.matchAll(pattern);
const matchArray = Array.from(matches);
if (matchArray.length > 0) {
const firstMatch = matchArray[0];
const lineInfo = this.getLineFromIndex(content, firstMatch.index);
this.addError('SEM_E019', message, {
path,
severity: 'critical',
line: lineInfo.line,
column: lineInfo.column,
position: lineInfo.position,
lineText: lineInfo.lineText
});
}
}
}
/**
* Get context around a match
* @param {string} content - Full content
* @param {number} index - Match index
* @param {number} contextLength - Characters before/after to include
* @returns {string} Context string
*/
getContext(content, index, contextLength = 50) {
const start = Math.max(0, index - contextLength);
const end = Math.min(content.length, index + contextLength);
const context = content.substring(start, end);
return (start > 0 ? '...' : '') + context + (end < content.length ? '...' : '');
}
/**
* Generate security report
* @param {object} component - Component to analyze
* @returns {Promise<object>} Security report
*/
async generateSecurityReport(component) {
const result = await this.validate(component);
const criticalIssues = result.errors.filter(e => e.metadata.severity === 'critical');
const highIssues = result.errors.filter(e => e.metadata.severity === 'high');
const mediumIssues = result.warnings.filter(w => w.metadata.severity === 'medium');
const lowIssues = result.warnings.filter(w => w.metadata.severity === 'low');
return {
safe: result.valid && result.warningCount === 0,
riskLevel: this.calculateRiskLevel(criticalIssues.length, highIssues.length, mediumIssues.length),
summary: {
critical: criticalIssues.length,
high: highIssues.length,
medium: mediumIssues.length,
low: lowIssues.length
},
issues: {
critical: criticalIssues,
high: highIssues,
medium: mediumIssues,
low: lowIssues
},
timestamp: new Date().toISOString()
};
}
/**
* Calculate overall risk level
*/
calculateRiskLevel(critical, high, medium) {
if (critical > 0) return 'CRITICAL';
if (high > 0) return 'HIGH';
if (medium > 0) return 'MEDIUM';
return 'LOW';
}
}
module.exports = SemanticValidator;