Skip to content

Security: uckix/threatlens

Security

SECURITY.md

Security Policy

ThreatLens installs software as root on a SIEM and sends alert content to a third-party LLM provider. Both of those deserve a straight explanation rather than a badge.

Supported versions

ThreatLens is alpha. Only the latest commit on main receives fixes. There is no back-porting to older tags yet.

Reporting a vulnerability

Please do not open a public issue for a vulnerability.

Open a private security advisory on this repository, or contact the maintainer directly through the profile at github.com/uckix.

Expect an acknowledgement within a few days. This is a personal project, not a vendor with an on-call rotation — please size your expectations accordingly, and set your own disclosure deadline as you see fit.

What ThreatLens changes on your system

Being explicit, because you should not have to read a 400-line shell script to find out:

Change Detail
System user ai-summarizer, no login shell, no home directory, added to the wazuh group for read access to alerts.json
Indexer account ai_summarizer, scoped to ai-summaries-* only, plus two cluster-monitor permissions
Secrets on disk /etc/ai-summarizer/summarizer.env, mode 600, owned by the service account
systemd unit Sandboxed: NoNewPrivileges, ProtectSystem=strict, ProtectHome, PrivateTmp, alerts directory mounted read-only
Dashboard config Two keys appended to opensearch_dashboards.yml; the original is backed up first
Dashboard plugin wazuhAiAssistant, installed from the checksum-verified zip in dist/
Outbound network HTTPS to your configured LLM provider, and to the local Indexer

The installer never receives or stores the Wazuh admin certificate beyond the single session in which it creates the Indexer account, and never grants the service account access to wazuh-alerts-*.

Threat model

Alert content leaves your network

Every summarized alert — including full_log, which may contain usernames, hostnames, file paths, command lines and occasionally credentials that were logged by accident — is sent to your configured LLM provider with no redaction.

If that is unacceptable for your environment, run a local model. See the README's "Using a local model" section. This is the single most important thing to decide before deploying.

Prompt injection

An attacker who can get text into a log line can attempt to influence the summary — for example, embedding instructions intended to push a verdict toward false_positive.

Mitigations in place: the system prompt frames raw log content explicitly as untrusted data rather than instructions, and the structured-output schema constrains the shape of every response. Neither of these is a guarantee about the model's judgment.

Treat verdicts as suggestions, never as authority. ThreatLens is triage assistance. An analyst still owns the decision, and an alert marked false_positive by the model has not been closed by anyone.

API key exposure

The key lives in one file, mode 600, owned by the service account. The installer refuses to accept a key as a command-line argument, because argv is visible in ps to every local user and lands in the root shell's history file. Provider probes pass the key in a header rather than a URL query string, so it does not reach proxy or access logs.

Anyone with root on the host can read the key. That is inherent to running the service there.

Supply chain

The dashboard plugin ships as a pre-built binary zip, and the installer verifies it against dist/SHA256SUMS before installing. CI verifies the same checksum on every push.

A checksum committed in the same repository as the artifact protects against corruption and accidental substitution, not against a compromise of the repository itself. Signed release artifacts are planned. Until then, if your threat model includes "this repository is compromised", build the plugin from source instead.

What ThreatLens deliberately cannot do

  • No mutating tools. All 29 Chat tools are read-only; there is no code path that restarts an agent, blocks an IP, or edits Wazuh configuration.
  • No access to wazuh-alerts-*. The service account is scoped out of it.
  • Indexer queries from the Chat feature pass through guardrails: index allowlist, mandatory bounded time window, size and aggregation caps, no scripts, no regexp, no leading wildcards.
  • Outbound provider URLs pass an SSRF guard that blocks link-local and cloud-metadata addresses, including DNS-rebinding attempts.

Known gaps

Honest list, current as of the alpha:

  • No field-level redaction before content reaches the provider.
  • No spend cap or rate limit on provider calls.
  • Release artifacts are not signed.
  • Tested on a single-node Wazuh 4.9.2 install only.

There aren't any published security advisories