Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions blt/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@
)
from website.views.queue import queue_list, update_txid
from website.views.repo import RepoListView, add_repo, refresh_repo_data
from website.views.Simulation import dashboard
from website.views.slack_handlers import slack_commands, slack_events
from website.views.social import queue_social_view
from website.views.teams import (
Expand Down Expand Up @@ -359,6 +360,7 @@
handler500 = "website.views.core.handler500"

urlpatterns = [
path("simulation/", dashboard, name="dashboard"),
path("banned_apps/", BannedAppsView.as_view(), name="banned_apps"),
path("api/banned_apps/search/", search_banned_apps, name="search_banned_apps"),
path("500/", TemplateView.as_view(template_name="500.html"), name="500"),
Expand Down
53 changes: 53 additions & 0 deletions website/management/commands/create_initial_labs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from django.core.management.base import BaseCommand

from website.models import Labs


class Command(BaseCommand):
help = "Creates initial security lab data"

def handle(self, *args, **kwargs):
# Define the initial labs
labs_data = [
{
"name": "SQL Injection",
"description": "Learn about SQL injection vulnerabilities and how to exploit them. This lab covers basic to advanced SQL injection techniques, including union-based, error-based, and blind SQL injection.",
"estimated_time": 60, # 60 minutes
"order": 1,
},
{
"name": "Cross-Site Scripting (XSS)",
"description": "Master the art of identifying and exploiting XSS vulnerabilities. Learn about different types of XSS attacks including reflected, stored, and DOM-based XSS.",
"estimated_time": 45, # 45 minutes
"order": 2,
},
{
"name": "Cross-Site Request Forgery",
"description": "Understand CSRF attacks and prevention techniques. Learn how to identify CSRF vulnerabilities and implement proper protection mechanisms.",
"estimated_time": 30, # 30 minutes
"order": 3,
},
{
"name": "Command Injection",
"description": "Learn about command injection vulnerabilities and exploitation. This lab covers how to identify and exploit command injection flaws in web applications.",
"estimated_time": 40, # 40 minutes
"order": 4,
},
]

# Create the labs
for lab_data in labs_data:
lab, created = Labs.objects.get_or_create(
name=lab_data["name"],
defaults={
"description": lab_data["description"],
"estimated_time": lab_data["estimated_time"],
"order": lab_data["order"],
"is_active": True,
},
)

if created:
self.stdout.write(self.style.SUCCESS(f'Successfully created lab "{lab.name}"'))
else:
self.stdout.write(self.style.WARNING(f'Lab "{lab.name}" already exists'))
31 changes: 31 additions & 0 deletions website/migrations/0242_labs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Generated by Django 5.1.8 on 2025-06-30 14:00

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("website", "0241_domain_has_security_txt_and_more"),
]

operations = [
migrations.CreateModel(
name="Labs",
fields=[
("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("name", models.CharField(max_length=255)),
("description", models.TextField()),
("estimated_time", models.PositiveIntegerField(help_text="Estimated time in minutes")),
("total_tasks", models.PositiveIntegerField(default=0)),
("is_active", models.BooleanField(default=True)),
("order", models.PositiveIntegerField(default=0)),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
],
options={
"verbose_name": "Lab",
"verbose_name_plural": "Labs",
"ordering": ["order"],
},
),
]
28 changes: 28 additions & 0 deletions website/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2454,6 +2454,34 @@ def __str__(self):
return f"{self.app_name} (Banned in {self.country_name})"


class Labs(models.Model):
name = models.CharField(max_length=255)
description = models.TextField()
estimated_time = models.PositiveIntegerField(help_text="Estimated time in minutes")
total_tasks = models.PositiveIntegerField(default=0) # Keep this field but default to 0
is_active = models.BooleanField(default=True)
order = models.PositiveIntegerField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

def update_total_tasks(self):
"""
Updates the total_tasks count based on related tasks.
This will be called when tasks are added/removed.
"""
if hasattr(self, "tasks"):
self.total_tasks = self.tasks.count()
self.save()

def __str__(self):
return self.name

class Meta:
verbose_name = "Lab"
verbose_name_plural = "Labs"
ordering = ["order"]


class Notification(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="notifications")
message = models.TextField()
Expand Down
96 changes: 96 additions & 0 deletions website/templates/Simulation.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<!-- website/templates/dashboard.html -->
{% extends 'base.html' %}
{% block content %}
<div class="min-h-screen bg-gray-100">
<!-- Header -->
<header class="bg-white shadow">
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
<h1 class="text-3xl font-bold text-gray-900">Security Labs</h1>
</div>
</header>
<!-- Main Content -->
<main>
<div class="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
<!-- Lab Cards Grid -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 gap-6">
{% for lab in labs %}
<div class="bg-white overflow-hidden shadow-lg rounded-lg hover:shadow-xl transition-shadow duration-300 ease-in-out">
<div class="p-6">
<!-- Card Header -->
<div class="flex items-center justify-between mb-4">
<!-- Icon and Title -->
<div class="flex items-center">
{% if lab.icon == 'database' %}
<svg class="h-8 w-8 text-[#e74c3c]"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4" />
</svg>
{% elif lab.icon == 'code' %}
<svg class="h-8 w-8 text-[#e74c3c]"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" />
</svg>
{% elif lab.icon == 'shield-check' %}
<svg class="h-8 w-8 text-[#e74c3c]"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
</svg>
{% elif lab.icon == 'terminal' %}
<svg class="h-8 w-8 text-[#e74c3c]"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
{% endif %}
<h2 class="ml-3 text-xl font-semibold text-gray-900">{{ lab.title }}</h2>
</div>
<!-- Task Count and Time -->
<div class="flex items-center space-x-2">
<span class="bg-gray-100 text-gray-700 px-3 py-1 rounded-full text-sm">{{ lab.total_tasks }} Tasks</span>
<span class="bg-gray-100 text-gray-700 px-3 py-1 rounded-full text-sm">{{ lab.estimated_time }} min</span>
</div>
</div>
<!-- Description -->
<p class="text-gray-600 mb-4">{{ lab.description }}</p>
<!-- Progress Bar -->
<div class="mb-4">
<div class="w-full bg-gray-200 rounded-full h-2.5">
<div class="bg-[#e74c3c] h-2.5 rounded-full"
style="width: {{ lab.progress }}%"></div>
</div>
<div class="mt-2 text-sm text-gray-600 text-right">Progress: {{ lab.progress }}%</div>
</div>
<!-- Start/Continue Button -->
<button class="w-full inline-flex justify-center items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-[#e74c3c] hover:bg-opacity-90 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[#e74c3c]">
{% if lab.progress > 0 %}
Continue Lab
{% else %}
Start Lab
{% endif %}
<svg class="ml-2 -mr-1 h-4 w-4"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
</button>
</div>
</div>
{% endfor %}
</div>
</div>
</main>
</div>
{% endblock %}
38 changes: 38 additions & 0 deletions website/views/Simulation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# website/views/Simulation.py

from django.contrib.auth.decorators import login_required
from django.shortcuts import render

from website.models import Labs


@login_required
def dashboard(request):
# Get all active labs ordered by their order field
labs = Labs.objects.filter(is_active=True).order_by("order")

labs_data = []
for lab in labs:
# Map lab icons based on lab name or add a default
icon = "database" # Default icon
if "xss" in lab.name.lower():
icon = "code"
elif "csrf" in lab.name.lower():
icon = "shield-check"
elif "command" in lab.name.lower():
icon = "terminal"

labs_data.append(
{
"id": lab.id,
"title": lab.name,
"description": lab.description,
"icon": icon,
"total_tasks": lab.total_tasks,
"color": "#e74c3c",
"progress": 0, # We'll implement progress tracking in the next PR_forward
"estimated_time": lab.estimated_time,
}
)

return render(request, "Simulation.html", {"labs": labs_data})