PLATO Security Audit Room
Automated security auditing as a PLATO engine block — the second room in the SuperInstance ecosystem.
What is this?
A PLATO Room that runs security audits on code changes. It follows the PLATO architecture:
| Concept | In this room | |
|---|---|---|
| Sensors | Diff vulnerability patterns, secret detection, dependency scan, file risk scoring | Pull data from GitHub on each tick |
| Actuators | Post security review, block merge, apply security labels | Push decisions back to GitHub |
| Tick loop | Configurable rate (default 0.1 Hz = every 10s) | Polls for PR state changes |
| Alarms | sql_injection, xss_detected, path_traversal, hardcoded_secrets, command_injection, critical_eval |
Fire on policy conditions |
| History | Ring buffer of last 1000 ticks | Full audit trail via history N command |
| Policies | FLUX bytecode rules in policies/ |
Declarative, versioned, reviewable |
The room exposes the standard PLATO wire protocol — any PLATO client can connect, read sensors, check alarms, and trigger actuators.
Quick Start
Standalone (single audit)
export GITHUB_TOKEN=ghp_your_token_here
python -m plato_room_security_audit.room --repo owner/repo --pr 42 --once
Prints a formatted security audit and exits.
Long-lived server
export GITHUB_TOKEN=ghp_your_token_here
python -m plato_room_security_audit.room --repo owner/repo --watch --port 1235
Then connect with any PLATO client:
from plato_core.protocol import PlatoClient
with PlatoClient.connect("localhost", 1235) as client:
welcome = client.recv_response()
print(f"Connected to {welcome.room_id}")
client.send("tick")
tick = client.recv_response()
print(f"Security state: {tick.data}")
client.send("alarm list")
alarms = client.recv_response()
for a in alarms.alarms:
print(f" {a.id}: {a.state}")
GitHub Action
# .github/workflows/security-audit.yml
name: PLATO Security Audit
on:
pull_request:
types: [opened, synchronize]
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install plato-core plato-room-security-audit
- run: |
python -m plato_room_security_audit.room \
--repo ${{ github.repository }} \
--pr ${{ github.event.pull_request.number }} \
--once
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Security Checks
All checks are heuristic (no LLM, no AST parsing) — fast, deterministic, and auditable.
| Check | What it catches | Severity |
|---|---|---|
sql_injection |
String concatenation in SQL execute() calls, f-string queries | critical |
xss |
Unescaped output in templates, innerHTML with variables |
critical |
path_traversal |
../ patterns in file paths, unsanitized open() calls |
critical |
hardcoded_secrets |
AWS keys, API keys, private keys, JWTs, Slack tokens, DB URLs | critical |
command_injection |
os.system(), subprocess with shell=True, eval() with user input |
critical |
insecure_random |
random module for security-sensitive contexts |
error |
insecure_crypto |
MD5, SHA1, DES, ECB mode for crypto operations | error |
debug_enabled |
DEBUG = True in settings files |
warning |
http_not_https |
HTTP (not HTTPS) URLs in source code | warning |
disabled_security |
Comments disabling security checks, # noqa, @SuppressWarnings |
warning |
sensitive_file_exposure |
Changes to .env, secrets files, key files |
error |
weak_password_hash |
hashlib.md5/sha1 for password hashing |
error |
Adding a check
# auditor.py
@check
def check_insecure_deserialization(diff: str) -> CheckResult:
"""Detect unsafe deserialization patterns."""
for line in diff.splitlines():
if line.startswith("+") and "pickle.loads" in line:
return CheckResult(
check_id="insecure_deserialization",
name="Insecure Deserialization",
passed=False,
message="pickle.loads() can execute arbitrary code",
severity="critical",
)
return CheckResult(
check_id="insecure_deserialization",
name="Insecure Deserialization",
passed=True,
message="No unsafe deserialization detected.",
)
The @check decorator registers it automatically.
Audit Reports
The report.py module generates structured markdown reports with severity ratings:
from plato_room_security_audit.auditor import run_all_checks
from plato_room_security_audit.report import generate_report, generate_short_summary
results = run_all_checks(diff)
# Full markdown report with CVSS-style severity ratings
report = generate_report(results, repo="owner/repo", pr_number=42, author="alice")
# One-line summary for status checks
summary = generate_short_summary(results)
# → "PLATO Security: 10/12 checks passed — BLOCK_MERGE"
Configuration
Configure checks via plato-security.yml in your repo root:
checks:
sql_injection: enabled
xss: enabled
path_traversal: enabled
hardcoded_secrets: enabled
command_injection: enabled
insecure_random: enabled
insecure_crypto: enabled
debug_enabled: warn
http_not_https: warn
disabled_security: warn
sensitive_file_exposure: enabled
weak_password_hash: enabled
Options: enabled, disabled, warn (report but don't block).
FLUX Policies
Policies live in policies/*.flx:
POLICY no_sql_injection {
SENSE sql_injection_count
GUARD sql_injection_count > 0
ALARM severity=critical
ACTUATE block_merge
EMIT "SQL injection vulnerability detected"
}
Policy files
| File | Triggers when |
|---|---|
no_sql_injection.flx |
SQL injection patterns detected in diff |
no_command_injection.flx |
os.system(), eval(), shell=True with interpolation |
no_hardcoded_secrets.flx |
Secret-like patterns in added lines |
no_path_traversal.flx |
Path traversal patterns in file operations |
Architecture
PLATO Wire Protocol
(TCP, JSON lines)
│
┌────────────────┼────────────────┐
│ │ │
tick command alarm list actuator cmd
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────┐
│ Security Audit Room │
│ │
│ Sensors Actuators │
│ ├─ vuln_patterns ├─ post_audit │
│ ├─ secret_scan ├─ block_merge │
│ ├─ risk_score └─ apply_label │
│ └─ dependency_check │
│ │
│ Alarms │
│ ├─ sql_injection (sql_injection_count > 0)│
│ ├─ xss_detected (xss_count > 0) │
│ ├─ path_traversal (traversal_count > 0) │
│ ├─ hardcoded_secrets (secret_count > 0) │
│ ├─ command_injection (cmd_injection > 0) │
│ └─ critical_eval (eval_count > 0) │
│ │
│ History (1000-tick ring buffer) │
└─────────────────────────────────────────────┘
│
GitHub API
│
┌────────────────┼────────────────┐
│ │ │
Fetch diff Fetch metadata Post audit
Scan patterns Fetch file list Block merge
Testing
pip install -e ".[test]"
pytest tests/ -v
All tests use simulated data — no GitHub API calls needed.
License
MIT — see LICENSE.
Part of
SuperInstance — the PLATO ecosystem.
Metadata
Release files for plato-room-security-audit 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| plato_room_security_audit-0.1.1.tar.gz | 30.3 kB | Without provenance | Details for plato_room_security_audit-0.1.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| plato_room_security_audit-0.1.1-py3-none-any.whl | Python 3 | none | any | Details for plato_room_security_audit-0.1.1-py3-none-any.whl |
Total release size: 54.1 kB
Release files / plato_room_security_audit-0.1.1.tar.gz
| Download URL | plato_room_security_audit-0.1.1.tar.gz |
|---|---|
| Size | 30.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
50fd47c1a246e4451636546b9bc6c4914e67a674fbde28605a579bd5f7de0c20
|
|
BLAKE2b-256 checksum How to use checksums |
f885aaa8d8e2facef5faac137d674d3b8466a8cf046a8b4f22b563558411613c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.15
|
Release files / plato_room_security_audit-0.1.1-py3-none-any.whl
| Download URL | plato_room_security_audit-0.1.1-py3-none-any.whl |
|---|---|
| Size | 23.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8bbbb9a4c8464cd0c6b0b6afb4b6b0d92cadcfecab9ce16c6fc5f9cd5d512de2
|
|
BLAKE2b-256 checksum How to use checksums |
c198e36d1e65b33924454ab9940fba2432d87a3ce3906f795debccbacc9e4bd6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.15
|
Security for plato-room-security-audit 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.