diff mbox series

[v6,1/4] generate-cve-exclusions: Add output format option

Message ID 20260129211012.623827-2-valentin.boudevin@gmail.com
State New
Headers show
Series generate-cve-exclusions: Add a .bbclass | expand

Commit Message

ValentinBoudevin Jan. 29, 2026, 9:10 p.m. UTC
This option "--output-json-file" can be used to return a json file instead of
the printing the output in a .inc file.
The JSON file can easily be manipulated contrary to the .inc file.

Example output structure of the JSON file:

```json
{
  "cve_status": {
    "CVE-2019-25160": {
      "active": false,
      "message": "fixed-version: Fixed from version 5.0"
    },
    "CVE-2019-25162": {
      "active": false,
      "message": "fixed-version: Fixed from version 6.0"
    },
...
```

Add a second option "--output-inc-file" to also create a .inc at a given
location.

Both "--output-inc-file" and "--output-json-file" can be used at the
same time.

This commit doesn't affect or modify any existing behaviour of the
script.

Signed-off-by: Valentin Boudevin <valentin.boudevin@gmail.com>
---
 .../linux/generate-cve-exclusions.py          | 107 +++++++++++++++---
 1 file changed, 90 insertions(+), 17 deletions(-)
diff mbox series

Patch

diff --git a/meta/recipes-kernel/linux/generate-cve-exclusions.py b/meta/recipes-kernel/linux/generate-cve-exclusions.py
index dfc16663a5..3df0e93e07 100755
--- a/meta/recipes-kernel/linux/generate-cve-exclusions.py
+++ b/meta/recipes-kernel/linux/generate-cve-exclusions.py
@@ -91,19 +91,38 @@  def main(argp=None):
     parser = argparse.ArgumentParser()
     parser.add_argument("datadir", type=pathlib.Path, help="Path to a clone of https://github.com/CVEProject/cvelistV5 or https://git.kernel.org/pub/scm/linux/security/vulns.git")
     parser.add_argument("version", type=Version, help="Kernel version number to generate data for, such as 6.1.38")
+    parser.add_argument("--output-json-file", type=pathlib.Path, help="Write CVE_STATUS mapping to this JSON file")
+    parser.add_argument("--output-inc-file", type=pathlib.Path, help="Write CVE_STATUS mapping to this INC file")
 
     args = parser.parse_args(argp)
     datadir = args.datadir.resolve()
     version = args.version
     base_version = Version(f"{version.major}.{version.minor}")
-
-    data_version = subprocess.check_output(("git", "describe", "--tags", "HEAD"), cwd=datadir, text=True)
-
-    print(f"""
+    print_to_stdout = not args.output_json_file and not args.output_inc_file
+
+    cve_status = {}
+    inc_lines = []
+    
+    if print_to_stdout:
+        data_version = subprocess.check_output(("git", "describe", "--tags", "HEAD"), cwd=datadir, text=True)
+        print(f"""
 # Auto-generated CVE metadata, DO NOT EDIT BY HAND.
 # Generated at {datetime.datetime.now(datetime.timezone.utc)} for kernel version {version}
 # From {datadir.name} {data_version}
 
+python check_kernel_cve_status_version() {{
+    this_version = "{version}"
+    kernel_version = d.getVar("LINUX_VERSION")
+    if kernel_version != this_version:
+        bb.warn("Kernel CVE status needs updating: generated for %s but kernel is %s" % (this_version, kernel_version))
+}}
+do_cve_check[prefuncs] += "check_kernel_cve_status_version"
+""")
+    elif args.output_inc_file:
+        inc_lines.append(f"""
+# Auto-generated CVE metadata, DO NOT EDIT BY HAND.
+# Generated at {datetime.datetime.now(datetime.timezone.utc)} for kernel version {version}
+
 python check_kernel_cve_status_version() {{
     this_version = "{version}"
     kernel_version = d.getVar("LINUX_VERSION")
@@ -131,26 +150,80 @@  do_cve_check[prefuncs] += "check_kernel_cve_status_version"
             continue
         first_affected, fixed, backport_ver = get_fixed_versions(cve_info, base_version)
         if not fixed:
-            print(f"# {cve} has no known resolution")
+            cve_status[cve] = {
+                "active": True,
+                "message": "no known resolution"
+            }
+            if not args.output_json_file:
+                print(f"# {cve} has no known resolution")
+            elif args.output_inc_file:
+                inc_lines.append(f'# {cve} has no known resolution')
         elif first_affected and version < first_affected:
-            print(f'CVE_STATUS[{cve}] = "fixed-version: only affects {first_affected} onwards"')
+            cve_status[cve] = {
+                "active": False,
+                "message": f"fixed-version: only affects {first_affected} onwards"
+            }
+            if not args.output_json_file:
+                print(f'CVE_STATUS[{cve}] = "fixed-version: only affects {first_affected} onwards"')
+            elif args.output_inc_file:
+                inc_lines.append(f'CVE_STATUS[{cve}] = "fixed-version: only affects {first_affected} onwards"')
         elif fixed <= version:
-            print(
-                f'CVE_STATUS[{cve}] = "fixed-version: Fixed from version {fixed}"'
-            )
+            cve_status[cve] = {
+                "active": False,
+                "message": f"fixed-version: Fixed from version {fixed}"
+            }
+            if not args.output_json_file:
+                print(f'CVE_STATUS[{cve}] = "fixed-version: Fixed from version {fixed}"')
+            elif args.output_inc_file:
+                inc_lines.append(f'CVE_STATUS[{cve}] = "fixed-version: Fixed from version {fixed}"')
         else:
             if backport_ver:
                 if backport_ver <= version:
-                    print(
-                        f'CVE_STATUS[{cve}] = "cpe-stable-backport: Backported in {backport_ver}"'
-                    )
+                    cve_status[cve] = {
+                        "active": False,
+                        "message": f"cpe-stable-backport: Backported in {backport_ver}"
+                    }
+                    if not args.output_json_file:
+                        print(f'CVE_STATUS[{cve}] = "cpe-stable-backport: Backported in {backport_ver}"')
+                    elif args.output_inc_file:
+                        inc_lines.append(f'CVE_STATUS[{cve}] = "cpe-stable-backport: Backported in {backport_ver}"')
                 else:
-                    print(f"# {cve} may need backporting (fixed from {backport_ver})")
+                    cve_status[cve] = {
+                        "active": True,
+                        "message": f"May need backporting (fixed from {backport_ver})"
+                    }
+                    if not args.output_json_file:
+                        print(f"# {cve} may need backporting (fixed from {backport_ver})")
+                    elif args.output_inc_file:
+                        inc_lines.append(f'# {cve} may need backporting (fixed from {backport_ver})')
             else:
-                print(f"# {cve} needs backporting (fixed from {fixed})")
-
-        print()
-
+                cve_status[cve] = {
+                    "active": True,
+                    "message": f"#Needs backporting (fixed from {fixed})"
+                }
+                if not args.output_json_file:
+                    print(f"# {cve} needs backporting (fixed from {fixed})")
+                elif args.output_inc_file:
+                    inc_lines.append(f'# {cve} needs backporting (fixed from {fixed})')
+
+        if print_to_stdout:
+            print()
+        elif args.output_inc_file:
+            inc_lines.append("")
+
+    # Emit structured output if --ret-struct was requested
+    if args.output_json_file:
+        args.output_json_file.write_text(
+        json.dumps(
+                {
+                    "cve_status": cve_status,
+                },
+                indent=2
+            ),
+            encoding="utf-8"
+        )
+    if args.output_inc_file:
+        args.output_inc_file.write_text("\n".join(inc_lines), encoding="utf-8")
 
 if __name__ == "__main__":
     main()