Skip to content

Commit a27c628

Browse files
committed
docs: rewrite README to professional standard
Developed by Romil Shah with assistance from Claude Code.
1 parent 89be04e commit a27c628

1 file changed

Lines changed: 106 additions & 18 deletions

File tree

README.md

Lines changed: 106 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,132 @@
11
# SOC Dashboard
22

3-
A Flask and PostgreSQL dashboard where a security analyst works through alerts, triages them, and watches response-time numbers.
3+
SOC Dashboard is a Flask and PostgreSQL web app for triaging security alerts. It takes alerts over a REST API (log-analyzer pushes them, though any tool sending the right JSON works), holds them in a severity-ranked queue, and lets an analyst mark each one true positive, false positive, or escalation in a single click. It timestamps every action and turns those into SOC KPIs (MTTR, SLA-breach rate, escalation rate) drawn with Chart.js. It is the triage stage of a two-part pipeline: [log-analyzer](https://github.com/Romil2112/log-analyzer) detects the incidents, SOC Dashboard is where a person works them.
44

5-
## What feeds it
5+
## How it works
66

7-
Alerts come from [log-analyzer](https://github.com/Romil2112/log-analyzer). Its `--push-soc` flag POSTs each detected incident to `POST /api/alerts` here, and the alert lands in the open queue. Any tool that can send the right JSON with a valid API key can feed it, but log-analyzer is what it was built against.
7+
Alerts reach the dashboard two ways and both land in one queue. A detector POSTs to the ingest endpoint with an API key; analysts sign in and work the queue in the browser. Every read and write goes through one PostgreSQL database holding three tables (alerts, analyst_actions, users), and `/api/stats` aggregates that into the charts and the SLA and MTTR numbers.
88

9-
## The queue
9+
Security sits on a few specific choices. The ingest endpoint checks its API key with a constant-time comparison, so response timing does not reveal how much of the key was right. Analyst passwords are stored as bcrypt hashes and there is no self-registration: accounts are created from the CLI. Session routes sit behind CSRF protection, while the machine-to-machine ingest route is exempt because it authenticates by key rather than by cookie. Errors return JSON with a fixed message and no stack trace, so a failed request does not leak internal file paths.
1010

11-
Alerts sort by severity, CRITICAL down to LOW. An analyst opens the queue and marks each one true positive, false positive, or escalate with a single click. Every action is timestamped, which is what the KPIs are built on.
11+
`/api/stats` was the slow path. The stats endpoint used to run a correlated subquery once per alert row; I replaced it with a single aggregate join and the query went from 24ms to 12ms at 20,000 alerts. The rewrite scans analyst_actions once and LEFT JOINs it to alerts instead of re-querying per row, and the SLA and MTTR values it returns are unchanged.
1212

13-
## API security
13+
## Features
1414

15-
The ingest endpoint checks its API key with a constant-time comparison, so response timing doesn't leak how much of the key was right. Session routes sit behind CSRF protection. Errors come back as JSON with no stack traces, so a failed request doesn't hand internal file paths to the caller.
15+
- Severity-ranked alert queue (CRITICAL down to LOW) with one-click triage
16+
- REST ingest API guarded by a constant-time `X-API-Key` check
17+
- Flask-Login analyst auth, bcrypt-hashed passwords, no self-registration
18+
- CSRF protection on session routes; JSON error handlers with no stack-trace leaks
19+
- SOC KPIs: MTTR per analyst, SLA-breach rate per severity target, escalation rate
20+
- Live filters by severity, detection source, and assignee that drive both the queue and the charts
21+
- Chart.js views: alerts by category, by severity, by source, and a 7-day MTTR trend
22+
- Fernet field-level encryption at rest for `title`, `source_ip`, and `description`
23+
- Configurable retention purge (`ALERT_RETENTION_DAYS`)
24+
- 50 pre-seeded demo alerts across 5 categories, 4 severities, and 5 detection sources
25+
- 48 pytest tests at 95% line / 92% branch coverage, run against a real PostgreSQL
1626

17-
## The /api/stats rewrite
27+
## Quick Start
1828

19-
`/api/stats` reports counts and SLA numbers across every alert. The first version ran a correlated subquery for each alert row to pull its matching action, so the database repeated the same lookup thousands of times over. I rewrote it as one aggregate join that groups the actions in a single pass. At 20,000 alerts the endpoint dropped from 24ms to 12ms, with the same output.
29+
Prerequisites: Python 3.12+ and PostgreSQL 14+.
2030

21-
## KPIs
31+
Install:
2232

23-
It tracks three things per analyst: MTTR (mean time to resolve), SLA-breach rate against per-severity response targets, and escalation rate, the share of triaged alerts sent on to incident response. Chart.js draws them, and the queue and charts both filter live by severity, detection source, and assignee.
24-
25-
## Tests
26-
27-
48 pytest tests at 95% line and 92% branch coverage. They run against a real PostgreSQL database, not a mock, through a Flask test client on GitHub Actions with a Postgres service container.
33+
```bash
34+
pip install -r requirements.txt
35+
```
2836

29-
## Running it locally
37+
Create and seed the database:
3038

3139
```bash
32-
pip install -r requirements.txt
3340
createdb soc_dashboard
3441
psql soc_dashboard -f schema.sql
3542
python seed.py
3643
```
3744

38-
Set `FLASK_SECRET_KEY` and `ALERTS_API_KEY` in a `.env` file (the app won't start without the secret key), create an analyst account with `python manage.py create-user alice 'passphrase' --role analyst`, then:
45+
Copy `.env.example` to `.env` and set at least `FLASK_SECRET_KEY` (the app will not start without it) and `ALERTS_API_KEY` (required for ingest). Generate a value with `python -c "import secrets; print(secrets.token_hex(32))"`. Create an analyst account, then run the app:
3946

4047
```bash
48+
python manage.py create-user alice 's0me-strong-passphrase' --role analyst
4149
python app.py
4250
```
4351

4452
It listens on <http://localhost:8000>. `docker compose up` starts Flask and PostgreSQL together instead.
53+
54+
## API reference
55+
56+
| Method | Endpoint | Description |
57+
|--------|----------|-------------|
58+
| GET | `/` | Main dashboard |
59+
| GET | `/analyst` | Analyst performance page |
60+
| GET | `/api/alerts` | Open alerts sorted by severity. Filterable: `?severity=&source=&assigned_to=` |
61+
| GET | `/api/alerts/all` | All alerts. Same filter query params as above |
62+
| POST | `/api/alerts` | **Ingest** a new alert `{title, category, severity, source?, source_ip?, description?}` → 201 |
63+
| POST | `/api/alerts/<id>/classify` | Classify alert `{analyst, action}` |
64+
| GET | `/api/stats` | Summary counts + `by_category` / `by_severity` / `by_source` + `escalation` + `sla` + MTTR by analyst + `assignees` |
65+
66+
`action` is one of `classify_tp` (→ `true_positive`), `classify_fp` (→ `false_positive`),
67+
or `escalate` (→ `escalated`). Filter query params are validated against a column
68+
whitelist, so they compose into parameterized SQL safely (no injection surface).
69+
70+
### Environment variables
71+
72+
Copy `.env.example` to `.env` and fill these in. The two required ones make the app refuse to start (or refuse ingest) if missing; the rest have safe defaults.
73+
74+
| Variable | Required | Default | Purpose |
75+
|---|---|---|---|
76+
| `FLASK_SECRET_KEY` | yes || Signs analyst login sessions; app won't start without it |
77+
| `ALERTS_API_KEY` | for ingest || `X-API-Key` that `POST /api/alerts` checks (constant-time) |
78+
| `DATABASE_URL` || `postgresql://localhost/soc_dashboard` | PostgreSQL connection string |
79+
| `DB_ENCRYPTION_KEY` || unset (plaintext) | Enables Fernet encryption of title/source_ip/description at rest |
80+
| `ALERT_RETENTION_DAYS` || `0` (keep forever) | Purge alerts older than N days at startup |
81+
| `FLASK_DEBUG` || off | Set `1`/`true` for the Werkzeug debugger (local dev only) |
82+
| `HOST` / `PORT` || `127.0.0.1` / `8000` | Bind address and port for `python app.py` |
83+
84+
## Architecture diagram
85+
86+
Two ways alerts arrive, one queue they land in. A detector (log-analyzer, or any tool) pushes alerts over the API-key-protected ingest endpoint; analysts sign in, work the queue, and classify each alert, which records an action and its response time. Everything reads and writes one PostgreSQL database, and the stats endpoint aggregates it into the charts and the SLA/MTTR numbers on the dashboard.
87+
88+
```mermaid
89+
flowchart LR
90+
D[Detector<br/>e.g. log-analyzer] -->|POST /api/alerts<br/>X-API-Key| ING[Ingest]
91+
A[Analyst<br/>browser] -->|login session| WEB[Dashboard + queue]
92+
ING --> DB[(PostgreSQL<br/>alerts · analyst_actions · users)]
93+
WEB -->|classify / escalate| DB
94+
DB --> STATS[/api/stats<br/>counts · MTTR · SLA · escalation]
95+
STATS --> CHARTS[Chart.js dashboard]
96+
subgraph Security
97+
CSRF[CSRF on session routes]
98+
ENC[Fernet field encryption at rest]
99+
end
100+
```
101+
102+
## Tests
103+
104+
48 pytest tests cover the ingest API, auth and CSRF, the classify/escalate flow, the KPI math (MTTR, SLA, escalation), the filter query params, encryption at rest, and the seed and user-management CLIs, at 95% line and 92% branch coverage. They run against a real PostgreSQL database through a Flask test client, both locally and on GitHub Actions with a Postgres service container. Point `DATABASE_URL` at a throwaway database and run:
105+
106+
```bash
107+
python -m pytest tests/ -v
108+
```
109+
110+
## ⚖️ Legal Notice & Responsible Use
111+
112+
This project is **free and open-source software**, released under the **MIT License** as a
113+
**demonstration / learning / trial project**. It is provided **"as is", without warranty of
114+
any kind**, and is **not an audited or certified commercial security product**.
115+
116+
- **Authorized use only.** Use it solely on systems, networks, and data that you own or are
117+
**explicitly authorized** to operate and analyze.
118+
- **Do no harm.** Do not use it to surveil, stalk, harass, invade the privacy of, or conduct
119+
unauthorized monitoring of any person or organization.
120+
- **Compliance is the operator's responsibility.** Alert data may include IP addresses and
121+
other details that qualify as personal data. Compliance with **GDPR, CCPA, HIPAA, and
122+
equivalent laws** — where applicable — rests with the operator.
123+
- **Misuse may be illegal.** Unauthorized access to or monitoring of computer systems may
124+
violate laws such as the U.S. **CFAA**, the UK **Computer Misuse Act**, and EU
125+
information-systems directives.
126+
127+
By using this software you accept responsibility for operating it lawfully. See
128+
[SECURITY.md](SECURITY.md) to report a vulnerability.
129+
130+
## License
131+
132+
MIT — see [LICENSE](LICENSE).

0 commit comments

Comments
 (0)