neuralmind.uk / Vulnerability Management Policy — NeuralMind Edit this page on GitHub ↗

Vulnerability Management Policy — NeuralMind

Effective Date: August 27, 2026 Last Updated: August 27, 2026 Owner: Cheval-Volant LLC (operating as NeuralMind) — hello@neuralmind.uk


1. Scope

This policy governs third-party dependency vulnerabilities — the alerts Dependabot, OSV, pip-audit, and npm audit raise against packages NeuralMind depends on.

It is deliberately separate from the two documents next to it:

Document Covers
SECURITY.md Vulnerabilities in NeuralMind’s own code — how to report one, response timelines, coordinated disclosure.
docs/SECURITY-GUIDE.md Operator hardening — RBAC, encryption at rest/in transit, secret management.
This document Vulnerabilities in code we did not write but ship or build with.

If you are reporting a flaw in NeuralMind itself, stop here and follow Reporting a Vulnerability instead.


2. What gets scanned

Every dependency manifest in the repository is in scope, whether or not .github/dependabot.yml opens version-update PRs for it. Dependabot’s alerting is repo-wide; its PR-raising is what that config tunes.

Manifest Ecosystem Version-update PRs
pyproject.toml pip Yes (weekly, limit 5)
requirements-pinned.txt pip Yes (weekly, limit 5)
site/package.json + site/package-lock.json npm No — open-pull-requests-limit: 0 (see §7)
editors/vscode/package.json + lockfile npm No config entry; alerts still apply
.github/workflows/*.yml GitHub Actions Yes (weekly, grouped into a single PR)
tests/fixtures/** npm / Go Dependency-free fixtures; nothing to resolve

3. The three dispositions

Every alert resolves to exactly one of these. There is no fourth option, and “leave it open and unexplained” is not one of them.

3.1 Fix — a patched release exists

Default disposition. If upstream has shipped a version clear of the advisory and it is reachable without a breaking major upgrade, take it. Bump, run the suite, push. No reachability argument is required or wanted: patching is cheaper than proving you are safe.

3.2 Raise the floor — the manifest admits a vulnerable version

A >= requirement is a security floor, not just a compatibility floor. An alert fires whenever the declared range still resolves to a vulnerable version, even when CI and every developer machine happen to install something newer.

This is the largest single source of alerts in a Python project and the easiest to prevent:

Floor rule. Every >= bound in pyproject.toml must sit at or above the highest fixed version among advisories affecting that package. When you raise a floor for this reason, say so in a comment naming the advisory, so the next person does not “tidy” it back down.

Raising a floor is not the same as pinning. Users still get the newest compatible release; they simply cannot resolve to a known-vulnerable one.

Verify the floor, not just the lockfile. pip-audit -r requirements-pinned.txt reports on pinned versions and will happily pass while pyproject.toml still admits a vulnerable range. Audit the resolved tree (§6) as well.

3.3 Documented non-fix — no patched release exists, or the code is unreachable

Permitted only when at least one of these holds, and only with the evidence written down:

Neither “low severity” nor “it’s only a dev dependency” is sufficient on its own. Both still get fixed under §3.1 when a patch exists.

Reachability arguments expire. They are re-checked whenever the surrounding code changes in a way that could open the path — see §8.


4. Remediation targets

Timelines match the severity table in SECURITY.md, measured from when the alert is triaged rather than when it was published:

Severity Patch available No patch available
Critical 24–72 hours Reachability analysis within 72 hours; watch opened; mitigation documented in SECURITY.md.
High 1 week Reachability analysis within 1 week; watch opened.
Medium 2–4 weeks Documented at next release pass.
Low Next release Documented at next release pass.

NeuralMind is a solo-operated project. These are commitments, not contractual warranties — the same caveat SECURITY.md carries.


5. Watches for unpatched advisories

When §3.3 applies because no fixed release exists, a dismissal is not the end of the work — something has to notice when upstream ships the fix, because Dependabot will not re-alert on an already-dismissed advisory.

The pattern is .github/workflows/chromadb-cve-watch.yml: a scheduled job that asks the OSV API whether the latest published version of the package is still flagged, and files a tracking issue the first time the answer is no. A plain version-update PR is the wrong tool here — it nags to bump to a version that does not clear the advisory.

Copy that workflow for any future no-fix-available advisory. One watch per advisory, concurrency-grouped, failing soft on network errors so a transient outage never breaks the schedule.


6. Reproducing the audit locally

Anyone can reproduce the state this policy is enforced against — no repository permissions required:

# Python — the pinned subset. NOTE: requirements-pinned.txt pins only some
# of the direct runtime dependencies (chromadb, mcp, pyyaml, toml, plus
# tooling); it does NOT include cryptography, pydantic, numpy, turbovec,
# onnxruntime, tokenizers or the tree-sitter grammars. Passing here is
# therefore NOT full coverage — the resolved-tree audit below is.
pip install pip-audit
pip-audit -r requirements-pinned.txt

# Python — the resolved tree the floors actually produce, which is where
# loose >= bounds hide. This is the check that catches §3.2 drift.
#
# --ignore-installed is load-bearing, not tidiness: without it pip reports
# only what it WOULD install, silently omitting anything already present in
# the current environment. Run bare in a dev venv it drops cryptography and
# numpy among others — i.e. it hides exactly the packages whose floors this
# check exists to police, and reports a clean audit while doing it.
pip install --dry-run --ignore-installed --report /tmp/resolve.json .
python - <<'PY'
import json
d = json.load(open("/tmp/resolve.json"))
with open("/tmp/resolved.txt", "w") as fh:
    fh.write("\n".join(sorted(
        f"{i['metadata']['name']}=={i['metadata']['version']}"
        for i in d["install"]
        if i["metadata"]["name"].lower() != "neuralmind"
    )) + "\n")
PY
# -r takes a requirements FILE, so audit the one just written. --no-deps
# because every version in it is already pinned.
pip-audit -r /tmp/resolved.txt --no-deps

# JavaScript
npm --prefix site audit
npm --prefix editors/vscode audit

The published CycloneDX SBOM — current release at neuralmind.uk/security, generated by Anchore syft over the full transitive tree (see .github/workflows/sbom.yml) — is the artifact to scan if you would rather not resolve the tree yourself.


7. Where decisions are recorded

Decision Recorded in
Floor raised for a security reason Inline comment in pyproject.toml naming the advisory
Pin held at an unpatched version Comment block in requirements-pinned.txt + a SECURITY.md “Known Security Considerations” entry
Alert dismissed on reachability grounds SECURITY.md for the Python package; site/DISMISSAL_DECISIONS.md for the marketing site
Watch for an upstream fix A workflow under .github/workflows/
Dependency advisory that doesn’t apply (§3.3) A not_affected statement in security/neuralmind.openvex.json (OpenVEX), published in every tagged tree. CI’s pip-audit ignores exactly those, and tests/test_vex.py checks each one is argued in SECURITY.md and matches the watch workflow

site/DISMISSAL_DECISIONS.md is the worked example of a §3.3 batch done properly: a capability-by-capability attack-surface table, per-alert dispositions with reasons, and named look-back triggers. Its later section is also the worked example of the correct reversal — once the site moved to a release line with patches available, the same advisories were fixed under §3.1 rather than dismissed again.

The site sits under open-pull-requests-limit: 0 because it is a Next.js static export to Cloudflare Pages with no middleware, no API routes, and no server components — version-update PRs there are noise, while security alerts still arrive and still get a disposition.


8. Review cadence

Trigger Action
A new alert arrives Triage to one of the three dispositions in §3 within the §4 window.
Every release pass Re-run §6 against pyproject.toml, requirements-pinned.txt, and both npm trees. A release does not ship with an untriaged alert.
A watch (§5) files a tracking issue Bump the held pin, re-run the suite, update the SECURITY.md entry, close the alert.
Code changes near a dismissed advisory’s path Re-run the §3.3 reachability analysis. Dismissals are conditional on the architecture that justified them.
The architecture that justified a dismissal changes Same — the clearest cases being the site gaining server-side behavior, or NeuralMind starting a ChromaDB server rather than embedding PersistentClient.

9. Standing exceptions

Current §3.3 dispositions. This list is the complete set; anything not here is expected to be fixed, not argued with.

Package Advisories Basis Watch
chromadb CVE-2026-45829 / GHSA-f4j7-r4q5-qw2c (Critical), CVE-2026-45833 / GHSA-36p7-vc44-83pf (Critical), CVE-2026-45830 / GHSA-2wm9-hf6c-p5cr (High), CVE-2026-45831 / GHSA-xph7-9rjv-w5fr (High) No fixed release exists — every published version is affected. All four are server-side: they need ChromaDB’s client/server HTTP API, its RBAC providers, or trust_remote_code=true. NeuralMind embeds chromadb.PersistentClient only, starts no server, and never sets trust_remote_code. ChromaDB is also not in the default install since v0.29 — it is a fallback on platforms without a turbovec wheel. Per-advisory analysis for all four in SECURITY.md. .github/workflows/chromadb-cve-watch.yml — tracks each advisory separately and files a tracking issue as soon as any one clears, naming which cleared and which remain

10. Reporting

Vulnerabilities in NeuralMind’s own code: follow SECURITY.md — GitHub Security Advisories preferred, do not open a public issue.

Disagreement with a disposition in §9, or a reachability argument you believe is wrong: that is exactly the kind of correction worth having, and a public issue is the right place for it.