feat: Add mission metadata and CyBOK integration with JSON schema and example files

This commit is contained in:
Z. Cliffe Schreuders
2025-11-25 14:59:57 +00:00
parent 9cad11a853
commit 797b075cbe
6 changed files with 923 additions and 0 deletions

View File

@@ -0,0 +1,550 @@
# Mission Metadata & CyBOK Integration Implementation Plan
## Overview
This plan implements mission metadata (`mission.json`) files for BreakEscape scenarios and adds CyBOK (Cyber Security Body of Knowledge) integration that works in both standalone and Hacktivity modes.
---
## Architecture Summary
### Current State
- **Missions table**: `break_escape_missions` with columns: `name`, `display_name`, `description`, `published`, `difficulty_level`
- **Seeds**: Loops through scenario directories, creates missions with fallback defaults
- **Scenarios**: Each mission has a `scenario.json.erb` for per-instance randomisation
### Target State
- **Missions table**: Add `secgen_scenario` (string), `collection` (string) columns
- **CyBOK table**: New `break_escape_cyboks` table (polymorphic, matches Hacktivity schema)
- **Metadata files**: New `mission.json` in each scenario directory
- **Dual-mode CyBOK**: Use Hacktivity's `::Cybok` if available, fallback to `BreakEscape::Cybok`
- **Seeds**: Read `mission.json`, update missions, sync CyBOK data to both tables when applicable
---
## Implementation TODO List
### Phase 1: Database Migrations
#### 1.1 Add missing columns to break_escape_missions
**File**: `db/migrate/YYYYMMDDHHMMSS_add_metadata_to_break_escape_missions.rb`
```ruby
class AddMetadataToBreakEscapeMissions < ActiveRecord::Migration[7.0]
def change
add_column :break_escape_missions, :secgen_scenario, :string
add_column :break_escape_missions, :collection, :string, default: 'default'
add_index :break_escape_missions, :collection
end
end
```
#### 1.2 Create break_escape_cyboks table
**File**: `db/migrate/YYYYMMDDHHMMSS_create_break_escape_cyboks.rb`
```ruby
class CreateBreakEscapeCyboks < ActiveRecord::Migration[7.0]
def change
create_table :break_escape_cyboks do |t|
t.string :ka # Knowledge Area code (e.g., "AC", "F", "WAM")
t.string :topic # Topic within the KA
t.string :keywords # Keywords as comma-separated string (matches Hacktivity)
t.string :cybokable_type # Polymorphic type
t.integer :cybokable_id # Polymorphic ID
t.timestamps
end
add_index :break_escape_cyboks, :cybokable_id
add_index :break_escape_cyboks, [:cybokable_type, :cybokable_id]
add_index :break_escape_cyboks, :ka
end
end
```
---
### Phase 2: Models
#### 2.1 Create BreakEscape::Cybok model
**File**: `app/models/break_escape/cybok.rb`
```ruby
# frozen_string_literal: true
module BreakEscape
class Cybok < ApplicationRecord
self.table_name = 'break_escape_cyboks'
belongs_to :cybokable, polymorphic: true
# Mirror Hacktivity's KA_CODES for consistency
KA_CODES = {
'IC' => 'Introduction to CyBOK',
'FM' => 'Formal Methods',
'RMG' => 'Risk Management & Governance',
'LR' => 'Law & Regulation',
'HF' => 'Human Factors',
'POR' => 'Privacy & Online Rights',
'MAT' => 'Malware & Attack Technologies',
'AB' => 'Adversarial Behaviours',
'SOIM' => 'Security Operations & Incident Management',
'F' => 'Forensics',
'C' => 'Cryptography',
'AC' => 'Applied Cryptography',
'OSV' => 'Operating Systems & Virtualisation Security',
'DSS' => 'Distributed Systems Security',
'AAA' => 'Authentication, Authorisation and Accountability',
'SS' => 'Software Security',
'WAM' => 'Web & Mobile Security',
'SSL' => 'Secure Software Lifecycle',
'NS' => 'Network Security',
'HS' => 'Hardware Security',
'CPS' => 'Cyber Physical Systems',
'PLT' => 'Physical Layer and Telecommunications Security'
}.freeze
CATEGORY_MAPPING = {
'Introductory Concepts' => ['IC'],
'Human, Organisational & Regulatory Aspects' => ['RMG', 'LR', 'HF', 'POR'],
'Attacks & Defences' => ['MAT', 'AB', 'SOIM', 'F'],
'Systems Security' => ['C', 'OSV', 'DSS', 'AAA', 'FM'],
'Software and Platform Security' => ['SS', 'WAM', 'SSL'],
'Infrastructure Security' => ['AC', 'NS', 'HS', 'CPS', 'PLT']
}.freeze
def ka_full_name
KA_CODES[ka] || 'Unknown KA'
end
def ka_category
CATEGORY_MAPPING.each do |category, kas|
return category if kas.include?(ka)
end
'Unknown Category'
end
# Parse keywords string back to array (matches Hacktivity behavior)
def keywords_array
return [] if keywords.blank?
# Handle both array-coerced strings and plain comma-separated
keywords.gsub(/[\[\]"]/, '').split(',').map(&:strip)
end
end
end
```
#### 2.2 Update BreakEscape::Mission model
**File**: `app/models/break_escape/mission.rb`
Add CyBOK association and dual-mode helper:
```ruby
module BreakEscape
class Mission < ApplicationRecord
self.table_name = 'break_escape_missions'
has_many :games, class_name: 'BreakEscape::Game', dependent: :destroy
# CyBOK associations - always use our table
has_many :break_escape_cyboks,
class_name: 'BreakEscape::Cybok',
as: :cybokable,
dependent: :destroy
# Also populate Hacktivity's cyboks table when available
if defined?(::Cybok)
has_many :cyboks, as: :cybokable, dependent: :destroy
end
validates :name, presence: true, uniqueness: true
validates :display_name, presence: true
validates :difficulty_level, inclusion: { in: 1..5 }
scope :published, -> { where(published: true) }
scope :by_collection, ->(collection) { where(collection: collection) }
scope :collections, -> { distinct.pluck(:collection).compact }
# Path to scenario directory
def scenario_path
BreakEscape::Engine.root.join('scenarios', name)
end
# Path to mission metadata file
def mission_json_path
scenario_path.join('mission.json')
end
# Check if mission.json exists
def has_mission_json?
File.exist?(mission_json_path)
end
# Load mission metadata from JSON file
def load_mission_metadata
return nil unless has_mission_json?
JSON.parse(File.read(mission_json_path))
rescue JSON::ParserError => e
Rails.logger.error "Invalid mission.json for #{name}: #{e.message}"
nil
end
# Get all CyBOK entries (prefers Hacktivity's if available for reads)
def all_cyboks
if defined?(::Cybok) && respond_to?(:cyboks)
cyboks
else
break_escape_cyboks
end
end
# ... existing methods (generate_scenario_data, ScenarioBinding) ...
end
end
```
---
### Phase 3: CyBOK Sync Service
#### 3.1 Create CyBOK sync service
**File**: `app/services/break_escape/cybok_sync_service.rb`
```ruby
# frozen_string_literal: true
module BreakEscape
class CybokSyncService
# Sync CyBOK data from mission.json to database tables
# Writes to both BreakEscape and Hacktivity tables when Hacktivity is present
def self.sync_for_mission(mission, cybok_data)
return if cybok_data.blank?
# Normalize input (handle both array and hash formats)
cybok_entries = Array.wrap(cybok_data)
# Clear existing entries
mission.break_escape_cyboks.destroy_all
mission.cyboks.destroy_all if mission.respond_to?(:cyboks) && defined?(::Cybok)
cybok_entries.each do |entry|
ka = entry['ka'] || entry[:ka]
topic = entry['topic'] || entry[:topic]
keywords = entry['keywords'] || entry[:keywords]
# Serialize keywords array to string (Hacktivity format)
keywords_str = keywords.is_a?(Array) ? keywords.join(', ') : keywords.to_s
# Always write to BreakEscape table
mission.break_escape_cyboks.create!(
ka: ka,
topic: topic,
keywords: keywords_str
)
# Also write to Hacktivity table if available
if mission.respond_to?(:cyboks) && defined?(::Cybok)
mission.cyboks.create!(
ka: ka,
topic: topic,
keywords: keywords_str
)
end
end
end
# Check if Hacktivity mode is active
def self.hacktivity_mode?
defined?(::Cybok)
end
end
end
```
---
### Phase 4: Mission Metadata JSON Format
#### 4.1 mission.json schema
**Location**: Each scenario directory (e.g., `scenarios/biometric_breach/mission.json`)
```json
{
"display_name": "Biometric Breach",
"description": "Investigate a security breach using biometric forensics in a high-security research facility.",
"difficulty_level": 3,
"secgen_scenario": null,
"collection": "security_investigations",
"cybok": [
{
"ka": "AAA",
"topic": "Authentication",
"keywords": ["Biometric authentication", "Fingerprint analysis", "Identity verification"]
},
{
"ka": "F",
"topic": "Artifact Analysis",
"keywords": ["Digital forensics", "Evidence collection"]
},
{
"ka": "SOIM",
"topic": "Security Operations",
"keywords": ["Incident response", "Security monitoring"]
}
]
}
```
#### 4.2 Example mission.json files to create
| Scenario | Collection | Difficulty | CyBOK KAs |
|----------|------------|------------|-----------|
| `biometric_breach` | security_investigations | 3 | AAA, F, SOIM |
| `ceo_exfil` | data_exfiltration | 4 | AB, MAT, F |
| `cybok_heist` | physical_security | 2 | C, AC, HF |
| `scenario1-4` | testing | 1-2 | Various |
| `npc-*` | testing | 1 | HF |
| `test-*` | testing | 1 | (none) |
---
### Phase 5: Updated Seeds
#### 5.1 Update db/seeds.rb
**File**: `db/seeds.rb`
```ruby
puts "Creating/Updating BreakEscape missions..."
# List all scenario directories
scenario_dirs = Dir.glob(BreakEscape::Engine.root.join('scenarios/*')).select { |f| File.directory?(f) }
# Directories to skip
SKIP_DIRS = %w[common compiled ink].freeze
scenario_dirs.each do |dir|
scenario_name = File.basename(dir)
next if SKIP_DIRS.include?(scenario_name)
# Check for scenario.json.erb (required for valid mission)
scenario_template = File.join(dir, 'scenario.json.erb')
next unless File.exist?(scenario_template)
mission = BreakEscape::Mission.find_or_initialize_by(name: scenario_name)
mission_json_path = File.join(dir, 'mission.json')
if File.exist?(mission_json_path)
# Load metadata from mission.json
begin
metadata = JSON.parse(File.read(mission_json_path))
mission.display_name = metadata['display_name'] || scenario_name.titleize
mission.description = metadata['description'] || "Play the #{scenario_name.titleize} scenario"
mission.difficulty_level = metadata['difficulty_level'] || 3
mission.secgen_scenario = metadata['secgen_scenario']
mission.collection = metadata['collection'] || 'default'
mission.published = true
if mission.save
# Sync CyBOK data
if metadata['cybok'].present?
BreakEscape::CybokSyncService.sync_for_mission(mission, metadata['cybok'])
cybok_count = mission.break_escape_cyboks.count
puts "#{mission.new_record? ? 'Created' : 'Updated'}: #{mission.display_name} (#{cybok_count} CyBOK entries)"
else
puts "#{mission.new_record? ? 'Created' : 'Updated'}: #{mission.display_name}"
end
else
puts " ✗ Failed: #{scenario_name} - #{mission.errors.full_messages.join(', ')}"
end
rescue JSON::ParserError => e
puts " ✗ Invalid mission.json for #{scenario_name}: #{e.message}"
# Fall back to defaults
apply_default_metadata(mission, scenario_name)
end
else
# No mission.json - use defaults
apply_default_metadata(mission, scenario_name)
end
end
def apply_default_metadata(mission, scenario_name)
mission.display_name ||= scenario_name.titleize
mission.description ||= "Play the #{scenario_name.titleize} scenario"
mission.difficulty_level ||= 3
mission.collection ||= infer_collection(scenario_name)
mission.published = true
if mission.save
puts "#{mission.new_record? ? 'Created' : 'Updated'} (defaults): #{mission.display_name}"
else
puts " ✗ Failed: #{scenario_name} - #{mission.errors.full_messages.join(', ')}"
end
end
def infer_collection(scenario_name)
return 'testing' if scenario_name.start_with?('test', 'npc-', 'scenario')
'default'
end
puts "\nDone! #{BreakEscape::Mission.count} missions total."
puts "Collections: #{BreakEscape::Mission.collections.join(', ')}"
if BreakEscape::CybokSyncService.hacktivity_mode?
puts "Hacktivity mode: CyBOK data synced to both tables"
else
puts "Standalone mode: CyBOK data in break_escape_cyboks only"
end
```
---
### Phase 6: File Creation Tasks
#### 6.1 Files to create
| File | Purpose |
|------|---------|
| `db/migrate/YYYYMMDDHHMMSS_add_metadata_to_break_escape_missions.rb` | Add secgen_scenario, collection columns |
| `db/migrate/YYYYMMDDHHMMSS_create_break_escape_cyboks.rb` | Create CyBOK table |
| `app/models/break_escape/cybok.rb` | CyBOK model with KA codes |
| `app/services/break_escape/cybok_sync_service.rb` | Dual-mode CyBOK sync |
| `scenarios/biometric_breach/mission.json` | Mission metadata |
| `scenarios/ceo_exfil/mission.json` | Mission metadata |
| `scenarios/cybok_heist/mission.json` | Mission metadata |
| (other scenario directories) | Mission metadata files |
#### 6.2 Files to update
| File | Changes |
|------|---------|
| `app/models/break_escape/mission.rb` | Add CyBOK associations, metadata loading |
| `db/seeds.rb` | Read mission.json, sync CyBOK |
---
## Detailed TODO Checklist
### Migrations
- [ ] Create migration: `add_metadata_to_break_escape_missions`
- [ ] Add `secgen_scenario` string column (nullable)
- [ ] Add `collection` string column (default: 'default')
- [ ] Add index on `collection`
- [ ] Create migration: `create_break_escape_cyboks`
- [ ] Columns: `ka`, `topic`, `keywords` (all strings)
- [ ] Polymorphic columns: `cybokable_type`, `cybokable_id`
- [ ] Indexes on `cybokable_id`, `[cybokable_type, cybokable_id]`, `ka`
### Models
- [ ] Create `app/models/break_escape/cybok.rb`
- [ ] Table name: `break_escape_cyboks`
- [ ] Polymorphic belongs_to: `cybokable`
- [ ] KA_CODES constant (copy from Hacktivity)
- [ ] CATEGORY_MAPPING constant
- [ ] `ka_full_name` method
- [ ] `ka_category` method
- [ ] `keywords_array` method (parse stored string)
- [ ] Update `app/models/break_escape/mission.rb`
- [ ] Add `has_many :break_escape_cyboks` association
- [ ] Add conditional `has_many :cyboks` for Hacktivity mode
- [ ] Add `scope :by_collection`
- [ ] Add `scope :collections`
- [ ] Add `mission_json_path` method
- [ ] Add `has_mission_json?` method
- [ ] Add `load_mission_metadata` method
- [ ] Add `all_cyboks` method
### Services
- [ ] Create `app/services/break_escape/cybok_sync_service.rb`
- [ ] `sync_for_mission(mission, cybok_data)` class method
- [ ] Handle array/hash input normalization
- [ ] Write to both tables when Hacktivity present
- [ ] `hacktivity_mode?` class method
### Seeds
- [ ] Update `db/seeds.rb`
- [ ] Skip compiled/common/ink directories
- [ ] Check for scenario.json.erb presence
- [ ] Load mission.json when present
- [ ] Apply default values for missing fields
- [ ] Call CybokSyncService for CyBOK data
- [ ] Add `infer_collection` helper for test scenarios
- [ ] Print summary with collection list and mode
### Mission JSON Files
- [ ] Create `scenarios/biometric_breach/mission.json`
- [ ] Create `scenarios/ceo_exfil/mission.json`
- [ ] Create `scenarios/cybok_heist/mission.json`
- [ ] Create mission.json for scenario1-4
- [ ] Create mission.json for npc-* scenarios (collection: testing)
- [ ] Create mission.json for test-* scenarios (collection: testing)
### Testing
- [ ] Run migrations: `rails db:migrate`
- [ ] Run seeds: `rails db:seed`
- [ ] Verify CyBOK data in `break_escape_cyboks` table
- [ ] Test in standalone mode
- [ ] Test with Hacktivity integration (if available)
---
## Mode Detection Logic
```ruby
# In any code needing mode detection:
if defined?(::Cybok)
# Hacktivity mode: Both tables exist
# Write to both, read from ::Cybok (Hacktivity's table)
else
# Standalone mode: Only BreakEscape::Cybok available
# Use break_escape_cyboks table exclusively
end
```
---
## Data Flow Diagram
```
mission.json (static) scenario.json.erb (per-instance)
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ db:seed loads │ │ Game.create │
│ metadata │ │ generates │
│ + CyBOK data │ │ random values │
└────────┬─────────┘ └────────┬─────────┘
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│break_escape_ │ │ Game instance │
│missions table │ │ (has scenario │
│ │ │ JSON data) │
│+ break_escape_ │ └──────────────────┘
│cyboks table │
│ │
│(+ Hacktivity │
│ cyboks table if │
│ available) │
└──────────────────┘
```
---
## Rollback Plan
If issues arise:
1. **Migrations**: Run `rails db:rollback` for each migration
2. **Models**: Revert to previous versions from git
3. **Seeds**: Old seeds work with new columns (uses defaults)
4. **mission.json**: Not required - seeds fall back gracefully
---
## Future Enhancements
1. **Admin UI**: Add mission metadata editing in admin panel
2. **Collection filtering**: Add collection dropdown to mission index
3. **CyBOK mapping view**: Show all missions grouped by KA
4. **Validation**: Add JSON schema validation for mission.json
5. **Import/Export**: Bulk import/export mission metadata

View File

@@ -0,0 +1,150 @@
# Mission JSON Schema
## File Location
Each scenario directory should contain a `mission.json` file:
```
scenarios/
├── biometric_breach/
│ ├── mission.json <-- Static mission metadata
│ └── scenario.json.erb <-- Per-instance randomised scenario
├── ceo_exfil/
│ ├── mission.json
│ └── scenario.json.erb
└── ...
```
## Schema Definition
```json
{
"display_name": "string (required)",
"description": "string (required)",
"difficulty_level": "integer 1-5 (required)",
"secgen_scenario": "string or null (optional)",
"collection": "string (required)",
"cybok": "array of CyBOK entries (optional)"
}
```
## Field Descriptions
### display_name (required)
Human-readable name for the mission. Displayed on index pages, mission cards, etc.
- Example: `"Biometric Breach"`
### description (required)
Brief description of the mission objectives and theme. Used for mission cards and detail views.
- Example: `"Investigate a security breach at a high-security research facility..."`
### difficulty_level (required)
Integer from 1-5 indicating mission difficulty:
- `1` = Beginner/Tutorial
- `2` = Easy
- `3` = Medium
- `4` = Hard
- `5` = Expert
### secgen_scenario (optional)
Path to a SecGen XML scenario file when the mission includes virtual machines.
- Example: `"scenarios/labs/introducing_attacks/1_intro_linux.xml"`
- Set to `null` if mission is game-only (no VMs)
### collection (required)
Grouping category for filtering on mission index. Common values:
- `"testing"` - Test scenarios, not for end users
- `"security_investigations"` - Forensics and investigation focused
- `"physical_security"` - Lock picking, safe cracking, physical bypass
- `"data_exfiltration"` - Data theft and covert operations
- `"network_security"` - Network-based challenges
- `"default"` - Uncategorised missions
### cybok (optional)
Array of CyBOK (Cyber Security Body of Knowledge) entries mapping the mission to educational topics.
Each entry:
```json
{
"ka": "string (2-4 letter code)",
"topic": "string (topic name)",
"keywords": ["array", "of", "keywords"]
}
```
## CyBOK Knowledge Area Codes
| Code | Full Name |
|------|-----------|
| IC | Introduction to CyBOK |
| FM | Formal Methods |
| RMG | Risk Management & Governance |
| LR | Law & Regulation |
| HF | Human Factors |
| POR | Privacy & Online Rights |
| MAT | Malware & Attack Technologies |
| AB | Adversarial Behaviours |
| SOIM | Security Operations & Incident Management |
| F | Forensics |
| C | Cryptography |
| AC | Applied Cryptography |
| OSV | Operating Systems & Virtualisation Security |
| DSS | Distributed Systems Security |
| AAA | Authentication, Authorisation and Accountability |
| SS | Software Security |
| WAM | Web & Mobile Security |
| SSL | Secure Software Lifecycle |
| NS | Network Security |
| HS | Hardware Security |
| CPS | Cyber Physical Systems |
| PLT | Physical Layer and Telecommunications Security |
## Complete Example
```json
{
"display_name": "Biometric Breach",
"description": "Investigate a security breach at a high-security research facility. Use biometric forensics tools to identify the intruder, track their movements through the facility, and recover stolen research data before it leaves the building.",
"difficulty_level": 3,
"secgen_scenario": null,
"collection": "security_investigations",
"cybok": [
{
"ka": "AAA",
"topic": "Authentication",
"keywords": ["Biometric authentication", "Fingerprint analysis", "Identity verification"]
},
{
"ka": "F",
"topic": "Artifact Analysis",
"keywords": ["Digital forensics", "Evidence collection", "Fingerprint forensics"]
},
{
"ka": "SOIM",
"topic": "Security Operations & Incident Management",
"keywords": ["Incident response", "Security monitoring", "Access control investigation"]
}
]
}
```
## Minimal Example (Testing Scenario)
```json
{
"display_name": "NPC Patrol Test",
"description": "Test scenario for NPC patrol behaviours",
"difficulty_level": 1,
"secgen_scenario": null,
"collection": "testing"
}
```
## Defaults When mission.json is Missing
If no `mission.json` exists, seeds will apply these defaults:
- `display_name`: Titleized directory name (e.g., "biometric_breach" → "Biometric Breach")
- `description`: "Play the {display_name} scenario"
- `difficulty_level`: 3
- `secgen_scenario`: null
- `collection`: Inferred from name prefix:
- Starts with "test" or "npc-" or "scenario" → "testing"
- Otherwise → "default"

View File

@@ -0,0 +1,146 @@
# Quick Start Checklist
Use this checklist to implement the mission metadata feature. Reference `IMPLEMENTATION_PLAN.md` for detailed code.
## Pre-Implementation
- [ ] Read and understand `IMPLEMENTATION_PLAN.md`
- [ ] Review existing Mission model (`app/models/break_escape/mission.rb`)
- [ ] Review current seeds (`db/seeds.rb`)
- [ ] Confirm Hacktivity's Cybok model structure (if integrating)
---
## Phase 1: Database Migrations
### 1.1 Add columns to missions table
```bash
rails g migration AddMetadataToBreakEscapeMissions secgen_scenario:string collection:string
```
Then edit migration to add:
- Default `'default'` for collection
- Index on collection
Run: `rails db:migrate`
### 1.2 Create CyBOK table
```bash
rails g migration CreateBreakEscapeCyboks
```
Edit to match schema in plan (ka, topic, keywords, polymorphic columns).
Run: `rails db:migrate`
---
## Phase 2: Models
### 2.1 Create Cybok model
- [ ] Create `app/models/break_escape/cybok.rb`
- [ ] Add KA_CODES and CATEGORY_MAPPING constants
- [ ] Add helper methods
### 2.2 Update Mission model
- [ ] Add `has_many :break_escape_cyboks` association
- [ ] Add conditional Hacktivity association
- [ ] Add scopes: `by_collection`, `collections`
- [ ] Add metadata loading methods
---
## Phase 3: Service Layer
### 3.1 Create sync service
- [ ] Create `app/services/break_escape/cybok_sync_service.rb`
- [ ] Implement `sync_for_mission` method
- [ ] Handle dual-mode (standalone vs Hacktivity)
---
## Phase 4: Seeds Update
### 4.1 Update seeds.rb
- [ ] Add mission.json loading logic
- [ ] Add CyBOK sync calls
- [ ] Add fallback defaults
- [ ] Add collection inference for test scenarios
---
## Phase 5: Mission JSON Files
### Priority missions (have scenario content):
- [ ] `scenarios/biometric_breach/mission.json`
- [ ] `scenarios/ceo_exfil/mission.json`
- [ ] `scenarios/cybok_heist/mission.json`
### Secondary (existing scenarios):
- [ ] `scenarios/scenario1/mission.json` (collection: testing)
- [ ] `scenarios/scenario2/mission.json` (collection: testing)
- [ ] `scenarios/scenario3/mission.json` (collection: testing)
- [ ] `scenarios/scenario4/mission.json` (collection: testing)
### Test scenarios (minimal metadata):
- [ ] `scenarios/npc-*/mission.json` (collection: testing)
- [ ] `scenarios/test-*/mission.json` (collection: testing)
---
## Phase 6: Testing
### Standalone mode testing
```bash
# Run migrations
rails db:migrate
# Run seeds
rails db:seed
# Verify in console
rails c
BreakEscape::Mission.count
BreakEscape::Mission.first.break_escape_cyboks.count
BreakEscape::Mission.collections
```
### Hacktivity mode testing (if applicable)
```bash
rails c
# Check both tables populated
BreakEscape::Mission.first.cyboks.count
::Cybok.where(cybokable_type: 'BreakEscape::Mission').count
```
---
## Verification Queries
```ruby
# All missions with CyBOK data
BreakEscape::Mission.joins(:break_escape_cyboks).distinct
# Missions by collection
BreakEscape::Mission.by_collection('testing')
BreakEscape::Mission.by_collection('security_investigations')
# All collections in use
BreakEscape::Mission.collections
# CyBOK entries for a mission
mission = BreakEscape::Mission.find_by(name: 'biometric_breach')
mission.break_escape_cyboks.map { |c| "#{c.ka}: #{c.topic}" }
# Missions by KA code
BreakEscape::Cybok.where(ka: 'AAA').map(&:cybokable).uniq
```
---
## Rollback Commands (if needed)
```bash
# Rollback migrations
rails db:rollback STEP=2
# Reseed with old data
rails db:seed
```

View File

@@ -0,0 +1,29 @@
{
"display_name": "Biometric Breach",
"description": "Investigate a security breach at a high-security research facility. Use biometric forensics tools to identify the intruder, track their movements through the facility, and recover stolen research data before it leaves the building.",
"difficulty_level": 3,
"secgen_scenario": null,
"collection": "security_investigations",
"cybok": [
{
"ka": "AAA",
"topic": "Authentication",
"keywords": ["Biometric authentication", "Fingerprint analysis", "Identity verification", "Multi-factor authentication"]
},
{
"ka": "F",
"topic": "Artifact Analysis",
"keywords": ["Digital forensics", "Evidence collection", "Fingerprint forensics"]
},
{
"ka": "SOIM",
"topic": "Security Operations & Incident Management",
"keywords": ["Incident response", "Security monitoring", "Access control investigation"]
},
{
"ka": "HF",
"topic": "Human Factors",
"keywords": ["Physical security bypass", "Social engineering awareness"]
}
]
}

View File

@@ -0,0 +1,24 @@
{
"display_name": "CEO Exfiltration",
"description": "A corporate espionage scenario where you must navigate executive offices to extract sensitive data. Test your skills in data exfiltration and covert operations.",
"difficulty_level": 4,
"secgen_scenario": null,
"collection": "data_exfiltration",
"cybok": [
{
"ka": "AB",
"topic": "Adversarial Behaviours",
"keywords": ["Corporate espionage", "Data theft", "Covert operations"]
},
{
"ka": "MAT",
"topic": "Malware & Attack Technologies",
"keywords": ["Data exfiltration techniques"]
},
{
"ka": "F",
"topic": "Forensics",
"keywords": ["Anti-forensics", "Evidence handling"]
}
]
}

View File

@@ -0,0 +1,24 @@
{
"display_name": "CyBOK Heist",
"description": "Recover the Professor's backup of the CyBOK LaTeX source files. Navigate through the department offices, solve puzzles, and crack the safe containing the precious backup HDD.",
"difficulty_level": 2,
"secgen_scenario": null,
"collection": "physical_security",
"cybok": [
{
"ka": "HF",
"topic": "Human Factors",
"keywords": ["Physical security", "Lock bypass", "Security awareness"]
},
{
"ka": "C",
"topic": "Cryptography",
"keywords": ["Safe combinations", "Code breaking"]
},
{
"ka": "AC",
"topic": "Applied Cryptography",
"keywords": ["Encoding", "Cipher solving"]
}
]
}