[LTP] [PATCH v3 1/2] doc: generate CVE catalog documentation

Sachin Sant sachinp@linux.ibm.com
Fri Apr 24 14:18:44 CEST 2026


Add a Sphinx builder hook to parse runtest/cve and generate a
comprehensive CVE catalog in a single documentation file.

The implementation:
- Parses runtest/cve to extract CVE IDs, test names, and options
- Generates a single CVE catalog file (_static/cves.rst) containing:
  * Total CVE count
  * All CVEs sorted in descending order (newest first)
  * For each CVE:
    - Links to CVE MITRE database
    - Cross-references to test catalog entries
    - Test command details and options
    - Vulnerability description
- Integrates CVE catalog into main documentation index

Closes: https://github.com/linux-test-project/ltp/issues/1254
Cc: Andrea Cervesato <andrea.cervesato@suse.com>
Cc: Petr Vorel <pvorel@suse.cz>
Signed-off-by: Sachin Sant <sachinp@linux.ibm.com>
---
V3 changes:
- CVEs sorted in descending order
- append test name to CVE id : CVE (Test Name)
- Separate page for CVE catalog
- Link cve testcases to Test catalog entry
- v2 link https://lore.kernel.org/ltp/0df5f75d-eb8f-428e-9888-bb7a90a6b1a4@linux.ibm.com/

V2 changes:
- Replace Fixes tag by Closes
- V1 link https://lore.kernel.org/ltp/20260423105304.59788-1-sachinp@linux.ibm.com/T/#u

---
 doc/Makefile              |   2 +-
 doc/conf.py               | 127 ++++++++++++++++++++++++++++++++++++++
 doc/index.rst             |   4 ++
 doc/users/cve_catalog.rst |   6 ++
 4 files changed, 138 insertions(+), 1 deletion(-)
 create mode 100644 doc/users/cve_catalog.rst

diff --git a/doc/Makefile b/doc/Makefile
index 3123b1cd7..baa228022 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -30,7 +30,7 @@ spelling:
 	$(RUN_VENV); sphinx-build -b spelling -d build/doctree . build/spelling
 
 clean:
-	rm -rf html/ build/ _static/syscalls.rst _static/tests.rst syscalls.tbl \
+	rm -rf html/ build/ _static/syscalls.rst _static/tests.rst _static/cves.rst syscalls.tbl \
 		${abs_top_builddir}/metadata/ltp.json
 
 distclean: clean
diff --git a/doc/conf.py b/doc/conf.py
index 63d09352e..6d470d0d0 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -30,6 +30,16 @@ extensions = [
     'sphinx.ext.extlinks',
 ]
 
+# Configure autosectionlabel to prefix labels with document name
+# This prevents duplicate labels when same test name appears in multiple files
+# Required for CVE catalog cross-references to work
+autosectionlabel_prefix_document = True
+# Only create labels for sections with unique names
+autosectionlabel_maxdepth = 2
+
+# Suppress duplicate label warnings for kernel-doc generated content
+suppress_warnings = ['autosectionlabel.*']
+
 exclude_patterns = ["html*", '_static*', '.venv*']
 extlinks = {
     'repo': (f'{ltp_repo}/%s', '%s'),
@@ -535,6 +545,122 @@ def generate_test_catalog(_):
     with open(output, 'w+', encoding='utf-8') as new_tests:
         new_tests.write('\n'.join(text))
 
+def generate_cve_catalog(_):
+    """
+    Generate CVE catalog in a single file. Parse runtest/cve file and
+    generate documentation with links to CVE databases and test sources.
+    Similar to test_catalog, creates a single _static/cves.rst file with
+    all CVE information.
+    """
+    output = '_static/cves.rst'
+    runtest_cve = '../runtest/cve'
+
+    # Parse runtest/cve file
+    cve_data = {}
+    cve_pattern = re.compile(r'^(cve-(\d{4})-\d+)\s+(\S+)(?:\s+(.*))?$')
+
+    try:
+        with open(runtest_cve, 'r', encoding='utf-8') as f:
+            for line in f:
+                line = line.strip()
+                if not line or line.startswith('#'):
+                    continue
+
+                match = cve_pattern.match(line)
+                if match:
+                    cve_id = match.group(1).upper()
+                    year = match.group(2)
+                    test_name = match.group(3)
+                    options = match.group(4) if match.group(4) else ''
+
+                    cve_data[cve_id] = {
+                        'cve_id': cve_id,
+                        'year': year,
+                        'test_name': test_name,
+                        'options': options,
+                    }
+    except FileNotFoundError:
+        logger = sphinx.util.logging.getLogger(__name__)
+        msg = f"Can't find runtest/cve file ({runtest_cve})"
+        logger.warning(msg)
+        return
+
+    # Generate single CVE catalog file
+    total_cves = len(cve_data)
+    text = [
+        '.. warning::',
+        '    The following CVE catalog has been generated from the',
+        '    runtest/cve file and includes all CVE reproducers in LTP.',
+        '',
+        f'LTP includes reproducers for {total_cves} known CVEs. These '
+        'tests help verify',
+        'that systems are patched against known vulnerabilities.',
+        '',
+    ]
+
+    # Load metadata to check which tests have documentation
+    metadata = None
+    metadata_file = '../metadata/ltp.json'
+    try:
+        with open(metadata_file, 'r', encoding='utf-8') as data:
+            metadata = json.load(data)
+    except FileNotFoundError:
+        pass
+
+    # Add CVEs in descending order (newest first)
+    for cve_id, cve_info in sorted(cve_data.items(), reverse=True):
+        cve_url = f"https://cve.mitre.org/cgi-bin/cvename.cgi?name={cve_id}"
+        test_name = cve_info["test_name"]
+
+        # Only create cross-reference if test exists in metadata
+        if metadata and test_name in metadata.get('tests', {}):
+            # Create anchor using the correct document path prefix
+            test_anchor = f"users/test_catalog:{test_name}"
+            test_link = f":ref:`{test_name} <{test_anchor}>`"
+        else:
+            # If test not in metadata, just use plain text formatting
+            test_link = f"``{test_name}``"
+
+        # Create section header with CVE ID and test name
+        section_title = f'{cve_id} ({test_name})'
+        text.extend([
+            section_title,
+            len(section_title) * '-',
+            '',
+            f'**CVE Reference:** `{cve_id} <{cve_url}>`_',
+            '',
+            f'**Test Name:** {test_link}',
+            '',
+        ])
+
+        if cve_info['options']:
+            text.extend([
+                f'**Test Options:** ``{cve_info["options"]}``',
+                '',
+            ])
+
+        # Build test command on a single line to avoid RST formatting issues
+        test_cmd = f'``{test_name}'
+        if cve_info['options']:
+            test_cmd += f' {cve_info["options"]}'
+        test_cmd += '``'
+
+        text.extend([
+            f'This test reproduces the vulnerability described in {cve_id}.',
+            'The test verifies that the system is properly patched against',
+            'this known security vulnerability.',
+            '',
+            f'* **CVE Year:** {cve_info["year"]}',
+            f'* **Test Command:** {test_cmd}',
+            '',
+            '.. raw:: html',
+            '',
+            '    <hr>',
+            '',
+        ])
+
+    with open(output, 'w+', encoding='utf-8') as cve_catalog:
+        cve_catalog.write('\n'.join(text))
 
 def setup(app):
     """
@@ -543,4 +669,5 @@ def setup(app):
     """
     app.add_css_file('custom.css')
     app.connect('builder-inited', generate_syscalls_stats)
+    app.connect('builder-inited', generate_cve_catalog)
     app.connect('builder-inited', generate_test_catalog)
diff --git a/doc/index.rst b/doc/index.rst
index 496a12f80..733495f51 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -12,6 +12,7 @@
    users/testers_guide
    users/supported_systems
    users/stats
+   users/cve_catalog
    users/test_catalog
 
 .. toctree::
@@ -58,6 +59,9 @@ For users
 :doc:`users/stats`
    Some LTP statistics
 
+:doc:`users/cve_catalog`
+   LTP reproducers for known CVEs
+
 :doc:`users/test_catalog`
    The LTP test catalog
 
diff --git a/doc/users/cve_catalog.rst b/doc/users/cve_catalog.rst
new file mode 100644
index 000000000..f109f01d0
--- /dev/null
+++ b/doc/users/cve_catalog.rst
@@ -0,0 +1,6 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+CVE Catalog
+===========
+
+.. include:: ../_static/cves.rst
-- 
2.39.1



More information about the ltp mailing list