diff --git a/app/__init__.py b/app/__init__.py
index e430742..461c546 100644
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -85,6 +85,8 @@ def create_app(config_path=None, config=None):
def inject_globals():
from flask import session
from app.models.user import user_get_unread_notifications
+ from app.services.crackme_fields import (
+ DIFFICULTY_CHOICES, LANG_CHOICES, ARCH_CHOICES, PLATFORM_CHOICES)
username = session.get('name', '')
unread_notifs = 0
@@ -96,7 +98,11 @@ def inject_globals():
'AuthLevel': 'auth' if username else 'anon',
'usersess': username,
'unread_notifications': unread_notifs,
- 'RECAPTCHA_SITEKEY': config['Recaptcha'].get('SiteKey', '') if config['Recaptcha'].get('Enabled') else ''
+ 'RECAPTCHA_SITEKEY': config['Recaptcha'].get('SiteKey', '') if config['Recaptcha'].get('Enabled') else '',
+ 'DIFFICULTY_CHOICES': DIFFICULTY_CHOICES,
+ 'LANG_CHOICES': LANG_CHOICES,
+ 'ARCH_CHOICES': ARCH_CHOICES,
+ 'PLATFORM_CHOICES': PLATFORM_CHOICES,
}
# Register template filters
diff --git a/app/services/crackme_fields.py b/app/services/crackme_fields.py
new file mode 100644
index 0000000..b288611
--- /dev/null
+++ b/app/services/crackme_fields.py
@@ -0,0 +1,53 @@
+"""Controlled vocabularies for a crackme's single-value classification fields.
+
+Language, architecture and platform are each a single value chosen on the
+upload and edit forms. Keeping the option lists here (instead of hardcoding
+them into each template) means the upload form, the edit form and any future
+consumer all agree on the exact same choices.
+"""
+
+# (submitted value, display label) pairs — the value is the stored integer.
+DIFFICULTY_CHOICES = [
+ ('1', '1. Very Easy'),
+ ('2', '2. Easy'),
+ ('3', '3. Medium'),
+ ('4', '4. Hard'),
+ ('5', '5. Very Hard'),
+ ('6', '6. Insane'),
+]
+
+LANG_CHOICES = [
+ 'C/C++',
+ 'Assembler',
+ 'Java',
+ 'Go',
+ 'Rust',
+ 'WebAssembly',
+ 'Python',
+ 'AutoIt',
+ '(Visual) Basic',
+ 'Borland Delphi',
+ 'Turbo Pascal',
+ '.NET',
+ 'Unspecified/other',
+]
+
+ARCH_CHOICES = [
+ 'x86',
+ 'x86-64',
+ 'java',
+ 'ARM',
+ 'MIPS',
+ 'RISC-V',
+ 'other',
+]
+
+PLATFORM_CHOICES = [
+ 'Mac OS X',
+ 'Multiplatform',
+ 'Unix/linux etc.',
+ 'Windows',
+ 'Android',
+ 'iOS',
+ 'Unspecified/other',
+]
diff --git a/static/css/custom.css b/static/css/custom.css
index b6cbb16..5545fbb 100644
--- a/static/css/custom.css
+++ b/static/css/custom.css
@@ -37,6 +37,19 @@ a:active { color: #9acc14;}
.form-select:focus {
border-color: #9acc14;
}
+/* Boxed inline radio/checkbox group (templates/partial/choice_inputs.html).
+ Keeps each metadata field (difficulty/lang/arch/platform) visually
+ separated instead of blending into the fields around it. */
+.choice-box {
+ border: .05rem solid #444;
+ border-radius: .1rem;
+ padding: 8px 10px 4px;
+}
+.choice-box .form-radio,
+.choice-box .form-checkbox {
+ margin-bottom: 4px;
+}
+
button, select {
text-transform: none;
}
diff --git a/templates/crackme/create.html b/templates/crackme/create.html
index 6b32371..9bfd1c3 100644
--- a/templates/crackme/create.html
+++ b/templates/crackme/create.html
@@ -38,14 +38,11 @@
Quick Rules
-
+ {% set choice_type = 'radio' %}
+ {% set choice_name = 'difficulty' %}
+ {% set choice_options = DIFFICULTY_CHOICES %}
+ {% set choice_selected = '' %}
+ {% include 'partial/choice_inputs.html' %}
@@ -53,21 +50,11 @@
Quick Rules
-
+ {% set choice_type = 'radio' %}
+ {% set choice_name = 'lang' %}
+ {% set choice_options = LANG_CHOICES %}
+ {% set choice_selected = '' %}
+ {% include 'partial/choice_inputs.html' %}
@@ -75,15 +62,11 @@
Quick Rules
-
+ {% set choice_type = 'radio' %}
+ {% set choice_name = 'arch' %}
+ {% set choice_options = ARCH_CHOICES %}
+ {% set choice_selected = '' %}
+ {% include 'partial/choice_inputs.html' %}
@@ -91,15 +74,11 @@
Quick Rules
-
+ {% set choice_type = 'radio' %}
+ {% set choice_name = 'platform' %}
+ {% set choice_options = PLATFORM_CHOICES %}
+ {% set choice_selected = '' %}
+ {% include 'partial/choice_inputs.html' %}
@@ -113,9 +92,11 @@
Quick Rules
applies, the specific technique underneath — ticking a technique ticks its category
automatically. Reviewers may adjust these later.
- {% set label_input_name = 'labels' %}
- {% set checked_labels = [] %}
- {% include 'partial/labels_checkboxes.html' %}
+
+ {% set label_input_name = 'labels' %}
+ {% set checked_labels = [] %}
+ {% include 'partial/labels_checkboxes.html' %}
+
-
+ {% set choice_type = 'radio' %}
+ {% set choice_name = 'lang' %}
+ {% set choice_options = LANG_CHOICES %}
+ {% set choice_selected = crackme.lang %}
+ {% include 'partial/choice_inputs.html' %}
@@ -38,15 +28,11 @@
Edit crackme: {{ crackme.name }}
-
+ {% set choice_type = 'radio' %}
+ {% set choice_name = 'arch' %}
+ {% set choice_options = ARCH_CHOICES %}
+ {% set choice_selected = crackme.arch %}
+ {% include 'partial/choice_inputs.html' %}
@@ -54,15 +40,11 @@
Edit crackme: {{ crackme.name }}
-
+ {% set choice_type = 'radio' %}
+ {% set choice_name = 'platform' %}
+ {% set choice_options = PLATFORM_CHOICES %}
+ {% set choice_selected = crackme.platform %}
+ {% include 'partial/choice_inputs.html' %}
diff --git a/templates/partial/choice_inputs.html b/templates/partial/choice_inputs.html
new file mode 100644
index 0000000..3123f18
--- /dev/null
+++ b/templates/partial/choice_inputs.html
@@ -0,0 +1,22 @@
+{# Inline radio/checkbox group, boxed and laid out like the label checkboxes
+ (templates/partial/labels_checkboxes.html) instead of an ugly multi-line
+ select list box.
+
+ Expects:
+ choice_type - 'radio' (single-select) or 'checkbox' (multi-select)
+ choice_name - form field name (e.g. 'lang')
+ choice_options - list of options; each item is either a plain string
+ (value == label) or a (value, label) pair
+ choice_selected - radio: the selected value string ('' when none)
+ checkbox: a list of the selected values #}
+
+{% for opt in choice_options %}
+ {% set val = opt if opt is string else opt[0] %}
+ {% set text = opt if opt is string else opt[1] %}
+ {% set is_checked = (val in choice_selected) if choice_type == 'checkbox' else (choice_selected == val) %}
+
+{% endfor %}
+