From a8c4f6576f92adbced1c4b3fcb13b1544e116247 Mon Sep 17 00:00:00 2001 From: "Z. Cliffe Schreuders" Date: Sun, 30 Nov 2025 00:02:08 +0000 Subject: [PATCH] Enhance standalone mode with VM IP handling and UI updates - Updated `GamesController` to parse and store VM IPs from JSON input, improving player state management. - Modified `Game` model to include VM IPs in the context for better integration with gameplay. - Enhanced `Mission` model's `vm_object` method to override IPs from the context when available. - Revamped `new.html.erb` to provide a clearer interface for entering VM IP addresses and flag hints. - Improved `vm-launcher-minigame.js` to display VM IPs and provide connection instructions in standalone mode. --- .../break_escape/games_controller.rb | 10 +++++ app/models/break_escape/game.rb | 5 ++- app/models/break_escape/mission.rb | 10 ++++- app/views/break_escape/games/new.html.erb | 31 +++++++++---- .../vm-launcher/vm-launcher-minigame.js | 45 ++++++++++++++++--- 5 files changed, 86 insertions(+), 15 deletions(-) diff --git a/app/controllers/break_escape/games_controller.rb b/app/controllers/break_escape/games_controller.rb index ba08907..e98f360 100644 --- a/app/controllers/break_escape/games_controller.rb +++ b/app/controllers/break_escape/games_controller.rb @@ -42,6 +42,16 @@ module BreakEscape end end + # Standalone mode with VM IPs JSON + if params[:vm_ips_json].present? + begin + vm_ips = JSON.parse(params[:vm_ips_json]) + initial_player_state['vm_ips'] = vm_ips if vm_ips.is_a?(Hash) + rescue JSON::ParserError => e + Rails.logger.warn "[BreakEscape] Invalid vm_ips_json: #{e.message}" + end + end + # Standalone mode with XML flag hints if params[:flag_hints_xml].present? flags_by_vm = Mission.parse_flag_hints_xml(params[:flag_hints_xml]) diff --git a/app/models/break_escape/game.rb b/app/models/break_escape/game.rb index a348de1..8d19429 100644 --- a/app/models/break_escape/game.rb +++ b/app/models/break_escape/game.rb @@ -555,11 +555,14 @@ module BreakEscape {} end - # Add flags_by_vm from player_state for standalone mode + # Add flags_by_vm and vm_ips from player_state for standalone mode state = player_state.is_a?(Hash) ? player_state : {} if state['flags_by_vm'].present? vm_context['flags_by_vm'] = state['flags_by_vm'] end + if state['vm_ips'].present? + vm_context['vm_ips'] = state['vm_ips'] + end # Generate with VM context (or empty context for non-VM missions) self.scenario_data = mission.generate_scenario_data(vm_context) diff --git a/app/models/break_escape/mission.rb b/app/models/break_escape/mission.rb index 08fee0e..4e3eda2 100644 --- a/app/models/break_escape/mission.rb +++ b/app/models/break_escape/mission.rb @@ -143,6 +143,8 @@ module BreakEscape attr_reader :random_password, :random_pin, :random_code, :vm_context # Get a VM from the context by title, or return a fallback VM object + # In Hacktivity mode: returns VM data from the VmSet + # In standalone mode: uses fallback but overrides IP from vm_ips if available # Usage in ERB: # "vm": <%= vm_object('kali', {"id":1,"title":"kali","ip":"192.168.1.10","enable_console":true}) %> def vm_object(title, fallback = {}) @@ -150,7 +152,13 @@ module BreakEscape vm = vm_context['vms'].find { |v| v['title'] == title } return vm.to_json if vm end - fallback.to_json + + # Standalone mode: use fallback, but override IP from vm_ips if available + result = fallback.dup + if vm_context && vm_context['vm_ips'] && vm_context['vm_ips'][title] + result['ip'] = vm_context['vm_ips'][title] + end + result.to_json end # Get flags for a specific VM from the context diff --git a/app/views/break_escape/games/new.html.erb b/app/views/break_escape/games/new.html.erb index 953f599..706cc72 100644 --- a/app/views/break_escape/games/new.html.erb +++ b/app/views/break_escape/games/new.html.erb @@ -71,14 +71,23 @@ <% else %> <%# Standalone Mode: XML Flag Hints Input %>
-

Configure Flag Hints

-

Paste the SecGen flag_hints.xml content below. Flags will be extracted per VM and validated at the corresponding flag stations during gameplay.

+

Configure VM Lab

+

Configure your SecGen VM lab with IP addresses and flags. This information will be used by the in-game terminals.

<%= form_with url: break_escape.games_path, method: :post, local: true, class: 'flags-form' do |f| %> <%= f.hidden_field :mission_id, value: @mission.id %>
- + + <%= f.text_field :vm_ips_json, + id: 'vm_ips_json', + placeholder: '{"desktop":"10.247.65.2","kali":"10.247.65.3"}', + class: 'flags-textarea ip-input' %> +

JSON mapping VM names to IP addresses, e.g. {"desktop":"10.247.65.2","kali":"10.247.65.3"}

+
+ +
+ <%= f.text_area :flag_hints_xml, id: 'flag_hints_xml', placeholder: ' @@ -101,11 +110,11 @@

💡 How it works:

    -
  • Run SecGen to generate your VMs and flag_hints.xml
  • -
  • Paste the XML content above
  • -
  • Flags are extracted per VM (system_name)
  • -
  • During gameplay, submit flags at the Flag Station for each VM
  • -
  • Correct flags unlock rewards and progression
  • +
  • Run SecGen to generate your VMs
  • +
  • Enter the VM IP addresses (from your VirtualBox network settings)
  • +
  • Optionally paste flag_hints.xml content for flag tracking
  • +
  • IP addresses will be shown in the VM Launcher terminals in-game
  • +
  • Submit flags at the Flag Station for each VM to unlock rewards
@@ -208,6 +217,12 @@ line-height: 1.4; } + .flags-textarea.ip-input { + font-size: 14px; + height: 42px; + padding: 10px 12px; + } + .form-hint { color: #888; font-size: 12px; diff --git a/public/break_escape/js/minigames/vm-launcher/vm-launcher-minigame.js b/public/break_escape/js/minigames/vm-launcher/vm-launcher-minigame.js index 526aa5e..d0aed70 100644 --- a/public/break_escape/js/minigames/vm-launcher/vm-launcher-minigame.js +++ b/public/break_escape/js/minigames/vm-launcher/vm-launcher-minigame.js @@ -121,6 +121,29 @@ export class VmLauncherMinigame extends MinigameScene { color: #ffaa00; } + .vm-ip-display { + background: rgba(255, 170, 0, 0.1); + border: 1px solid #ffaa00; + padding: 12px 15px; + margin-top: 10px; + text-align: center; + } + + .vm-ip-display .vm-detail-label { + display: block; + color: #888; + font-size: 12px; + margin-bottom: 5px; + } + + .vm-ip-value { + font-family: 'Courier New', monospace; + font-size: 20px; + font-weight: bold; + color: #ffaa00; + letter-spacing: 1px; + } + .vm-actions { margin-top: 15px; display: flex; @@ -276,7 +299,7 @@ export class VmLauncherMinigame extends MinigameScene { const hasConsole = this.vm.enable_console !== false; const statusClass = hasConsole ? 'console' : 'online'; const statusText = hasConsole ? 'Console' : 'Active'; - let html = `

You've discovered a computer terminal in the game. To interact with it, ` + let html = `

You've discovered a computer terminal in the game. To interact with it, `; if (this.hacktivityMode) { html += ` @@ -289,15 +312,17 @@ export class VmLauncherMinigame extends MinigameScene { } html += ` -

${this.escapeHtml(this.vm.title)} ${statusText}
-
- ${this.vm.ip ? `IP: ${this.escapeHtml(this.vm.ip)}` : ''} -
+ ${this.vm.ip ? ` +
+ IP Address: + ${this.escapeHtml(this.vm.ip)} +
+ ` : ''}
`; @@ -310,6 +335,16 @@ export class VmLauncherMinigame extends MinigameScene {
`; + } else if (this.vm.ip) { + // Standalone mode: show connection instructions + html += ` +
+

Connection Instructions

+

1. Start your VM in VirtualBox: ${this.escapeHtml(this.vm.title)}

+

2. Connect via SSH or VNC to: ${this.escapeHtml(this.vm.ip)}

+

3. Complete the challenges and capture flags

+
+ `; } return html;