The Problem
Organizations struggle to capture, score, and track IT & cyber risk across the full life cycle. Existing tools either focus on ticketing, asset inventory, or vulnerability scans, but not on a continuous risk register that records probability‑impact scores, action plans, and audit trails.
What This Does
RiskOps is a PHP 8.3 web app that stores a risk register, computes a 5 × 5 probability‑impact matrix on the server, and links each risk to mitigation actions.
- The core data model lives in
database/schema.sql;includes/risk.phpcontains the logic for score calculation (GENERATED ALWAYS AS (likelihood * impact)). - Risk CRUD is handled by the actions folder (
actions/create.php,actions/update.php, etc.), while administration of categories, departments, and users is inadmin/. - User authentication is done in
auth/(e.g.,auth/login.php), and session handling is bootstrapped inincludes/bootstrap.php. - Reporting and export are in
reports/and the CSV exports are driven byreports/export_csv.php.
How It Is Wired
Execution starts at the web root index.php.
index.phpincludesincludes/bootstrap.php.bootstrap.phploadsconfig/config.php, establishes a PDO connection (includes/db.php) and sets up global helpers (includes/functions.php).- The requested URL is parsed by PHP’s native routing (each
.phpfile is a separate endpoint). For example, a POST toactions/create.phptriggersinclude 'includes/risk.php', then callsRisk::create($data)which inserts into theriskstable. Risk::createwrites the record, relies on the database trigger for thescorecolumn, and returns the new ID.- The script then redirects or returns JSON, depending on the
X-Requested-Withheader. - Audit logs are written by
includes/audit.phpwheneveradmin/endpoints modify data, and user actions are tracked throughincludes/debug.php.
The biggest blast‑radius functions are Risk::create/update/delete and Audit::log; they touch the database and the audit log table. The routing layer is a simple include‑by‑URL scheme, so a change in any endpoint file only affects that path. No cyclic dependencies are detected in the call graph; all helpers are stateless.
How To Use It
Setup
git clone https://github.com/moses-y/riskops
cd riskops
cp config/database.example.php config/database.php
docker compose up -d
Configuration Edit config/database.php with your MariaDB credentials. The Docker image automatically creates a database using database/schema.sql and seeds it via database/seed.sql.
Running Access the web UI at http://localhost:8080. No PHP built‑in server is needed because Docker exposes port 8080.
Real‑World Use
A security analyst creates a new risk:
curl -X POST http://localhost:8080/actions/create.php \
-H "Content-Type: application/json" \
-d '{"title":"Third‑party API exposure","likelihood":4,"impact":5,"category_id":2}'
The response contains the new risk ID, and the risk appears in the dashboard (dashboard/index.php).
Code Health & Issues
- No CI/CD – the repo lacks a
.github/workflows/folder or other CI config. - Limited tests – only four test files are present, not covering the full data model.
- No linting or static analysis – no
phpcs.xml,composer.json, or similar. - Secrets – no obvious secrets committed; configuration files are placeholders.
These observations come solely from the file structure; deeper static analysis was not performed.
The Bottom Line
RiskOps delivers a focused risk‑management platform without the overhead of a full framework. It is ready to run in Docker with minimal configuration. The lack of automated testing and CI makes it suitable for internal use where rapid iteration is acceptable, but it would need additional quality gates before production deployment. The code is straightforward to extend; the routing is simple, and the database layer is isolated, making maintenance manageable for a mid‑size dev team.