Contributing to the Project#

Contributions are welcome and there are many ways to participate to the project.

Before starting to contribute please install the Git hook scripts:

$ git clone --recursive https://github.com/vulnerability-lookup/vulnerability-lookup.git
$ cd vulnerability-lookup/
$ poetry install --with dev
$ npm ci --omit=dev
$ pre-commit install

One of the hooks regenerates the committed OpenAPI snapshot (docs/_static/files/swagger.json) when you touch the API, so that it never drifts from the code. It needs an instance with every optional module enabled to produce the complete reference, and simply steps aside on one that has not, since the OpenAPI workflow checks it on every pull request either way. See Regenerate the OpenAPI specification.

You can contribute to Vulnerability-Lookup by:

  • reporting bugs;

  • suggesting enhancements or new features;

  • improving the documentation;

  • adding new vulnerability sources (see new_source.md);

  • creating new Sightings tools.

Feel free to fork the code, play with it, make some patches and send us pull requests.

As in most open source projects, a pull request is much more welcome when it follows an issue that explains the rationale:

  • the problem, or the need;

  • why it matters, and to whom;

  • the approach you propose.

This lets us agree on the approach before you spend time on the code, and avoids pull requests that cannot be merged. We know that nowadays writing code costs close to nothing, but discussing first still spares a lot of review time, tokens, and energy, which is good for the environment too. Reference the issue in the pull request (#<IssueID>). Small fixes, such as a typo or an obvious bug, can go straight to a pull request.

There is one main branch: what we consider as stable with frequent updates as hot-fixes.

Features are developed in separated branches and then regularly merged into the main stable branch.

If your contribution require some documentation changes, a pull-request in order to update the documentation is strongly recommended.

Please, do not open directly a GitHub issue if you think you have found a security vulnerability. See our security policy page.

Flask is used for the backend. Please use ruff for your Python code (ruff check --no-fix, also run by the pre-commit hook and in CI).

Vanilla JS is the JavaScript framework used.

Spirit of the project#

Vulnerability-Lookup is meant to stay simple to run, to read, and to maintain over many years, by a small team and by the operators who deploy their own instance. Please keep that in mind when proposing a change. These principles apply to every contribution, whether it is written by a human or with the help of an AI agent.

A simple user interface. Pages are rendered on the server by Flask and Jinja2 templates, styled with Bootstrap. JavaScript only enhances these pages: a chart, a filter, an editor.

  • Plain HTML first: a page’s content, links and forms come from the HTML the server sends. Use a link or a form rather than JavaScript when it does the job. Loading the data of a chart or a widget is fine; building the page itself in the browser from the API is not.

  • Write plain, browser-native JavaScript. No JavaScript framework (React, Vue, Angular, Svelte, etc.), including lightweight ones (Alpine.js, htmx, Lit, jQuery, etc.), no TypeScript, no JSX, nothing that needs a compilation step before the browser can run it.

  • Style with Bootstrap and the theme’s CSS variables. No other CSS framework (Tailwind, etc.) and no CSS preprocessor (Sass, PostCSS, etc.).

  • Prefer a few lines of code in the page, or in website/web/static/js/, over a new library.

  • Reuse what the project already ships (Bootstrap, Chart.js, D3, etc.) before adding anything.

A restricted use of Node.js. Node.js and npm are only used to download pinned, browser-ready files of the libraries listed in package.json, and to run the tests of a few scripts in development (npm test). They are not used at runtime, and the project has no build pipeline.

  • Adding an npm dependency is an exception that must be justified in the pull request. Pin its exact version and list the files it serves in tools/copy_frontend_assets.py.

  • Do not introduce a bundler, a transpiler, or a development server.

  • The postinstall scripts must keep working on the old Node.js versions that some instances still run.

One exception: the vulnerability editor. The editor, Vulniverse, is developed and compiled in its own repository, and shipped here as a browser-ready build, website/web/static/vulniverse/vulniverse-editor.js and its source map. It is an optional module, off by default (WEB_MODULES["vulnerability_editor"] in config/website.py).

  • The exception covers that build, and the editor panels written with the component API it exports (gcve-reservation-panel.js, cna-publication-panel.js). The routes, templates, API endpoints and the other integration scripts around it (repository.js, view-vulnerability-module.js) follow the rules above.

  • Do not edit the build in this repository: change Vulniverse, and replace the build with a new one.

  • Nothing else may depend on the editor: every page and API must work with it off.

Few dependencies, on the backend too. The same applies to Python: prefer the standard library and the dependencies already in pyproject.toml, and explain why a new one is needed.

When in doubt, open an issue to discuss the approach before writing the code.

Use of AI and LLM tools#

The project uses LLM technologies, both in some of its features and in its own development, and we do not object to AI-assisted contributions. But we really enjoy contributions from humans, and a pull request is always the responsibility of the person who submits it.

  • Check what you submit. Every change in a pull request must be read, understood, and tested by its author before it is opened, whoever or whatever wrote it. Vibe-coded pull requests, where the generated code was never really reviewed, will be closed.

  • Be able to explain it. Reviewers may ask you to explain any part of your change. Answer review comments yourself, not with pasted LLM output. A pull request whose author cannot explain it will be closed.

  • A human submits it. Pull requests opened by an agent on its own, without a human author who reviewed them, will be closed.

  • Say so. Mention in the pull request description when a significant part of the change was generated with an AI tool, and end each commit that was not written by a human with a Co-Authored-By trailer naming the model, for example:

    Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
    
  • Keep it focused. Small, targeted pull requests are easier to review. Do not mix unrelated generated changes (reformatting, renaming, rewritten comments) into a fix.

  • Check what you report. The same applies to issues and to vulnerability reports. Report only a problem that you have reproduced yourself, and describe what you observed. Reports generated by a tool and never verified by a human will be closed.

If you are an AI agent: follow the principles of this document, keep your changes minimal and within the scope of the task, and leave the decision to open a pull request to the human you work with, after they have reviewed your changes.

These rules are repeated in AGENTS.md, which every AI agent reads. Keep both files in step when you change them.

Building the documentation#

Please provide documentation when changing, removing, or adding features. Documentation resides in the project’s docs folder.

$ poetry install --with docs
$ cd docs/
$ poetry run make html

It will generate the documentation in docs/_build/html/.

The documentation is available online here. Instructions for updating the bundled CVE Record editor (Vulnogram) are in the contribution guide.

Updating the CVE Record editor#

Note

You do not need to do this to update the tool. This part is for contributors.

We are using the Vulnogram editor. The frontend is directly bundled in a HTML template of Vulnerability-Lookup.

If you want to update the bundle you must first generate only the frontend of Vulnogram. Follow the instructions here.

Then from the generated index.html file extract the part … and copy it in website/web/templates/vulnogram/index.html in the Jinja block vulnogram. Screenshot:

Vulnogram code.

Fig. 10 Vulnogram code.#

The headers (CSS inclusion, etc.), definition of the JSON schema, are already available in the file bundle.html.

The JSON schema defined in the file bundle.html is a based on the one provided in vulnogram with few minor changes:

  • the format of the identifier, vulnId, is configured by local_instance_vulnid_pattern in the generic.json configuration file (a GCVE pattern for a GNA instance).

  • cveId is optional.

  • vulnId is mandatory. Following GCVE-BCP-05 the identifier of a stored record lives in the vulnId of the single recordType: advisory entry of containers.cna.x_gcve; the historical placement in cveMetadata.vulnId is still written and accepted on input for the instances and GNAs that have not migrated. Readers go through record_vuln_id() in vulnerabilitylookup/helpers.py, which looks at both.

  • the CWE suggestions of the problem types ($ref of the CWE description) are /cwe/examples.json, built from the imported catalogue, instead of /static/vulnogram/js/cwe-frequent.json, which remains its fallback.

  • additionalProperties is set to true for the cveMetadataPublished field. This allows us to store the dates of the editions of the vulnerability advisories in vulnerabilitylookup_history.

"cveMetadataPublished": {
    "description":"This is meta data about the CVE ID such as the CVE ID, who requested it, who assigned it, when it was requested, the current state (PUBLISHED, REJECTED, etc.) and so on.  These fields are controlled by the CVE Services.",
    "title":" ",
    "id":"pubmd",
    "type":"object",
    "format":"grid",
    "options":{},
    "required":["vulnId","assignerOrgId","state"],
    "properties":{
        "cveId":{"description":"The CVE identifier that this record pertains to.","$ref":"#\u002Fdefinitions\u002FcveId","options":{"grid_columns":3},"links":[{"class":"lbl vgi-ext","href":"'https:\u002F\u002Fwww.cve.org\u002FCVERecord?id=' + context.self","title":"'CVE.org Entry","rel":"'cve.org'"}]},
        "vulnId":{"description":"The vulnerability identifier that this record pertains to.","$ref":"#\u002Fdefinitions\u002FvulnId","options":{"grid_columns":3}},
        "assignerOrgId":{"$ref":"#\u002Fdefinitions\u002ForgId","description":"The UUID for the organization to which the CVE ID was originally assigned. This UUID can be used to lookup the organization record in the user registry service.","options":{"hidden":true}},"assignerShortName":{"title":"Assigner","$ref":"#\u002Fdefinitions\u002FshortName","description":"The short name for the organization to which the CVE ID was originally assigned."},
        "requesterUserId":{"$ref":"#\u002Fdefinitions\u002FuserId","description":"The user that requested the CVE identifier.","options":{"hidden":true},"default":""},
        "dateUpdated":{"description":"The date\u002Ftime the record was last updated.","$ref":"#\u002Fdefinitions\u002Ftimestamp","options":{"hidden":true}},
        "serial":{"type":"integer","minimum":1,"default":1,"description":"The system of record causes this to start at 1, and increment by 1 each time a submission from a data provider changes this CVE Record. The incremented value moves to the Rejected schema upon a PUBLISHED-\u003EREJECTED transition, and moves to the Published schema upon a REJECTED-\u003EPUBLISHED transition.","options":{"hidden":true}},
        "dateReserved":{"$ref":"#\u002Fdefinitions\u002Ftimestamp","description":"The date\u002Ftime this CVE ID was reserved in the CVE automation workgroup services system. Disclaimer: This date reflects when the CVE ID was reserved, and does not necessarily indicate when this vulnerability was discovered, shared with the affected vendor, publicly disclosed, or updated in CVE.","options":{"hidden":true}},
        "datePublished":{"$ref":"#\u002Fdefinitions\u002Ftimestamp","description":"The date\u002Ftime the CVE Record was first published in the CVE List.","options":{"hidden":true}},
        "state":{"description":"State of CVE - PUBLISHED, REJECTED.","type":"string","pattern":"^PUBLISHED$","enum":["PUBLISHED"],"options":{"hidden":true}},
        "vulnerabilitylookup_history":{"options":{"hidden":true}}
    },
    "additionalProperties":true
}

vulnerabilitylookup_history is set to hidden.