content-guard
Version:
🛡️ Advanced content analysis and moderation system with multi-variant optimization. Features context-aware detection, harassment prevention, and ML-powered toxicity analysis. Pre-1.0 development version.
1,146 lines (984 loc) • 41.9 kB
JavaScript
/**
* 🛡️ ContentGuard v0.3.1 - Advanced Content Analysis System (Beta)
*
* Modern content moderation and spam detection with context-aware analysis,
* harassment detection, and ML-powered toxicity classification.
*
* Pre-1.0 development version - API may change between releases.
* Use in production at your own risk.
*
* @author trevor050
* @version 0.3.1
* @license MIT
* @see https://github.com/trevor050/content-guard
*/
const PluginManager = require('./lib/core/plugin-manager')
const { LRUCache, deepMerge, fastHash, safeRegexTest } = require('./lib/utils')
const { TextPreprocessor } = require('./lib/utils/preprocessing')
const { ContextDetector } = require('./lib/core/context-detector')
const presets = require('./lib/presets')
// Lazy-loaded plugins
let ObscenityPlugin = null
let SentimentPlugin = null
let HarassmentPlugin = null
let SocialEngineeringPlugin = null
// v4.0 ML Plugins
const { EmojiSentimentPlugin } = require('./lib/plugins/emoji-sentiment-plugin')
const { ConfusablesAdvancedPlugin } = require('./lib/plugins/confusables-advanced-plugin')
const { MLToxicityPlugin } = require('./lib/plugins/ml-toxicity-plugin')
const { CrossCulturalPlugin } = require('./lib/plugins/cross-cultural-plugin')
/**
* ContentGuard v3.0 - The most advanced content analysis system
*/
class ContentGuard {
constructor(preset = 'moderate', options = {}) {
this.preset = preset
// Merge preset configuration with user options
const presetConfig = presets[preset] || presets.moderate
this.options = this.mergeDefaultOptions({
...presetConfig,
...options,
enableContextDetection: true,
enableHarassmentDetection: true,
enableSocialEngineering: true,
enableMLFeatures: true, // NEW: Enable v4.0 ML features
enableEmojiAnalysis: true, // NEW: Emoji sentiment
enableCrossCultural: true, // NEW: Cross-cultural analysis
maxProcessingTime: 10000, // 10 second timeout
})
this.plugins = {}
this.mlPlugins = {} // NEW: Separate ML plugin registry
this.stats = {
totalAnalyses: 0,
totalTime: 0,
averageTime: 0,
mlAnalyses: 0,
mlSuccessRate: 0
}
this.initializePlugins()
this.initializeMLPlugins() // NEW: Initialize ML plugins
}
initializePlugins() {
// Initialize plugin manager first
this.pluginManager = new PluginManager()
// Setup default plugins with lazy loading
this.setupDefaultPlugins()
// Context detection
this.contextDetector = new ContextDetector()
this.preprocessor = new TextPreprocessor()
}
async initializeMLPlugins() {
try {
// v4.0 ML Plugins - Silent by default
if (this.options.enableMLFeatures) {
// Emoji sentiment analysis
if (this.options.enableEmojiAnalysis) {
this.mlPlugins.emojiSentiment = new EmojiSentimentPlugin()
if (this.options.debug) console.log('✅ Emoji sentiment plugin ready')
}
// Advanced confusables (always enabled for preprocessing)
this.mlPlugins.confusablesAdvanced = new ConfusablesAdvancedPlugin()
if (this.options.debug) console.log('✅ Advanced confusables plugin ready')
// Cross-cultural analysis
if (this.options.enableCrossCultural) {
this.mlPlugins.crossCultural = new CrossCulturalPlugin()
if (this.options.debug) console.log('✅ Cross-cultural analysis plugin ready')
}
// ML toxicity detection (async initialization) - silent unless debug enabled
this.mlPlugins.mlToxicity = new MLToxicityPlugin({ silent: !this.options.debug })
await this.mlPlugins.mlToxicity.initialize(this.options.debug)
if (this.options.debug) console.log('✅ ML toxicity plugin ready')
if (this.options.debug) console.log('🚀 All v4.0 ML plugins initialized successfully')
}
} catch (error) {
if (this.options.debug) {
console.warn('⚠️ Some ML plugins failed to initialize:', error.message)
console.log('📝 Falling back to rule-based analysis only')
}
}
}
/**
* Merge user options with intelligent defaults
*/
mergeDefaultOptions(userOptions) {
const defaults = {
// Core settings - ONLY set if not already provided by user
spamThreshold: userOptions.spamThreshold ?? 5,
enableEarlyExit: userOptions.enableEarlyExit ?? true,
criticalThreshold: userOptions.criticalThreshold ?? 20,
// Performance optimization
enableCaching: userOptions.enableCaching ?? true,
cacheSize: userOptions.cacheSize ?? 1000,
// v3.0 Enhanced plugin configuration
plugins: deepMerge({
obscenity: { weight: 1.0, contextAware: true },
sentiment: { weight: 1.0, contextAware: true },
patterns: { weight: 1.0, contextAware: true },
validation: { weight: 0.5 },
harassment: { weight: 1.2, contextAware: true }, // New in v3.0
socialEngineering: { weight: 1.5, contextAware: true } // New in v3.1
}, userOptions.plugins || {}),
// v3.0 Preprocessing options
preprocessing: deepMerge({
normalizeUnicode: true,
normalizeLeetSpeak: true,
expandSlang: true,
removeExcessiveSpacing: true,
contextAware: true
}, userOptions.preprocessing || {}),
// v3.0 Context detection options
contextDetection: deepMerge({
enableDomainDetection: true,
enablePatternMatching: true,
enableVocabularyAnalysis: true,
confidenceThreshold: 0.3
}, userOptions.contextDetection || {}),
// Feature toggles
enableLazyLoading: userOptions.enableLazyLoading ?? true,
debug: userOptions.debug ?? false,
enableMetrics: userOptions.enableMetrics ?? true,
// Context awareness
contextAware: userOptions.contextAware ?? true,
// v3.0 Advanced features
enableAdversarialDetection: userOptions.enableAdversarialDetection ?? true,
enableSophisticatedHarassment: userOptions.enableSophisticatedHarassment ?? true,
enableContextualAdjustments: userOptions.enableContextualAdjustments ?? true,
// Backwards compatibility
enableLayers: userOptions.enableLayers || {},
layerWeights: userOptions.layerWeights || {}
}
// Merge with all user options taking precedence
return { ...defaults, ...userOptions }
}
/**
* Setup default plugins with lazy loading
*/
setupDefaultPlugins() {
// Register built-in plugins
this.registerBuiltinPlugins()
// Enable plugins based on configuration
this.enableConfiguredPlugins()
}
/**
* Register built-in plugins
*/
registerBuiltinPlugins() {
// Obscenity plugin (enhanced in v3.0)
this.pluginManager.register('obscenity', {
init: async (config) => {
if (!ObscenityPlugin) {
ObscenityPlugin = require('./lib/plugins/obscenity-plugin')
}
this._obscenityInstance = new ObscenityPlugin()
await this._obscenityInstance.init(config)
},
analyze: async (content, input, options) => {
return await this._obscenityInstance.analyze(content, input, options)
}
})
// Sentiment plugin (enhanced in v3.0)
this.pluginManager.register('sentiment', {
init: async (config) => {
if (!SentimentPlugin) {
SentimentPlugin = require('./lib/plugins/sentiment-plugin')
}
this._sentimentInstance = new SentimentPlugin()
await this._sentimentInstance.init(config)
},
analyze: async (content, input, options) => {
return await this._sentimentInstance.analyze(content, input, options)
}
})
// NEW v3.0: Advanced harassment detection plugin
this.pluginManager.register('harassment', {
init: async (config) => {
if (!HarassmentPlugin) {
HarassmentPlugin = require('./lib/plugins/harassment-plugin')
}
this._harassmentInstance = new HarassmentPlugin()
await this._harassmentInstance.init(config)
},
analyze: async (content, input, options) => {
return await this._harassmentInstance.analyze(content, input, options)
}
})
// NEW v3.1: Social engineering detection plugin
this.pluginManager.register('socialEngineering', {
init: async (config) => {
if (!SocialEngineeringPlugin) {
SocialEngineeringPlugin = require('./lib/plugins/social-engineering-plugin')
}
this._socialEngineeringInstance = new SocialEngineeringPlugin()
await this._socialEngineeringInstance.init(config)
},
analyze: async (content, input, options) => {
return await this._socialEngineeringInstance.analyze(content, input, options)
}
})
// Enhanced inline plugins
this.registerInlinePlugins()
}
/**
* Register lightweight inline plugins (enhanced for v3.0)
*/
registerInlinePlugins() {
const {
HARASSMENT_PATTERNS, SCAM_PATTERNS, EVASION_PATTERNS,
GAMING_TROLL_KEYWORDS, TROLL_NAMES, HARASSMENT_KEYWORDS,
PROBLEMATIC_MODERN_TERMS
} = require('./lib/constants/context-data')
// Enhanced patterns plugin with context awareness
this.pluginManager.register('patterns', {
init: (config) => { this._patternsConfig = config },
analyze: (content, input, options) => {
let score = 0
const flags = []
// Get context from the content
const context = content.context || {}
// Enhanced game development context detection
const isGameDev = context.isTechnical || (
content.allTextLower.includes('game') &&
(content.allTextLower.includes('development') ||
content.allTextLower.includes('developer') ||
content.allTextLower.includes('balance') ||
content.allTextLower.includes('character') ||
content.emailDomain.includes('game'))
)
// v3.0: Context-aware harassment patterns
HARASSMENT_PATTERNS.forEach(pattern => {
if (safeRegexTest(pattern, content.processed || content.allText)) {
let patternScore = 6
// Reduce score in professional contexts where it might be legitimate
if (context.isProfessional && this.isLegitimateInContext(pattern, content)) {
patternScore = Math.floor(patternScore * 0.3)
flags.push(`Professional context harassment pattern (reduced): "${pattern}"`)
} else {
flags.push('Harassment pattern detected')
}
score += patternScore
}
})
// Enhanced scam patterns with adversarial resistance
SCAM_PATTERNS.forEach(pattern => {
if (safeRegexTest(pattern, content.processed || content.allText)) {
score += 8
flags.push('Scam pattern detected')
}
})
// v3.0: Sophisticated doxxing/threat patterns
const doxxingPatterns = [
/would be unfortunate/i,
/personal information.*public/i,
/find.*information.*online/i,
/interesting.*about you/i,
/know where you/i,
/found.*information.*about/i,
/accidents happen.*to people who/i,
/hope nothing bad happens/i
]
doxxingPatterns.forEach(pattern => {
if (safeRegexTest(pattern, content.processed || content.allText)) {
score += 12 // Higher score for sophisticated threats
flags.push('Sophisticated threat pattern detected')
}
})
// Enhanced evasion patterns with context
EVASION_PATTERNS.forEach(pattern => {
if (safeRegexTest(pattern, content.processed || content.allText)) {
const currentPatternSource = pattern.source; // Get the regex source string
// Ensure localContext is defined correctly once before these checks
const localContext = content.context || {};
// Skip in specific contexts where these might be legitimate
if (isGameDev && content.allTextLower.includes('nerf')) {
if (currentPatternSource.includes('nerf')) {
return;
}
}
const killPatternSource = "[k][i1!][l1!][l1!]"; // Source of the /k[i1!][l1!][l1!]/i pattern
// Check for technical contexts where "kill" (but not "kys") might be legitimate
if (localContext.isTechnical || localContext.domains?.includes('DEVOPS')) {
if (currentPatternSource === killPatternSource || pattern.source.includes('kill')) {
const techPhrases = ['kill process', 'kill task', 'kill command', 'kill server', 'kill container', 'process', 'server', 'system', 'container', 'docker', 'instance', 'job', 'session', 'service', 'node', 'pod', 'cluster'];
if (techPhrases.some(phrase => content.allTextLower.includes(phrase))) {
flags.push(`[INFO] Evasion pattern for 'kill' skipped in technical context: "${pattern}"`);
return;
}
}
}
// Check for business/financial context for "kill"
if (localContext.isBusiness || localContext.domains?.includes('FINANCE')) {
if (currentPatternSource === killPatternSource || pattern.source.includes('kill')) {
const businessPhrases = ['killed the deal', 'deal killer', 'killing the market', 'kill the competition', 'killed our profits'];
if (businessPhrases.some(phrase => content.allTextLower.includes(phrase))) {
flags.push(`[INFO] Evasion pattern for 'kill' skipped in business context: "${pattern}"`);
return;
}
}
}
// Check for hyperbolic/figurative uses of "kill"
if (currentPatternSource === killPatternSource) { // Specifically for the main /k[i1!][l1!][l1!]/i pattern
const hyperbolicKillPhrases = ['killed my soul', 'killed my vibe', 'killing me softly', 'killing me rn', 'literally killing me', 'killed it', 'killing it'];
const hasHyperbolicKill = hyperbolicKillPhrases.some(phrase => content.allTextLower.includes(phrase));
const hasLmaoLol = content.allTextLower.includes('lmao') || content.allTextLower.includes('lol');
if (hasHyperbolicKill || (content.allTextLower.includes('killing me') && hasLmaoLol)) {
if (!content.allTextLower.match(/killing you\\b|kill you\\b/i)) {
flags.push(`[INFO] Evasion pattern for 'kill' skipped due to hyperbolic use: "${pattern}"`);
return;
}
}
}
// If no context-specific skip occurred, apply standard penalty
score += 7; // Original Evasion patterns penalty
flags.push('[PATTERNS] Adversarial evasion attempt detected');
return; // Stop after first evasion match
}
});
// Enhanced gaming troll detection with context
const trollCount = GAMING_TROLL_KEYWORDS.filter(keyword =>
content.allTextLower.includes(keyword)
).length
if (trollCount >= 5) {
score += 8
flags.push(`Heavy gaming troll language (${trollCount} terms)`)
} else if (trollCount >= 3) {
score += 5
flags.push(`Multiple gaming troll terms (${trollCount})`)
}
// Enhanced toxic gaming phrases
const toxicGamingPhrases = [
'uninstall', 'go touch grass', 'basement dweller', 'ratio + l', 'cope harder',
'you are trash at', 'git gud scrub', 'skill issue', 'get rekt', 'mad cuz bad'
]
const toxicCount = toxicGamingPhrases.filter(phrase =>
content.allTextLower.includes(phrase)
).length
if (toxicCount >= 2) {
score += 10
flags.push(`Toxic gaming harassment (${toxicCount} phrases)`)
}
// Troll names detection
if (TROLL_NAMES.some(name => content.name.toLowerCase().includes(name))) {
score += 6
flags.push('Troll name detected')
}
// Modern toxic communication patterns
const modernToxicCount = PROBLEMATIC_MODERN_TERMS.filter(term =>
content.allTextLower.includes(term.toLowerCase())
).length
if (modernToxicCount >= 3) {
score += 12
flags.push(`Heavy modern toxic language (${modernToxicCount} terms)`)
} else if (modernToxicCount >= 2) {
score += 8
flags.push(`Multiple modern toxic terms (${modernToxicCount})`)
} else if (modernToxicCount === 1) {
score += 4
flags.push('Modern toxic language detected')
}
// Direct harassment with severity scaling
HARASSMENT_KEYWORDS.forEach(keyword => {
if (content.allTextLower.includes(keyword)) {
const localContext = content.context || {}; // Renamed to avoid conflict with outer scope
// Check for technical contexts where "kill" or "die" might be legitimate
if ((keyword.includes('kill') || keyword.includes('die')) &&
(localContext.isTechnical || localContext.domains?.includes('DEVOPS'))) {
const techPhrases = ['kill process', 'kill task', 'kill command', 'process', 'server', 'system'];
const hasTechPhrase = techPhrases.some(phrase => content.allTextLower.includes(phrase));
if (hasTechPhrase) {
flags.push(`[INFO] Harassment keyword '${keyword}' skipped in technical context.`);
return; // Skip harassment detection for legitimate technical content
}
}
let harassmentScore = 10;
let isHyperbolicKys = false;
// Enhanced contextual handling for "kys" and "kill yourself"
if (keyword === 'kys' || keyword === 'kill yourself') {
// Get the original text before preprocessing to check for hyperbolic context
const originalText = content.originalText || content.allText;
const lowerOriginalText = originalText.toLowerCase();
// Check if this is a technical/gaming context with hyperbolic indicators
const isTechnicalBug = (localContext.isTechnical || localContext.domains?.includes('DEVOPS') || lowerOriginalText.includes('bug'));
const isGamingContext = localContext.domains?.includes('GAMING');
const hasHyperboleIndicators = ['lmao', 'lol', 'rofl', 'literally', 'fr', 'deadass', 'bruh', 'smh'].some(ind => lowerOriginalText.includes(ind));
const hasKillingMePhrase = ['killing me', 'killed me'].some(phrase => lowerOriginalText.includes(phrase));
// If it's the original "kys" in a hyperbolic technical/gaming context
if ((isTechnicalBug || isGamingContext) && hasHyperboleIndicators && hasKillingMePhrase) {
// Check if original contained "kys" (not just expanded "kill yourself")
if (lowerOriginalText.includes('kys')) {
harassmentScore = 1; // Drastically reduce score for hyperbolic "kys"
isHyperbolicKys = true;
flags.push(`[INFO] Reduced score for hyperbolic 'kys' in technical/gaming context.`);
}
}
}
// Scale based on severity (unless it's a reduced hyperbolic "kys")
if (!isHyperbolicKys && (keyword.includes('kill') || keyword.includes('die'))) {
harassmentScore = 15;
}
score += harassmentScore;
flags.push(`Direct harassment: "${keyword}"`);
}
});
return {
score: Math.round(score * (this._patternsConfig?.weight || 1)),
flags,
contextAdjustments: content.contextAdjustments || []
}
}
})
// Enhanced validation plugin
this.pluginManager.register('validation', {
init: (config) => { this._validationConfig = config },
analyze: (content, input, options) => {
const { isValidEmail } = require('./lib/utils')
let score = 0
const flags = []
// Email validation with enhanced detection
if (input.email && !isValidEmail(input.email)) {
score += 3
flags.push('Invalid email format')
}
// Enhanced suspicious email patterns
if (input.email) {
const suspiciousPatterns = [
/\d{6,}@/, // Long numeric sequences
/temp.*mail/i,
/disposable/i,
/10.*minute/i, // 10 minute mail
/guerrilla.*mail/i,
/mailinator/i,
/trash.*mail/i
]
suspiciousPatterns.forEach(pattern => {
if (pattern.test(input.email)) {
score += 3
flags.push('Suspicious email pattern')
}
})
}
return { score: Math.round(score * (this._validationConfig?.weight || 1)), flags }
}
})
}
/**
* Check if a potentially harmful pattern is legitimate in professional context
*/
isLegitimateInContext(pattern, content) {
const context = content.context || {}
// Technical contexts
if (context.isTechnical || context.domains?.includes('DEVOPS')) {
const techPhrases = ['kill process', 'kill task', 'kill command', 'critical system', 'urgent fix']
return techPhrases.some(phrase => content.allTextLower.includes(phrase))
}
// Medical contexts
if (context.isMedical || context.domains?.includes('CLINICAL')) {
const medicalPhrases = ['critical care', 'urgent surgery', 'critical condition']
return medicalPhrases.some(phrase => content.allTextLower.includes(phrase))
}
// Business contexts
if (context.isBusiness || context.domains?.includes('FINANCE')) {
const businessPhrases = ['critical analysis', 'urgent matter', 'assess performance']
return businessPhrases.some(phrase => content.allTextLower.includes(phrase))
}
return false
}
/**
* Enable plugins based on configuration
*/
enableConfiguredPlugins() {
const { plugins, enableLayers, layerWeights } = this.options
// Handle backwards compatibility
const pluginConfigs = {}
// New plugin configuration
if (plugins) {
Object.assign(pluginConfigs, plugins)
}
// Backwards compatibility with old layer system
if (enableLayers) {
Object.entries(enableLayers).forEach(([layer, enabled]) => {
if (enabled && !pluginConfigs[layer]) {
pluginConfigs[layer] = { weight: layerWeights?.[layer] || 1.0 }
}
})
}
// Enable default plugins if no specific configuration
if (Object.keys(pluginConfigs).length === 0) {
pluginConfigs.obscenity = { weight: 1.0, contextAware: true }
pluginConfigs.sentiment = { weight: 1.0, contextAware: true }
pluginConfigs.patterns = { weight: 1.0, contextAware: true }
pluginConfigs.validation = { weight: 0.5 }
pluginConfigs.harassment = { weight: 1.2, contextAware: true } // v3.0 default
}
// Enable configured plugins
Object.entries(pluginConfigs).forEach(([name, config]) => {
try {
this.pluginManager.enable(name, config)
} catch (error) {
console.warn(`Failed to enable plugin ${name}:`, error.message)
}
})
}
/**
* Initialize metrics collection
*/
initializeMetrics() {
if (!this.options.enableMetrics) return null
return {
totalAnalyses: 0,
spamDetected: 0,
cleanContent: 0,
averageProcessingTime: 0,
cacheHits: 0,
cacheMisses: 0,
pluginPerformance: {},
// v3.0 new metrics
adversarialDetected: 0,
contextAdjustments: 0,
preprocessingModifications: 0,
harassmentDetected: 0
}
}
/**
* v3.0 ENHANCED Main analysis function with preprocessing and context detection
*/
async analyze(input) {
const startTime = performance.now()
try {
// Add input validation to prevent null/undefined errors
if (input === null || input === undefined) {
return this.createResult(0, 'CLEAN', performance.now() - startTime, {
flags: ['[ERROR] Input cannot be null or undefined'],
recommendation: 'Invalid input provided'
}, { error: 'Invalid input: null or undefined' });
}
// Handle both string input and object input
let analysisInput
if (typeof input === 'string') {
analysisInput = {
name: '',
email: '',
subject: '',
message: input
}
} else if (typeof input === 'object' && input !== null) {
analysisInput = {
name: input.name || '',
email: input.email || '',
subject: input.subject || '',
message: input.message || ''
}
} else {
// Convert other types to string
analysisInput = {
name: '',
email: '',
subject: '',
message: String(input)
}
}
// Create combined text for analysis
const allText = [analysisInput.name, analysisInput.email, analysisInput.subject, analysisInput.message]
.filter(Boolean)
.join(' ')
if (!allText || allText.trim().length === 0) {
return this.createResult(0, 'CLEAN', performance.now() - startTime, {}, { error: 'Invalid input text' })
}
// Enhanced preprocessing with l33tspeak detection
const preprocessingResult = this.preprocessor.preprocess(allText, this.options.preprocessing)
const processedText = preprocessingResult.text || allText
const preprocessingMetadata = preprocessingResult.metadata || {}
// Create enhanced content object
const content = {
allText: processedText,
allTextLower: processedText.toLowerCase(),
originalText: allText,
name: analysisInput.name || '',
email: analysisInput.email || '',
subject: analysisInput.subject || '',
message: analysisInput.message || '',
originalInput: analysisInput,
preprocessing: preprocessingMetadata
}
let totalScore = 0
let allFlags = []
let highestIndividualScore = 0
// NEW: Enhanced L33tspeak Analysis - Test all variations
if (preprocessingMetadata.hasLeetSpeak && preprocessingMetadata.leetSpeakVariations) {
const leetAnalysisResult = await this.analyzeAllLeetSpeakVariations(
preprocessingMetadata.leetSpeakVariations,
content
)
if (leetAnalysisResult.maxScore > highestIndividualScore) {
highestIndividualScore = leetAnalysisResult.maxScore
}
totalScore += leetAnalysisResult.bonusScore
allFlags.push(...leetAnalysisResult.flags)
}
// Context detection
const context = { isProfessional: false, isPersonal: false, isNeutral: true }
if (this.options.enableContextDetection && this.contextDetector) {
try {
const contextResult = await this.contextDetector.analyzeContext(content)
Object.assign(context, contextResult)
} catch (error) {
console.error('Context detection error:', error)
}
}
// Run plugin analysis
const pluginResults = await this.pluginManager.analyze(content, context)
Object.entries(pluginResults).forEach(([pluginName, pluginResult]) => {
if (pluginName.startsWith('_')) return
const weight = this.options.plugins[pluginName]?.weight || 1
const pluginScore = (pluginResult.score || 0) * weight
totalScore += pluginScore
allFlags.push(...(pluginResult.flags || []))
if (pluginScore > highestIndividualScore) {
highestIndividualScore = pluginScore
}
})
// Use the higher of total accumulated score or highest individual score
const finalScore = Math.max(totalScore, highestIndividualScore)
const processingTime = performance.now() - startTime
return this.createResult(
finalScore,
this.getRiskLevel(finalScore),
processingTime,
{
flags: allFlags,
recommendation: this.getRecommendation(finalScore, this.getRiskLevel(finalScore)),
confidence: this.calculateConfidence(finalScore, this.options.spamThreshold, { pluginResults })
},
{
version: this.version,
pluginsUsed: Object.keys(pluginResults).filter(name => !name.startsWith('_')),
processedText: processedText,
preprocessing: preprocessingMetadata,
leetSpeakAnalysis: preprocessingMetadata.hasLeetSpeak ? {
detected: true,
variationsCount: preprocessingMetadata.leetSpeakVariations?.length || 0,
highestVariationScore: highestIndividualScore
} : { detected: false }
}
)
} catch (error) {
console.error('❌ Analysis error:', error)
const processingTime = performance.now() - startTime
return this.createResult(0, 'CLEAN', processingTime, {}, {
error: true,
message: error.message
})
}
}
/**
* Analyze all l33tspeak variations to find the most toxic interpretation
*/
async analyzeAllLeetSpeakVariations(variations, originalContent) {
let maxScore = 0
let totalBonus = 0
const flags = []
const variationResults = []
if (this.options.debug) {
console.log(`🔍 Analyzing ${variations.length} l33tspeak variations...`)
}
for (let i = 0; i < variations.length; i++) {
const variation = variations[i]
if (variation === originalContent.originalText) continue // Skip original
// Create content object for this variation
const variationContent = {
...originalContent,
allText: variation,
allTextLower: variation.toLowerCase(),
isLeetSpeakVariation: true,
variationIndex: i
}
try {
// Analyze this specific variation
const variationScore = await this.analyzeSingleLeetSpeakVariation(variationContent)
variationResults.push({
text: variation,
score: variationScore.score,
flags: variationScore.flags,
index: i
})
if (variationScore.score > maxScore) {
maxScore = variationScore.score
}
if (this.options.debug && variationScore.score > 0) {
console.log(`📝 Variation ${i}: "${variation}" -> Score: ${variationScore.score}`)
}
} catch (error) {
console.error(`Error analyzing l33tspeak variation ${i}:`, error)
}
}
// Calculate bonus based on l33tspeak detection
if (maxScore > 5) {
// Significant l33tspeak evasion detected
totalBonus = Math.min(maxScore * 0.3, 10) // Up to 10 bonus points
flags.push(`[LEETSPEAK-EVASION] Detected toxic content via l33tspeak decoding (+${totalBonus.toFixed(1)})`)
if (this.options.debug) {
console.log(`🚨 L33tspeak evasion detected! Max variation score: ${maxScore}, Bonus: ${totalBonus}`)
}
} else if (maxScore > 0) {
// Minor l33tspeak usage
totalBonus = 1
flags.push(`[LEETSPEAK-MINOR] Minor l33tspeak usage detected (+1.0)`)
}
// Add the best matching variation to flags
const bestVariation = variationResults.reduce((best, current) =>
current.score > best.score ? current : best,
{ score: 0, text: '', flags: [] }
)
if (bestVariation.score > 0) {
flags.push(`[LEETSPEAK-DECODED] Best match: "${bestVariation.text}" (Score: ${bestVariation.score})`)
}
return {
maxScore,
bonusScore: totalBonus,
flags,
variationResults,
bestVariation
}
}
/**
* Analyze a single l33tspeak variation using core detection patterns
*/
async analyzeSingleLeetSpeakVariation(variationContent) {
let score = 0
const flags = []
// Use the patterns from constants for direct harassment detection
const { HARASSMENT_KEYWORDS } = require('./lib/constants/context-data')
HARASSMENT_KEYWORDS.forEach(keyword => {
if (variationContent.allTextLower.includes(keyword)) {
let harassmentScore = 8 // Base score for l33tspeak harassment
// Scale based on severity
if (keyword.includes('kill') || keyword.includes('die')) {
harassmentScore = 12
}
score += harassmentScore
flags.push(`L33T-HARASSMENT: "${keyword}" found in decoded variation`)
}
})
// Check for other toxic patterns that might be revealed
const toxicPatterns = [
{ pattern: /fuck\s+you/gi, score: 10, name: 'Direct profanity' },
{ pattern: /you\s+suck/gi, score: 6, name: 'Mild harassment' },
{ pattern: /go\s+to\s+hell/gi, score: 8, name: 'Death wish' },
{ pattern: /piece\s+of\s+shit/gi, score: 12, name: 'Severe insult' },
{ pattern: /worthless\s+trash/gi, score: 10, name: 'Dehumanizing language' },
{ pattern: /stupid\s+bitch/gi, score: 12, name: 'Gendered harassment' }
]
toxicPatterns.forEach(({ pattern, score: patternScore, name }) => {
if (pattern.test(variationContent.allText)) {
score += patternScore
flags.push(`L33T-PATTERN: ${name} detected`)
}
})
return { score, flags }
}
// Analytics and insights
getAnalyticsReport() {
return {
version: '0.3.1',
totalAnalyses: this.stats.totalAnalyses,
performance: {
averageTime: `${this.stats.averageTime.toFixed(2)}ms`,
totalTime: `${this.stats.totalTime}ms`,
throughput: `${(this.stats.totalAnalyses / (this.stats.totalTime / 1000)).toFixed(2)} analyses/sec`
},
mlMetrics: {
mlAnalyses: this.stats.mlAnalyses,
mlSuccessRate: `${(this.stats.mlSuccessRate * 100).toFixed(1)}%`,
mlCoverage: `${((this.stats.mlAnalyses / this.stats.totalAnalyses) * 100).toFixed(1)}%`
},
features: {
enabledPlugins: Object.keys(this.plugins).length,
enabledMLPlugins: Object.keys(this.mlPlugins).length,
preset: this.preset,
mlFeatures: this.options.enableMLFeatures,
emojiAnalysis: this.options.enableEmojiAnalysis,
crossCultural: this.options.enableCrossCultural
}
}
}
// Test ML features
async testMLFeatures(text) {
const results = {}
if (this.mlPlugins.emojiSentiment) {
results.emoji = this.mlPlugins.emojiSentiment.getEmojiInsights(text)
}
if (this.mlPlugins.crossCultural) {
results.cultural = this.mlPlugins.crossCultural.getCulturalInsights(text)
}
if (this.mlPlugins.mlToxicity) {
results.sentiment = await this.mlPlugins.mlToxicity.testSentiment(text)
}
if (this.mlPlugins.confusablesAdvanced) {
results.unicode = this.mlPlugins.confusablesAdvanced.getUnicodeAnalysis(text)
}
return results
}
// ===== Result helper methods =====
getRiskLevel(score) {
if (score >= 15) return 'CRITICAL'
if (score >= 10) return 'HIGH'
if (score >= 5) return 'MEDIUM'
if (score >= 2) return 'LOW'
return 'CLEAN'
}
getRecommendation(score, riskLevel) {
switch (riskLevel) {
case 'CRITICAL':
return 'Block immediately - High confidence spam/harassment detected'
case 'HIGH':
return 'Block - Likely spam or inappropriate content'
case 'MEDIUM':
return 'Review - Potentially problematic content detected'
case 'LOW':
return 'Monitor - Slightly concerning patterns detected'
default:
return 'Allow - Clean content detected'
}
}
calculateConfidence(score, threshold, metadata = {}) {
let confidence = 0.5
if (score >= threshold) {
const overage = score - threshold
confidence = Math.min(0.95, 0.6 + overage * 0.1)
} else {
const underage = threshold - score
confidence = Math.min(0.95, 0.6 + underage * 0.05)
}
if (metadata.mlAnalysis && metadata.mlAnalysis.confidence) {
confidence = Math.min(0.98, confidence + metadata.mlAnalysis.confidence * 0.1)
}
if (metadata.performance && metadata.performance.pluginsUsed?.length > 4) {
confidence = Math.min(0.99, confidence + 0.05)
}
return Math.round(confidence * 100) / 100
}
createResult(score, riskLevel, processingTime, additionalData = {}, metadata = {}) {
const isSpam = Object.prototype.hasOwnProperty.call(additionalData, 'isSpam')
? additionalData.isSpam
: score >= this.options.spamThreshold
return {
score,
isSpam,
riskLevel,
processingTime: Math.round(processingTime * 1000) / 1000,
recommendation: additionalData.recommendation || this.getRecommendation(score, riskLevel),
confidence: additionalData.confidence || this.calculateConfidence(score, this.options.spamThreshold, metadata),
flags: additionalData.flags || [],
variant: 'core',
details: additionalData.details || {},
metadata: {
...metadata,
...(additionalData.metadata || {}),
timestamp: new Date().toISOString(),
},
}
}
// === Convenience Methods (Backwards Compatibility) ===
/**
* Quick spam check
*/
async isSpam(text, options = {}) {
const result = await this.analyze({ message: text }, options)
return result.isSpam
}
/**
* Get spam score only
*/
async getScore(text, options = {}) {
const result = await this.analyze({ message: text }, options)
return result.score
}
/**
* Add custom plugin
*/
addPlugin(name, plugin) {
this.pluginManager.register(name, plugin)
return this
}
/**
* Enable plugin
*/
enablePlugin(name, config = {}) {
this.pluginManager.enable(name, config)
return this
}
/**
* Disable plugin
*/
disablePlugin(name) {
this.pluginManager.disable(name)
return this
}
/**
* v3.0 Enhanced metrics
*/
getMetrics() {
if (!this.metrics) return { error: 'Metrics disabled' }
return {
...this.metrics,
spamRate: this.metrics.totalAnalyses > 0 ?
`${(this.metrics.spamDetected / this.metrics.totalAnalyses * 100).toFixed(1)}%` : '0%',
cacheEfficiency: (this.metrics.cacheHits + this.metrics.cacheMisses) > 0 ?
`${(this.metrics.cacheHits / (this.metrics.cacheHits + this.metrics.cacheMisses) * 100).toFixed(1)}%` : '0%',
// v3.0: Enhanced metrics
adversarialRate: this.metrics.totalAnalyses > 0 ?
`${(this.metrics.adversarialDetected / this.metrics.totalAnalyses * 100).toFixed(1)}%` : '0%',
contextAdjustmentRate: this.metrics.totalAnalyses > 0 ?
`${(this.metrics.contextAdjustments / this.metrics.totalAnalyses * 100).toFixed(1)}%` : '0%',
preprocessingRate: this.metrics.totalAnalyses > 0 ?
`${(this.metrics.preprocessingModifications / this.metrics.totalAnalyses * 100).toFixed(1)}%` : '0%',
harassmentDetectionRate: this.metrics.totalAnalyses > 0 ?
`${(this.metrics.harassmentDetected / this.metrics.totalAnalyses * 100).toFixed(1)}%` : '0%'
}
}
/**
* Clear cache and metrics
*/
reset() {
if (this.cache) this.cache.clear()
if (this.metrics) {
Object.keys(this.metrics).forEach(key => {
this.metrics[key] = typeof this.metrics[key] === 'number' ? 0 : {}
})
}
}
}
// Export the class and utilities
module.exports = {
ContentGuard,
// v4.5 Variants - New high-performance models
ContentGuardV4Fast: require('./lib/variants/v4-fast').ContentGuardV4Fast,
ContentGuardV4Balanced: require('./lib/variants/v4-balanced').ContentGuardV4Balanced,
ContentGuardV4Large: require('./lib/variants/v4-large'),
ContentGuardV4Turbo: require('./lib/variants/v4-turbo').ContentGuardV4Turbo,
// Backwards compatibility
UltimateAntiTroll: ContentGuard,
// Convenience factory
createFilter: (options = {}) => new ContentGuard(options),
// v4.5 Variant factory
createGuard: (variant = 'balanced', options = {}) => {
switch(variant.toLowerCase()) {
case 'fast': return new (require('./lib/variants/v4-fast').ContentGuardV4Fast)(options)
case 'balanced': return new (require('./lib/variants/v4-balanced').ContentGuardV4Balanced)(options)
case 'large': return new (require('./lib/variants/v4-large'))(options)
case 'turbo': return new (require('./lib/variants/v4-turbo').ContentGuardV4Turbo)(options)
default: return new (require('./lib/variants/v4-balanced').ContentGuardV4Balanced)(options)
}
},
// Plugin system exports
PluginManager: require('./lib/core/plugin-manager'),
// v3.0: New system exports
TextPreprocessor: require('./lib/utils/preprocessing').TextPreprocessor,
ContextDetector: require('./lib/core/context-detector').ContextDetector,
HarassmentPlugin: require('./lib/plugins/harassment-plugin'),
// Utility exports
utils: require('./lib/utils'),
// Constants
constants: require('./lib/constants/context-data'),
// Enhanced presets from separate module
presets: require('./lib/presets')
}