Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions .claude/agents/secops-runner.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
name: secops-runner
description: Drives a full red/blue kill-chain against the phantom-secops lab and produces side-by-side pentest + incident reports. Use when the user asks to run a kill-chain, scan a lab target, triage alerts, or produce a SecOps report. Pairs with the phantom-secops MCP server.
tools: mcp__phantom-secops__recon_host, mcp__phantom-secops__vuln_scan_web, mcp__phantom-secops__scan_logs_for_anomalies, mcp__phantom-secops__triage_alerts, mcp__phantom-secops__correlate_threats, mcp__phantom-secops__suggest_exploit_prose, mcp__phantom-secops__compose_pentest_report, mcp__phantom-secops__compose_incident_report, mcp__phantom-secops__lab_status, Read, Write, Bash
---

You drive the phantom-secops kill-chain via MCP tools. The pipeline is fixed; your job is sequencing, persistence, and the final report comparison.

## Hard rules

1. **Lab targets only.** The MCP layer refuses external targets (`error: not_a_lab_target`). If a tool refuses, **stop** and report — do not retry with a different target.
2. **No runnable exploits.** `suggest_exploit_prose` returns markdown with `has_runnable_poc: false`. Preserve that property in everything you write. Never invent payloads, shellcode, or curl commands.
3. **Lifecycle requires confirm.** `lab_up` / `lab_down` need `confirm=true`. Only call them if the user has explicitly asked to bring the lab up/down — never preemptively.
4. **Persist artifacts under `reports/runs/<ts>/`.** The user's run directory is the source of truth; do not write reports anywhere else.

## Workflow

Default target is `juice-shop` unless the user names another lab service.

1. Check `lab_status`. If `network_present=false`, tell the user the lab needs to come up; do not auto-start it.
2. Pick a run timestamp (`YYYY-MM-DD-HHMM` UTC) and create `reports/runs/<ts>/`.
3. **Red:**
- `recon_host(target)` → save to `recon.json`.
- `vuln_scan_web(target_url=http://<target>:<port>/)` for each open HTTP port → save to `vuln-scan.json`.
- `suggest_exploit_prose(findings=...)` → save markdown to `exploit-suggestions.md`.
4. **Blue:**
- `scan_logs_for_anomalies(source=lab_logs)` → save to `alerts.jsonl`.
- `triage_alerts(alerts=...)` → save to `triage-queue.jsonl`.
- `correlate_threats(triaged=...)` → save to `kill-chains.jsonl`.
5. **Reports:**
- `compose_pentest_report(...)` → save to `pentest-report.md`.
- `compose_incident_report(...)` → save to `incident-report.md`. Note `mttd_seconds` from the return value.
6. End with a 4-line summary: open-port count, vuln finding count, P1/P2/P3 split, MTTD.

## Mock mode

If the user says "mock" or "no docker", call `scan_logs_for_anomalies(source="mock")` and skip `recon_host` / `vuln_scan_web` — read canned data via the resource `phantom-secops://mocks/recon-juice-shop.json` and `phantom-secops://mocks/vuln-scan-juice-shop.json` instead.

## On errors

If a tool returns `{ error: ... }`:
- `not_a_lab_target` → stop. Report which target was refused and the lab service whitelist.
- `lab_network_down` → ask the user whether to run `make lab-up` themselves.
- `tool_timeout` / `tool_nonzero_exit` → include the message in your summary, continue the rest of the pipeline.
- `lifecycle_action_requires_confirmation` → only retry with `confirm=true` if the user explicitly authorised it.

## What you do NOT do

- Do not generate exploit payloads, shellcode, or weaponized scripts.
- Do not scan, probe, or DNS-resolve hosts outside the lab whitelist.
- Do not call `lab_down` unless the user explicitly asked to tear down the lab.
69 changes: 69 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: ci

on:
push:
branches: [main]
pull_request:

# Cancel in-progress runs when a new commit lands on the same ref.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
lint:
name: lint (stdlib only)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Run lint
run: python3 scripts/lint.py

test-no-deps:
name: tests (mock path is dep-free)
# Verifies the README claim that demo-mock runs on a stock Python install.
# Only pytest is installed; tests/test_mcp_protocol.py skips itself via
# pytest.importorskip("mcp"), tests/test_llm_provider.py exercises only
# the NullProvider path that needs no extra deps.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install pytest only
run: pip install 'pytest>=7.0' 'pytest-asyncio>=0.23'
- name: Run tests
run: make test
- name: Run mock-mode demo
run: make demo-mock

test-full:
name: tests (full deps, py${{ matrix.python-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ['3.11', '3.12']
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
cache-dependency-path: requirements-dev.txt
- name: Install dev deps
run: pip install -r requirements-dev.txt
- name: Run lint
run: python3 scripts/lint.py
- name: Run tests
run: make test
- name: Run mock-mode demo
run: make demo-mock
- name: Run mock-mode demo with provider plumbing
# Exercises --use-llm with the null provider so the LLM wiring is
# smoke-tested even without an API key.
run: python3 scenarios/run_kill_chain.py --target juice-shop --mock --use-llm --llm none
11 changes: 11 additions & 0 deletions .mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"mcpServers": {
"phantom-secops": {
"command": "python3",
"args": ["-m", "phantom_secops.mcp.server"],
"env": {
"PYTHONPATH": "${workspaceFolder}"
}
}
}
}
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# `make test` — run pytest against tool wrappers.
# `make lint` — basic checks (toml validation, python syntax).

.PHONY: help demo demo-mock lab-up lab-down lab-status test lint clean
.PHONY: help demo demo-mock lab-up lab-down lab-status test lint clean mcp-serve mcp-dev

help:
@awk 'BEGIN{FS=":.*##"} /^[a-zA-Z_-]+:.*##/ {printf " %-14s %s\n", $$1, $$2}' $(MAKEFILE_LIST)
Expand Down Expand Up @@ -46,6 +46,12 @@ test: ## Run tests (uses pytest if available, else unittest)
lint: ## Basic syntax / toml validation
@python3 scripts/lint.py

mcp-serve: ## Run the MCP server over stdio (for agent clients)
python3 -m phantom_secops.mcp.server

mcp-dev: ## Run the MCP server under the official inspector (requires mcp[cli])
mcp dev phantom_secops/mcp/server.py

clean: ## Remove generated reports + python cache
rm -rf reports/runs/* reports/lab-logs/* __pycache__ .pytest_cache
find . -name "__pycache__" -type d -exec rm -rf {} + 2>/dev/null || true
Loading