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 inpyproject.tomlmust sit at or above the highestfixedversion 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:
- No fixed version exists. Every published release is affected, so a bump
cannot clear the advisory. Record the advisory’s
introduced/last_affectedrange as proof, and set up a watch (§5) so the pin moves the moment upstream ships a fix. - The vulnerable code path is unreachable in NeuralMind’s usage. Requires a written reachability analysis naming the vulnerable entry point and the concrete reason it cannot be called here — which API we use instead, which configuration flag is never set, which server we never start.
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.