Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,35 @@ repos:
name: Detect if mixed line ending is used (\r vs. \r\n)
- id: trailing-whitespace
name: Remove trailing whitespace at end of line
# Project-local checks for the `generate-cve-json` Python project at
# `tools/vulnogram/generate-cve-json/`. Each hook sets the working
# directory via `uv run --directory` so ruff / mypy / pytest pick up
# config from the project's pyproject.toml without explicit paths.
# The `files:` pattern scopes each hook to changes inside the project
# directory.
- repo: local
hooks:
- id: generate-cve-json-ruff-check
name: ruff check (generate-cve-json)
language: system
entry: uv run --directory tools/vulnogram/generate-cve-json ruff check
files: ^tools/vulnogram/generate-cve-json/(src|tests|pyproject\.toml)
pass_filenames: false
- id: generate-cve-json-ruff-format
name: ruff format (generate-cve-json)
language: system
entry: uv run --directory tools/vulnogram/generate-cve-json ruff format --check
files: ^tools/vulnogram/generate-cve-json/(src|tests|pyproject\.toml)
pass_filenames: false
- id: generate-cve-json-mypy
name: mypy (generate-cve-json)
language: system
entry: uv run --directory tools/vulnogram/generate-cve-json mypy
files: ^tools/vulnogram/generate-cve-json/(src|tests|pyproject\.toml)
pass_filenames: false
- id: generate-cve-json-pytest
name: pytest (generate-cve-json)
language: system
entry: uv run --directory tools/vulnogram/generate-cve-json pytest
files: ^tools/vulnogram/generate-cve-json/(src|tests|pyproject\.toml)
pass_filenames: false
10 changes: 10 additions & 0 deletions tools/vulnogram/generate-cve-json/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ paste into the Vulnogram **"#source"** tab of the ASF CVE tool. The goal is
to eliminate the manual "copy each field from the issue into the right
Vulnogram form input" step when you are preparing to publish an advisory.

> **Project-agnostic by design.** All project-specific values
> (vendor, top-level product / package name, provider display map,
> CNA org id, generator tag, …) are loaded from a TOML config the
> adopting project ships at `<project-config>/tools/vulnogram/cve-json-config.toml`.
> Examples in this document use Apache Airflow's configuration as a
> running illustration, since that's where the tool was originally
> developed; any adopter writes their own config per the schema in
> the package [README](README.md) and the tool emits CVE records
> against their own product taxonomy.

**Golden rule:** the script generates a *proposal* JSON document. It
parses a handful of structured fields from the issue body, but it cannot
read the security team member's mind. Always review the generated JSON
Expand Down
94 changes: 94 additions & 0 deletions tools/vulnogram/generate-cve-json/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "generate-cve-json"
version = "0.1.0"
description = "Generate a CVE 5.x JSON record from a tracking issue in the active project's security tracker (Vulnogram adapter)."
readme = "README.md"
requires-python = ">=3.11"
license = { text = "Apache-2.0" }
# Runtime deps are deliberately empty — the script is stdlib-only and shells
# out to `gh` for GitHub access. Keeping the runtime closed means `uv run`
# can resolve the environment in milliseconds.
dependencies = []

[project.scripts]
generate-cve-json = "generate_cve_json:main"

[dependency-groups]
dev = [
"mypy>=1.11",
"pytest>=8.0",
"ruff>=0.6",
]

[tool.hatch.build.targets.wheel]
packages = ["src/generate_cve_json"]

[tool.ruff]
line-length = 110
target-version = "py311"
src = ["src", "tests"]

[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort
"B", # flake8-bugbear
"UP", # pyupgrade
"SIM", # flake8-simplify
"C4", # flake8-comprehensions
"RUF", # ruff-specific
]
ignore = [
"E501", # line-too-long — the 110-char limit above is already generous
]

[tool.ruff.lint.per-file-ignores]
"tests/**" = ["B", "SIM"] # test clarity beats these

[tool.mypy]
python_version = "3.11"
files = ["src", "tests"]
# The script manipulates untyped JSON-dict shapes extensively; bare
# `dict` / `list` annotations are pragmatic. Keep the useful checks
# (unreachable code, implicit Optional, return types) and turn off the
# ones that would require a heavy TypedDict refactor.
warn_unused_ignores = true
warn_redundant_casts = true
warn_unreachable = true
check_untyped_defs = true
no_implicit_optional = true
disallow_untyped_defs = true
disallow_incomplete_defs = true

[[tool.mypy.overrides]]
module = "tests.*"
# Tests aren't expected to type-annotate everything and often mock freely.
disallow_untyped_defs = false
disallow_incomplete_defs = false

[tool.pytest.ini_options]
minversion = "8.0"
addopts = "-ra -q"
testpaths = ["tests"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
"""Public entry points for the ``generate-cve-json`` project.

The implementation lives in :mod:`generate_cve_json.cve_json`; this
package re-exports the names that callers (``generate-cve-json`` console
script, ``python -m generate_cve_json``, and the test suite) depend on
so they can keep using ``from generate_cve_json import X``.
"""

from __future__ import annotations

from generate_cve_json.cve_json import (
COLLECTION_URL_TO_PROJECT_URL_TEMPLATE,
NEXT_VERSION_TOKEN_RE,
PROVIDER_DISPLAY_MAP,
_build_attachment_body,
_has_vendor_advisory_reference,
_is_cna_ready_for_review,
_product_for_package,
attach_to_issue,
build_affected,
build_cna_container,
build_credits,
build_descriptions,
build_metrics,
build_problem_types,
build_references,
classify_reference,
combine_remediation_developers,
compute_cna_private_state,
compute_package_url,
emit_json,
extract_field,
fetch_issue,
format_version_range,
main,
parse_affected_versions,
parse_args,
parse_credits_from_field,
parse_cve_id,
parse_cwe,
parse_url_list,
resolve_title,
wrap_cve_record,
)

__all__ = [
"COLLECTION_URL_TO_PROJECT_URL_TEMPLATE",
"NEXT_VERSION_TOKEN_RE",
"PROVIDER_DISPLAY_MAP",
"_build_attachment_body",
"_has_vendor_advisory_reference",
"_is_cna_ready_for_review",
"_product_for_package",
"attach_to_issue",
"build_affected",
"build_cna_container",
"build_credits",
"build_descriptions",
"build_metrics",
"build_problem_types",
"build_references",
"classify_reference",
"combine_remediation_developers",
"compute_cna_private_state",
"compute_package_url",
"emit_json",
"extract_field",
"fetch_issue",
"format_version_range",
"main",
"parse_affected_versions",
"parse_args",
"parse_credits_from_field",
"parse_cve_id",
"parse_cwe",
"parse_url_list",
"resolve_title",
"wrap_cve_record",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
"""Entry point for ``python -m generate_cve_json``."""

from __future__ import annotations

from generate_cve_json import main

if __name__ == "__main__":
main()
Loading