Login
Sign Up
Invalid email or password.

⚠️ Educational purposes only. CyberQuest teaches defensive security awareness. No actual hacking tools or exploits are used.

Dashboard
⚑ 0 XP
πŸ”₯ 0 day streak
Level 1
. What happens?',opts:['Nothing β€” browsers block scripts in comments','The script displays as text only','The script executes in every visitor\'s browser who views that comment','Only affects the attacker\'s browser'],correct:2,exp:'This is Stored XSS. The script is saved in the database and executes in every victim\'s browser. Attackers use document.cookie to steal session tokens.'}, {q:'Which defense is most effective against XSS?',opts:['Using HTTPS','Requiring user login to post','Encoding/escaping output and implementing Content Security Policy','Limiting comment length to 100 characters'],correct:2,exp:'Output encoding converts < to < treating injected code as text. CSP adds a policy restricting which scripts can execute.'}, {q:'What can an XSS attack achieve?',opts:['Only display annoying pop-ups','Steal credentials, hijack sessions, redirect users, and deface pages','Only attack the database','Only work on Internet Explorer'],correct:1,exp:'XSS is powerful. With document.cookie access, attackers fully hijack sessions. They can also log keystrokes, redirect to phishing pages, and more.'} ] }, { id:'wv-2', title:'SQL Injection Awareness', xp:40, content:{ icon:'πŸ’Ύ', intro:'SQL Injection inserts malicious SQL code into a query, potentially exposing, modifying, or deleting database contents.', points:[ {icon:'πŸ—„οΈ',title:'How SQL injection works',text:'If a login builds: SELECT * FROM users WHERE name=\'[input]\' β€” entering admin\'-- makes: WHERE name=\'admin\'-- bypassing the password check.'}, {icon:'πŸ’₯',title:'Impact',text:'Successful SQLi can extract entire databases, modify or delete data, or even execute OS commands in some cases.'}, {icon:'πŸ›‘οΈ',title:'Parameterized queries',text:'The primary defense. Prepared statements separate SQL code from data β€” the database treats user input as data, never as commands.'}, {icon:'πŸ”',title:'Testing only with authorization',text:'Security professionals test for SQLi only on systems they own or have explicit written permission to test. Unauthorized testing is illegal.'} ] }, quiz:[ {q:'A login query is: WHERE user=\'[input]\' AND pass=\'[input]\'. What does entering \' OR \'1\'=\'1 do?',opts:['Causes a syntax error','Makes the query return all users, potentially bypassing authentication','Selects only users named "1"','Has no effect β€” password still blocks access'],correct:1,exp:'The resulting query WHERE user=\'\' OR \'1\'=\'1\' is always true, returning all users. The first user returned is often the admin account.'}, {q:'Which is the BEST defense against SQL injection?',opts:['Client-side JavaScript validation','Filtering out apostrophes from input','Parameterized queries (prepared statements)','Using HTTPS'],correct:2,exp:'Parameterized queries are the gold standard. They separate SQL logic from data β€” user input is always literal data, never executable SQL.'}, {q:'Can you legally test a website for SQL injection without permission?',opts:['Yes, if you report vulnerabilities responsibly','Yes, if the site has no bug bounty program','No β€” unauthorized testing is illegal in most jurisdictions','Yes, as long as you don\'t steal data'],correct:2,exp:'Unauthorized penetration testing is illegal (Computer Fraud and Abuse Act in US, Computer Misuse Act in UK, etc.). Always get written authorization first.'} ] } ], simulation:{id:'wv-sim',title:'Spot the Vulnerability',type:'vuln-spotter',xp:70} }, { id:'networking', title:'Network Basics', icon:'🌐', color:'#ffcc00', xpReward:170, locked:true, requiredModule:'phishing', description:'Understand how networks and the internet work at a fundamental level', lessons:[ { id:'net-1', title:'IP, DNS, and Routing', xp:35, content:{ icon:'πŸ—ΊοΈ', intro:'Every device on the internet has an address. DNS is the phone book that translates domain names to those addresses.', points:[ {icon:'πŸ“',title:'IP Addresses',text:'IPv4 addresses (like 192.168.1.1) are 32-bit numbers. Your public IP identifies you on the internet; private IPs are used within local networks.'}, {icon:'πŸ“–',title:'DNS (Domain Name System)',text:'DNS translates human-readable names (google.com) to IP addresses (142.250.80.46). DNS servers answer "What IP is this domain?" for every visit.'}, {icon:'πŸ”€',title:'Routing',text:'Packets travel through multiple routers to reach their destination. Each router reads the destination IP and forwards the packet toward it.'}, {icon:'⚠️',title:'DNS Spoofing',text:'Attackers can poison DNS caches to redirect users from legitimate sites to malicious ones. DNSSEC and DoH help prevent this.'} ] }, quiz:[ {q:'What does DNS do?',opts:['Encrypts internet traffic','Translates domain names (google.com) to IP addresses','Assigns IP addresses to devices','Blocks malicious websites'],correct:1,exp:'DNS (Domain Name System) is like the internet\'s phone book β€” it translates human-readable domain names into numerical IP addresses computers use to locate servers.'}, {q:'What is a "private" IP address?',opts:['An IP address hidden from everyone','An IP address only visible on the internet','An IP address used within a local network (192.168.x.x)','An IP address used only by government'],correct:2,exp:'Private IP ranges (192.168.x.x, 10.x.x.x) are used within local networks. They\'re not directly accessible from the internet β€” your router translates them using NAT.'}, {q:'What is DNS cache poisoning?',opts:['Corrupting DNS server\'s cache to redirect users to malicious sites','Clearing your browser\'s DNS cache','Blocking specific DNS queries','Encrypting DNS traffic'],correct:0,exp:'DNS cache poisoning corrupts a DNS server\'s cached entries, causing it to return wrong IP addresses. Users trying to visit legitimate sites get sent to attacker-controlled servers.'} ] }, { id:'net-2', title:'HTTP vs HTTPS & TLS', xp:35, content:{ icon:'πŸ”', intro:'HTTPS protects your data in transit. Understanding the difference helps you recognize when your communications are truly secure.', points:[ {icon:'πŸ“¦',title:'HTTP (plain text)',text:'HTTP transmits data as plain text. Anyone on the network (ISP, Wi-Fi operator, attacker) can read every request. Never enter passwords over HTTP.'}, {icon:'πŸ”’',title:'HTTPS (encrypted)',text:'HTTPS wraps HTTP in TLS encryption. Data is encrypted before transmission and only decrypted by the intended recipient.'}, {icon:'🀝',title:'TLS Handshake',text:'When connecting, client and server verify identity via certificate, agree on encryption method, and exchange keys β€” all in milliseconds.'}, {icon:'πŸ“œ',title:'SSL Certificates',text:'Certificates are issued by Certificate Authorities (CAs) and prove a site is who it claims to be.'} ] }, quiz:[ {q:'You\'re at a coffee shop on public Wi-Fi, logging in via HTTP. What can the cafΓ©\'s operator see?',opts:['Nothing β€” Wi-Fi protects the data','Only that you visited the site','Everything β€” your username, password, and all submitted data','Only encrypted packets they can\'t read'],correct:2,exp:'HTTP is plain text. On public Wi-Fi, anyone with network access can read all HTTP traffic, including form submissions, using tools like Wireshark.'}, {q:'A site has a padlock in the browser. What does this guarantee?',opts:['The site is safe and legitimate','The site has no malware','Your connection is encrypted, but not necessarily that the site is trustworthy','Your payment info is secure with this company'],correct:2,exp:'HTTPS means your connection is encrypted β€” but phishing sites can also get certificates for fake domains. "Secure connection" does not equal "safe website".'}, {q:'What is a Man-in-the-Middle (MitM) attack?',opts:['When an attacker intercepts and reads/modifies traffic between two parties','When an attacker is physically between two computers','A social engineering attack targeting middle management','When an attacker controls both routers'],correct:0,exp:'In a MitM attack, the attacker secretly intercepts communications between two parties, potentially reading or modifying them. HTTPS with valid certificates prevents most MitM attacks.'} ] } ] }, { id:'encryption', title:'Encryption Fundamentals', icon:'πŸ”‘', color:'#ff8c00', xpReward:190, locked:true, requiredModule:'networking', description:'Demystify encryption and understand how it protects your data', lessons:[ { id:'enc-1', title:'Symmetric vs Asymmetric', xp:40, content:{ icon:'πŸ”', intro:'Encryption converts readable data (plaintext) into scrambled data (ciphertext). Only those with the right key can decrypt it.', points:[ {icon:'πŸ”‘',title:'Symmetric encryption',text:'Uses the same key for encryption and decryption. Fast and efficient for large data. Example: AES-256. Challenge: securely sharing the key.'}, {icon:'πŸ—οΈ',title:'Asymmetric encryption',text:'Uses a key pair: public key (encrypt) and private key (decrypt). Anyone can encrypt with your public key; only you decrypt with your private key.'}, {icon:'🀝',title:'How they work together',text:'HTTPS uses asymmetric encryption for the handshake (key exchange), then switches to symmetric encryption for bulk communication.'}, {icon:'πŸ“',title:'Key length matters',text:'AES-128 vs AES-256, RSA-2048 vs RSA-4096. Longer keys provide more security. AES-256 is considered quantum-resistant.'} ] }, quiz:[ {q:'You want to send an encrypted message to a colleague. Which key do you use?',opts:['Your own private key','Your own public key','Your colleague\'s public key','Your colleague\'s private key'],correct:2,exp:'In asymmetric encryption, you encrypt with the RECIPIENT\'s public key. Only they can decrypt it with their private key. This is how anyone can send you an encrypted message.'}, {q:'Why is symmetric encryption used for bulk data transfer?',opts:['It\'s more secure than asymmetric encryption','It\'s significantly faster than asymmetric for large amounts of data','It doesn\'t require a key','It works without internet'],correct:1,exp:'Asymmetric encryption (RSA) is computationally expensive β€” encrypting large files would be impractically slow. AES (symmetric) is vastly faster for bulk data.'}, {q:'What is end-to-end encryption (E2EE)?',opts:['Encryption starting and ending at the server','Encryption where only the communicating parties can read messages β€” not even the provider','Encryption used only for email','Encryption from browser to website'],correct:1,exp:'E2EE means messages are encrypted on the sender\'s device and only decrypted on the recipient\'s device. The service provider has no keys to read your messages.'} ] }, { id:'enc-2', title:'Real-World Applications', xp:40, content:{ icon:'🌍', intro:'Encryption is everywhere in modern digital life β€” from messaging to banking to secure file storage.', points:[ {icon:'πŸ’¬',title:'Secure messaging',text:'Signal and WhatsApp use E2EE. Signal\'s open-source protocol is the gold standard. SMS messages are NOT end-to-end encrypted.'}, {icon:'πŸ’³',title:'Payment security',text:'Credit card data uses TLS in transit and tokenization at rest. PCI-DSS mandates encryption standards for all payment card systems.'}, {icon:'πŸ’Ύ',title:'Disk encryption',text:'BitLocker (Windows), FileVault (macOS), and LUKS (Linux) encrypt entire drives. Without the key, a stolen laptop\'s data is unreadable.'}, {icon:'πŸ”',title:'Digital signatures',text:'Senders sign data with their private key. Anyone can verify the signature with the public key β€” confirming authenticity.'} ] }, quiz:[ {q:'Your laptop is stolen, but it had full disk encryption enabled. What happens to your data?',opts:['The thief can access everything with the right cable','Data is permanently deleted when stolen','Without the decryption password, all data appears as meaningless, unreadable gibberish','The laptop auto-wipes after 10 attempts'],correct:2,exp:'Full disk encryption means all data is stored encrypted. Without the correct password/key, even removing the drive only reveals encrypted noise.'}, {q:'Are regular SMS text messages encrypted end-to-end?',opts:['Yes β€” all mobile communications are encrypted','Only if you enable it in settings','No β€” SMS has only basic carrier-level encryption, not E2EE','Yes β€” by law, all communications must be encrypted'],correct:2,exp:'SMS has only basic carrier-level encryption. It\'s not E2EE β€” carriers and sophisticated attackers can potentially intercept SMS. Use Signal for sensitive communications.'}, {q:'What does a "digital signature" verify?',opts:['A document was scanned from a handwritten signature','Data was created by the holder of the private key and hasn\'t been modified','An email has no viruses','A website has valid HTTPS'],correct:1,exp:'A digital signature uses the sender\'s private key to sign data. Recipients verify with the public key, confirming origin and that the data hasn\'t been altered.'} ] } ] } ]; const BADGES = [ {id:'first-steps',emoji:'🎯',name:'First Steps',desc:'Complete your first lesson',condition:u=>u.completedLessons.length>=1}, {id:'password-pro',emoji:'πŸ”',name:'Password Pro',desc:'Complete Password Security',condition:u=>isModuleComplete(u,'passwords')}, {id:'phish-detector',emoji:'🎣',name:'Phish Detector',desc:'Complete Phishing Detection',condition:u=>isModuleComplete(u,'phishing')}, {id:'social-guard',emoji:'🎭',name:'Social Guard',desc:'Complete Social Engineering',condition:u=>isModuleComplete(u,'social-eng')}, {id:'web-defender',emoji:'πŸ•ΈοΈ',name:'Web Defender',desc:'Complete Web Vulnerabilities',condition:u=>isModuleComplete(u,'web-vuln')}, {id:'net-wizard',emoji:'🌐',name:'Net Wizard',desc:'Complete Network Basics',condition:u=>isModuleComplete(u,'networking')}, {id:'crypto-sage',emoji:'πŸ”‘',name:'Crypto Sage',desc:'Complete Encryption module',condition:u=>isModuleComplete(u,'encryption')}, {id:'centurion',emoji:'⚑',name:'Centurion',desc:'Earn 100 XP',condition:u=>u.xp>=100}, {id:'scholar',emoji:'πŸ“š',name:'Scholar',desc:'Complete 5 lessons',condition:u=>u.completedLessons.length>=5}, {id:'week-warrior',emoji:'πŸ”₯',name:'Week Warrior',desc:'Maintain a 7-day streak',condition:u=>u.streak>=7}, {id:'sim-master',emoji:'πŸ§ͺ',name:'Sim Master',desc:'Complete 3 simulations',condition:u=>(u.completedSims||[]).length>=3}, {id:'elite',emoji:'πŸ†',name:'Elite Defender',desc:'Earn 500 XP',condition:u=>u.xp>=500}, ]; const FAKE_USERS = [ {name:'Alex Chen',xp:920,level:8,avatar:'AC'}, {name:'Priya Sharma',xp:875,level:8,avatar:'PS'}, {name:'Marcus Webb',xp:740,level:7,avatar:'MW'}, {name:'Sarah Kim',xp:690,level:7,avatar:'SK'}, {name:'James Okafor',xp:580,level:6,avatar:'JO'}, {name:'Lin Zhang',xp:510,level:6,avatar:'LZ'}, {name:'Emma Torres',xp:430,level:5,avatar:'ET'}, {name:'Ravi Patel',xp:360,level:5,avatar:'RP'}, {name:'Chloe Martin',xp:290,level:4,avatar:'CM'}, ]; // ==================== STATE ==================== let currentUser = null; let lessonState = {moduleId:null, lessonIndex:0, step:'content', qIndex:0, score:0}; let simState = {moduleId:null}; // ==================== AUTH ==================== function getUsers(){return JSON.parse(localStorage.getItem('cq_users')||'{}');} function saveUsers(u){localStorage.setItem('cq_users',JSON.stringify(u));} function getCurrentUser(){return JSON.parse(localStorage.getItem('cq_current')||'null');} function saveCurrentUser(u){localStorage.setItem('cq_current',JSON.stringify(u));currentUser=u;} function switchAuthTab(tab){ document.querySelectorAll('.auth-tab').forEach((t,i)=>t.classList.toggle('active',i===(tab==='login'?0:1))); document.getElementById('login-form').style.display=tab==='login'?'block':'none'; document.getElementById('signup-form').style.display=tab==='signup'?'block':'none'; } function handleLogin(){ const email=document.getElementById('login-email').value.trim().toLowerCase(); const pass=document.getElementById('login-password').value; const users=getUsers(); const errEl=document.getElementById('login-error'); if(!email||!pass){errEl.style.display='block';errEl.textContent='Please fill in all fields.';return;} const user=Object.values(users).find(u=>u.email===email&&u.password===pass); if(!user){errEl.style.display='block';errEl.textContent='Invalid email or password.';return;} errEl.style.display='none'; checkStreak(user); saveCurrentUser(user); initApp(); } function handleSignup(){ const name=document.getElementById('signup-name').value.trim(); const email=document.getElementById('signup-email').value.trim().toLowerCase(); const pass=document.getElementById('signup-password').value; const errEl=document.getElementById('signup-error'); if(!name||!email||!pass){errEl.style.display='block';errEl.textContent='Please fill in all fields.';return;} if(pass.length<6){errEl.style.display='block';errEl.textContent='Password must be at least 6 characters.';return;} const users=getUsers(); if(Object.values(users).find(u=>u.email===email)){errEl.style.display='block';errEl.textContent='Email already registered.';return;} errEl.style.display='none'; const newUser={ id:'u_'+Date.now(),name,email,password:pass, xp:0,level:1,streak:1,lastActive:new Date().toDateString(), completedLessons:[],completedSims:[],badges:[], moduleProgress:{} }; users[newUser.id]=newUser; saveUsers(users); saveCurrentUser(newUser); initApp(); } function handleLogout(){ localStorage.removeItem('cq_current'); location.reload(); } // ==================== STREAK ==================== function checkStreak(user){ const today=new Date().toDateString(); const yesterday=new Date(Date.now()-86400000).toDateString(); if(user.lastActive===today) return; if(user.lastActive===yesterday){user.streak=(user.streak||0)+1;} else{user.streak=1;} user.lastActive=today; saveUserData(user); } // ==================== XP/LEVEL ==================== function levelFromXP(xp){ if(xp<100) return 1; if(xp<250) return 2; if(xp<500) return 3; if(xp<850) return 4; if(xp<1300) return 5; if(xp<1900) return 6; if(xp<2700) return 7; if(xp<3700) return 8; if(xp<5000) return 9; return 10; } function xpForNextLevel(level){ const thresholds=[0,100,250,500,850,1300,1900,2700,3700,5000,Infinity]; return thresholds[level]||Infinity; } function xpForCurrentLevel(level){ const thresholds=[0,0,100,250,500,850,1300,1900,2700,3700,5000]; return thresholds[level]||0; } function addXP(amount){ const user=currentUser; const oldLevel=user.level; user.xp+=amount; user.level=levelFromXP(user.xp); if(user.level>oldLevel){ setTimeout(()=>showToast(`πŸŽ‰ Level Up! You're now Level ${user.level}!`,'achievement'),800); } saveUserData(user); updateHeaderStats(); } function saveUserData(user){ const users=getUsers(); users[user.id]=user; saveUsers(users); saveCurrentUser(user); } // ==================== MODULE HELPERS ==================== function isModuleComplete(user, moduleId){ const mod=MODULES.find(m=>m.id===moduleId); if(!mod) return false; const lessonIds=mod.lessons.map(l=>l.id); return lessonIds.every(id=>user.completedLessons.includes(id)); } function isModuleLocked(user, mod){ if(!mod.locked) return false; if(mod.requiredModule && !isModuleComplete(user, mod.requiredModule)) return true; return false; } function getModuleProgress(user, mod){ const total=mod.lessons.length; const done=mod.lessons.filter(l=>user.completedLessons.includes(l.id)).length; return {done, total, pct: total>0 ? Math.round(done/total*100) : 0}; } // ==================== BADGES ==================== function checkBadges(){ const user=currentUser; BADGES.forEach(b=>{ if(!user.badges.includes(b.id) && b.condition(user)){ user.badges.push(b.id); saveUserData(user); setTimeout(()=>showToast(`πŸ… Badge Earned: ${b.name}!`,'achievement'),1000); } }); } // ==================== UI INIT ==================== function initApp(){ document.getElementById('auth-screen').style.display='none'; document.getElementById('main-app').style.display='block'; currentUser=getCurrentUser(); updateHeaderStats(); updateSidebarUser(); renderDashboard(); showPage('dashboard'); } function updateHeaderStats(){ const u=currentUser; const lvl=u.level; document.getElementById('header-xp').textContent=`⚑ ${u.xp} XP`; document.getElementById('header-streak').textContent=`πŸ”₯ ${u.streak} day streak`; document.getElementById('header-level').textContent=`Level ${lvl}`; } function updateSidebarUser(){ const u=currentUser; document.getElementById('sidebar-avatar').textContent=u.name.split(' ').map(w=>w[0]).join('').slice(0,2).toUpperCase(); document.getElementById('sidebar-name').textContent=u.name; document.getElementById('sidebar-level').textContent=`Level ${u.level} Β· ${u.xp} XP`; } function showPage(page){ document.querySelectorAll('.page').forEach(p=>p.classList.remove('active')); document.querySelectorAll('.nav-item').forEach(n=>n.classList.toggle('active',n.dataset.page===page)); document.getElementById('page-'+page).classList.add('active'); const titles={dashboard:'Dashboard',modules:'Skill Tree',lesson:'Lesson',profile:'Profile',leaderboard:'Leaderboard'}; document.getElementById('header-title').textContent=titles[page]||page; if(page==='dashboard') renderDashboard(); if(page==='modules') renderModules(); if(page==='profile') renderProfile(); if(page==='leaderboard') renderLeaderboard(); } // ==================== DASHBOARD ==================== function renderDashboard(){ const u=currentUser; const lvl=u.level; const xpCurr=u.xp-xpForCurrentLevel(lvl); const xpNeeded=xpForNextLevel(lvl)-xpForCurrentLevel(lvl); const pct=Math.min(100,Math.round(xpCurr/xpNeeded*100)); const totalCompleted=u.completedLessons.length; const totalLessons=MODULES.reduce((a,m)=>a+m.lessons.length,0); const unlockedMods=MODULES.filter(m=>!isModuleLocked(u,m)); const dailyDone=(u.dailyDate===new Date().toDateString()); let recentHTML=''; const activity=[...u.completedLessons].reverse().slice(0,5); if(activity.length===0){ recentHTML=`
🎯No activity yet β€” start a lesson!
`; } else { activity.forEach(lid=>{ let lesson,mod; MODULES.forEach(m=>m.lessons.forEach(l=>{if(l.id===lid){lesson=l;mod=m;}})); if(lesson&&mod){ recentHTML+=`
${mod.icon}${lesson.title} β€” ${mod.title}+${lesson.xp} XP
`; } }); } document.getElementById('dashboard-content').innerHTML=`
Welcome back, ${u.name.split(' ')[0]}! πŸ‘‹
Keep securing the digital world, one lesson at a time.
Level ${lvl}${u.xp} / ${xpForNextLevel(lvl)} XP
πŸ›‘οΈ
Daily Challenge
Complete any lesson or simulation today
Bonus: +20 XP for your daily activity streak
Total XP
${u.xp}
Experience Points
Streak
${u.streak}
Days in a row
Lessons Done
${totalCompleted}
of ${totalLessons} total
Modules Unlocked
${unlockedMods.length}
of ${MODULES.length} total
πŸ“‹ Recent Activity
${recentHTML}
`; } // ==================== MODULES ==================== function renderModules(){ const u=currentUser; let html=`
πŸ—ΊοΈ
Master cybersecurity from the ground up. Complete modules to unlock new topics. For educational purposes only β€” no real exploits.
`; MODULES.forEach(mod=>{ const locked=isModuleLocked(u,mod); const prog=getModuleProgress(u,mod); const complete=prog.done===prog.total&&prog.total>0; const inProg=prog.done>0&&!complete; const hasSim=!!mod.simulation; const simDone=(u.completedSims||[]).includes(mod.simulation?.id); const totalItems=mod.lessons.length+(hasSim?1:0); const doneItems=prog.done+(simDone?1:0); const totalPct=Math.round(doneItems/totalItems*100); let badgeHTML=`${locked?'πŸ”’ Locked':complete?'βœ“ Complete':inProg?'In Progress':'Available'}`; let requiredNote=''; if(locked&&mod.requiredModule){ const reqMod=MODULES.find(m=>m.id===mod.requiredModule); requiredNote=`
Requires: ${reqMod?.title||mod.requiredModule}
`; } html+=`
${mod.icon}
${badgeHTML}
${mod.title}
${mod.description}
${!locked?`
`:''}
${prog.done}/${prog.total} lessons${hasSim?` · ${simDone?'1':'0'}/1 sim`:''}${requiredNote} ⚑ ${mod.xpReward} XP
${locked?`
πŸ”’
Complete ${MODULES.find(m=>m.id===mod.requiredModule)?.title||'previous module'} first
`:''}
`; }); html+='
'; document.getElementById('modules-content').innerHTML=html; } function openModule(moduleId){ const u=currentUser; const mod=MODULES.find(m=>m.id===moduleId); if(!mod||isModuleLocked(u,mod)) return; // Find first incomplete lesson or sim let lessonIndex=0; for(let i=0;im.id===lessonState.moduleId); const lesson=mod.lessons[lessonState.lessonIndex]; const isComplete=u.completedLessons.includes(lesson.id); const totalSteps=1+lesson.quiz.length+(mod.simulation?1:0); let stepsHTML=''; for(let i=0;i0&&i<=lesson.quiz.length&&lessonState.step==='quiz'&&(i-1)===lessonState.qIndex) cls+=' current'; else if(lessonState.step==='complete') cls+=' done'; else if(i<(lessonState.step==='quiz'?lessonState.qIndex+1:lessonState.step==='complete'?999:0)) cls+=' done'; stepsHTML+=`
`; } let contentHTML=''; if(lessonState.step==='content'){ const c=lesson.content; let pointsHTML=c.points.map(p=>`
${p.icon}
${p.title}
${p.text}
`).join(''); contentHTML=`
${c.icon}
${c.intro}
${pointsHTML}
`; } else if(lessonState.step==='quiz'){ contentHTML=renderQuiz(lesson.quiz, lessonState.qIndex); } else if(lessonState.step==='complete'){ contentHTML=renderLessonComplete(mod, lesson); } const lessonNavHTML=mod.lessons.map((l,i)=>` ${u.completedLessons.includes(l.id)?'βœ“ ':''} ${l.title} `).join('β€Ί'); document.getElementById('lesson-content').innerHTML=`
${mod.icon} ${mod.title}
${lesson.title} ⚑ +${lesson.xp} XP on completion
${stepsHTML}
${contentHTML} `; } function renderQuiz(quiz, qIndex){ const q=quiz[qIndex]; const letters=['A','B','C','D']; let optsHTML=q.opts.map((o,i)=>`
${letters[i]} ${o}
`).join(''); return `
Question ${qIndex+1} of ${quiz.length}
${q.q}
${optsHTML}
`; } function selectQuizOption(idx){ const mod=MODULES.find(m=>m.id===lessonState.moduleId); const lesson=mod.lessons[lessonState.lessonIndex]; const q=lesson.quiz[lessonState.qIndex]; const opts=document.querySelectorAll('.quiz-option'); if(opts[0].classList.contains('disabled')) return; opts.forEach(o=>o.classList.add('disabled')); const correct=q.correct; const isCorrect=idx===correct; opts[idx].classList.add(isCorrect?'correct':'wrong'); if(!isCorrect) opts[correct].classList.add('correct'); if(isCorrect) lessonState.score++; const fb=document.getElementById('quiz-feedback'); fb.className=`quiz-feedback show ${isCorrect?'correct':'wrong'}`; fb.innerHTML=`${isCorrect?'βœ“':'βœ—'}${q.exp}`; document.getElementById('btn-next-quiz').classList.add('show'); } function nextQuizStep(){ const mod=MODULES.find(m=>m.id===lessonState.moduleId); const lesson=mod.lessons[lessonState.lessonIndex]; if(lessonState.qIndexm.id===lessonState.moduleId); const lesson=mod.lessons[lessonState.lessonIndex]; if(!u.completedLessons.includes(lesson.id)){ u.completedLessons.push(lesson.id); addXP(lesson.xp); if(!u.dailyDate||u.dailyDate!==new Date().toDateString()){ u.dailyDate=new Date().toDateString(); addXP(20); showToast('πŸ“… Daily bonus: +20 XP!','info'); } checkBadges(); launchConfetti(); } lessonState.step='complete'; renderLesson(); } function renderLessonComplete(mod, lesson){ const u=currentUser; const allLessonsDone=mod.lessons.every(l=>u.completedLessons.includes(l.id)); const hasSim=!!mod.simulation; const simDone=(u.completedSims||[]).includes(mod.simulation?.id); let nextBtn=''; if(lessonState.lessonIndexNext Lesson β†’`; } else if(hasSim&&!simDone){ nextBtn=``; } else { nextBtn=``; } return `
πŸŽ‰
Lesson Complete!
${lesson.title} β€” you scored ${lessonState.score}/${lesson.quiz.length} on the quiz
⚑ +${lesson.xp} XP Earned
${nextBtn}
`; } // ==================== SIMULATIONS ==================== function startSimulation(moduleId){ simState={moduleId}; showPage('lesson'); const mod=MODULES.find(m=>m.id===moduleId); if(!mod.simulation) return; const type=mod.simulation.type; let html=''; if(type==='password-strength') html=renderPasswordSim(mod); else if(type==='phishing-detector') html=renderPhishingSim(mod); else if(type==='vuln-spotter') html=renderVulnSim(mod); document.getElementById('lesson-content').innerHTML=`
${mod.icon} ${mod.simulation.title}
Interactive Simulation⚑ +${mod.simulation.xp} XP
${html}`; if(type==='password-strength') initPasswordSim(); } // ---- Password Strength Sim ---- function renderPasswordSim(mod){ return `
πŸ” Password Strength Analyzer
Test different passwords to understand what makes them strong or weak. This is a local analysis β€” nothing is sent to any server.
Enter a password above
β—‹ At least 12 characters
β—‹ Uppercase letter (A-Z)
β—‹ Lowercase letter (a-z)
β—‹ Number (0-9)
β—‹ Special character (!@#...)
β—‹ Not a common password
Character Set Sizeβ€”
Password Entropyβ€”
Est. Crack Time (offline)β€”
Combinationsβ€”
`; } const COMMON_PASSWORDS=['password','password123','123456','12345678','qwerty','abc123','monkey','1234567890','letmein','trustno1','dragon','master','sunshine','princess','welcome','shadow','superman','iloveyou','admin','login']; let pwTestCount=0; function initPasswordSim(){pwTestCount=0;} function analyzePW(val){ if(!val){ ['sw-fill','sw-label','c-len','c-upper','c-lower','c-num','c-sym','c-unique','e-charset','e-entropy','e-crack','e-combo'].forEach(id=>{ const el=document.getElementById(id); if(!el) return; if(id==='sw-label'){el.textContent='Enter a password above';el.style.color='var(--text2)';} if(id==='sw-fill'){el.style.width='0';el.style.background='';} }); return; } pwTestCount++; const hasUpper=/[A-Z]/.test(val); const hasLower=/[a-z]/.test(val); const hasNum=/[0-9]/.test(val); const hasSym=/[^A-Za-z0-9]/.test(val); const isLong=val.length>=12; const notCommon=!COMMON_PASSWORDS.includes(val.toLowerCase()); let charset=0; if(hasLower) charset+=26; if(hasUpper) charset+=26; if(hasNum) charset+=10; if(hasSym) charset+=32; if(charset===0) charset=26; const entropy=val.length*Math.log2(charset); const score=[hasUpper,hasLower,hasNum,hasSym,isLong,notCommon].filter(Boolean).length; setCriterion('c-len',isLong); setCriterion('c-upper',hasUpper); setCriterion('c-lower',hasLower); setCriterion('c-num',hasNum); setCriterion('c-sym',hasSym); setCriterion('c-unique',notCommon); const fill=document.getElementById('sw-fill'); const label=document.getElementById('sw-label'); const colors=['#ff5555','#ff8c00','#ffcc00','#00d4ff','#00ff88','#00ff88']; const labels=['Very Weak','Weak','Fair','Good','Strong','Very Strong']; const pcts=[15,30,50,70,87,100]; const lvl=Math.min(score,5); fill.style.width=pcts[lvl]+'%'; fill.style.background=colors[lvl]; fill.style.boxShadow=`0 0 10px ${colors[lvl]}55`; label.textContent=`Strength: ${labels[lvl]}`; label.style.color=colors[lvl]; document.getElementById('e-charset').textContent=charset+' chars'; document.getElementById('e-entropy').textContent=entropy.toFixed(1)+' bits'; const guessesPerSec=1e10; const totalCombos=Math.pow(charset,val.length); const secsToCrack=totalCombos/guessesPerSec/2; document.getElementById('e-crack').textContent=formatCrackTime(secsToCrack); document.getElementById('e-combo').textContent=formatSci(totalCombos); if(pwTestCount>=3){ const completeBtn=document.getElementById('sim-complete-pw'); const note=document.getElementById('sim-pw-note'); if(completeBtn) completeBtn.style.display='block'; if(note) note.style.display='block'; } } function setCriterion(id,pass){ const el=document.getElementById(id); if(!el) return; el.className='criterion '+(pass?'pass':'fail'); el.querySelector('.crit-icon').textContent=pass?'βœ“':'β—‹'; } function formatCrackTime(secs){ if(secs<60) return '<1 minute'; if(secs<3600) return Math.round(secs/60)+' minutes'; if(secs<86400) return Math.round(secs/3600)+' hours'; if(secs<2592000) return Math.round(secs/86400)+' days'; if(secs<31536000) return Math.round(secs/2592000)+' months'; if(secs<3.154e9) return Math.round(secs/31536000)+' years'; if(secs<3.154e12) return Math.round(secs/3.154e9)+' thousand years'; return '> billion years'; } function formatSci(n){ if(n<1e6) return n.toFixed(0); const exp=Math.floor(Math.log10(n)); const mantissa=(n/Math.pow(10,exp)).toFixed(2); return `${mantissa} Γ— 10^${exp}`; } function tryExample(pw){ const input=document.getElementById('pw-sim-input'); if(input){input.value=pw;analyzePW(pw);} } function togglePWVisibility(){ const input=document.getElementById('pw-sim-input'); if(input) input.type=input.type==='password'?'text':'password'; } // ---- Phishing Sim ---- const PHISHING_EMAILS=[ { from:'security-alert@paypa1-verification.com',fromName:'PayPal Security', subject:'⚠️ URGENT: Your account will be suspended in 24 hours', body:`Dear Valued Customer,\n\nWe have detected unusual activity on your account. Your account will be suspended unless you verify your identity immediately.\n\nClick here to verify: \n\nFailure to verify within 24 hours will result in permanent suspension.\n\nPayPal Security Team`, isPhish:true, explanation:'Red flags: Wrong domain (paypa1.com not paypal.com), artificial urgency, threatening consequences, suspicious link URL.' }, { from:'noreply@github.com',fromName:'GitHub', subject:'[GitHub] Please verify your email address', body:`Hi there,\n\nThis is an automated message from GitHub. We need you to verify your email address.\n\nClick the link below to verify:\n\n\nIf you didn't sign up for GitHub, please ignore this email.\n\nThanks,\nThe GitHub Team`, isPhish:false, explanation:'Legitimate: Correct domain (@github.com), no urgency, explains what to do if you didn\'t sign up, professional tone.' }, { from:'hr-department@company-bonus2024.net',fromName:'HR Department', subject:'Your Q4 Bonus Payment - Action Required', body:`Dear Employee,\n\nCongratulations! You have been selected for a Q4 performance bonus of $2,500.\n\nTo process your payment, please provide your bank account details by clicking the link below:\n\n\nThis offer expires in 48 hours.\n\nHR Department`, isPhish:true, explanation:'Red flags: Suspicious domain not matching your company, asking for banking details via email, artificial urgency, "too good to be true" offer.' }, { from:'support@netflix.com',fromName:'Netflix', subject:'Your Netflix payment was declined', body:`Hi,\n\nWe're having trouble with your current billing information and were unable to process your payment.\n\nTo keep your membership, update your payment info at:\n\n\nIf you need help, visit our Help Center or contact us.\n\nThe Netflix Team`, isPhish:false, explanation:'Legitimate: Correct sender domain (@netflix.com), link points to netflix.com, no threatening language, offers help center alternative.' }, { from:'it-support@microsoft-helpdesk-global.org',fromName:'Microsoft IT Support', subject:'Critical: Your Windows license has expired', body:`WARNING: Your Windows license has expired.\n\nYour computer will be DISABLED in 2 hours unless you renew immediately.\n\nCall our toll-free number: 1-800-555-0187 (available 24/7)\n\nOr click here: \n\nDo NOT ignore this message.`, isPhish:true, explanation:'Red flags: Not from microsoft.com, ALL CAPS threats, unrealistic "disabled in 2 hours" claim, pressure to call a phone number or click suspicious link.' } ]; let phishAnswers={}; function renderPhishingSim(mod){ let emailsHTML=PHISHING_EMAILS.map((e,i)=>` `).join(''); return `
🎣 Phishing Email Identifier
Analyze each email below and decide: is it a phishing attempt or a legitimate email? Look for red flags!
Your Score: 0 / ${PHISHING_EMAILS.length}
`; } function answerPhish(idx, answeredPhish){ const email=PHISHING_EMAILS[idx]; const correct=email.isPhish===answeredPhish; phishAnswers[idx]=correct; const card=document.getElementById('email-'+idx); card.classList.add('answered'); const result=document.getElementById('er-'+idx); result.className='email-result '+(correct?'correct':'wrong'); result.style.display='flex'; result.innerHTML=correct?'βœ“ Correct!':'βœ— Incorrect'; const explain=document.getElementById('ee-'+idx); explain.className='vuln-explain show'; explain.innerHTML=`${email.isPhish?'🎣 This WAS phishing':'βœ“ This was legitimate'}: ${email.explanation}`; const score=Object.values(phishAnswers).filter(Boolean).length; document.getElementById('phish-score').textContent=score; if(Object.keys(phishAnswers).length===PHISHING_EMAILS.length){ document.getElementById('phish-complete-btn').classList.add('show'); } } // ---- Vulnerability Spotter ---- const VULN_SNIPPETS=[ { lang:'JavaScript (Node.js)', q:'Is this code vulnerable to SQL Injection?', code:`// User login endpoint const query = \`SELECT * FROM users WHERE name='${'${'}req.body.username${'}'}'\`; db.query(query, callback);`, opts:['Vulnerable','Secure'], correct:0, explanation:'This is vulnerable to SQL Injection! User input is directly interpolated into the SQL string. Fix: use parameterized queries: db.query("SELECT * FROM users WHERE name=?", [username], callback)' }, { lang:'JavaScript (Node.js)', q:'Is this code vulnerable to SQL Injection?', code:`// Safe parameterized query const query = "SELECT * FROM users WHERE name = ?"; db.query(query, [req.body.username], callback);`, opts:['Vulnerable','Secure'], correct:1, explanation:'This is SECURE! The query uses a parameterized placeholder (?). User input is passed separately as data, never interpreted as SQL code β€” SQL injection is impossible.' }, { lang:'Python (Flask)', q:'Is this code vulnerable to XSS?', code:`from flask import request, render_template_string @app.route('/greet') def greet(): name = request.args.get('name') return render_template_string(f"<h1>Hello {name}!</h1>")`, opts:['Vulnerable','Secure'], correct:0, explanation:'This is vulnerable to XSS! The "name" parameter is directly rendered in HTML. An attacker visiting /greet?name= would inject malicious JavaScript. Fix: use Jinja2 auto-escaping with {{ name }}.' }, { lang:'Python (Flask)', q:'Is this code vulnerable to XSS?', code:`from flask import request from markupsafe import escape @app.route('/greet') def greet(): name = escape(request.args.get('name', '')) return render_template('greet.html', name=name)`, opts:['Vulnerable','Secure'], correct:1, explanation:'This is SECURE! The "name" input is escaped using markupsafe.escape(), converting < to < and > to > β€” so injected scripts appear as text, not executable code.' } ]; let vulnAnswers={}; function renderVulnSim(mod){ let snippetsHTML=VULN_SNIPPETS.map((s,i)=>`
πŸ’»${s.lang}
${s.code}
${s.q}
`).join(''); return `
πŸ” Spot the Vulnerability
Analyze each code snippet and identify whether it contains a security vulnerability. This helps you recognize patterns developers should avoid.
${snippetsHTML}
Score: 0 / ${VULN_SNIPPETS.length}
`; } function answerVuln(idx, answer){ const s=VULN_SNIPPETS[idx]; const correct=answer===s.correct; vulnAnswers[idx]=correct; const snippet=document.getElementById('vs-'+idx); const btns=snippet.querySelectorAll('.snippet-opt'); btns.forEach(b=>b.style.pointerEvents='none'); btns[answer].className='snippet-opt '+(correct?'ans-correct':'ans-wrong'); if(!correct) btns[s.correct].className='snippet-opt ans-correct'; const explain=document.getElementById('ve-'+idx); explain.className='vuln-explain show'; explain.innerHTML=`${correct?'βœ“ Correct!':'βœ— Incorrect.'} ${s.explanation}`; const score=Object.values(vulnAnswers).filter(Boolean).length; document.getElementById('vuln-score').textContent=score; if(Object.keys(vulnAnswers).length===VULN_SNIPPETS.length){ document.getElementById('vuln-complete-btn').classList.add('show'); } } function completeSimulation(moduleId){ const u=currentUser; const mod=MODULES.find(m=>m.id===moduleId); if(!(u.completedSims||[]).includes(mod.simulation.id)){ u.completedSims=u.completedSims||[]; u.completedSims.push(mod.simulation.id); addXP(mod.simulation.xp); checkBadges(); launchConfetti(); showToast(`πŸ§ͺ Simulation complete! +${mod.simulation.xp} XP`,'achievement'); } showPage('modules'); } // ==================== PROFILE ==================== function renderProfile(){ const u=currentUser; const lvl=u.level; const xpCurr=u.xp-xpForCurrentLevel(lvl); const xpNeeded=xpForNextLevel(lvl)-xpForCurrentLevel(lvl); const pct=Math.min(100,Math.round(xpCurr/xpNeeded*100)); const initials=u.name.split(' ').map(w=>w[0]).join('').slice(0,2).toUpperCase(); let badgesHTML=BADGES.map(b=>{ const earned=u.badges.includes(b.id); return `
${b.emoji}
${b.name}
${b.desc}
${earned?'
βœ“ Earned
':''}
`; }).join(''); const totalLessons=MODULES.reduce((a,m)=>a+m.lessons.length,0); const totalSims=MODULES.filter(m=>m.simulation).length; document.getElementById('profile-content').innerHTML=`
${initials}
${u.name}
${u.email}
πŸŽ–οΈ Level ${u.level} CyberGuard
Total XP
${u.xp}
Points earned
Streak
${u.streak}
Day streak
Lessons
${u.completedLessons.length}/${totalLessons}
Completed
Badges
${u.badges.length}/${BADGES.length}
Earned
πŸ… Achievements & Badges
${badgesHTML}
`; } // ==================== LEADERBOARD ==================== function renderLeaderboard(){ const u=currentUser; const allUsers=[ ...FAKE_USERS, {name:u.name,xp:u.xp,level:u.level,avatar:u.name.split(' ').map(w=>w[0]).join('').slice(0,2).toUpperCase(),isYou:true} ].sort((a,b)=>b.xp-a.xp); const avatarColors=['#00ff88','#00d4ff','#a855f7','#ff8c00','#ffcc00','#ff5555','#00ff88','#00d4ff','#a855f7','#ff8c00']; let rows=allUsers.map((usr,i)=>`
${i===0?'πŸ₯‡':i===1?'πŸ₯ˆ':i===2?'πŸ₯‰':i+1}
${usr.avatar||usr.name.slice(0,2)}
${usr.name}${usr.isYou?' (You)':''}
⚑ ${usr.xp} XP Lv.${usr.level}
`).join(''); const yourRank=allUsers.findIndex(u=>u.isYou)+1; document.getElementById('leaderboard-content').innerHTML=`
πŸ†
You are ranked #${yourRank} out of ${allUsers.length} learners. Keep studying to climb the board!
${rows}
`; } // ==================== TOAST ==================== function showToast(msg,type='info'){ const icons={success:'βœ“',error:'βœ—',info:'β„Ή',achievement:'πŸ…'}; const toast=document.createElement('div'); toast.className=`toast ${type}`; toast.innerHTML=`${icons[type]||'β„Ή'}${msg}`; document.getElementById('toasts').appendChild(toast); setTimeout(()=>{toast.style.opacity='0';toast.style.transform='translateX(120%)';toast.style.transition='.3s';setTimeout(()=>toast.remove(),300)},3500); } // ==================== CONFETTI ==================== let confParticles=[]; function launchConfetti(){ const canvas=document.getElementById('confetti-canvas'); canvas.width=window.innerWidth; canvas.height=window.innerHeight; const ctx=canvas.getContext('2d'); const colors=['#00ff88','#00d4ff','#a855f7','#ffcc00','#ff8c00','#ff5555']; confParticles=Array.from({length:120},()=>({ x:Math.random()*canvas.width, y:Math.random()*canvas.height-canvas.height, r:Math.random()*6+2, color:colors[Math.floor(Math.random()*colors.length)], vx:(Math.random()-0.5)*4, vy:Math.random()*3+2, op:1,rot:Math.random()*360,rv:(Math.random()-0.5)*8 })); let frame; function animate(){ ctx.clearRect(0,0,canvas.width,canvas.height); confParticles.forEach(p=>{ p.x+=p.vx;p.y+=p.vy;p.op-=0.008;p.rot+=p.rv; ctx.save();ctx.globalAlpha=Math.max(0,p.op); ctx.translate(p.x,p.y);ctx.rotate(p.rot*Math.PI/180); ctx.fillStyle=p.color;ctx.fillRect(-p.r,-p.r/2,p.r*2,p.r); ctx.restore(); }); confParticles=confParticles.filter(p=>p.op>0); if(confParticles.length>0) frame=requestAnimationFrame(animate); else ctx.clearRect(0,0,canvas.width,canvas.height); } cancelAnimationFrame(frame); animate(); } // ==================== BOOT ==================== (function(){ const user=getCurrentUser(); if(user){ currentUser=user; checkStreak(user); initApp(); } })();