30 Commits

Author SHA1 Message Date
Z. Cliffe Schreuders
61afc0a666 Refactor character assets and player preferences
- Deleted unused character images: woman_in_science_lab_coat.png and woman_with_black_long_hair_bow_in_hair_long_sleeve_(1).png.
- Added new padlock icon asset for UI.
- Introduced player_preferences.css for styling the player preferences configuration screen.
- Updated game.js to load new character atlases with simplified filenames.
- Enhanced player.js to create custom idle animations for characters.
- Implemented sprite-grid.js for sprite selection UI, including a preview feature.
- Updated database schema to include break_escape_player_preferences table for storing player settings.
- Modified convert_pixellab_to_spritesheet.py to map character names to simplified filenames and extract headshots from character images.
2026-02-12 14:35:14 +00:00
Z. Cliffe Schreuders
b7223af010 Refactor MissionsController and enhance mission display
- Refactored the MissionsController to use local variable `missions` instead of instance variable `@missions` for better clarity and performance.
- Introduced grouping of missions by collection for display, improving the organization of mission listings.
- Updated the view to iterate over `@missions_by_collection`, enhancing the presentation of missions by their respective collections.
- Added new CSS styles for collection headings to improve UI aesthetics.

Additionally, removed invalid missions from the database and updated mission collections for better categorization.

Files modified:
- missions_controller.rb: Refactored mission retrieval logic.
- index.html.erb: Updated mission display logic and added new styles.
- migration file: Added cleanup for invalid missions.
- Various mission JSON files: Updated collections for consistency.
2026-01-14 13:24:46 +00:00
Z. Cliffe Schreuders
92330b04dc Enhance flag handling and XML integration for standalone mode
- Updated `GamesController` to support XML flag hints for standalone mode, improving backward compatibility with legacy flag input.
- Introduced `parse_flag_hints_xml` method in `Mission` model to extract flags from XML content.
- Enhanced `Game` model to incorporate `flags_by_vm` from player state for better flag management.
- Modified `new.html.erb` to update UI for flag hints input, replacing the previous comma-separated flags format.
- Improved `FlagStationMinigame` to display accepted VM flags and handle flag submissions more effectively.
- Adjusted scenario JSON to include flag stations with VM-specific flag handling.
2025-11-29 20:57:39 +00:00
Z. Cliffe Schreuders
bb2b0c206e Implement VM object retrieval and enhance game visuals
- Added `vm_object` method in `mission.rb` to fetch VM details from context or return a fallback.
- Introduced new images for VM launchers and flag stations in the assets directory.
- Updated `game.js` to preload new VM launcher and flag station images.
- Refactored `vm-launcher-minigame.js` to streamline VM handling and improve UI messaging.
- Adjusted scenario JSON to utilize the new `vm_object` method for VM data retrieval.
2025-11-29 01:35:20 +00:00
Z. Cliffe Schreuders
ca44cef068 Enhance game setup for VM and standalone modes
- Updated `GamesController` to use `current_player` for VM set retrieval.
- Improved `MissionsController` to handle redirection for both Hacktivity and standalone modes.
- Modified `new.html.erb` to include UI for standalone flag input alongside VM set selection.
- Added new CSS styles for standalone mode instructions and flags input form.
- Introduced new mission configuration for the SecGen VM Lab, integrating VM and flag capture elements.
2025-11-28 17:41:17 +00:00
Z. Cliffe Schreuders
0d250d71f0 Refactor game redirection and VM context handling
- Updated `MissionsController` to use explicit paths for redirection to ensure compatibility in engine context.
- Enhanced `Game` model to build VM context conditionally based on mission requirements and Hacktivity mode.
- Adjusted `show.html.erb` to reflect explicit API base paths and improved handling of player state for JSON responses.
2025-11-28 16:28:25 +00:00
Z. Cliffe Schreuders
ea079b11c9 WiP implementing VM integration 2025-11-28 15:36:10 +00:00
Z. Cliffe Schreuders
9d6d7709c3 feat: Implement Objectives System with UI and Server Sync
- Added ObjectivesManager to track mission objectives and tasks.
- Created ObjectivesPanel for displaying objectives in a collapsible HUD.
- Integrated objectives state restoration from the server during game initialization.
- Implemented task completion and unlocking mechanisms via game actions.
- Added CSS styles for the objectives panel with a pixel-art aesthetic.
- Developed a test scenario to validate the objectives system functionality.
- Updated database schema to include fields for tracking completed objectives and tasks.
2025-11-26 00:50:32 +00:00
Z. Cliffe Schreuders
26fc297ad8 Refactor tests and improve NPC handling
- Updated NPC ink loading tests to ensure proper handling of missing story files.
- Adjusted lazy loading tests for rooms to enhance clarity and maintainability.
- Enhanced unlock system tests by adding inventory checks for keys.
- Refined filtered scenario tests to ensure accurate preservation of game state.
- Improved game model tests to validate unlock functionality with various inventory scenarios.
2025-11-25 16:28:18 +00:00
Z. Cliffe Schreuders
3cc9fafcec feat: Enhance mission management with CyBOK integration and collection filtering
- Added `Cybok` model to manage CyBOK entries associated with missions.
- Implemented `by_collection` scope in `Mission` model for filtering missions by collection.
- Updated `missions_controller` to filter missions based on the selected collection.
- Introduced `CybokSyncService` for syncing CyBOK data from mission metadata to the database.
- Created new views and partials for displaying CyBOK information with tooltips using Tippy.js.
- Added metadata fields to `break_escape_missions` for `secgen_scenario` and `collection`.
- Enhanced mission seeding logic to support new metadata and CyBOK entries.
- Added multiple new mission scenarios with associated metadata.
2025-11-25 15:20:05 +00:00
Z. Cliffe Schreuders
96cec569f7 Add tests for door unlock mechanics using keys and lockpicks
- Implemented tests to validate unlocking doors with the correct key in inventory.
- Added tests to ensure doors cannot be unlocked without the required key.
- Included scenarios for using lockpicks to unlock doors, bypassing key requirements.
- Ensured that keys take precedence over lockpick attempts when both are available.
- Added checks for accessing unlocked doors regardless of the unlocking method.
- Created helper tests to verify the presence of keys and lockpicks in the player's inventory.
2025-11-24 11:19:45 +00:00
Z. Cliffe Schreuders
ef27265c8c Simplify NPC unlock to use standard unlock flow
PROBLEM:
Previous implementation had unnecessary complexity with npcUnlockedTargets
tracking. NPC unlocks should just work like any other unlock method.

SOLUTION:
1. Removed npcUnlockedTargets tracking (not needed)
2. NPC unlocks now use standard unlockedRooms/unlockedObjects arrays
3. Updated validate_unlock to check if already unlocked FIRST:
   - If in player_state unlocked list → grant access
   - If method='unlocked' → verify against scenario data locked field
   - Otherwise → validate normally

This fixes the race condition issue:
- NPC calls unlock API with method='npc'
- Server validates NPC has permission
- Server adds to unlockedRooms (normal unlock)
- Later when player opens door, client sends method='unlocked'
- Server sees it's already in unlockedRooms OR unlocked in scenario → grants access

Changes:
- app/models/break_escape/game.rb: Remove npc_unlock_target!/npc_unlocked?, check unlocked state first
- app/controllers/break_escape/games_controller.rb: Remove npc_unlock_target! calls
- test/integration/unlock_system_test.rb: Update tests for simplified approach

All 37 tests passing, 122 assertions
2025-11-22 00:46:56 +00:00
Z. Cliffe Schreuders
a36c0da04e Fix test errors and add route name for room endpoint
- Added 'as: room' to room route to fix room_game_url helper
- Fixed TypeError in initialize_player_state by using .dup instead of .deep_dup
- Simplified npcUnlockedTargets initialization test to avoid edge case
- All 38 tests now passing with 129 assertions
2025-11-22 00:46:56 +00:00
Z. Cliffe Schreuders
c5eca9cc60 Fix NPC unlock race condition with persistent server-side tracking
PROBLEM:
NPC unlocks had timing-dependent behavior:
- If NPC unlocked door BEFORE room loaded: client saw it as unlocked
- If NPC unlocked door AFTER room loaded: door sprite stayed locked

SOLUTION:
1. Server-side persistent tracking:
   - Added npcUnlockedTargets array to player_state
   - Track all NPC unlocks separately from unlockedRooms/unlockedObjects
   - Initialize npcUnlockedTargets in new games

2. Server merges NPC unlock state:
   - filtered_room_data checks npcUnlockedTargets
   - Marks doors/containers as unlocked if NPC unlocked them
   - Works regardless of when room is loaded

3. Client updates existing sprites:
   - NPC unlock handler finds ALL door sprites for target room
   - Updates sprite state immediately after server unlock
   - Handles both pre-loaded and late-loaded rooms

Changes:
- app/models/break_escape/game.rb: Add npc_unlock_target!, npc_unlocked?, merge state in filtered_room_data
- app/controllers/break_escape/games_controller.rb: Track NPC unlocks in unlock endpoint
- public/break_escape/js/minigames/person-chat/person-chat-conversation.js: Update all door sprites after NPC unlock
- public/break_escape/js/systems/doors.js: Export unlockDoor globally
- test/integration/unlock_system_test.rb: Add 4 tests for persistent NPC unlock state
2025-11-22 00:46:56 +00:00
Z. Cliffe Schreuders
d3b31b4368 Add comprehensive unlock system tests
Created test suite with 34 tests covering all unlock scenarios and security:

DOOR TESTS (10 tests):
- PIN/password validation (correct/incorrect, case sensitivity)
- Key unlocks (client-validated)
- Unlocked doors (method='unlocked')

CONTAINER TESTS (8 tests):
- PIN/password validation
- Key, lockpick, biometric, bluetooth, RFID unlocks
- Unlocked containers

NPC UNLOCK TESTS (6 tests):
 NPC can unlock door/container if encountered and has permission
🔒 SECURITY: Fails if NPC not encountered
🔒 SECURITY: Fails if NPC lacks permission for that target
🔒 SECURITY: Fails for non-existent NPC
🔒 SECURITY: Fails if unlockable is not an array

SECURITY TESTS - BYPASS PREVENTION (4 tests):
🔒 CRITICAL: Locked door CANNOT be bypassed with method='unlocked'
🔒 CRITICAL: Locked container CANNOT be bypassed with method='unlocked'
 Unlocked door CAN use method='unlocked'
 Unlocked container CAN use method='unlocked'

ERROR CASES (3 tests):
- Non-existent doors/objects return 422
- Invalid methods return 422

DATA FILTERING (2 tests):
- Verify 'requires' field filtered from responses
- Verify recursive filtering of contents

INTEGRATION (1 test):
- Multiple sequential unlocks
- Idempotent operations

Test Results: 34 runs, 115 assertions, 0 failures

Server Implementation:
- validate_npc_unlock: Validates NPC encounter and permission list
- find_npc_in_scenario: Searches all rooms for NPC
- method='npc': New unlock method requiring NPC id as attempt

Client Implementation:
- Updated handleUnlockDoor to call server API with method='npc'
- Server validates all NPC unlock requests
- No client-side lock manipulation

Security Principle: All unlock authorization is server-side.
Client cannot bypass locks by manipulating state or claiming NPC unlocks.
2025-11-22 00:46:56 +00:00
Z. Cliffe Schreuders
65ede66810 CRITICAL SECURITY FIX: Prevent client bypass with method='unlocked'
Fixed critical vulnerability where ANY locked door/container could be bypassed
by sending method='unlocked' to the server.

The Vulnerability:
- Server used OR logic: if method == 'unlocked' || !room['locked']
- This meant: "If client says unlocked OR room is unlocked, grant access"
- Attacker could bypass ANY lock by sending method='unlocked'
- Example exploit: {targetType: "door", targetId: "ceo", method: "unlocked"}

The Fix:
- Changed to AND logic: if method == 'unlocked' && !room['locked']
- Now means: "Only if client says unlocked AND room is ACTUALLY unlocked"
- Added explicit rejection: return false if method='unlocked' on locked item
- Server now logs SECURITY VIOLATION when bypass is attempted

Changes:
- game.rb:151: Changed || to && for doors
- game.rb:157-160: Added explicit rejection for locked doors
- game.rb:185: Changed || to && for objects
- game.rb:191-194: Added explicit rejection for locked objects

Tests Added (4 new security tests):
1. Verify locked door CANNOT be bypassed with method='unlocked' (422 error)
2. Verify locked container CANNOT be bypassed with method='unlocked' (422 error)
3. Verify unlocked door CAN use method='unlocked' (200 success)
4. Verify unlocked container CAN use method='unlocked' (200 success)

Test Results: 28 tests, 95 assertions, 0 failures

Security Principle: Client state is NEVER trusted for authorization.
Server validates against its own scenario_data, not client claims.
2025-11-22 00:46:56 +00:00
Z. Cliffe Schreuders
81e9c71b6f Add comprehensive unlock system tests
Created extensive test suite covering all lock types and unlock scenarios:

DOOR TESTS:
- PIN validation (server-side) - correct/incorrect attempts
- Password validation (server-side) - correct/incorrect, case sensitivity
- Key unlocks (client-validated, server-trusted)
- Unlocked doors (method='unlocked')

CONTAINER TESTS:
- PIN validation (server-side) - correct/incorrect attempts
- Password validation (server-side) - correct/incorrect, empty attempts
- Key unlocks (client-validated)
- Lockpick unlocks (client-validated)
- Biometric unlocks (client-validated)
- Bluetooth unlocks (client-validated)
- RFID unlocks (client-validated)
- Unlocked containers (method='unlocked')

ERROR CASES:
- Non-existent doors/objects
- Invalid methods
- Multiple unlocks and idempotency

SECURITY TESTS:
- Verify 'requires' field is filtered from responses
- Verify contents are filtered recursively

INTEGRATION TESTS:
- Multiple sequential unlocks
- State persistence
- Idempotent operations

Also fixed: Game model generate_scenario_data now uses ||= to allow
test scenarios to override mission data.

Test Results: 24 tests, 83 assertions, 0 failures
2025-11-22 00:46:56 +00:00
Z. Cliffe Schreuders
d2893f06cf Update test database: modified test.sqlite3 to reflect new schema changes 2025-11-21 15:27:54 +00:00
Z. Cliffe Schreuders
c2fadcd169 Add integration and model tests for BreakEscape game mechanics
- Implement RoomLazyLoadTest to verify room data retrieval and error handling for non-existent rooms.
- Create FilteredScenarioTest to ensure scenario data is filtered correctly for game initialization, preserving navigation structure while removing unnecessary details like objects and NPCs.
- Add tests for lock requirements and ensure original scenario data remains unmodified after filtering.
2025-11-21 15:27:54 +00:00
Z. Cliffe Schreuders
6e65b9030d Add integration tests for asset loading and NPC story file handling
- Added tests to verify serving of door tile image and key-operations minigame module.
- Created new NPCInkLoadingTest to check various scenarios for NPC story file loading:
  - Return 404 for NPCs without story files and for non-existent NPCs.
  - Validate handling of missing NPC parameter.
  - Ensure correct API endpoint construction in npc-lazy-loader.
  - Verify person-chat and phone-chat minigames use the correct story loading endpoint.
  - Confirm npc-manager loads stories via API endpoint without direct fetch of storyPath.
  - Check asset path resolution in person-chat-portraits and phone-chat-ui.
  - Ensure ink endpoint returns correct MIME type and handles special characters in NPC IDs.
2025-11-21 15:27:54 +00:00
Z. Cliffe Schreuders
9ac86a2c8d Add integration and unit tests for asset loading and content type determination
- Created `AssetLoadingIntegrationTest` to verify the loading of game assets in the correct order, including JavaScript, CSS, and sound files.
- Implemented tests to ensure proper handling of asset paths, security constraints, and response headers.
- Added `StaticFilesControllerUnitTest` to test the content type determination logic for various file extensions, ensuring case insensitivity and handling of multiple dots in filenames.
2025-11-21 15:27:54 +00:00
Z. Cliffe Schreuders
e758a0bef1 chore: Update test artifacts after running new server response tests 2025-11-21 15:27:54 +00:00
Z. Cliffe Schreuders
4764f5881e test: Add comprehensive server response tests
- Add HTML response tests for missions index (checks mission cards rendered)
- Add game page tests (checks game container and config injection)
- Add API endpoint tests:
  * scenario endpoint returns JSON scenario data
  * bootstrap endpoint returns complete game state
  * sync_state endpoint updates player position
  * unlock endpoint validates and rejects invalid attempts
  * inventory endpoint adds items to player inventory
- Fix inventory API to use action_type instead of reserved 'action' param

All 23 tests passing with 64 assertions verifying server returns correct data!
2025-11-21 15:27:54 +00:00
Z. Cliffe Schreuders
e18950c6d7 feat: Configure dummy app for development with engine migrations
- Add root route that redirects to missions list
- Fix ApplicationController to fallback to demo user when current_user unavailable
- Configure dummy app to load engine migrations in application.rb
- Create test/dummy/db/migrate directory
- Successfully migrated and seeded database with 26 missions

Now visiting http://localhost:3000 will show the missions selection page!
2025-11-21 15:27:54 +00:00
Z. Cliffe Schreuders
19b52cd826 chore: Add .gitignore, update Gemfile and Gemfile.lock for Rails 7.2.3, and clean up development environment settings 2025-11-21 15:27:54 +00:00
Z. Cliffe Schreuders
2d3ce4a6ad chore: Update test artifacts after successful test run 2025-11-21 15:27:54 +00:00
Z. Cliffe Schreuders
08a89eace5 refactor: Move scenarios to root and update paths
- Move scenarios from app/assets/scenarios/ to scenarios/
- Update Mission model to use BreakEscape::Engine.root instead of Rails.root
- Update seeds.rb to use engine root for scenario discovery
- Update tests to use engine root for path assertions

This ensures scenarios are found correctly in both mounted (Hacktivity)
and standalone (test) environments.

All 12 tests now passing with 19 assertions!
2025-11-21 15:27:54 +00:00
Z. Cliffe Schreuders
f44c211d53 fix: Make tests database-agnostic and fix fixture loading
- Update migration to support both PostgreSQL (jsonb) and SQLite (json)
- Fix Rails 8 compatibility (remove config.assets)
- Configure Pundit to use current_player instead of current_user
- Add explicit fixture class mappings for engine fixtures
- Configure standalone mode for tests
- Update test fixtures with proper timestamps and structure
- Improve game test to create data programmatically

Tests now run with 10/12 assertions passing. Remaining errors are due to
missing test scenario files, which can be addressed separately.
2025-11-21 15:27:54 +00:00
Z. Cliffe Schreuders
8b71cf5f2f test: Add comprehensive test suite
- Add fixtures for missions, demo_users, games
- Add model tests for Mission and Game
- Add controller tests for MissionsController
- Test validations, scopes, and methods
- Test state management and health clamping
- Ready for integration testing in host app
2025-11-21 15:27:54 +00:00
Z. Cliffe Schreuders
680c7bfcae feat: Generate Rails Engine structure
- Create mountable engine with isolated namespace
- Configure Pundit authorization
- Set up gemspec with dependencies
- Configure generators for test_unit with fixtures
2025-11-21 15:27:53 +00:00