sanket3280/code-execution
0
1const SuspiciousActivity = require('../models/SuspiciousActivity');2const CodeHistory = require('../models/CodeHistory');3 4/**5 * Detect large paste operations6 */7function detectLargePaste(code, previousCode) {8 if (!previousCode) return false;9 10 const diff = code.length - previousCode.length;11 const linesDiff = code.split('\n').length - previousCode.split('\n').length;12 13 return linesDiff > 50 || diff > 1000;14}15 16/**17 * Detect rapid problem switching18 */19async function detectRapidSwitching(participantId, challengeId) {20 try {21 // Get recent code history22 const recentHistory = await CodeHistory.find({23 participantId,24 challengeId,25 timestamp: { $gte: new Date(Date.now() - 60000) } // Last minute26 }).lean();27 28 // Count unique problems29 const uniqueProblems = new Set(recentHistory.map(h => h.problemId.toString()));30 31 return uniqueProblems.size > 3;32 } catch (error) {33 console.error('Error detecting rapid switching:', error);34 return false;35 }36}37 38/**39 * Detect code similarity using simple comparison40 */41async function detectCodeSimilarity(participantId, code, challengeId, problemId) {42 try {43 // Get all participant codes for same problem44 const allCodes = await CodeHistory.find({45 challengeId,46 problemId,47 participantId: { $ne: participantId }48 }).sort({ timestamp: -1 }).limit(50).lean();49 50 // Simple similarity check51 for (const otherCode of allCodes) {52 const similarity = calculateSimilarity(code, otherCode.code);53 if (similarity > 0.8) {54 return {55 isSimilar: true,56 similarity,57 otherParticipantId: otherCode.participantId58 };59 }60 }61 62 return { isSimilar: false };63 } catch (error) {64 console.error('Error detecting code similarity:', error);65 return { isSimilar: false };66 }67}68 69/**70 * Calculate simple similarity ratio71 */72function calculateSimilarity(str1, str2) {73 // Normalize strings74 const normalize = (str) => str.replace(/\s+/g, ' ').trim().toLowerCase();75 const s1 = normalize(str1);76 const s2 = normalize(str2);77 78 if (s1 === s2) return 1.0;79 if (s1.length === 0 || s2.length === 0) return 0.0;80 81 // Simple character-based similarity82 const longer = s1.length > s2.length ? s1 : s2;83 const shorter = s1.length > s2.length ? s2 : s1;84 85 let matches = 0;86 for (let i = 0; i < shorter.length; i++) {87 if (longer[i] === shorter[i]) matches++;88 }89 90 return matches / longer.length;91}92 93/**94 * Flag suspicious activity95 */96async function flagActivity(challengeId, participantId, activityType, severity, details) {97 try {98 const flag = await SuspiciousActivity.create({99 challengeId,100 participantId,101 activityType,102 severity: severity || 'medium',103 details: details || {},104 detectedAt: new Date(),105 status: 'pending'106 });107 108 return { success: true, flagId: flag._id };109 } catch (error) {110 console.error('Error flagging activity:', error);111 return { success: false, error: error.message };112 }113}114 115/**116 * Check code for suspicious patterns117 */118async function checkCodeForSuspiciousPatterns(io, participantId, code, previousCode, challengeId, problemId) {119 try {120 const flags = [];121 122 // Check for large paste123 if (detectLargePaste(code, previousCode)) {124 const linesDiff = code.split('\n').length - (previousCode ? previousCode.split('\n').length : 0);125 await flagActivity(challengeId, participantId, 'large_paste', 'high', {126 linesPasted: linesDiff,127 charactersPasted: code.length - (previousCode ? previousCode.length : 0)128 });129 130 flags.push('large_paste');131 132 // Broadcast to spectators133 io.to(`challenge:${challengeId}:spectators`).emit('spectator:suspiciousActivity', {134 flagId: Date.now(),135 participantId,136 participantName: 'Participant',137 activityType: 'large_paste',138 severity: 'high',139 details: { linesPasted: linesDiff },140 timestamp: Date.now()141 });142 }143 144 // Check for rapid switching145 const isRapidSwitching = await detectRapidSwitching(participantId, challengeId);146 if (isRapidSwitching) {147 await flagActivity(challengeId, participantId, 'rapid_switching', 'medium', {148 switchCount: 4149 });150 151 flags.push('rapid_switching');152 153 io.to(`challenge:${challengeId}:spectators`).emit('spectator:suspiciousActivity', {154 flagId: Date.now(),155 participantId,156 activityType: 'rapid_switching',157 severity: 'medium',158 timestamp: Date.now()159 });160 }161 162 // Check for code similarity (expensive, run less frequently)163 if (Math.random() < 0.1) { // 10% of the time164 const similarity = await detectCodeSimilarity(participantId, code, challengeId, problemId);165 if (similarity.isSimilar) {166 await flagActivity(challengeId, participantId, 'code_similarity', 'high', {167 similarity: similarity.similarity,168 otherParticipantId: similarity.otherParticipantId169 });170 171 flags.push('code_similarity');172 173 io.to(`challenge:${challengeId}:spectators`).emit('spectator:suspiciousActivity', {174 flagId: Date.now(),175 participantId,176 activityType: 'code_similarity',177 severity: 'high',178 details: { similarity: similarity.similarity },179 timestamp: Date.now()180 });181 }182 }183 184 return { flags };185 } catch (error) {186 console.error('Error checking for suspicious patterns:', error);187 return { flags: [] };188 }189}190 191module.exports = {192 detectLargePaste,193 detectRapidSwitching,194 detectCodeSimilarity,195 flagActivity,196 checkCodeForSuspiciousPatterns197};198 