- Updated `validate_scenario.rb` to enforce correct usage of `targetKnot` in timed conversations, ensuring compliance with new requirements. - Added checks for missing properties in timed conversations, including `delay` and `targetKnot`, to improve scenario integrity. - Enhanced logging for validation issues, providing clearer feedback on scenario configuration errors. - Updated relevant scenarios to align with the new validation rules, ensuring consistency across gameplay elements.
11 KiB
Lab Conversion Prompt
Overview
Convert one of the existing lab Ink files from story_design/ink/lab_sheets/ into a new Break Escape scenario following the same pattern as scenarios/lab_intro_linux. This involves creating a new scenario directory, copying and editing the Ink file, configuring the scenario JSON, and setting up objectives with flag tracking.
Available Lab Files
Choose one of these lab Ink files to convert:
encoding_encryption.inkexploitation.inkfeeling_blu_ctf.inkintro_linux.ink(already converted - use as reference)malware_metasploit.inkphishing_social_engineering.inkpost_exploitation.inkscanning.inkvulnerabilities_exploits.inkvulnerability_analysis.ink
Step 1: Determine the Lab Configuration
First, identify which SecGen scenario corresponds to your chosen lab by examining the SecGen XML files:
cat /home/cliffe/Files/Projects/Code/SecGen/scenarios/labs/introducing_attacks/1_intro_linux.xml
cat /home/cliffe/Files/Projects/Code/SecGen/scenarios/labs/introducing_attacks/2_malware_msf_payloads.xml
cat /home/cliffe/Files/Projects/Code/SecGen/scenarios/labs/introducing_attacks/3_vulnerabilities.xml
# ... etc for other lab numbers
From the XML file, identify:
- System names (e.g., "desktop", "kali", "server")
- IP addresses for each system
- Number of flags and which systems they're on
- Lab sheet URL (if present in XML)
- Difficulty level (from
<difficulty>tag) - CyBOK topics - CRITICAL: Copy these DIRECTLY from the XML file (see CyBOK section below)
Step 2: Create New Scenario Directory
-
Copy the entire
scenarios/lab_intro_linuxdirectory to a new directory named after your lab (e.g.,scenarios/lab_malware_metasploit) -
Update
mission.json:- Change
display_nameto match your lab topic - Update
descriptionto describe the lab content - Set
difficulty_levelappropriately (1-5) - Update
secgen_scenarioto match the XML path (e.g.,"labs/introducing_attacks/2_malware_msf_payloads.xml") - Update
cybokarray: Copy CyBOK entries DIRECTLY from the SecGen XML file (see detailed instructions below)
- Change
2.1 Extracting CyBOK Metadata from SecGen XML
IMPORTANT: The CyBOK metadata in mission.json must match exactly what is defined in the SecGen XML scenario file. Do not create your own CyBOK entries - copy them from the XML.
In the SecGen XML file, CyBOK entries look like this:
<CyBOK KA="MAT" topic="Attacks and exploitation">
<keyword>EXPLOITATION</keyword>
<keyword>EXPLOITATION FRAMEWORKS</keyword>
</CyBOK>
<CyBOK KA="SOIM" topic="PENETRATION TESTING">
<keyword>PENETRATION TESTING - SOFTWARE TOOLS</keyword>
<keyword>PENETRATION TESTING - ACTIVE PENETRATION</keyword>
</CyBOK>
Convert each <CyBOK> element to a JSON object in the cybok array:
"cybok": [
{
"ka": "MAT",
"topic": "Attacks and exploitation",
"keywords": ["EXPLOITATION", "EXPLOITATION FRAMEWORKS"]
},
{
"ka": "SOIM",
"topic": "PENETRATION TESTING",
"keywords": ["PENETRATION TESTING - SOFTWARE TOOLS", "PENETRATION TESTING - ACTIVE PENETRATION"]
}
]
Conversion rules:
KAattribute →"ka"field (keep exact value, e.g., "MAT", "SOIM", "NS")topicattribute →"topic"field (keep exact capitalization and wording)- Each
<keyword>element → add to"keywords"array (keep exact capitalization)
Example from converted labs:
lab_vulnerabilities/mission.jsoncorrectly matches its SecGen XML filelab_intro_linux/mission.jsonmay need correction to match its XML (currently has different CyBOK entries)
Step 3: Copy and Edit the Ink File
-
Copy the relevant Ink file from
story_design/ink/lab_sheets/toscenarios/{your_lab_name}/ink/instructor.ink -
Apply the following transformations to the Ink file:
3.1 Remove Speaker Tags
- Remove all "Tech Instructor:" prefixes from dialogue lines (using sed or find and replace)
- Dialogue should flow naturally without explicit speaker attribution (the game engine handles this)
3.2 Add Influence Tags
- Find all instances where influence/rapport/favour variables are incremented (e.g.,
~ instructor_rapport += 5) - Add
#influence_increasedtag immediately after each increment - Example:
~ instructor_rapport += 5 #influence_increased - If there are any decrements, add
#influence_decreasedafter them
3.3 Convert Lists to Flowing Sentences
- Find all bullet-point lists and numbered lists in dialogue
- Convert them to comma-separated sentences with "and" for the final item
- Example:
❌ WRONG: You've shown you can: - Navigate Linux systems effectively - Use SSH for remote access ✅ RIGHT: You've shown you can navigate Linux systems effectively, use SSH for remote access, and perform security testing with tools like Hydra.
3.4 Ensure Hub-Based Structure
- Verify the conversation follows the hub pattern (see
@docs/INK_BEST_PRACTICES.md) - Ensure
=== start ===goes to=== hub === - Ensure all topic knots return to hub with
-> hub - Ensure hub has at least one
+ [Exit/Leave] #exit_conversationchoice - Remove any
-> ENDstatements (replace with-> hub)
3.5 Add Timed Intro Conversation
- Create a new
=== intro_timed ===knot that:- Introduces the technical instructor
- Explains the three key resources:
- Lab Sheet Workstation (for written instructions)
- VM terminals (for hands-on practice)
- Flag Submission Terminal (for submitting captured flags)
- Mentions players can talk to NPC for concepts and tips
- Flows to
linux_training_hub(or your hub name)
- Update
=== start ===to be a simple entry point that goes directly to hub
3.6 Remove Markdown Formatting
- Remove any
**bold**markdown syntax (Ink doesn't support it) - Remove any lines starting with
*that aren't choices - Use plain text for emphasis instead
3.7 Update Variable Names
- If the Ink file uses different variable names (e.g.,
instructor_rapport), keep them consistent - Ensure global variables match what's declared in
scenario.json.erb
Step 4: Update scenario.json.erb
4.1 Update Global Variables
- Add any new variables needed for the lab
- Remove variables specific to intro_linux if not needed
4.2 Update Objectives
- Create a
submit_flagstask withtargetFlagsarray - Generate flag identifiers based on VM names and flag positions:
- Format:
{vmId}-flag{index}(1-indexed) - Example: If desktop VM has 2 flags, they're
"desktop-flag1"and"desktop-flag2" - Example: If kali VM has 1 flag, it's
"kali-flag1"
- Format:
- Set
targetCountto match the number of flags - Set
showProgress: trueto display progress like "(1/2 flags)"
4.3 Update Rooms
- Update room names to match your lab theme
- Configure VM launchers:
- Use
vm_object()ERB helper for each VM - Set correct
title(system name from XML),ip(from XML), andid(sequential)
- Use
- Configure flag station:
- Set
acceptsVmsarray to list which VMs' flags are accepted - Use
flags_for_vm()ERB helper to configure flags - Remove individual flag task rewards (the
submit_flagstask handles tracking)
- Set
4.4 Update NPC Configuration
- Update NPC
displayNameif needed - Set
timedConversation:Important: Use"timedConversation": { "delay": 5000, "targetKnot": "intro_timed" }targetKnot(notknot) and set delay to 5000ms (5 seconds) to give players time to see the room before the conversation starts - Add
eventMappingsif you want to trigger conversations on aim completion:"eventMappings": [ { "eventPattern": "objective_aim_completed:complete_vm_lab", "targetKnot": "flags_completed_congrats", "conversationMode": "person-chat", "autoTrigger": true, "cooldown": 0 } ]
4.5 Update Lab Workstation
- Update
labUrlto match the lab sheet URL from the SecGen XML (if present) - Or use the pattern:
https://cliffe.github.io/HacktivityLabSheets/labs/introducing_attacks/{lab_number}-{lab_name}/
Step 5: Compile and Validate
-
Compile the Ink file:
./bin/inklecate -jo scenarios/{your_lab_name}/ink/instructor.json scenarios/{your_lab_name}/ink/instructor.ink- Fix any compilation errors (check for "apparent loose end" warnings)
- Ensure compilation succeeds with no errors
-
Validate the scenario:
ruby scripts/validate_scenario.rb scenarios/{your_lab_name}/scenario.json.erb- Fix any schema validation errors
- Address any warnings (especially missing required fields)
- Suggestions are optional but recommended
Step 6: Reference Materials
-
Best Practices: Follow
@docs/INK_BEST_PRACTICES.mdfor:- Hub-based conversation structure
- Speaker tags (optional for single-NPC conversations)
- Choice formatting (dialogue in brackets)
- Exit conversation patterns
- Influence system requirements
-
Reference Implementation: Use
scenarios/lab_intro_linuxas a reference for:- Scenario structure
- Objectives configuration
- Flag tracking setup
- NPC configuration
- VM and flag station setup
Step 7: Testing Checklist
Before considering the conversion complete:
- Ink file compiles without errors
- Scenario validates without errors
- All
influence +=statements have#influence_increasedtags - All lists converted to flowing sentences
- Hub structure is correct (all knots return to hub)
- Timed intro conversation exists and flows to hub
- Flag identifiers match VM names and positions
submit_flagstask has correcttargetFlagsarray- VM launchers configured with correct IPs and titles
- Flag station configured with correct
acceptsVmsand flags - Lab workstation URL is correct
- Mission.json has correct SecGen scenario path
- CyBOK metadata in
mission.jsonmatches the SecGen XML file exactly (KA codes, topics, and keywords must be identical)
Example Conversion Pattern
Here's the pattern used for lab_intro_linux:
-
Ink Changes:
- Removed "Tech Instructor:" prefixes (58+ instances)
- Added
#influence_increasedafter everyinstructor_rapport +=(58 instances) - Converted lists to sentences
- Added
intro_timedknot for game start - Updated
startknot to go directly to hub
-
Scenario Changes:
- Created
submit_flagstask withtargetFlags: ["desktop-flag1", "desktop-flag2"] - Configured VM launchers for "kali" and "desktop" systems
- Configured flag station to accept flags from "desktop" VM
- Added timed conversation with 5 second delay (5000ms) using
targetKnot: "intro_timed" - Added event mapping for aim completion
- Created
-
Result:
- Clean, hub-based conversation
- Proper flag tracking with progress display
- Timed intro explains lab format
- All best practices followed
Notes
- The
submit_flagstask type automatically tracks flag submissions via theflag_submittedevent - Flag identifiers are generated server-side in the format
{vmId}-flag{index} - The system handles progress tracking, state persistence, and aim completion automatically
- Optional features (like lockpicking challenge) can be added after the core conversion is complete