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.
This commit is contained in:
Z. Cliffe Schreuders
2025-11-30 00:02:08 +00:00
parent bca619aeac
commit a8c4f6576f
5 changed files with 86 additions and 15 deletions

View File

@@ -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])

View File

@@ -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)

View File

@@ -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

View File

@@ -71,14 +71,23 @@
<% else %>
<%# Standalone Mode: XML Flag Hints Input %>
<div class="standalone-flags">
<h2>Configure Flag Hints</h2>
<p class="flags-help">Paste the SecGen <code>flag_hints.xml</code> content below. Flags will be extracted per VM and validated at the corresponding flag stations during gameplay.</p>
<h2>Configure VM Lab</h2>
<p class="flags-help">Configure your SecGen VM lab with IP addresses and flags. This information will be used by the in-game terminals.</p>
<%= form_with url: break_escape.games_path, method: :post, local: true, class: 'flags-form' do |f| %>
<%= f.hidden_field :mission_id, value: @mission.id %>
<div class="form-group">
<label for="flag_hints_xml">Flag Hints XML:</label>
<label for="vm_ips_json">VM IP Addresses (JSON):</label>
<%= 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' %>
<p class="form-hint">JSON mapping VM names to IP addresses, e.g. <code>{"desktop":"10.247.65.2","kali":"10.247.65.3"}</code></p>
</div>
<div class="form-group">
<label for="flag_hints_xml">Flag Hints XML (Optional):</label>
<%= f.text_area :flag_hints_xml,
id: 'flag_hints_xml',
placeholder: '<?xml version="1.0"?>
@@ -101,11 +110,11 @@
<div class="flags-info">
<p><strong>💡 How it works:</strong></p>
<ul>
<li>Run SecGen to generate your VMs and <code>flag_hints.xml</code></li>
<li>Paste the XML content above</li>
<li>Flags are extracted per VM (system_name)</li>
<li>During gameplay, submit flags at the Flag Station for each VM</li>
<li>Correct flags unlock rewards and progression</li>
<li>Run SecGen to generate your VMs</li>
<li>Enter the VM IP addresses (from your VirtualBox network settings)</li>
<li>Optionally paste <code>flag_hints.xml</code> content for flag tracking</li>
<li>IP addresses will be shown in the VM Launcher terminals in-game</li>
<li>Submit flags at the Flag Station for each VM to unlock rewards</li>
</ul>
</div>
@@ -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;

View File

@@ -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 = `<p>You've discovered a computer terminal in the game. To interact with it, `
let html = `<p>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 += `
<div class="vm-card">
<div class="vm-header">
<span class="vm-title">${this.escapeHtml(this.vm.title)}</span>
<span class="vm-status ${statusClass}">${statusText}</span>
</div>
<div class="vm-details">
${this.vm.ip ? `<span><span class="vm-detail-label">IP:</span> <span class="vm-ip">${this.escapeHtml(this.vm.ip)}</span></span>` : ''}
</div>
${this.vm.ip ? `
<div class="vm-ip-display">
<span class="vm-detail-label">IP Address:</span>
<span class="vm-ip-value">${this.escapeHtml(this.vm.ip)}</span>
</div>
` : ''}
</div>
`;
@@ -310,6 +335,16 @@ export class VmLauncherMinigame extends MinigameScene {
</div>
<div class="launch-status" id="launch-status"></div>
`;
} else if (this.vm.ip) {
// Standalone mode: show connection instructions
html += `
<div class="standalone-instructions">
<h4>Connection Instructions</h4>
<p>1. Start your VM in VirtualBox: <code>${this.escapeHtml(this.vm.title)}</code></p>
<p>2. Connect via SSH or VNC to: <code>${this.escapeHtml(this.vm.ip)}</code></p>
<p>3. Complete the challenges and capture flags</p>
</div>
`;
}
return html;