Install on EL9:
sudo rpm --import https://repo.techhack.nl/RPM-GPG-KEY-techhack sudo tee /etc/yum.repos.d/techhack.repo <<'EOF' [techhack] name=techhack tools (EL9) baseurl=https://repo.techhack.nl/el9 enabled=1 gpgcheck=1 repo_gpgcheck=1 gpgkey=https://repo.techhack.nl/RPM-GPG-KEY-techhack EOF sudo dnf install vulnscan-ai
abyss-top
* Sat Aug 01 2026 David <david@cerberus.io> - 1.2.0-1
- New LIBVIRT column showing the real libvirt domain state, polled from
`virsh list --all` on a background thread every ~5s (never blocking the
UI, even if libvirtd is wedged). This closes the gap documented since
1.1.0: the existing STATE column only ever reflected the QEMU OS process
state, so a libvirt-"paused" guest still showed "up". If virsh isn't
installed the column falls back to "-" and stays passive for the rest of
the run; a transient libvirtd connection failure just skips that poll and
keeps the last-known state. No new dependencies (stdlib thread + mpsc
only).
* Tue Jul 21 2026 David <david@cerberus.io> - 1.1.0-1
- Guest table gains three columns/rows: per-VM STATE (the QEMU process state
from /proc/<pid>/stat via sysinfo — up / io / stop / dead, so a hung,
SIGSTOP'd or defunct guest stands out; note this is the OS process state,
not the libvirt domain state, so a libvirt-"paused" guest still reads
"up"), per-VM UPTIME (process run time), and a bottom TOTAL row summing
CPU / memory / disk / net across all guests. Still pure /proc + /sys, no
new dependencies.
* Tue Jul 14 2026 David <david@cerberus.io> - 1.0.0-2
- Per-vCPU NUMA placement + host-contention detail view: press Enter on a
selected guest to see each vCPU thread's current physical CPU, NUMA node,
and a schedstat-based "wait" contention percentage, plus guest memory
NUMA locality (from /proc/<pid>/numa_maps). Pure /proc + /sys, no
QMP/libvirt. Adds Up/Down row selection (was a flat table before).
- BUGFIX: CAP_DAC_READ_SEARCH alone was not sufficient to read
/proc/<pid>/io, /proc/<pid>/fd/* (tap-resolution) or /proc/<pid>/numa_maps
for a QEMU process owned by a DIFFERENT Linux user (e.g. the standard
libvirt 'qemu' service account) — those are ptrace-mode-gated, not just
DAC-gated. This silently zeroed disk/net throughput for non-root,
setcap-only deployments whenever guests don't run under the same uid as
abyss-top (root use masked it). %post now also sets CAP_SYS_PTRACE.
vulnscan-ai
* Sat Aug 15 2026 vulnscan-ai <noreply@example.invalid> - 1:0.4.14-1
- AI provider model ids refreshed across the board. Four provider defaults
had gone dead or were about to: `gemini-2.0-flash` was shut down,
`open-mixtral-8x7b` was retired in March 2025, `deepseek-coder` went away
with the DeepSeek V4 launch, and the `moonshot-v1-*` series is sunset on
2026-08-31. New defaults: claude-sonnet-5, gpt-5.6-terra,
gemini-2.5-flash, deepseek-v4-flash, mistral-small-4, kimi-k3. The curated
offline (Ollama) menu moves qwen2.5 -> qwen3. Nothing failed loudly
before: a retired model id simply made every remediation for that provider
fail at runtime.
- Fix: the Claude provider capped `max_tokens` at 2048 when no `--effort` was
given. The Claude 5-series models think by default even when no `thinking`
key is sent, and `max_tokens` bounds thinking AND answer together, so the
JSON remediation plan could be truncated mid-object. Raised to 8000.
- Fix: the OpenAI provider always sent `temperature: 0.1`, which the GPT-5
reasoning models reject with a 400. That would have broken the provider
outright on the new default model. Removed; the sibling OpenAI-compatible
providers (deepseek/mistral/kimi) still send it, as their APIs accept it.
- The Claude provider now reports a classifier refusal (HTTP 200 with empty
content) and its category, instead of a bare "empty response from Claude".
A finding's own wording can trip the cyber category.
- New: `setup --update` re-pulls the Ollama models already downloaded, so a
re-published tag is picked up; only changed layers are fetched. Ollama
never refreshes a model on its own. It reports per model whether the tag
actually moved, and — since a re-pull cannot cross model generations —
lists any model that has dropped off the curated recommendation list.
Also reachable from the interactive menu.
- `info` now names the local model actually in use (the provider row shows
the class default, which is not the same thing), whether it is downloaded,
and whether the recommended list has moved past it.
- CI/release workflows: actions/checkout and actions/upload-artifact v4 -> v7.
* Fri Jul 31 2026 vulnscan-ai <noreply@example.invalid> - 1:0.4.13-1
- Fix: a scan covering only some scanners (the configured default, an
explicit `--scanner`, or the dashboard's old cfg.scanners-only default)
fully overwrote `findings.json`, silently discarding every OTHER
scanner's last results. Concretely: probing just the `network` scanner
(a legitimate narrow check) wiped out real dnf/oscap/ssh findings from
the last full scan. Fixed via `_merge_with_previous()`: a partial run now
carries forward any previously-saved finding whose source wasn't part of
this run; only `--all` (or a `--scanner` list covering every scanner)
still fully replaces the saved set.
- Fix: the dashboard's "Scan now" button only ran the configured default
scanner list (`cfg.scanners`, typically just `dnf`), so it could show far
fewer findings than a real full scan with no indication why. It now runs
every available scanner, like `scan --all`.
- Fix: `fix`/`rollback`/`report` exited 1 with zero explanation when there
were no actionable findings -- in the interactive menu this looked like a
bare "(command exited with status 1)". All three now print why before
exiting.
- 370 tests (+9 regressions), bandit clean.
* Fri Jul 31 2026 vulnscan-ai <noreply@example.invalid> - 1:0.4.12-1
- Fix: `write_user_config()` only wrote a setting change to disk, never to
the in-memory `Config` object -- a long-lived caller (the interactive
menu holds ONE `Config` for its whole session, never reloading from disk)
didn't see the change until restarted. Real-world symptom: adding a
target via `vulnscan-ai network --add` (CLI or the menu's Network scan ->
Add) saved correctly, but the very next menu screen in the same session
still showed "no targets configured" and a scan run from there found the
scanner unavailable. Fixed at the root in `write_user_config()` itself
(applies the update to `self` too), which also fixes the same
pre-existing bug in `dashboard --allow`/`--deny`/`--set-password`.
* Thu Jul 30 2026 vulnscan-ai <noreply@example.invalid> - 1:0.4.11-1
- Network scanner v2: authorize targets without hand-editing config.json
(`vulnscan-ai network --add/--remove/--list/--ports`, plus a setup-wizard
step and a menu fan-out under the existing "Network scan" entry). IPv6
targets (addresses/CIDRs/hostnames) are now supported -- nmap runs a
separate `-6` invocation for them, since it can't mix address families in
one scan. A risky service found on a non-standard port is now also
flagged, gated on a real confirmed nmap `-sV` probe match (never its
unconfirmed port-number guess); widen what gets probed with the new
`network_scan_ports` config/`--ports` flag (`known`/`top1000`/`all`/a
literal spec). CVE/version matching remains out of scope (too high a
false-positive risk). Also: the `vulnscan-ai.service` scheduled-scan unit's
`TimeoutStartSec` is now explicit, since the network scanner can now run
two nmap invocations per scan.
* Sun Jul 26 2026 vulnscan-ai <noreply@example.invalid> - 1:0.4.10-1
- Fix: the interactive menu's "Network scan" entry (option 3) silently
returned to the menu when `network_targets` wasn't configured yet -- it
printed an explanatory hint but never paused, so the next screen redraw
wiped it before it could be read. Now pauses so the hint stays visible.
Docs: the `menu` command reference was missing `compliance`, `network`
and `audit` from its list of covered commands; the website's command
table gained a dedicated row for `scan --scanner network` (it previously
only appeared as a scanner chip, unlike every other first-class command).
* Fri Jul 24 2026 vulnscan-ai <noreply@example.invalid> - 1:0.4.9-1
- `info` now explains why the `network` scanner is unavailable (missing
`network_targets` config, missing nmap, or both) instead of just showing
"unavailable". `Recommends:` weak dependencies only auto-install on a
fresh `dnf install`, not on an upgrade of an already-installed package, so
a host upgrading from an earlier release does not get nmap for free; when
nmap is the only thing missing, `info` now offers to install it right
there via `dnf install -y nmap` (interactively, TTY only) or prints the
command to run manually otherwise. 328 tests, bandit clean.
* Fri Jul 24 2026 vulnscan-ai <noreply@example.invalid> - 1:0.4.8-1
- Network exposure scanner (`network`, 10th scanner): the first scanner that
inspects hosts OTHER than the one it runs on. Shells out to `nmap -sV` for
host discovery, a scoped port scan and service/version detection, then flags
the same plaintext/legacy-protocol and sensitive-service exposures as the
`ports` scanner (shared risk taxonomy, extracted into new net_classify.py) —
the same risk model, observed remotely instead of via local `ss`. No CVE/CPE
matching on detected service versions yet (parked). Safety: gated on an
explicit, config-only `network_targets` allow-list (hosts/CIDRs the operator
is authorized to test) -- never a CLI flag -- and stays genuinely unavailable
until configured, printing an authorization reminder on every real
invocation. New `Finding.target` field (None for every existing scanner, so
finding-id hashing is unaffected) lets a finding describe a remote host
without corrupting cross-scanner dedup. `remediation.py` hard-refuses to
execute anything against a target-bearing finding (fixes for a remote host
cannot run on this one) -- `propose()` strips any AI-returned commands and
`apply()` unconditionally refuses, independent of the AI. New "Network scan"
menu entry. Optional `Recommends: nmap`. 323 tests, bandit clean.
* Sun Jul 19 2026 vulnscan-ai <noreply@example.invalid> - 1:0.4.7-1
- Correctness & security hardening from a two-agent adversarial code review.
Truthfulness: an auto-rollback whose file restore OR service revert failed no
longer reports a clean rollback (the CLI + audit log were claiming a reverted,
healthy host while it was half-reverted); an empty prose-only fix plan is no
longer reported as "applied" (the dashboard apply path was writing a false
audit entry for a no-op). Security: the exported bash script now uses a unique
heredoc delimiter so crafted file content can't break out and inject root
commands; OVAL feed decompression is size-capped (decompression-bomb guard,
runs as root) and refuses a DOCTYPE. Robustness: malformed CVE-feed JSON
(NVD/EPSS/KEV) can no longer abort a whole scan; a package-less oscap advisory
is no longer dropped on an empty-but-successful updateinfo set (it was hiding
real advisories). Plus null-tolerant report/dashboard rendering, version-
ordered (not install-ordered) kernel-currency check, corrupt-config tolerance,
string-aware model-JSON extraction, and a capped dashboard request body. 305
tests, bandit clean. No user-facing surface change.
* Tue Jul 14 2026 vulnscan-ai <noreply@example.invalid> - 1:0.4.6-1
- SCAP-grounded AI remediation prompts: for config/service findings
(ssh/systemd/ports/webroot) the `fix` AI step is now optionally grounded with
a vetted remediation snippet retrieved from the host's own SCAP Security
Guide (SSG) XCCDF datastream (the same one `scan --compliance` reads) — real
peer-reviewed fix scripts already sitting on the host, unused until now. New
`vulnscanai/scap_kb.py`: pure stdlib lexical matching (bag-of-words cosine,
no ML/embeddings) between the finding's text and each SSG rule's
title/description; a candidate must clear BOTH a cosine-similarity floor
AND a minimum number of distinct overlapping tokens, since a short rule
title sharing a single word with the finding (e.g. an exposed-port finding
vs. "Uninstall nginx Package") can otherwise out-score a genuinely relevant
match. The model is instructed to adapt the reference into vulnscan-ai's own
schema, never copy its shell syntax verbatim (still enforced by the existing
no-shell command screen either way). Silent no-op when
`scap-security-guide` isn't installed or nothing clears the threshold — same
behaviour as before this change. New config key `scap_grounding` (default
true) and `fix --no-scap-grounding` to disable it; reuses the existing
`compliance_datastream` config key for a custom datastream path.
Package/CVE findings (dnf/oscap) are unaffected (catalog-first, no AI call
in the common path; SSG hardening rules aren't CVE patches anyway). 293
tests (+16), bandit clean, live-verified against this host's installed SSG
datastream (correct grounding for a real ssh finding, correctly absent for
an unrelated one and for every package finding).
* Sun Jul 12 2026 vulnscan-ai <noreply@example.invalid> - 1:0.4.5-1
- FIPS-posture scanner (`fips`, 9th scanner): audits whether the host's
cryptography is actually hardened. Reads /proc/sys/crypto/fips_enabled,
/proc/cmdline and the crypto-policies state files (plus `fips-mode-setup
--check` when present) and reports only real gaps: the half-enabled FIPS trap
(kernel in FIPS mode but the system crypto-policy is not, so OpenSSL/OpenSSH
still negotiate non-approved algorithms, or the reverse; important), an
inconsistent state, a weakened crypto-policy (LEGACY, or a SHA-1-restoring
sub-policy; flagged on any host), and a pending policy change (configured !=
applied). A consistent non-FIPS host produces NO findings; set
`fips_required: true` to treat a non-FIPS host as an important finding. Pure
stdlib; in `--all`, not in the default scanner set. `info` now also shows the
active crypto-policy. New config key `fips_required` (default false).
* Tue Jul 07 2026 vulnscan-ai <noreply@example.invalid> - 1:0.4.4-1
- Effective-state scanner (`effective`): a patch on disk is not a patch in RAM.
Reports what the RUNNING system is still using — a host on an older kernel than
the one installed (package scanners call it patched the moment the RPM lands,
but you keep executing the vulnerable kernel until you reboot; severity
important), and services still mapping a deleted/replaced library (restart to
load the fix). Pure stdlib via /proc + rpm; uses dnf-utils' `needs-restarting
-r` as the authoritative reboot verdict when present (now a Recommends). A
membership check avoids false positives on kernel-rt/debug hosts.
- `fix` now overwrites a package fix's `requires_reboot` with this ground truth
after applying, so the "reboot required" note is a fact, not an AI guess.
- Safety: `reboot`/`shutdown`/`poweroff`/`halt`/`kexec`/`init 0,6`/`systemctl
reboot` are on the remediation deny-list — a fix never takes the host down
itself; rebooting stays the operator's call.
* Tue Jul 07 2026 vulnscan-ai <noreply@example.invalid> - 1:0.4.3-1
- Offline deterministic remediation catalog: `fix` now works fully air-gapped.
Package/advisory findings (dnf/oscap) are planned locally as a scoped
`dnf update -y --advisory=<id>` (or `dnf update -y <package>`) with NO AI call
and no network, so a host with no provider configured is no longer a dead end
and package fixes are reproducible (same finding -> same plan). The AI is
reserved for config/service findings that need reasoning.
* New `vulnscanai/catalog.py`; `remediation.propose_all` is catalog-first by
default. New `fix --offline` (catalog only, never call a provider) and
`fix --no-catalog` (AI for everything) flags, and `offline_catalog` config
key (default true; `--offline` overrides it).
* Command construction is injection-safe: the advisory is accepted only on a
full-string match and the package name is allowlist-validated, so a crafted
finding cannot inject extra dnf arguments.
* The web dashboard's apply-fix is catalog-first too; `cmd_scheduled --plan`
produces a deterministic plan offline. Findings with no offline plan are
surfaced and skipped (never falsely reported as applied).
* Mon Jul 06 2026 vulnscan-ai <noreply@example.invalid> - 1:0.4.2-1
- Structured file writes: config/drop-in fixes apply in-process again. A fix that
creates or replaces a file (e.g. a systemd hardening drop-in) now carries the
file path + content, and the transactional engine writes it safely WITHOUT a
shell — closing the gap the 0.4.1 no-shell hardening opened (previously such a
fix was blocked and only worked via `--export-script`).
* New `write_files` remediation field; the model is prompted to use it instead
of a shell redirect. Sanitised on the way in (absolute path only; placeholder/
relative/non-dict entries dropped) and stripped from package-CVE findings.
* The engine screens write targets (refuses non-absolute paths, /etc/shadow,
/etc/passwd, /dev|/proc|/sys|/boot, directories), snapshots the written files
alongside backup_paths, writes them before the commands (so a following
`systemctl daemon-reload` picks up a new drop-in), and on any failure rolls
back — removing a created file or restoring an overwritten one.
* `fix --export-script`/`--export-ansible` now render the file writes too
(bash heredoc / Ansible copy-with-content); fixed the exported backup/restore
helpers so a newly-created file no longer trips `set -e` and rollback removes
it, matching the in-process engine.
- +11 tests (227), integration suite still green, bandit clean.
* Mon Jul 06 2026 vulnscan-ai <noreply@example.invalid> - 1:0.4.1-1
- Stability release — hardening of what the tool already does, no new surface.
- Remediation engine (the code that changes the system):
* Rollback can no longer report success when it failed. `rollback` (and the
automatic rollback) now return the true outcome; a missing/partial restore
is surfaced as ROLLBACK INCOMPLETE instead of a false "reverted", and the
audit log records it truthfully.
* File-writing fixes no longer silently no-op into a false success. Commands
that need a shell (redirect/pipe/&&/$()/backtick) — which the no-shell
runner cannot execute as written — are now blocked with a message pointing
at `--export-script`, instead of running e.g. `echo … > file` as a no-op and
reporting "applied".
- Parser robustness: the scanner/feed parsers survive malformed external data
(a null item or wrong-typed field in CISA KEV / Rocky Apollo JSON or a
container inspect object) by skipping the bad item instead of crashing
`news`/container scans.
- Never-crash CLI: a last-resort guard means an unexpected error prints a
readable message and exits non-zero instead of dumping a traceback
(VULNSCANAI_DEBUG=1 re-raises); a corrupt config or findings.json degrades
with a warning instead of bricking the command.
- Tests: +22 (fuzz battery for every parser, engine rollback/shell cases,
never-crash paths) plus an opt-in integration suite that runs the real
ss/systemd-analyze/oscap and the scanners against a live host
(VULNSCANAI_INTEGRATION=1).
* Mon Jul 06 2026 vulnscan-ai <noreply@example.invalid> - 1:0.4.0-1
- New `audit` command: an append-only remediation audit log. Every fix actually
applied (and every rollback), from the CLI or the web dashboard, is recorded
as one JSON line in <state-dir>/audit.log (0600) — timestamp, source, actor,
finding, result, and the AI provider/model that generated the plan. Dry-run
previews are not logged. `vulnscan-ai audit [--limit N] [--json]`; interactive
menu gains an Audit entry. Dashboard-applied fixes record the login user.
- EL10 packages are now genuinely installable AND importable. Previously the
el10 tree served the el9 noarch build, whose files live under the build
interpreter's site-packages path (…/python3.9/…) and pin `python(abi) = 3.9`,
so it failed to install (or import) on EL10's Python 3.12. Each EL is now
built natively (release.sh builds el10 in an almalinux:10 container); the spec
also drops the exact `python(abi)` auto-requirement, keeping `python3 >= 3.9`
as the honest floor.
- CI/release hardening: the tag workflow no longer hangs on a missing
self-hosted runner — it verifies build+install on el9 AND el10 on
GitHub-hosted runners, and the sign+publish job is opt-in (manual dispatch).
Unit tests also run on el10 (Python 3.12) on every push.
- Packaging for Fedora Copr (.copr/Makefile + packaging/COPR.md) for free
el9/el10/fedora builds and a public repo URL.
* Mon Jul 06 2026 vulnscan-ai <noreply@example.invalid> - 1:0.3.1-1
- Dashboard apply-fix toggle is now a first-class control:
* new `vulnscan-ai dashboard --enable-fix` / `--disable-fix` write
`dashboard_allow_fix` (no more hand-editing the config file); `--list`
reports the current state.
* the interactive menu's "Web dashboard" screen gains an Enable/Disable
applying-fixes entry that shows the current state and prints a clear
warning (enabling grants dashboard users root-equivalent remediation
power) before it opts you in.
* Sat Jul 04 2026 vulnscan-ai <noreply@example.invalid> - 1:0.3.0-1
- Compliance benchmark scanning (CIS / DISA STIG / PCI-DSS / HIPAA / ANSSI):
* new `scan --compliance <profile>` mode runs `oscap xccdf eval` against the
SCAP Security Guide and reports a compliance score plus every failing rule
(sorted by severity, with CCE/CIS/STIG identifiers and whether an automated
remediation ships). A distinct mode — not part of `--all` (XCCDF is a
minutes-long full-system audit).
* `scan --list-profiles` lists the profiles the host's datastream offers;
friendly aliases (cis-l1, cis-l2, stig, pci-dss, hipaa, ospp, anssi-high, …)
or a full XCCDF profile id both resolve.
* results saved to <state-dir>/compliance.json; `--pdf`/`--json`/`--sarif`
export them; exits 3 when any rule fails (for CI/timers).
* dashboard gains a read-only Compliance tab (score tile + failing rules);
interactive menu gains a Compliance entry; `info` shows availability.
* requires oscap + the scap-security-guide package.
* Fri Jul 03 2026 vulnscan-ai <noreply@example.invalid> - 1:0.2.5-1
- AI provider/model UX + error visibility:
* setup now picks the cloud model from a MENU of known ids per provider (with a
custom-id escape hatch), so a typo'd id like 'Sonnet 5' can't be saved and
silently break every remediation.
* setup reuses an already-saved API key ("reuse it? [Y/n]"), so you can switch
provider or just change the model without pasting the key again.
* picking the local (Ollama) backend now takes effect immediately — the choice
is persisted up front, so a deferred/offline download no longer leaves the
tool silently using the previous cloud provider.
- HTTP errors now surface the server's own message (e.g. "credit balance is too
low") instead of a bare "HTTP 400: Bad Request", for every provider.
- fix: when an AI proposal fails it prints the real reason, and if EVERY proposal
fails (systemic — credits/key/model) it stops with that reason instead of
walking the operator through empty approval prompts.
- The interactive menu draws the banner as its header, so it stays visible in the
full-screen (curses) view.
* Thu Jul 02 2026 vulnscan-ai <noreply@example.invalid> - 1:0.2.4-1
- STABLE. Graduates the 0.2.4b0 interactive menu to final and adds a small
dashboard touch: the summary row now carries an actively-exploited (CISA KEV)
tile and an EPSS >=50% tile whenever a finding matches, surfacing the
highest-priority exploitation signals up front instead of only in the list.
- Introduces Epoch 1 so this release cleanly supersedes the 0.2.4b0 pre-release
(RPM ranks '0.2.4b0' above '0.2.4', so without an epoch 'dnf update' would not
move beta hosts to the final). Epoch stays 1 for subsequent releases.
* Thu Jul 02 2026 vulnscan-ai <noreply@example.invalid> - 0.2.4b0-1
- BETA. Interactive, menu-driven front-end: run 'vulnscan-ai' with no command on
a terminal (or 'vulnscan-ai menu') for a navigable menu that covers every
command — scan, fix, rollback, report, news, info, providers, dashboard,
scheduled, update-oval and setup — so flags need not be memorised. Arrow-key
curses UI with a numbered-prompt fallback for terminals without cursor support
(or VULNSCANAI_NO_CURSES=1); non-interactive/piped runs still print help. Each
choice is turned into the ordinary command and run through the real parser, so
behaviour is identical to typing it by hand.
* Tue Jun 30 2026 vulnscan-ai <noreply@example.invalid> - 0.2.3-1
- BUGFIX (0.2.2 already-patched filter): the filter matched on package name
only, so it never caught 'oscap' findings (which carry an advisory but no
package) — the lingering-old-kernel ALSA advisories still showed up. The
filter now also consults 'dnf updateinfo list --updates' (the realistic
installable-advisory set) and drops a finding when its advisory isn't actionable;
both signals are considered and it stays fail-safe. 'fix' now applies the filter
to the saved findings too, so already-patched advisories no longer waste an AI
proposal or a no-op apply even without re-scanning.
* Tue Jun 30 2026 vulnscan-ai <noreply@example.invalid> - 0.2.2-1
- STABLE. Already-patched filter: a package finding whose fix is in the repo
metadata but has no installable update per 'dnf check-update' is dropped — the
host already has it. Clears the common lingering-old-kernel noise (old kernels
stay installed, so the scanners keep listing historical kernel advisories that
dnf reports as "Nothing to do"). Won't-fix advisories are never dropped this
way; fail-safe (drops nothing if dnf can't be queried). Toggle: patched_filter.
- fix: the interactive prompt gains '[i]gnore' — accept a reviewed finding and
add it to the persistent baseline (~/.config/vulnscan-ai/ignore) on the spot,
so it isn't reported again (handy for accepted hardening items on a LAN host).
- fix: harden the '--advisory=' rewrite — collapse space/comma-separated id lists
into one argument and drop garbage tokens, so a model emitting
'--advisory=RHSA-1, RHSA-2' no longer makes dnf fail with "No match for
argument"; the finding's own advisory is preferred when well-formed.
* Tue Jun 30 2026 vulnscan-ai <noreply@example.invalid> - 0.2.1-1
- Distro errata feed now covers all three RHEL clones, picked automatically:
AlmaLinux (errata RSS), Rocky Linux (RESF/Apollo advisories JSON) and Oracle
Linux (year-scoped ELSA OVAL, with bounded decompression as a bomb guard).
- OVAL auto-refresh: a scan that uses the oscap scanner now downloads the OVAL
feed automatically when it is missing or older than oval_max_age_days (default
7) — no manual 'update-oval' needed. TTL-gated, fail-soft (falls back to the
existing feed), and skipped when offline (--no-enrich) or oval_auto_update is
false. New config keys oval_auto_update, oval_max_age_days.
* Tue Jun 30 2026 vulnscan-ai <noreply@example.invalid> - 0.2.0-1
- STABLE release. Exploitation-aware prioritisation: every finding's CVE is
checked against the CISA KEV catalog (actively exploited in the wild) and the
FIRST.org EPSS score during enrichment. KEV findings are tagged [KEV], sorted
to the top and raised to at least 'important'; high EPSS shows as [EPSS xx%].
Toggle with 'exploit_enrich'.
- New 'news' command + dashboard "Advisories" tab: recent vulnerability news from
CISA KEV, NIST NVD and the host distribution's errata (AlmaLinux today), cached
to the state dir so it works offline. Advisories matching the last scan are
flagged. New 'feeds' module (stdlib only, FIPS TLS); config keys news_enabled,
news_sources, news_refresh_hours.
- Security hardening: HTTP restricted to http/https schemes (no file:// SSRF) with
a response-size cap; untrusted feed XML rejects DOCTYPE/ENTITY (no XXE); all
feed content HTML-escaped in the dashboard. New bandit SAST job in CI (fails on
medium+). Man page gains the dashboard and news sections.
* Tue Jun 30 2026 vulnscan-ai <noreply@example.invalid> - 0.1.26-1
- BUGFIX (regression in 0.1.25): 'fix' crashed with "TypeError: 'NoneType'
object is not iterable" when the AI returned a null list field (e.g.
"config_changes": null). dict.get(k, []) returns None — not [] — when the key
is present but null; every list field (commands/config_changes/backup_paths/
rollback_commands) is now coerced safely, and a single scalar is tolerated.
* Tue Jun 30 2026 vulnscan-ai <noreply@example.invalid> - 0.1.25-1
- fix: stream every apply step live (backup, each command, validate, service
reload/health check, rollback) together with the command's own output, so you
can see what a fix does while it runs instead of waiting for a silent finish.
- fix: sanitise AI remediation plans before they can run — normalise an invalid
restart_mode (a model echoing "reload|restart|none" no longer silently skips
the reload), drop echoed schema placeholders and non-command "verify" strings,
rewrite a malformed --advisory= id to the finding's real advisory, and strip
config backups/validate/service from package-CVE (dnf/oscap) fixes where the
model tends to hallucinate unrelated sshd/httpd scaffolding.
- fix: a 'dnf update' that reports "Nothing to do" is now shown as [no-change]
(not a false [ok]), so an advisory that didn't actually apply is visible.
* Tue Jun 30 2026 vulnscan-ai <noreply@example.invalid> - 0.1.24-1
- New 'container' scanner (7th): inspects running Podman/Docker containers and
flags unsafe runtime settings CIS-Docker style — --privileged, runtime control
socket or sensitive host paths bind-mounted, host network/PID/IPC namespaces,
dangerous added capabilities (SYS_ADMIN, SYS_MODULE, --cap-add ALL), disabled
seccomp/AppArmor/SELinux, and root as the container user. Read-only and
conservative (benign mounts ignored, read-only mounts downgraded, --privileged
reported once). Selectable via --scanner container / --all.
* Fri Jun 26 2026 vulnscan-ai <noreply@example.invalid> - 0.1.23-1
- Dashboard: show a loading-spinner overlay while a Preview/Apply fix runs, so
the page doesn't look frozen during the (synchronous) AI call on slow local
models.
* Fri Jun 26 2026 vulnscan-ai <noreply@example.invalid> - 0.1.22-1
- Fix: the 'local' (Ollama) provider crashed with TypeError on the 'effort'
argument introduced in 0.1.19, breaking 'fix' and the dashboard Preview/Apply
with a local model. All providers now accept the effort kwarg.
- Dashboard: an unexpected handler error now returns a 500 page (with the
traceback logged) instead of an empty response (ERR_EMPTY_RESPONSE).
* Fri Jun 26 2026 vulnscan-ai <noreply@example.invalid> - 0.1.21-1
- Dashboard gains interactive actions: a 'Scan now' button runs the scanners
in the background, per-finding 'Preview fix' shows the AI plan (dry-run), and
'Apply fix' runs the fix transactionally on the host. Apply is opt-in only
(config dashboard_allow_fix, default false) so the dashboard stays read-only
by default; login and the allow-list still gate access.
* Wed Jun 24 2026 vulnscan-ai <noreply@example.invalid> - 0.1.20-1
- Setup wizard can configure a cloud AI provider + API key: pick claude/
openai/gemini/kimi/deepseek/mistral, enter the key (hidden), optional model
and (Claude) effort. Stored in the 0600 user config (new 'api_keys') and
injected into the environment on load; a real env var always wins.
* Wed Jun 24 2026 vulnscan-ai <noreply@example.invalid> - 0.1.19-1
- Claude reasoning-effort selection: global --effort low|medium|high|xhigh|max
(config claude_effort, env VULNSCANAI_CLAUDE_EFFORT) turns on adaptive
thinking for the Claude provider; other providers ignore it.
* Wed Jun 24 2026 vulnscan-ai <noreply@example.invalid> - 0.1.18-1
- scan: a per-severity tally line below the table and a colour-coded SEV
column (TTY-aware; NO_COLOR honoured, never coloured when piped).
- Refresh bash/zsh completions for the current commands, scanners, providers
and options (dashboard, webroot, --all, --no-banner, …).
* Tue Jun 23 2026 vulnscan-ai <noreply@example.invalid> - 0.1.17-1
- New 'webroot' scanner: finds web-exposed sensitive files in document roots
(*.sql/*.sqlite dumps, .env/wp-config.php secrets, .git/, *.bak/*~ backups,
archives, private keys) plus world-writable files. Roots read from nginx/
apache/lighttpd/litespeed configs and defaults. Picked up by 'scan --all'.
- Dashboard login page and header now carry the vulnscan-ai brand logo.
* Tue Jun 23 2026 vulnscan-ai <noreply@example.invalid> - 0.1.16-1
- Dashboard prints the login username on start, and a copy-paste firewalld
rule when the port looks closed in a running firewalld (network binds only).
* Tue Jun 23 2026 vulnscan-ai <noreply@example.invalid> - 0.1.15-1
- Dashboard default port is now 65101 (was 6666, which browsers block with
ERR_UNSAFE_PORT). The dashboard warns when started on a browser-blocked port.
* Tue Jun 23 2026 vulnscan-ai <noreply@example.invalid> - 0.1.14-1
- New 'dashboard' command: a stdlib HTTPS web UI behind a login that shows
saved findings with their explanations, CVEs and any AI fix plan.
Self-signed cert on first run; PBKDF2-SHA256 admin password; localhost-only
by default with an IP/CIDR allow-list for specific network clients. Ships a
vulnscan-ai-dashboard.service unit.
- scan/fix/scheduled gain '--all' to run every available scanner.
* Tue Jun 23 2026 vulnscan-ai <noreply@example.invalid> - 0.1.13-1
- Branded startup banner (MOTD) on interactive runs; suppressed for pipes,
machine output, scheduled runs and via --no-banner / VULNSCANAI_NO_BANNER.
- Scan drift: scan and scheduled report what is new vs resolved since the
previous saved scan.
- Email notifications: a scheduled scan can email a summary when findings
reach a severity or new ones appear (SMTP config + setup-wizard section;
password via VULNSCANAI_SMTP_PASSWORD). A failed mail never breaks a scan.
* Tue Jun 23 2026 vulnscan-ai <noreply@example.invalid> - 0.1.12-1
- Relicensed from Apache-2.0 to AGPL-3.0-or-later. The package now ships the
AGPL-3.0 LICENSE and all sources carry SPDX headers. Contributions are
accepted under a CLA so the project can also be offered commercially.
- No functional change to scanning or remediation.
* Mon Jun 22 2026 vulnscan-ai <noreply@example.invalid> - 0.1.11-1
- Runtime-exposure filter: a vulnerable daemon package whose systemd
service/socket units are all stopped AND disabled/masked is downgraded
to "low" and annotated (not exposed until the service is started),
cutting noise without hiding the issue. Conservative — packages with no
service unit (libraries, CLI tools) are untouched, enabled/static/
socket-listening units count as exposed, undetermined state keeps full
severity. Toggle "service_state_filter" (default on).
- Released signed with the production GPG key (techhack release signing).
* Fri Jun 19 2026 vulnscan-ai <noreply@example.invalid> - 0.1.10-1
- Fewer false positives via Red Hat per-CVE package_state: findings the
vendor marks "Not affected" for this RHEL release are dropped; the
won't-fix family ("Will not fix"/"Out of support scope"/"Fix deferred")
is kept but annotated so no pointless dnf update is proposed (toggle
with config 'vendor_state_filter').
- ports scanner gains nftables firewall-awareness: when firewalld isn't
running it parses 'nft --json list ruleset' (default-deny input policy,
accept rules incl. named sets/ranges, explicit drop/reject) and only
suppresses ports it can confidently prove blocked.
* Tue Jun 16 2026 vulnscan-ai <noreply@example.invalid> - 0.1.9-1
- Minimize false positives: OVAL scanner reports only patch-class
definitions (drops inventory/compliance) with real CVE ids + severity;
ports scanner suppresses firewalld-blocked ports; dnf+oscap findings
sharing an advisory/CVE are merged; new baseline/allowlist (config
'ignore', ~/.config/vulnscan-ai/ignore, VULNSCANAI_IGNORE, --ignore).
* Tue Jun 16 2026 vulnscan-ai <noreply@example.invalid> - 0.1.8-1
- Add systemd service-hardening scanner (--scanner systemd) via
systemd-analyze security; conservative defaults, drop-in remediation.
- Add network exposure scanner (--scanner ports) via ss; flags risky
plaintext/legacy and sensitive services listening off-host.
- Transactional rollback now runs systemctl daemon-reload before restart.
- Readable scan output for config findings (show title when no package).
* Tue Jun 16 2026 vulnscan-ai <noreply@example.invalid> - 0.1.7-1
- Transactional remediation: config/service fixes now back up the file(s),
validate before restart (e.g. sshd -t), reload + health-check the service,
and auto-roll back on failure. New 'rollback' command restores a fix.
- Add SSH hardening scanner (--scanner ssh): root login, weak ciphers/MACs/
KEX, password auth, X11 forwarding, legacy protocol.
- fix --export-script / --export-ansible: emit a bash script or Ansible
playbook instead of applying.
* Tue Jun 16 2026 vulnscan-ai <noreply@example.invalid> - 0.1.6-1
- Add DeepSeek (DeepSeek-Coder) and Mistral (Mixtral 8x7B) AI providers,
both OpenAI-compatible. StarCoder 2 is supported via the local/Ollama
provider (--provider local --model starcoder2).
- repo: per-version index pages (el9/el10) listing downloadable packages.
* Sun Jun 14 2026 vulnscan-ai <noreply@example.invalid> - 0.1.5-1
- Add a man page (man vulnscan-ai) and bash + zsh shell completion.
- Ship COMMANDS.md reference; point systemd Documentation= at the man page.
* Sun Jun 14 2026 vulnscan-ai <noreply@example.invalid> - 0.1.4-1
- GPU support: detect NVIDIA/AMD GPUs and size the model menu against VRAM
(offering larger models on GPU hosts); 'info' reports GPU/CPU. Ollama runs
the chosen model GPU-accelerated automatically when a GPU is present.
* Sun Jun 14 2026 vulnscan-ai <noreply@example.invalid> - 0.1.3-1
- Add interactive first-run setup wizard ('vulnscan-ai setup', also offered
automatically on first interactive run) to choose and download an offline
AI model via Ollama; saves the choice as the default provider/model.
- config: merge system + per-user config so a user choice overrides /etc.
* Sat Jun 13 2026 vulnscan-ai <noreply@example.invalid> - 0.1.2-1
- http: catch read timeouts / socket errors and surface them cleanly instead
of crashing (slow local CPU inference no longer aborts a run).
- local: patient default timeout (300s, OLLAMA_TIMEOUT to override) for local
model load + CPU inference.
* Sat Jun 13 2026 vulnscan-ai <noreply@example.invalid> - 0.1.1-1
- local (Ollama) provider: JSON-constrained output for reliable structured
remediation from small models; live server readiness check; OLLAMA_MODEL
support and clearer "server down" / "model not pulled" errors.
* Sat Jun 13 2026 vulnscan-ai <noreply@example.invalid> - 0.1.0-1
- Initial package: scanner, AI remediation, PDF reporting, systemd timer.
Packages are GPG-signed; metadata is signed (repo_gpgcheck).