Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Open-SOC

A SOC shift simulator for training analysts. One hour on the floor, an alert every two to three minutes, eleven initial-access scenarios spanning machines and identities, plus six short-form alerts so that no two shifts ever look alike. The student investigates in a fictional SIEM and EDR, decides, isolates, disables, or notifies the client, writes a short report at every closure, and gets a detailed score at the end of the shift.

The simulated analyst is the one who responds, not the one who escalates: there is no higher tier to hand the case off to. An escalated alert would be a dropped one.

Static application: three files opened in a browser are enough. No dependencies, no server, no network connection, no data transmitted.

🔗 Live demo: https://spellskite-coding.github.io/Open-SOC/

Companion app to Open-Forensics: where Open-SOC trains decision-making under pressure, Open-Forensics trains methodical reconstruction.


Getting started

git clone <your-repo> Open-SOC
cd Open-SOC

Then, either:

python3 -m http.server 8000
# then http://localhost:8000/

The test suite opens the same way: tests/run.html. It runs in the browser and displays ALL TESTS PASS or the list of failures. No external tooling, no npm install.


What the exercise trains

The simulator targets precisely the response analyst's craft on initial access:

  1. Triage under pressure. Alerts land every two to three minutes, regardless of the chosen difficulty or shift length: the pace isn't a setting, it's the constraint of the job. Difficulty doesn't change the cadence, it changes the subtlety of the cases. Each alert's countdown starts when it's received, not when it's opened.
  2. Correlate. Scenarios arrive as several alerts interleaved with those of other scenarios and buried in realistic background noise. No case ID is provided: matching happens on entities (host, account, address).
  3. Investigate with internal sources only. SIEM, EDR, identity directory, and knowledge base. No OSINT, no external CTI: everything needed to decide is inside the app, including the internal intelligence sheets.
  4. Decide and remediate. Isolate the right host, disable and reset the right account, revoke MFA methods when identity persistence has been established.
  5. Escalate to the client when that's the correct response. Some situations admit neither inaction nor isolation: a brute force against a domain controller isn't addressed by cutting off the directory for the entire company, and perimeter blocking isn't the SOC's call to make. The Client Communication button is then the expected action, and it's scored as such. Playbook PB-005, inside the app, gives the two criteria: remediation doesn't belong to the SOC, or containment would have a major business impact.
  6. Know when to do nothing. Four cases out of seventeen are legitimate activity that looks like an attack (an authorized scan, a declared relocation, a contracted penetration test). Isolating a security scanner or disabling an executive's account on the strength of an unusual country is scored as a production error.
  7. Report. Every closure requires a verdict, a severity, an ATT&CK technique, and an 80-to-700-character report citing at least one factual element from the case. The technique palette covers the full range observable from a SOC — 140 techniques across 14 tactics, from reconnaissance to impact, including lateral movement, collection, and exfiltration. It's grouped by tactic and filterable by keyboard (lsass, rdp, lateral, T1021…), because a list this size would be unusable under pressure otherwise. Picking the wrong tactic is part of the exercise: the palette isn't limited to the techniques actually used by the shipped scenarios.

The library

Eleven complete scenarios ranked by difficulty (3 easy, 4 medium, 4 hard), each made of two or three alerts to correlate, including three benign cases spread across the levels. Families covered: phishing and user execution, password spraying, MFA fatigue and abusive OAuth consent, fake browser update, RDP brute force against an exposed service, webshell on an exposed application, trojanized installer and LSASS dumping, brute force against an exposed domain controller, authorized vulnerability scan, declared business travel, authorized internal penetration test.

Six short-form alerts are added on top: single-alert cases (antivirus test file outside the expected window, out-of-scope service account, mass email send, declared PsExec deployment, execution blocked from a USB drive, deletion of restore points). They fill the shift's remaining capacity, which removes idle stretches and means a replayed shift never quite looks like the previous one.

Two composition modes:

  • Random draw — the shift pulls from the library to fill the chosen duration (15, 30, 60, or 90 minutes) according to the difficulty filter, with at least one benign case guaranteed. A 90-minute shift plays all eleven complete scenarios.
  • Manual selection — you check exactly which cases to play, and the shift duration adapts to chain them at the floor's pace. Titles stay hidden by default to preserve the exercise; a checkbox reveals them to prepare a session.

A preview shown under the settings announces, before clocking in, the number of scenarios, alerts, the effective duration, and the actual gap between two alerts.

Instructors: ground truth (verdict, correct actions, debrief) lives in js/data/scenarios.js and js/data/injects.js. Both files carry a warning at the top. Ask students not to open them before playing — it's the only way to cheat, and it's explicit.


Scoring

Each closed alert is worth up to 12 points: verdict (4), severity (2), technique (2), report quality (2), meeting the deadline (2). Each scenario is additionally worth its remediation points: isolation (6), account disabling (5), client communication (5), password reset (3), MFA revocation (3), plus a containment-speed bonus (up to 4). A benign case correctly identified and left untouched earns 8 restraint points — but doing nothing at all earns nothing: restraint is only rewarded if the case was correctly identified.

Penalties target the damage caused:

Fault Cost
Explicitly forbidden action (isolating a domain controller, for example) −8
Action on a host or account unrelated to any case in the shift −6 (−3 if undone before the end)
Unnecessary action on an entity from the case −1.5, capped at −4.5 per scenario
Client communication about activity the client itself declared −1.5
Client communication about an entity outside the case −2

Notifying the client of a real incident is never penalized, even when it wasn't required. And an action required by one case is never held against you under another case that happens to mention the same host: a faulty action is only charged once, at the highest rate it deserves.

The final debrief gives a score out of 100, a grade from A to E, five axes (detection, containment, accuracy, responsiveness, reporting), the real story of each scenario played, actions taken, missed, and unnecessary, the case's pitfalls, an alert-by-alert breakdown, the action log, and an exportable JSON report.

Results from the last twelve shifts are kept in the browser's localStorage, solely to display progress. No written report is stored there.


Security posture

The application is meant to be audited: it's a security-training tool, and it would be poor form for it to set a bad example.

  • No dependencies. Zero third-party libraries, zero remote resources, zero external fonts. The whole repository can be read in one sitting.
  • No network. connect-src 'none': the page cannot issue a request, even if code were added to try. It works in an isolated environment.
  • Strict Content Security Policy, declared in index.html: default-src 'none'; scripts and styles restricted to the origin; no inline script or style; object-src 'none'; base-uri 'none'; form-action 'none'.
  • No HTML injection possible. The DOM is built exclusively with createElement and textContent. There is not a single occurrence of innerHTML, outerHTML, insertAdjacentHTML, document.write, eval, or new Function in the code. Reports written by the analyst are rendered as plain text.
  • Locked-down element factory. util.el() refuses by construction any on* attribute, as well as style, href, src, srcdoc, action, formaction, ping, and background, none of which are used anywhere.
  • Local storage validated on read. History entries are filtered, typed, and bounded in length and count before being displayed; unavailable or corrupted storage does not prevent the app from working.
  • Frozen data. The inventory and scenarios are recursively frozen with Object.freeze at startup.

Quick check:

grep -rnE "innerHTML|outerHTML|insertAdjacentHTML|document\.write|eval\(|new Function" js/
# should return nothing

Everything is fictional

No organization, person, machine, or address in this repository exists. IP addresses are drawn from the ranges reserved for documentation (RFC 5737: 192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24), and domains use the reserved TLDs .test, .invalid, and .example (RFC 2606). No indicator of compromise from this simulator can therefore point to a real resource, and the exercise logs won't pollute a threat intelligence platform if a student accidentally copies them out.


Structure

index.html            Single page, Content Security Policy
css/opensoc.css       Single stylesheet, dark theme
js/util.js            Safe DOM, deterministic randomness, formatting
js/data/org.js        Fictional organization: fleet, directory, knowledge base
js/data/scenarios.js  The eleven complete scenarios and their ground truth (SPOILERS)
js/data/injects.js    The six short-form alerts and their ground truth (SPOILERS)
js/engine.js          Shift generation, clock, remediation, scoring (no DOM)
js/ui.js              Rendering and interactions
js/app.js             Bootstrapping and safety net
tests/run.html        Test suite runnable in the browser
tests/tests.js        85 assertions: data, engine, pacing, scoring, timing leaks

The engine never touches the DOM: that's what lets the tests simulate complete shifts — a perfect shift, a passive shift, a reckless shift — and verify that scoring separates them correctly.


Browser compatibility

The code is written in ES5 JavaScript: no modern syntax (no let, const, arrow functions, template literals, or modules), no promises, no network calls. The only slightly recent APIs are guarded: Math.imul has a built-in fallback, crypto.getRandomValues and localStorage are wrapped in try/catch with fallback behavior, globalThis is tested before use, and the escape key recognizes both Escape and Esc as well as key code 27.

On the stylesheet side, :focus-visible is preceded by a :focus rule that serves as a fallback, and the score gauge keeps a solid background if conic-gradient isn't recognized. The application therefore works on Firefox, Chrome, Edge, Safari, and their mobile counterparts, both on desktop and over file://.

This repository was validated under headless Firefox (rendering every view and running the test suite). If you notice a defect on another engine, open an issue with the browser and its version.


Adding a scenario

Add an object to js/data/scenarios.js — or to js/data/injects.js if it's a short single-alert case meant to be mixed in with the others. The contract is described at the top of the file; in short: alerts[] with their triggering logs, hosts{} and accounts{} for telemetry revealed over time, involved for the case's entities, and truth for the verdict, the required and tolerated actions, the narrative, and the pitfalls. at offsets are in minutes relative to the scenario's start; a negative value is an earlier event already present in the logs.

The technique palette in js/data/org.js is deliberately much broader than what the scenarios actually use: a new case can almost always be written without touching it. If you do need to extend it, follow the { id, tactic, name } format — the tactic must exist in ORG.tactics and the label must start with the ID followed by a colon, both of which the tests check.

Then open tests/run.html: the tests automatically verify that the entities cited exist in the inventory, that required actions target entities from the case, that the expected techniques appear in the closure selector, and that the two-to-three-minute pace holds across every combination of duration and difficulty.


License

MIT — see LICENSE. Free to use in training centers, schools, or internally.

About

SOC shift simulator for training analysts!

Topics

Resources

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages