Skip to content

Security: mrBenL/controlsense

Security

SECURITY.md

ControlSense Security Policy

Threat Model

ControlSense accepts arbitrary document uploads and injects retrieved content into LLM prompts. This creates the following primary attack surface:

1. Prompt Injection via Uploaded Documents

Threat: A malicious actor uploads a document containing embedded instructions designed to override the model's behavior, exfiltrate data, or produce harmful outputs.

Mitigations:

  • System prompt is structurally separated from user-uploaded context: framework knowledge (system) and retrieved org content occupy distinct, delimited prompt positions, so retrieved text is presented as reference material and is not interpreted as instructions that can redefine the system role

This structural separation is the primary current control against prompt injection. Input-side instruction-pattern detection and structured audit logging are roadmap items, not current guarantees (see Planned Hardening).

2. Training Data Poisoning

Threat: Malicious or low-quality training examples bias the model toward incorrect framework mappings or suppress gap identification for specific control types.

Mitigations:

  • All training examples are human-reviewed before inclusion
  • Provenance metadata recorded per example (source doc, section, reviewer, date)
  • Eval harness with a 100-example held-out set scores required-mapping accuracy and a fabricated-ID rate validated against official framework enumerations. Measured baseline: 53.1% required-mapping accuracy (scoped to NIST CSF 2.0, expert-adjudicated) and a 0.0% fabricated-ID rate at the product level (the runtime sanitizer rejects fabricated and retired category IDs before output). See MODEL_CARD.md for full method and scope
  • Training data versioned alongside model weights

3. Model Hallucination of Framework Citations

Threat: Model produces plausible-sounding but non-existent framework category IDs (e.g. PR.AA-99), causing practitioners to build compliance posture on fictional mappings.

Mitigations:

  • Structured output schema (Pydantic) enforces valid output format
  • Eval harness specifically tests for hallucinated categories
  • Confidence scores surfaced to user — low confidence flags require human verification
  • Citations reference specific RAG source chunks, enabling verification

4. RAG Data Integrity

Threat: Vector store is poisoned via malicious document upload, causing retrieval to surface harmful or misleading context into model prompts.

Mitigations:

  • Per-chunk source metadata (source document, section path, chunk index) is recorded at ingestion, enabling every retrieved chunk to be traced back to its origin document
  • Chunk-level citations in every response enable source verification
  • Model output is schema-validated and confidence-filtered before display (see threat 3), so poisoned content cannot silently surface as a spurious "valid" framework mapping

Document-side input sanitization and per-query audit logging of retrieved chunks are roadmap items, not current guarantees (see Planned Hardening).

5. Dependency and Supply Chain Risk

Threat: Malicious or vulnerable dependencies introduce vulnerabilities into the runtime.

The application's runtime dependency surface is Gradio (web UI), ChromaDB (embedded vector store), sentence-transformers (embeddings), PyMuPDF and python-docx (document parsing), and the Python requests library (HTTP to the Ollama container). Model inference is delegated to a separate Ollama container; the transformers and PEFT stack is used only at training time and is not present in the application runtime path.

Mitigations:

  • Dependencies pinned in requirements.txt
  • Image and dependency tree scanned with Trivy before each release (see Known Vulnerabilities below)
  • Application source scanned with Bandit for code-level security issues before each release
  • A CycloneDX and an SPDX SBOM are generated per release and published as release artifacts
  • Model adapter downloaded from HuggingFace Hub over HTTPS, with the integrity metadata the Hub provides
  • Docker image built from a pinned base image digest

Planned Hardening (not yet implemented)

These are referenced as roadmap items, not current guarantees. They are listed so deployers do not assume coverage that is not yet present:

  • Input-side document sanitization with detection of anomalous, instruction-like content on the upload path (prompt-injection hardening)
  • Structured audit logging of queries, retrieved chunks, and model responses with timestamps, for traceability and incident review
  • Upgrade of Gradio (>= 6.7.0) and Pillow (>= 12.2.0) to clear the Group A CVEs (see Known Vulnerabilities)

Responsible Disclosure

If you discover a security vulnerability in ControlSense, please do not open a public GitHub issue.

Preferred channel: open a private report via this repository's Security → Report a vulnerability (GitHub private vulnerability reporting).

Alternative: email support@benluthy.com.

Please include:

  • Description of the vulnerability
  • Steps to reproduce
  • Potential impact assessment
  • Any suggested mitigations

We will acknowledge receipt within 5 business days and provide a remediation timeline within 15 business days.


Out of Scope

The following are known limitations, not vulnerabilities:

  • Model hallucination on controls outside the training domain
  • Incorrect mappings for framework versions not in training data
  • Performance degradation on very long control statements
  • LLM non-determinism producing slightly different outputs across identical queries

Deployer Security Responsibilities

ControlSense ships bound to localhost for single-user local use. Organizations that deploy it beyond that default are responsible for:

  • Network access controls to the deployed instance
  • An authentication layer in front of the Gradio UI (note: this is a compensating control for external exposure, not a substitute for the Gradio upgrade noted against CVE-2026-28416 below)
  • Secure storage of uploaded organizational documents
  • Review and approval of model outputs before use in audit or compliance decisions
  • Keeping the base model and dependencies updated

ControlSense is a decision-support tool. It does not replace qualified human judgment in compliance and audit contexts.


Secrets Management

  • No credentials are committed to the repository. The .env file is git-ignored and has never been tracked; only .env.example, which contains placeholders, is committed.
  • The HuggingFace token and any other secrets are read from the environment at runtime and are never written into the Docker image (.env is excluded via .dockerignore).
  • Contributors should run a secret scan before committing. The repository history has been verified clean of committed secrets.

Known Vulnerabilities

This project is scanned with Trivy (image and dependency vulnerabilities) and Bandit (Python source) prior to release. This section discloses the findings present in the current published image, the deployment context that bears on their exploitability, and their disposition. We publish this rather than silently shipping it, so that anyone running ControlSense can make their own informed decision.

Scan tools: Trivy (image scan, --severity HIGH,CRITICAL); Bandit (bandit -r .) Image: lacunae-controlsense:latest (Debian 13.5 base, Python 3.12) Last scanned: 2026-06-06 Trivy findings: 19 total — 14 OS/base-image (12 HIGH, 2 CRITICAL), 5 Python (5 HIGH) Bandit findings: 0 actionable (see Static Analysis below)

Reproduce with:

trivy image --severity HIGH,CRITICAL --ignorefile .trivyignore lacunae-controlsense:latest
bandit -r . -x ./.venv,./venv,./training

Disposition summary

ControlSense is a locally-running, single-user tool bound to localhost. The app container runs the Gradio UI as a non-root user and delegates all model inference to a separate Ollama container. It ingests DOCX, PDF, and TXT policy documents and communicates with Ollama over HTTP via the Python requests library. This deployment shape is the basis for the contextual risk notes below.

Findings are grouped by whether they sit in the application's actual execution path or are merely present in the base image.


Group A — Application-reachable (tracked)

These touch the real attack surface (the Gradio web UI and the document parsing path). They are the findings worth revisiting first.

CVE Component Installed Severity Fix available Contextual risk in this deployment Disposition
CVE-2026-28416 gradio 5.49.1 HIGH 6.6.0 SSRF to internal services. In the default localhost single-user bind, the endpoint is not externally reachable. For any networked deployment, an auth layer reduces external exposure but the SSRF remains the definitive concern; the version upgrade is the real fix. Accepted for the default local bind; upgrade required before any networked deployment
CVE-2026-28414 gradio 5.49.1 HIGH 6.7.0 Absolute path traversal; precondition is Windows + Python 3.13+. Image ships Linux + Python 3.12, so not applicable as deployed. Not applicable as deployed
CVE-2026-25990 pillow 11.3.0 HIGH 12.2.0 Out-of-bounds write via crafted PSD. The upload component allowlists .pdf/.docx/.txt only; there is no image-input path and Pillow is never invoked on user input. Pillow is a transitive dependency. Not reachable (upload allowlist excludes images)
CVE-2026-40192 pillow 11.3.0 HIGH 12.2.0 Decompression-bomb DoS via FITS image processing. No image-input path; FITS is never handled. Not reachable (upload allowlist excludes images)
CVE-2026-42311 pillow 11.3.0 HIGH latest Pillow image-parsing issue; same reasoning — no attacker-controlled image path. Not reachable (upload allowlist excludes images)

Remediation path: upgrade Gradio to >= 6.7.0 (clears both Gradio CVEs) and Pillow to >= 12.2.0 (clears the Pillow CVEs). The Gradio upgrade is a major-version jump and may require UI changes, which is why it is staged as a fast-follow rather than blocking the initial local-only release. It is a prerequisite for any networked deployment.


Group B — Present in base image, not in application execution path

These carry high or critical CVSS scores but are not reachable through ControlSense as deployed. The application is Python; it does not invoke Perl at runtime, does not extract untrusted tar archives, does not use the system curl binary (it uses Python requests), does not open SSH connections, and does not use ncurses in a web-service runtime path. No fixed package versions are available from the upstream distribution for any of these at scan time, so there is currently no patch to apply; they will be cleared by a base-image rebuild once upstream ships fixes.

CVE(s) Component Installed Severity Why not reachable here
CVE-2026-42496, CVE-2026-8376 perl-base 5.40.1-6 CRITICAL Archive::Tar path traversal / heap overflow. Perl is not in the app runtime; no untrusted archive extraction occurs.
CVE-2026-42497, CVE-2026-48962, CVE-2026-9538 perl-base 5.40.1-6 HIGH Archive::Tar hardlink handling / IO-Compress code execution / Archive::Tar memory exhaustion. Same — Perl not invoked at runtime.
CVE-2026-5773, CVE-2026-6276 curl, libcurl4t64 8.14.1-2+deb13u3 HIGH SMB connection-reuse file transfer / cookie leak on connection reuse. App uses Python requests, not system curl; SMB and the reuse patterns are not exercised.
CVE-2026-7598 libssh2-1t64 1.11.1-1 HIGH Integer overflow via large SSH username/password. App makes no SSH connections.
CVE-2025-69720 libncursesw6, libtinfo6, ncurses-base, ncurses-bin 6.5+20250216-2 HIGH ncurses buffer overflow. Terminal-UI library, not in the web-service execution path.

Static Analysis (Bandit)

The application source is scanned with Bandit before each release. The current scan reports no actionable findings:

  • B322 (input()) — two hits in scripts/prepare_model.py. False positive: B322 flags Python 2 behavior where input() evaluated as code; on Python 3 (the only supported runtime) input() is safe. The lines are interactive prompts in a manual offline setup script and are suppressed with # nosec B322.
  • B303 (MD5) — one hit in rag/ingest.py. The MD5 call generates a non-cryptographic content ID for vector-store chunks. It is declared usedforsecurity=False and suppressed with # nosec B303.
  • B110 (try/except/pass) — five low-severity hits. These are intentional graceful-degradation paths (for example, the JSON-repair fallback in the inference pipeline). They are accepted as designed and do not represent a security issue.

Revisit triggers

This disposition is re-evaluated if any of the following change:

  • The Gradio UI is exposed beyond localhost (e.g., via a tunnel, reverse proxy, or public bind) — at which point CVE-2026-28416 (SSRF) becomes a priority fix and the Gradio upgrade must be applied.
  • The application begins processing untrusted image input — which would make the Pillow findings reachable.
  • The application is run natively on Windows with Python 3.13+ rather than in the provided container — which would make CVE-2026-28414 applicable.
  • Upstream ships fixed packages for the base-image CVEs — at which point a rebuild clears Group B at no functional cost.

A fresh trivy image and bandit scan is run on each release, and this section is updated to match.

There aren't any published security advisories