Spaces:
Sleeping
Sleeping
| # OpenSecOpsEnv β Full Audit, MDP Definition & Next Steps | |
| --- | |
| ## 1 Β· Full MDP Definition | |
| This is the formal Markov Decision Process that your environment implements. | |
| ### State Space S | |
| The **hidden state** (never given to the agent directly) is: | |
| | Variable | Type | Description | | |
| |---|---|---| | |
| | `true_root_cause` | `str` | `"infra_failure"` or `"cyber_attack"` | | |
| | `subtype` | `str` | `"memory_leak"`, `"ddos"`, `"data_exfiltration"` | | |
| | `affected_services` | `list[str]` | Services that are actually compromised | | |
| | `attack_progress` | `float β [0,1]` | How far the attack/failure has progressed | | |
| | `noise_level` | `float β [0,1]` | Fraction of log/alert signals that are misleading | | |
| | `metrics[svc]` | `dict` | Per-service `{cpu, memory, latency, error_rate}` | | |
| | `isolated_services` | `set[str]` | Services the agent has isolated | | |
| | `blocked_ips` | `set[str]` | IPs the agent has blocked | | |
| | `step_count` | `int` | Current episode step | | |
| The **observation** (what the agent actually sees) is a stochastic projection of the hidden state: | |
| ``` | |
| O = f(S) + noise | |
| = {alerts, metrics_snapshot, partial_logs, topology, last_action_result, time_step} | |
| ``` | |
| --- | |
| ### Action Space A | |
| Discrete (9 actions), parameterized: | |
| | Action | Parameters | Class | | |
| |---|---|---| | |
| | `query_logs` | `{service}` | Investigation | | |
| | `inspect_metrics` | `{service?}` | Investigation | | |
| | `run_security_scan` | `{target}` | Investigation | | |
| | `restart_service` | `{service}` | Mitigation | | |
| | `scale_service` | `{service, replicas}` | Mitigation | | |
| | `block_ip` | `{ip}` | Mitigation | | |
| | `rollback_deployment` | `{service, version}` | Mitigation | | |
| | `isolate_service` | `{service}` | Mitigation | | |
| | `submit_diagnosis` | `{label}` | Terminal | | |
| --- | |
| ### Transition Function T(s, a) β s' | |
| State evolves deterministically (seeded RNG) after each action: | |
| 1. **`_drift_metrics()`** β Brownian drift + scenario-specific escalation: | |
| - Memory leak: `memory += 1.5/step`, `latency += 10/step` per affected svc | |
| - DDoS: `cpu += attack_progress Γ 5`, `latency += attack_progress Γ 100` | |
| - Exfiltration: `cpu += attack_progress Γ 1.5`, `memory += attack_progress Γ 0.8` | |
| 2. **`_evolve_attack()`** β `attack_progress += 0.08/step` (Γ0.5 if an affected service is isolated) | |
| 3. **Mitigation side-effects** (when correct action taken): | |
| - `restart_service` β resets memory to 35%, latency to 80ms | |
| - `block_ip` β reduces affected svc cpu/latency, attack_progress β0.2 | |
| - `isolate_service` β attack_progress β0.35 | |
| - `scale_service` β cpu β20%, latency β200ms | |
| --- | |
| ### Reward Function R(s, a) β β | |
| Dense per-step reward β [β1.0, +1.0]: | |
| ``` | |
| Investigation on affected service β +0.2 | |
| inspect_metrics({}) (global) β +0.2 (always useful) | |
| run_security_scan on affected + cyber β +0.3 | |
| Correct mitigation β +0.5 | |
| Correct submit_diagnosis β +1.0 | |
| Investigation on NON-affected svc β β0.05 | |
| Ineffective mitigation β β0.1 | |
| Wrong service (not in metrics) β β0.2 | |
| Harmful action (wrong ip/isolate) β β0.5 | |
| Wrong submit_diagnosis β β1.0 | |
| ``` | |
| --- | |
| ### Terminal Condition | |
| Episode ends when: | |
| - Agent calls `submit_diagnosis` (always terminates, reward Β±1.0), OR | |
| - `step_count >= max_steps` (30 / 40 / 50 per task) | |
| --- | |
| ### Grader G(episode) β [0,1] | |
| ``` | |
| score = 0.5 Γ diagnosis_correct | |
| + 0.3 Γ action_efficiency | |
| + 0.2 Γ investigation_quality | |
| diagnosis_correct = 1.0 (exact) | 0.5 (category) | 0.0 | |
| action_efficiency = 0.7 Γ mitigation_recall + 0.3 Γ step_bonus | |
| mitigation_recall = |done β© correct| / |correct| | |
| step_bonus = min(1, ideal_steps / step_count) ideal = max(n_correctΓ3, 5) | |
| investigation_quality = |investigated_affected| / |affected| | |
| ``` | |
| --- | |
| ## 2 Β· Gap Analysis vs Competition Rubric | |
| ### Real-world utility (30%) β **Current: ~25/30 β Target: 28/30** | |
| | Issue | Impact | Fix? | | |
| |---|---|---| | |
| | Only 3 scenario types (memory_leak, DDoS, exfil) | Medium | Add 4th task type | | |
| | False-alert signal in HARD task is labeled "FALSE POSITIVE" in the alert text itself | Medium | Remove the self-labeling hint | | |
| | gateway not treated as a valid investigation target (penalized) | Low-Med | See Β§3-B | | |
| --- | |
| ### Task & grader quality (25%) β **Current: ~20/25 β Target: 24/25** | |
| | Issue | Impact | Fix? | | |
| |---|---|---| | |
| | **run_security_scan counted as investigation not mitigation** β but HARD task lists it in `correct_mitigations`. Results in the agent never getting credit for scans even when done. | **Critical** | Β§3-A | | |
| | Hard task: `affected_services = ["db","auth"]`, but agent gets no IQ credit for scanning β only for `query_logs`/`inspect_metrics`. `run_security_scan:db` should count toward investigation_quality | High | Β§3-A | | |
| | grader `step_bonus` uses `ideal_steps = max(n_correctΓ3, 5)` β for easy task with 1 mitigation this gives `ideal=5` but optimal is 4 steps. Slightly penalizes perfect play | Low | Β§3-C | | |
| --- | |
| ### Environment design (20%) β **Current: ~17/20 β Target: 19/20** | |
| | Issue | Impact | Fix? | | |
| |---|---|---| | |
| | `inspect_metrics({})` gives same +0.2 as targeted inspect. No penalty for already-seen services | Low | Cosmetic | | |
| | `_generate_logs()` called twice per step (in `_build_observation` **and** in `_act_query_logs`) β same pool, different random shuffle | Low | Β§3-D | | |
| | No cooldown on repeat actions β agent can `query_logs:auth` 20Γ for +4.0 reward with no diminishing returns | Medium | Β§3-E | | |
| --- | |
| ### Code quality & spec compliance (15%) β **Current: ~13/15 β Target: 15/15** | |
| | Issue | Impact | Fix? | | |
| |---|---|---| | |
| | `score` in `log_end()` formatted as `:.2f` β spec says 2 decimal places, this is fine | None | β | | |
| | `openenv.yaml` lists `run_security_scan` under `correct_mitigations` for HARD task, but grader never counts it as mitigation | Minor | Β§3-A | | |
| | No `LICENSE` file despite README saying MIT | Low | Β§3-F | | |
| | Docker HEALTHCHECK hits `/health` β β good | β | β | | |
| | `inference.py` falls back to heuristic when no LLM key β β good | β | β | | |
| --- | |
| ### Creativity & novelty (10%) β **Current: ~8/10 β Target: 9/10** | |
| | Issue | Impact | Fix? | | |
| |---|---|---| | |
| | The cache false-alert says "FALSE POSITIVE" in the message text β removes the discovery challenge | Medium | Β§3-B | | |
| | Adding a 4th task would add novelty | Medium | Β§3-G (optional) | | |
| --- | |
| ## 3 Β· Prioritized Fixes (High β Low) | |
| ### A Β· **[CRITICAL] Fix run_security_scan in HARD grader** β `grader.py` + `env.py` | |
| **Problem:** `run_security_scan:db` is listed in `correct_mitigations` for the hard task (`openenv.yaml` line 143-144), but `INVESTIGATION_ACTIONS` in `env.py` means it's never appended to `state.mitigation_actions` β only to `state.investigation_actions`. The grader only counts `mitigation_actions` toward `mitigation_recall`. So an agent doing the right scans gets 0 mitigation credit. | |
| **Fix in `grader.py`:** Count security scans listed in `correct_mitigations` as valid β check both `mitigation_actions` and `investigation_actions` whose keys appear in `correct_mitigations`. | |
| **Also fix in `grader.py`:** Let `run_security_scan:X` count toward `investigation_quality` for service X (already works because investigation_actions includes them, but the set intersection logic uses `parts[1]` which gives the target correctly β this part is actually fine). | |
| ```python | |
| # In grade(), replace mitigation recall calculation: | |
| # Check BOTH lists for items that appear in correct_mitigations | |
| all_taken_actions = set(mitigation_actions) | set(investigation_actions) | |
| n_correct = len(all_taken_actions & set(correct_mitigations)) | |
| ``` | |
| --- | |
| ### B Β· **[HIGH] Remove self-labeling hint from HARD task alert** β `task_definitions.py` | |
| **Problem:** The false-alert message says `"(FALSE POSITIVE)"` in the text. This tells the agent exactly that it's a false alarm, defeating the point of the noise. | |
| **Fix in `task_definitions.py`:** Change: | |
| ```python | |
| # Before | |
| "message": "Cache memory 82% β possible memory leak (FALSE POSITIVE)" | |
| # After | |
| "message": "Cache memory 82% β triggering high-memory threshold policy" | |
| ``` | |
| --- | |
| ### C Β· **[MEDIUM] Fix step_bonus for easy task** β `grader.py` | |
| **Problem:** `ideal_steps = max(n_correct * 3, 5)` gives 5 for easy task (1 correct mitigation Γ 3 = 3, max with 5 = 5). But the optimal trajectory is 4 steps (inspect, query, restart, submit). An agent doing it perfectly in 4 steps gets `step_bonus = 5/4 = 1.25 β clamped to 1.0` β still OK. For medium: `3 Γ 3 = 9`, optimal is 7 β fine. This is actually acceptable, just add a note. | |
| --- | |
| ### D Β· **[MEDIUM] Prevent log double-generation and add repeat-action diminishing returns** β `env.py` | |
| **Problem:** When `query_logs` is called, `_act_query_logs` calls `_generate_logs()` and then `_build_observation` also calls `_generate_logs()` again. Two different shuffles, slightly incoherent. Fix by building the observation first with the same log pool. | |
| **Also:** An agent can harvest +0.2 per step by repeatedly calling `query_logs:auth` on the affected service. Add a per-service "already investigated" diminishing return. | |
| **Fix in `env.py`:** Maintain a `_investigated: dict[str, int]` counter. First time: full reward. Second time same service: +0.05. Third+ time: 0. | |
| ```python | |
| # In __init__: | |
| self._investigated: dict[str, int] = {} | |
| # In _act_query_logs / _act_inspect_metrics: | |
| count = self._investigated.get(svc, 0) | |
| self._investigated[svc] = count + 1 | |
| if svc in self._hidden.affected_services: | |
| reward = 0.2 if count == 0 else (0.05 if count == 1 else 0.0) | |
| ``` | |
| --- | |
| ### E Β· **[LOW] Add LICENSE file** β root | |
| Create a `LICENSE` file with the MIT license text. Required for open-source credibility and referenced by README. | |
| --- | |
| ### F Β· **[LOW / OPTIONAL] Add a 4th task** β `task_definitions.py` + `openenv.yaml` | |
| A `medium_hard` task such as a **bad deployment rollback** scenario would: | |
| - Fill the gap between medium (0.73) and hard (0.795) β scores are surprisingly close | |
| - Add a `misconfiguration:bad_config` or `infra_failure:service_crash` scenario using `rollback_deployment` action (currently never the correct mitigation in any task!) | |
| This is optional but would increase **creativity score** and make `rollback_deployment` useful. | |
| --- | |
| ## 4 Β· Files to Edit (Summary Table) | |
| | File | Change | Priority | | |
| |---|---|---| | |
| | `opensecops_env/grader.py` | Fix mitigation_recall to include investigation_actions β© correct_mitigations | π΄ Critical | | |
| | `opensecops_env/tasks/task_definitions.py` | Remove "(FALSE POSITIVE)" from hard task alert text | π High | | |
| | `opensecops_env/env.py` | Add diminishing returns for repeat investigation on same service | π‘ Medium | | |
| | `opensecops_env/env.py` | Fix double log generation (minor) | π‘ Medium | | |
| | `openenv.yaml` | Clarify that run_security_scan mitigations in HARD task are also graded as investigation | π‘ Medium | | |
| | `LICENSE` | Add MIT license text | π’ Low | | |
| | `README.md` | Update baseline scores to actual measured values (1.00, 0.73, 0.795) | π’ Low | | |
| | `opensecops_env/tasks/task_definitions.py` | (Optional) Add 4th task | π΅ Optional | | |
| --- | |
| ## 5 Β· Should You Fine-tune? | |
| **No, you do not need to fine-tune any model.** Your environment is the submission, not the model. | |
| The competition evaluates **your environment quality** β judges will run their own models against it. The inference script produces a *baseline* score that shows the environment is solvable. | |
| What you should optimize instead: | |
| | Concern | What to do | | |
| |---|---| | |
| | Baseline score too low | Improve the system prompt in `inference.py` β more explicit strategy hints | | |
| | Baseline score too high | Make tasks harder (more noise, more red herrings) | | |
| | Scores not deterministic | Ensure `_rng` is seeded before EVERY `_generate_logs` call | | |
| | Runtime > 20min | `MAX_STEPS = 20` already set β keep it; 3 tasks Γ 20 steps Γ ~1s/LLM call β 1 min | | |
| **System prompt improvements to `inference.py`** that would raise the baseline score: | |
| 1. Add explicit instruction: *"When investigating, always check ALL services in the topology, not just the most obvious one"* | |
| 2. Add: *"The gateway / frontend service is rarely the root cause; look at the backend services it calls"* | |
| 3. Add: *"You MUST call `run_security_scan` on suspicious services before submitting if you suspect a cyber attack"* | |
| --- | |
| ## 6 Β· Deployment Checklist (Before Submitting to HF Spaces) | |
| - [ ] Run `docker build -t opensecops-env:latest .` locally β (user is doing this now) | |
| - [ ] Run `docker run -p 8000:8000 opensecops-env:latest` and verify `/health` returns 200 | |
| - [ ] `curl -X POST http://localhost:8000/reset -d '{}' -H 'Content-Type: application/json'` returns observation | |
| - [ ] Set `HF_TOKEN` and run `python inference.py` β verify 3Γ `[START]`/`[STEP]`/`[END]` blocks print | |
| - [ ] Push to HF Spaces with correct `README.md` `sdk: docker` header | |
| - [ ] Set Space secrets: `API_BASE_URL`, `MODEL_NAME`, `HF_TOKEN` | |
| - [ ] Ping `https://your-space.hf.space/reset` with POST β verify 200 | |
| ### HF Spaces `README.md` header (required at top of HF repo README): | |
| ```yaml | |
| --- | |
| title: OpenSecOpsEnv | |
| emoji: π | |
| colorFrom: blue | |
| colorTo: green | |
| sdk: docker | |
| app_port: 8000 | |
| tags: | |
| - openenv | |
| - reinforcement-learning | |
| - secops | |
| - agent-evaluation | |
| --- | |
| ``` | |