diff --git a/app/controllers/break_escape/games_controller.rb b/app/controllers/break_escape/games_controller.rb index 2a0002c..2b1dfc5 100644 --- a/app/controllers/break_escape/games_controller.rb +++ b/app/controllers/break_escape/games_controller.rb @@ -10,10 +10,23 @@ module BreakEscape end # GET /games/:id/scenario - # Returns scenario JSON for this game instance + # Returns filtered scenario JSON for this game instance + # Uses filtered_scenario_for_bootstrap for lazy-loading support def scenario authorize @game if defined?(Pundit) - render json: @game.scenario_data + + begin + # Use filtered bootstrap scenario and remove 'requires' fields for security + filtered = @game.filtered_scenario_for_bootstrap + + # Remove 'requires' fields recursively for security + filter_requires_recursive(filtered) + + render json: filtered + rescue => e + Rails.logger.error "[BreakEscape] scenario error: #{e.message}\n#{e.backtrace.first(5).join("\n")}" + render_error("Failed to generate scenario: #{e.message}", :internal_server_error) + end end # GET /games/:id/scenario_map @@ -277,6 +290,19 @@ module BreakEscape @game = Game.find(params[:id]) end + def filter_requires_recursive(obj) + case obj + when Hash + # Remove 'requires' (the answer/solution) from all objects + obj.delete('requires') + + # Recursively filter nested structures + obj.each_value { |value| filter_requires_recursive(value) } + when Array + obj.each { |item| filter_requires_recursive(item) } + end + end + def track_npc_encounters(room_id, room_data) return unless room_data['npcs'].present? diff --git a/public/break_escape/js/core/game.js b/public/break_escape/js/core/game.js index c5acfbc..e70d2c3 100644 --- a/public/break_escape/js/core/game.js +++ b/public/break_escape/js/core/game.js @@ -441,9 +441,9 @@ export function preload() { // Load scenario from Rails API endpoint if available, otherwise try URL parameter if (window.breakEscapeConfig?.apiBasePath) { - // Load scenario map from Rails API endpoint (minimal metadata for lazy-loading) + // Load scenario from Rails API endpoint (returns filtered scenario for security) // Use absolute URL with origin to prevent Phaser baseURL from interfering - const scenarioUrl = `${window.location.origin}${window.breakEscapeConfig.apiBasePath}/scenario_map`; + const scenarioUrl = `${window.location.origin}${window.breakEscapeConfig.apiBasePath}/scenario`; this.load.json('gameScenarioJSON', scenarioUrl); } else { // Fallback to old behavior for standalone HTML files