Skip to content

Repository files navigation

Minimal GCVE BCP-11 community contribution service

A small Flask reference implementation for the BCP-11 workflow:

  • fetch an existing CVE from db.gcve.eu or vulnerability.circl.lu;
  • accept unauthenticated community proposals for existing CVEs;
  • generate deterministic GCVE-65530-YEAR-SEQUENCE umbrella identifiers;
  • generate UUIDv7 contributionId values;
  • preserve immutable technical proposal data;
  • let one administrator accept (review.status=reviewed) or reject (review.status=rejected) a proposal;
  • preserve administrative revision history;
  • publish accepted BCP-11 fragments, including superseded/withdrawn history;
  • expose /api/gcve/publication as a simple BCP-03-style publication endpoint.
  • generate a complete derived view that preserves the authoritative CVE and attaches all published contributions under x_gcve.

This is deliberately a small reference service, not a complete production deployment.

Run

python -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt

export BCP11_ADMIN_TOKEN='replace-with-a-long-random-secret'
export BCP11_ADMIN_REVIEWER='GCVE BCP-11 maintainer'
export BCP11_DATABASE='./data/bcp11.sqlite3'
export BCP11_SECRET_KEY='replace-with-a-second-long-random-secret'

flask --app app run

Open http://127.0.0.1:5000/ for the community web interface. Visitors can submit a structured BCP-11 proposal without an account, follow its review status, and browse accepted contributions. Maintainers can sign in at /admin/login with BCP11_ADMIN_TOKEN to inspect the authoritative snapshot and accept or reject pending proposals. Set a stable, random BCP11_SECRET_KEY in deployed environments so administrator sessions remain valid across restarts.

Production deployments should terminate TLS at a reverse proxy and replace the in-process rate limiter with a shared limiter (for example Redis/nginx/HAProxy).

Fetch a CVE

Auto-try db.gcve.eu, then vulnerability.circl.lu:

curl 'http://127.0.0.1:5000/api/cve/CVE-2024-38063'

Force one source:

curl 'http://127.0.0.1:5000/api/cve/CVE-2024-38063?source=gcve'
curl 'http://127.0.0.1:5000/api/cve/CVE-2024-38063?source=circl'

The upstream request is fixed to:

https://db.gcve.eu/api/vulnerability/<CVE-ID>
https://vulnerability.circl.lu/api/vulnerability/<CVE-ID>

No user-supplied upstream URL is fetched, avoiding an SSRF-style fetch primitive.

Submit an unauthenticated proposal

curl -sS -X POST \
  -H 'Content-Type: application/json' \
  --data @example-proposal.json \
  http://127.0.0.1:5000/api/proposals

The service checks that the target CVE exists before storing the proposal. Proposal fields such as review, state, revision, contributionId, recordType, and relationships are server-controlled.

Anonymous submissions are supported by omitting contributors and setting:

{"anonymous": true}

Evidence is mandatory even for anonymous submissions.

Review as the single administrator

List pending proposals:

curl -H "Authorization: Bearer $BCP11_ADMIN_TOKEN" \
  'http://127.0.0.1:5000/api/admin/proposals?status=pending'

Accept:

curl -X POST \
  -H "Authorization: Bearer $BCP11_ADMIN_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"notes":"Evidence checked against the vendor advisory."}' \
  'http://127.0.0.1:5000/api/admin/proposals/urn:uuid:.../accept'

BCP-11 does not define an accepted review value, so the workflow state accepted is published as review.status = reviewed.

Reject:

curl -X POST \
  -H "Authorization: Bearer $BCP11_ADMIN_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"notes":"The evidence refers to a different vulnerability."}' \
  'http://127.0.0.1:5000/api/admin/proposals/urn:uuid:.../reject'

Accepted contributions can also be withdrawn, and their upstream administrative metadata can be updated. Each administrative change increments revision, changes dateUpdated, and appends a revision snapshot.

Public BCP-11 endpoints

Status of any submitted proposal:

GET /api/proposals/<contributionId>
GET /api/proposals/<contributionId>/history

Accepted/public contributions:

GET /api/bcp11/<CVE-ID>
GET /api/bcp11/umbrella/<GCVE-65530-ID>
GET /api/bcp11/contributions/<contributionId>
GET /api/bcp11/combined/<CVE-ID>
GET /api/gcve/publication?page=1&per_page=30&since=2026-08-01T00:00:00Z

The CVE/umbrella lookup returns the BCP-11 convenience index. The contribution endpoint returns the normative standalone fragment.

The combined endpoint fetches the latest authoritative CVE, leaves all CNA fields unchanged, and appends active, accepted BCP-11 contribution objects to x_gcve by default. Pass ?contributions=all to include all accepted contributions, including withdrawn or superseded history. x_gcve_resolution identifies the source record, policy, included active/reviewed inputs, and published contributions excluded from resolution (for example withdrawn or superseded history). The result is explicitly derived community data, not an authoritative replacement CVE record. The same view is available in the web UI at /combined/<CVE-ID>.

Supersession

A new proposal may contain:

{
  "supersedes": ["urn:uuid:..."]
}

When the administrator accepts it, each referenced accepted contribution for the same CVE is retained but gets a new administrative revision with state = superseded.

Security choices in this reference

  • request body capped at 128 KiB by default;
  • JSON nesting, collection sizes, and string lengths constrained;
  • evidence/contributor URLs restricted to HTTP(S);
  • evidence URLs are never fetched automatically;
  • only fixed operator-controlled CVE upstreams are fetched;
  • submission rate limiting is enabled (single-process implementation);
  • admin operations use a long Bearer token from the environment;
  • technical fields are immutable and protected by an internal SHA-256 digest check;
  • accepted/superseded/withdrawn fragments and all administrative revisions remain stored.

For a real public deployment, add a shared rate limiter, moderation/abuse telemetry, backups, signed publication feeds, schema validation against the final published BCP-11 schema, and a durable database such as PostgreSQL.

Tests

The format/storage unit tests do not require a live upstream:

PYTHONPATH=. python -m unittest discover -s tests -p 'test_*.py'

About

Implementation of BCP-11

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages