ai-security-trainer-2026

v2026.09.24

Browser-based cybersecurity education platform with guided lessons, quizzes, gamified progression, and adaptive learning scenarios for information security practice.

GitHub
Install command
npx skhub add reason-machines/ai-security-trainer-2026
Markdown
SKILL.md

AI Security Trainer v2026 Skill

Skill by ara.so — Security Skills collection.

Overview

AI Security Trainer v2026 is a browser-based cybersecurity education platform that provides interactive security training through guided lessons, quizzes, gamified progression, and adaptive learning scenarios. The platform is built with HTML/JavaScript frontend and Flask backend, offering features like XP systems, badges, leaderboards, skills mapping, and user profile management.

Installation

Local Setup

# Clone the repository
git clone https://github.com/dylanhwiireed3200/ai-security-trainer-2026.git
cd ai-security-trainer-2026

# Install Python dependencies (typically requires Flask)
pip install -r requirements.txt

# Start the Flask application
python app.py
# or
flask run

# Access the platform at http://localhost:5000

Docker Deployment

# Build the Docker image
docker build -t ai-security-trainer:2026 .

# Run the container
docker run -d -p 5000:5000 \
  -v $(pwd)/data:/app/data \
  --name security-trainer \
  ai-security-trainer:2026

# Access at http://localhost:5000

Project Structure

ai-security-trainer-2026/
├── app.py                 # Flask application entry point
├── static/
│   ├── css/              # Styling (dark theme)
│   ├── js/               # Frontend logic
│   └── images/           # Assets and avatars
├── templates/
│   ├── index.html        # Main interface
│   ├── lessons.html      # Lesson content
│   ├── dashboard.html    # User progress
│   └── skills.html       # Skills map
├── data/                 # User data and progress storage
├── lessons/              # Lesson content files
├── Dockerfile
└── requirements.txt

Core Concepts

User Progression System

The platform tracks learning through:

  • XP (Experience Points): Earned by completing lessons and quizzes
  • Levels: Automatic progression based on accumulated XP
  • Badges: Achievement milestones for completing challenges
  • Leaderboard: Competitive ranking system

Lesson Structure

Lessons follow a structured format with:

  • Learning objectives
  • Interactive content
  • Embedded quizzes
  • Practical scenarios
  • Progress checkpoints

Key Features Implementation

Creating Custom Lessons

// static/js/lesson-creator.js
const Lesson = {
  id: "sql-injection-basics",
  title: "SQL Injection Fundamentals",
  category: "web-security",
  difficulty: "intermediate",
  xpReward: 150,
  
  content: [
    {
      type: "text",
      content: "SQL injection allows attackers to manipulate database queries..."
    },
    {
      type: "code",
      language: "sql",
      content: "SELECT * FROM users WHERE username = '' OR '1'='1' --"
    },
    {
      type: "quiz",
      question: "What does the SQL comment operator '--' do?",
      options: [
        "Starts a multi-line comment",
        "Comments out the rest of the query",
        "Ends the SQL statement",
        "Escapes special characters"
      ],
      correctAnswer: 1
    }
  ],
  
  scenario: {
    type: "interactive",
    description: "Identify vulnerable SQL code",
    challenge: "Find the SQL injection vulnerability in the following code",
    solution: "The user input is directly concatenated into the query"
  }
};

// Add lesson to the system
function addLesson(lesson) {
  fetch('/api/lessons', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${localStorage.getItem('authToken')}`
    },
    body: JSON.stringify(lesson)
  })
  .then(response => response.json())
  .then(data => {
    console.log('Lesson created:', data.lessonId);
  });
}

User Profile Management

// static/js/profile.js
class UserProfile {
  constructor() {
    this.userId = localStorage.getItem('userId');
    this.profile = null;
  }
  
  async loadProfile() {
    const response = await fetch(`/api/users/${this.userId}/profile`);
    this.profile = await response.json();
    return this.profile;
  }
  
  async updateAvatar(avatarFile) {
    const formData = new FormData();
    formData.append('avatar', avatarFile);
    
    const response = await fetch(`/api/users/${this.userId}/avatar`, {
      method: 'POST',
      body: formData
    });
    
    return await response.json();
  }
  
  async getProgress() {
    const response = await fetch(`/api/users/${this.userId}/progress`);
    return await response.json();
  }
  
  calculateLevel(xp) {
    // Level calculation: level = sqrt(xp / 100)
    return Math.floor(Math.sqrt(xp / 100));
  }
}

// Usage
const profile = new UserProfile();
await profile.loadProfile();
console.log(`Level: ${profile.calculateLevel(profile.profile.xp)}`);

Quiz System Implementation

// static/js/quiz-engine.js
class QuizEngine {
  constructor(lessonId) {
    this.lessonId = lessonId;
    this.currentQuestion = 0;
    this.score = 0;
    this.answers = [];
  }
  
  async loadQuiz() {
    const response = await fetch(`/api/lessons/${this.lessonId}/quiz`);
    this.quiz = await response.json();
    return this.quiz;
  }
  
  submitAnswer(questionIndex, answerIndex) {
    const question = this.quiz.questions[questionIndex];
    const isCorrect = answerIndex === question.correctAnswer;
    
    this.answers.push({
      questionIndex,
      answerIndex,
      isCorrect,
      timestamp: Date.now()
    });
    
    if (isCorrect) {
      this.score += question.points || 10;
    }
    
    return {
      isCorrect,
      explanation: question.explanation,
      correctAnswer: question.correctAnswer
    };
  }
  
  async completeQuiz() {
    const result = {
      lessonId: this.lessonId,
      score: this.score,
      totalQuestions: this.quiz.questions.length,
      answers: this.answers,
      completedAt: new Date().toISOString()
    };
    
    const response = await fetch('/api/quiz/submit', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(result)
    });
    
    return await response.json();
  }
}

Skills Map Tracking

// static/js/skills-map.js
class SkillsMap {
  constructor() {
    this.skills = {
      'web-security': ['XSS', 'SQL Injection', 'CSRF', 'SSRF'],
      'network-security': ['Packet Analysis', 'Firewall Rules', 'VPN'],
      'cryptography': ['Symmetric', 'Asymmetric', 'Hashing', 'PKI'],
      'incident-response': ['Detection', 'Analysis', 'Containment', 'Recovery']
    };
  }
  
  async getUserSkills(userId) {
    const response = await fetch(`/api/users/${userId}/skills`);
    return await response.json();
  }
  
  calculateSkillProgress(category, completedLessons) {
    const totalSkills = this.skills[category].length;
    const masteredSkills = completedLessons.filter(
      lesson => lesson.category === category && lesson.score >= 80
    ).length;
    
    return {
      category,
      total: totalSkills,
      mastered: masteredSkills,
      percentage: (masteredSkills / totalSkills) * 100
    };
  }
  
  renderSkillsMap(userProgress) {
    const categories = Object.keys(this.skills);
    const skillsData = categories.map(cat => 
      this.calculateSkillProgress(cat, userProgress)
    );
    
    // Render visualization (radar chart, progress bars, etc.)
    return skillsData;
  }
}

Flask Backend Implementation

Main Application Setup

# app.py
from flask import Flask, render_template, request, jsonify, session
from flask_cors import CORS
import json
import os
from datetime import datetime

app = Flask(__name__)
app.secret_key = os.environ.get('SECRET_KEY', 'dev-secret-key')
CORS(app)

# Configuration
app.config['UPLOAD_FOLDER'] = 'static/uploads/avatars'
app.config['MAX_CONTENT_LENGTH'] = 2 * 1024 * 1024  # 2MB max
app.config['DATA_FOLDER'] = 'data'

# Ensure directories exist
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
os.makedirs(app.config['DATA_FOLDER'], exist_ok=True)

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/api/lessons', methods=['GET', 'POST'])
def lessons():
    if request.method == 'GET':
        lessons_file = os.path.join(app.config['DATA_FOLDER'], 'lessons.json')
        with open(lessons_file, 'r') as f:
            lessons = json.load(f)
        return jsonify(lessons)
    
    elif request.method == 'POST':
        lesson_data = request.json
        lesson_data['id'] = generate_lesson_id()
        lesson_data['created_at'] = datetime.utcnow().isoformat()
        
        # Save lesson
        lessons_file = os.path.join(app.config['DATA_FOLDER'], 'lessons.json')
        with open(lessons_file, 'r+') as f:
            lessons = json.load(f)
            lessons.append(lesson_data)
            f.seek(0)
            json.dump(lessons, f, indent=2)
        
        return jsonify({'lessonId': lesson_data['id']}), 201

@app.route('/api/users/<user_id>/progress', methods=['GET', 'POST'])
def user_progress(user_id):
    progress_file = os.path.join(app.config['DATA_FOLDER'], f'user_{user_id}_progress.json')
    
    if request.method == 'GET':
        if os.path.exists(progress_file):
            with open(progress_file, 'r') as f:
                return jsonify(json.load(f))
        return jsonify({'xp': 0, 'level': 1, 'completedLessons': [], 'badges': []})
    
    elif request.method == 'POST':
        progress_data = request.json
        with open(progress_file, 'w') as f:
            json.dump(progress_data, f, indent=2)
        return jsonify({'success': True})

@app.route('/api/quiz/submit', methods=['POST'])
def submit_quiz():
    quiz_data = request.json
    user_id = session.get('user_id')
    
    # Calculate XP based on score
    xp_earned = calculate_xp(quiz_data['score'], quiz_data['totalQuestions'])
    
    # Update user progress
    progress_file = os.path.join(app.config['DATA_FOLDER'], f'user_{user_id}_progress.json')
    with open(progress_file, 'r+') as f:
        progress = json.load(f)
        progress['xp'] += xp_earned
        progress['completedLessons'].append({
            'lessonId': quiz_data['lessonId'],
            'score': quiz_data['score'],
            'completedAt': quiz_data['completedAt']
        })
        
        # Check for level up
        new_level = calculate_level(progress['xp'])
        if new_level > progress.get('level', 1):
            progress['level'] = new_level
            progress['badges'].append({
                'type': 'level_up',
                'level': new_level,
                'earnedAt': datetime.utcnow().isoformat()
            })
        
        f.seek(0)
        json.dump(progress, f, indent=2)
    
    return jsonify({
        'xpEarned': xp_earned,
        'totalXp': progress['xp'],
        'level': progress['level'],
        'leveledUp': new_level > progress.get('level', 1)
    })

def calculate_xp(score, total):
    percentage = (score / total) * 100
    base_xp = 100
    return int(base_xp * (percentage / 100))

def calculate_level(xp):
    import math
    return math.floor(math.sqrt(xp / 100))

def generate_lesson_id():
    import uuid
    return str(uuid.uuid4())[:8]

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=5000)

Adaptive Learning Engine

# adaptive_learning.py
import json
from datetime import datetime, timedelta

class AdaptiveLearningEngine:
    def __init__(self, user_id):
        self.user_id = user_id
        self.user_progress = self.load_progress()
    
    def load_progress(self):
        try:
            with open(f'data/user_{self.user_id}_progress.json', 'r') as f:
                return json.load(f)
        except FileNotFoundError:
            return {'completedLessons': [], 'weakAreas': [], 'strengths': []}
    
    def analyze_performance(self):
        """Analyze user performance to identify strengths and weaknesses"""
        lessons = self.user_progress.get('completedLessons', [])
        
        category_scores = {}
        for lesson in lessons:
            category = lesson.get('category', 'general')
            if category not in category_scores:
                category_scores[category] = []
            category_scores[category].append(lesson['score'])
        
        weak_areas = []
        strengths = []
        
        for category, scores in category_scores.items():
            avg_score = sum(scores) / len(scores)
            if avg_score < 70:
                weak_areas.append({
                    'category': category,
                    'avgScore': avg_score,
                    'attempts': len(scores)
                })
            elif avg_score > 90:
                strengths.append({
                    'category': category,
                    'avgScore': avg_score,
                    'attempts': len(scores)
                })
        
        return {'weakAreas': weak_areas, 'strengths': strengths}
    
    def recommend_next_lessons(self, available_lessons):
        """Recommend lessons based on performance and progress"""
        analysis = self.analyze_performance()
        completed_ids = [l['lessonId'] for l in self.user_progress.get('completedLessons', [])]
        
        recommendations = []
        
        # Prioritize weak areas
        for weak in analysis['weakAreas']:
            related_lessons = [
                l for l in available_lessons 
                if l['category'] == weak['category'] 
                and l['id'] not in completed_ids
                and l.get('difficulty') in ['beginner', 'intermediate']
            ]
            recommendations.extend(related_lessons[:2])
        
        # Add progressive lessons from strengths
        for strength in analysis['strengths']:
            advanced_lessons = [
                l for l in available_lessons
                if l['category'] == strength['category']
                and l['id'] not in completed_ids
                and l.get('difficulty') == 'advanced'
            ]
            recommendations.extend(advanced_lessons[:1])
        
        # Fill with uncompleted lessons
        remaining = [l for l in available_lessons if l['id'] not in completed_ids]
        recommendations.extend(remaining[:5 - len(recommendations)])
        
        return recommendations[:5]
    
    def adjust_difficulty(self, lesson_id, user_score):
        """Adjust lesson difficulty based on performance"""
        if user_score >= 90:
            return 'increase'  # User is ready for harder content
        elif user_score < 60:
            return 'decrease'  # User needs more foundational work
        else:
            return 'maintain'  # Current difficulty is appropriate

Configuration

Environment Variables

# .env
SECRET_KEY=your-secret-key-here
DATABASE_URL=sqlite:///data/security_trainer.db
UPLOAD_FOLDER=static/uploads/avatars
MAX_CONTENT_LENGTH=2097152
FLASK_ENV=production
FLASK_DEBUG=False

Docker Configuration

# Dockerfile
FROM python:3.9-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

RUN mkdir -p data static/uploads/avatars

EXPOSE 5000

CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "4", "app:app"]
# docker-compose.yml
version: '3.8'

services:
  web:
    build: .
    ports:
      - "5000:5000"
    environment:
      - SECRET_KEY=${SECRET_KEY}
      - FLASK_ENV=production
    volumes:
      - ./data:/app/data
      - ./static/uploads:/app/static/uploads
    restart: unless-stopped

Common Patterns

Creating Interactive Scenarios

// static/js/scenario-engine.js
class SecurityScenario {
  constructor(scenarioConfig) {
    this.config = scenarioConfig;
    this.state = { phase: 0, score: 0, actions: [] };
  }
  
  async loadScenario() {
    const response = await fetch(`/api/scenarios/${this.config.id}`);
    this.scenario = await response.json();
    this.renderPhase(0);
  }
  
  renderPhase(phaseIndex) {
    const phase = this.scenario.phases[phaseIndex];
    
    // Display phase description
    document.getElementById('scenario-description').textContent = phase.description;
    
    // Render available actions
    const actionsContainer = document.getElementById('scenario-actions');
    actionsContainer.innerHTML = '';
    
    phase.actions.forEach((action, index) => {
      const button = document.createElement('button');
      button.textContent = action.label;
      button.onclick = () => this.executeAction(index);
      actionsContainer.appendChild(button);
    });
  }
  
  executeAction(actionIndex) {
    const phase = this.scenario.phases[this.state.phase];
    const action = phase.actions[actionIndex];
    
    this.state.actions.push({
      phase: this.state.phase,
      action: actionIndex,
      timestamp: Date.now()
    });
    
    // Update score based on action correctness
    if (action.isOptimal) {
      this.state.score += 20;
      this.showFeedback('Excellent choice!', 'success');
    } else if (action.isAcceptable) {
      this.state.score += 10;
      this.showFeedback('Good, but there might be a better approach', 'warning');
    } else {
      this.showFeedback('This action could have negative consequences', 'error');
    }
    
    // Move to next phase or complete scenario
    if (this.state.phase < this.scenario.phases.length - 1) {
      this.state.phase++;
      this.renderPhase(this.state.phase);
    } else {
      this.completeScenario();
    }
  }
  
  async completeScenario() {
    const result = await fetch('/api/scenarios/complete', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        scenarioId: this.config.id,
        score: this.state.score,
        actions: this.state.actions
      })
    });
    
    const data = await result.json();
    this.showResults(data);
  }
  
  showFeedback(message, type) {
    const feedback = document.createElement('div');
    feedback.className = `feedback feedback-${type}`;
    feedback.textContent = message;
    document.getElementById('scenario-feedback').appendChild(feedback);
  }
  
  showResults(data) {
    document.getElementById('scenario-score').textContent = `Score: ${this.state.score}`;
    document.getElementById('scenario-xp-earned').textContent = `XP Earned: ${data.xpEarned}`;
  }
}

Leaderboard System

# leaderboard.py
from datetime import datetime, timedelta
import json
import os

class Leaderboard:
    def __init__(self, data_folder='data'):
        self.data_folder = data_folder
        self.leaderboard_file = os.path.join(data_folder, 'leaderboard.json')
    
    def get_global_leaderboard(self, limit=10):
        """Get top users by total XP"""
        users = self._load_all_users()
        sorted_users = sorted(users, key=lambda x: x['xp'], reverse=True)
        return sorted_users[:limit]
    
    def get_weekly_leaderboard(self, limit=10):
        """Get top users by XP earned this week"""
        users = self._load_all_users()
        week_ago = datetime.now() - timedelta(days=7)
        
        weekly_scores = []
        for user in users:
            weekly_xp = sum(
                lesson['xpEarned'] for lesson in user.get('completedLessons', [])
                if datetime.fromisoformat(lesson['completedAt']) > week_ago
            )
            if weekly_xp > 0:
                weekly_scores.append({
                    'userId': user['userId'],
                    'username': user['username'],
                    'weeklyXp': weekly_xp,
                    'avatar': user.get('avatar')
                })
        
        return sorted(weekly_scores, key=lambda x: x['weeklyXp'], reverse=True)[:limit]
    
    def get_user_rank(self, user_id):
        """Get specific user's ranking"""
        leaderboard = self.get_global_leaderboard(limit=None)
        for index, user in enumerate(leaderboard, 1):
            if user['userId'] == user_id:
                return {
                    'rank': index,
                    'xp': user['xp'],
                    'level': user['level'],
                    'totalUsers': len(leaderboard)
                }
        return None
    
    def _load_all_users(self):
        """Load all user progress files"""
        users = []
        for filename in os.listdir(self.data_folder):
            if filename.startswith('user_') and filename.endswith('_progress.json'):
                with open(os.path.join(self.data_folder, filename), 'r') as f:
                    user_data = json.load(f)
                    user_id = filename.replace('user_', '').replace('_progress.json', '')
                    user_data['userId'] = user_id
                    users.append(user_data)
        return users

Troubleshooting

Flask Not Starting

# Check Python version
python --version  # Should be 3.7+

# Verify dependencies
pip install -r requirements.txt

# Check for port conflicts
lsof -i :5000  # On Linux/Mac
netstat -ano | findstr :5000  # On Windows

# Run with debug mode
export FLASK_DEBUG=1
flask run

Data Not Persisting

# Verify data folder permissions
import os
data_folder = 'data'
if not os.path.exists(data_folder):
    os.makedirs(data_folder, exist_ok=True)
    print(f"Created {data_folder}")

# Check write permissions
test_file = os.path.join(data_folder, 'test.txt')
try:
    with open(test_file, 'w') as f:
        f.write('test')
    os.remove(test_file)
    print("Write permissions OK")
except PermissionError:
    print("Permission denied - check folder permissions")

Avatar Upload Issues

# app.py - Enhanced avatar upload with validation
from werkzeug.utils import secure_filename
import imghdr

ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}

def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

@app.route('/api/users/<user_id>/avatar', methods=['POST'])
def upload_avatar(user_id):
    if 'avatar' not in request.files:
        return jsonify({'error': 'No file provided'}), 400
    
    file = request.files['avatar']
    if file.filename == '':
        return jsonify({'error': 'No file selected'}), 400
    
    if not allowed_file(file.filename):
        return jsonify({'error': 'Invalid file type'}), 400
    
    # Validate image
    file_bytes = file.read()
    if imghdr.what(None, h=file_bytes) not in ['png', 'jpg', 'jpeg', 'gif']:
        return jsonify({'error': 'File is not a valid image'}), 400
    
    file.seek(0)  # Reset file pointer
    
    filename = secure_filename(f"{user_id}_avatar.{file.filename.rsplit('.', 1)[1]}")
    filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
    file.save(filepath)
    
    return jsonify({
        'avatarUrl': f'/static/uploads/avatars/{filename}',
        'success': True
    })

Quiz Score Not Updating

// Ensure proper async handling
async function submitQuizAndUpdate() {
  try {
    const quizEngine = new QuizEngine('lesson-123');
    const result = await quizEngine.completeQuiz();
    
    // Verify response
    if (!result.xpEarned) {
      console.error('XP not returned from server');
      return;
    }
    
    // Update UI
    updateProgressBar(result.totalXp);
    displayLevelUp(result.level, result.leveledUp);
    
    // Refresh profile
    const profile = new UserProfile();
    await profile.loadProfile();
    
  } catch (error) {
    console.error('Quiz submission failed:', error);
    alert('Failed to save quiz results. Please try again.');
  }
}

Best Practices

  1. Always validate user input on both client and server side
  2. Use environment variables for sensitive configuration
  3. Implement proper error handling for all API calls
  4. Store user data securely with appropriate file permissions
  5. Regularly backup the data folder
  6. Test quiz and lesson content before deployment
  7. Monitor XP calculations to prevent gaming the system
  8. Use transaction-like operations when updating user progress
  9. Implement rate limiting to prevent abuse
  10. Log important events for debugging and analytics
Discovery
Tags

No tags published for this skill.

Version
Latest version metadata

Version

v2026.09.24

Published

Sep 24, 2026

Category

Uncategorized

License

NOASSERTION

Source path

skills/ai-security-trainer-2026

Default branch

main

Latest commit

304c245

Tree SHA

010f47d