fix(security): SECURITY.md disclosure policy (#320) + CORS allowlist (#560) (#577)

- Add SECURITY.md: private disclosure via GitHub PVR or ruv@ruv.net, scope,
  and response SLAs. Closes the responsible-disclosure gap raised in #320
  (gives reporters a channel without enabling beg-bounty noise).
- mcp-brain-server CORS: add https://app.conceptmapping.org and
  https://conceptmapping.org to the default allowlist so pi.ruv.io/v1/*
  returns Access-Control-Allow-Origin for those browser origins (#560).
  Kept an explicit per-origin allowlist (not `*`) since callers authenticate
  with Bearer tokens. cargo check -p mcp-brain-server: clean.

Refs #320 #560

Co-authored-by: ruv <ruvnet@users.noreply.github.com>
This commit is contained in:
rUv 2026-06-17 10:28:38 -04:00 committed by GitHub
parent 644af43236
commit 716dbede1d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 70 additions and 2 deletions

65
SECURITY.md Normal file
View file

@ -0,0 +1,65 @@
# Security Policy
## Reporting a Vulnerability
We take the security of RuVector and the π collective (`pi.ruv.io`) seriously and
appreciate responsible disclosure.
**Please do not open a public issue for security vulnerabilities.** Public issues
disclose the flaw before a fix is available and put users at risk.
Instead, report privately through either channel:
1. **GitHub Private Vulnerability Reporting (preferred).**
Use the **"Report a vulnerability"** button under the repository's
[Security tab](https://github.com/ruvnet/ruvector/security/advisories/new).
This opens a private advisory visible only to you and the maintainers.
2. **Email.** Send details to **ruv@ruv.net**. Encrypt with the maintainer's
public key on request. Use a subject line beginning with `[SECURITY]`.
### What to include
A useful report contains:
- The affected component and version/commit (e.g. a crate under `crates/`, an
npm package under `npm/packages/`, or a `pi.ruv.io` endpoint).
- A clear description of the impact (what an attacker can do).
- A minimal, reproducible proof-of-concept, or precise steps to reproduce.
- Any relevant logs, payloads, or configuration.
The more concrete and reproducible the report, the faster we can verify and fix it.
### Our commitment
- We aim to **acknowledge** your report within **3 business days**.
- We will provide an initial **assessment** within **10 business days**.
- We will keep you informed of remediation progress and coordinate a disclosure
timeline with you. We are happy to credit reporters who wish to be named.
## Scope
In scope:
- The RuVector Rust crates (`crates/`) and published npm packages
(`npm/packages/`).
- The `pi.ruv.io` / mcp-brain-server API and its data-handling
(provenance, differential privacy, witness chains).
- Cryptographic, memory-safety, authentication, authorization, injection,
data-poisoning, and denial-of-service issues.
Out of scope:
- Reports without a demonstrable security impact or a reproducible
proof-of-concept.
- Findings in third-party dependencies — please report those upstream (we will
still help coordinate if RuVector is affected).
- Social engineering, physical attacks, and volumetric DDoS.
## Supported versions
Security fixes are applied to the latest released version of each package and
crate. We do not backport fixes to older majors unless a fix is trivial and the
older line is still in wide use.
Thank you for helping keep RuVector and its users safe.

View file

@ -413,9 +413,12 @@ pub async fn create_router() -> (Router, AppState) {
.route("/v1/consciousness/status", get(consciousness_status))
.layer({
// CORS origins: configurable via CORS_ORIGINS env var (comma-separated).
// Falls back to safe defaults if unset.
// Falls back to safe defaults if unset. Explicit per-origin allowlist
// (not `*`) — callers authenticate with Bearer tokens, so credentials
// are not cookie-based, but an allowlist keeps the surface tight.
// conceptmapping.org origins added per issue #560.
let origins: Vec<axum::http::HeaderValue> = std::env::var("CORS_ORIGINS")
.unwrap_or_else(|_| "https://brain.ruv.io,https://pi.ruv.io,http://localhost:8080,http://127.0.0.1:8080".to_string())
.unwrap_or_else(|_| "https://brain.ruv.io,https://pi.ruv.io,https://app.conceptmapping.org,https://conceptmapping.org,http://localhost:8080,http://127.0.0.1:8080".to_string())
.split(',')
.filter_map(|s| s.trim().parse::<axum::http::HeaderValue>().ok())
.collect();