Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CVE and CWE roles #11781

Merged
merged 10 commits into from
Oct 5, 2024
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
3 changes: 3 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ Features added
such as :py:mod:`time` or :py:mod:`datetime` in :file:`conf.py`.
See :ref:`the docs <config-copyright>` for further detail.
Patch by Adam Turner.
* #11781: Add roles for referencing CVEs (:rst:role:`:cve: <cve>`)
and CWEs (:rst:role:`:cwe: <cwe>`).
Patch by Hugo van Kemenade.

Bugs fixed
----------
Expand Down
28 changes: 28 additions & 0 deletions doc/usage/restructuredtext/roles.rst
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,34 @@ There is also an :rst:role:`index` role to generate index entries.

The following roles generate external links:

.. rst:role:: cve

A reference to a `Common Vulnerabilities and Exposures`_ record.
This generates appropriate index entries.
The text "CVE *number*\ " is generated;
with a link to an online copy of the specified CVE.
You can link to a specific section by using ``:cve:`number#anchor```.
picnixz marked this conversation as resolved.
Show resolved Hide resolved

.. _Common Vulnerabilities and Exposures: https://www.cve.org/

For example: :cve:`2020-10735`

.. versionadded:: 8.1

.. rst:role:: cwe

A reference to a `Common Weakness Enumeration`_.
This generates appropriate index entries.
The text "CWE *number*\ " is generated; in the HTML output,
with a link to an online copy of the specified CWE.
You can link to a specific section by using ``:cwe:`number#anchor```.

.. _Common Weakness Enumeration: https://cwe.mitre.org/

For example: :cwe:`787`

.. versionadded:: 8.1

.. rst:role:: pep

A reference to a Python Enhancement Proposal. This generates appropriate
Expand Down
4 changes: 4 additions & 0 deletions sphinx/environment/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@
'image_loading': 'link',
'embed_stylesheet': False,
'cloak_email_addresses': True,
'cve_base_url': 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-',
'cve_references': None,
'cwe_base_url': 'https://cwe.mitre.org/data/definitions/',
'cwe_references': None,
'pep_base_url': 'https://peps.python.org/',
'pep_references': None,
'rfc_base_url': 'https://datatracker.ietf.org/doc/html/',
Expand Down
93 changes: 93 additions & 0 deletions sphinx/roles.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,94 @@ def process_link(
return result


class CVE(ReferenceRole):
def run(self) -> tuple[list[Node], list[system_message]]:
target_id = f'index-{self.env.new_serialno("index")}'
entries = [
(
'single',
_('Common Vulnerabilities and Exposures; CVE %s') % self.target,
target_id,
'',
None,
)
]

index = addnodes.index(entries=entries)
target = nodes.target('', '', ids=[target_id])
self.inliner.document.note_explicit_target(target)

try:
refuri = self.build_uri()
reference = nodes.reference(
'', '', internal=False, refuri=refuri, classes=['cve']
)
if self.has_explicit_title:
reference += nodes.strong(self.title, self.title)
else:
title = f'CVE {self.title}'
reference += nodes.strong(title, title)
except ValueError:
msg = self.inliner.reporter.error(
__('invalid CVE number %s') % self.target, line=self.lineno
)
prb = self.inliner.problematic(self.rawtext, self.rawtext, msg)
return [prb], [msg]

return [index, target, reference], []

def build_uri(self) -> str:
base_url = self.inliner.document.settings.cve_base_url
ret = self.target.split('#', 1)
if len(ret) == 2:
return f'{base_url}{ret[0]}#{ret[1]}'
return f'{base_url}{ret[0]}'


class CWE(ReferenceRole):
hugovk marked this conversation as resolved.
Show resolved Hide resolved
def run(self) -> tuple[list[Node], list[system_message]]:
target_id = f'index-{self.env.new_serialno("index")}'
entries = [
(
'single',
_('Common Weakness Enumeration; CWE %s') % self.target,
target_id,
'',
None,
)
]

index = addnodes.index(entries=entries)
target = nodes.target('', '', ids=[target_id])
self.inliner.document.note_explicit_target(target)

try:
refuri = self.build_uri()
reference = nodes.reference(
'', '', internal=False, refuri=refuri, classes=['cwe']
)
if self.has_explicit_title:
reference += nodes.strong(self.title, self.title)
else:
title = f'CWE {self.title}'
reference += nodes.strong(title, title)
except ValueError:
msg = self.inliner.reporter.error(
__('invalid CWE number %s') % self.target, line=self.lineno
)
prb = self.inliner.problematic(self.rawtext, self.rawtext, msg)
return [prb], [msg]

return [index, target, reference], []

def build_uri(self) -> str:
base_url = self.inliner.document.settings.cwe_base_url
ret = self.target.split('#', 1)
if len(ret) == 2:
return f'{base_url}{int(ret[0])}.html#{ret[1]}'
return f'{base_url}{int(ret[0])}.html'


class PEP(ReferenceRole):
def run(self) -> tuple[list[Node], list[system_message]]:
target_id = 'index-%s' % self.env.new_serialno('index')
Expand Down Expand Up @@ -454,12 +542,17 @@ def code_role(
'download': XRefRole(nodeclass=addnodes.download_reference),
# links to anything
'any': AnyXRefRole(warn_dangling=True),
# external links
'cve': CVE(),
'cwe': CWE(),
'pep': PEP(),
'rfc': RFC(),
# emphasised things
'guilabel': GUILabel(),
'menuselection': MenuSelection(),
'file': EmphasizedLiteral(),
'samp': EmphasizedLiteral(),
# other
'abbr': Abbreviation(),
'manpage': Manpage(),
}
Expand Down
68 changes: 68 additions & 0 deletions tests/test_markup/test_markup.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,74 @@ def get(name):
@pytest.mark.parametrize(
('type', 'rst', 'html_expected', 'latex_expected'),
[
(
# cve role
'verify',
':cve:`2020-10735`',
(
'<p><span class="target" id="index-0"></span><a class="cve reference external" '
'href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-10735">'
'<strong>CVE 2020-10735</strong></a></p>'
),
(
'\\sphinxAtStartPar\n'
'\\index{Common Vulnerabilities and Exposures@\\spxentry{Common Vulnerabilities and Exposures}'
'!CVE 2020\\sphinxhyphen{}10735@\\spxentry{CVE 2020\\sphinxhyphen{}10735}}'
'\\sphinxhref{https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-10735}'
'{\\sphinxstylestrong{CVE 2020\\sphinxhyphen{}10735}}'
),
),
(
# cve role with anchor
'verify',
':cve:`2020-10735#id1`',
(
'<p><span class="target" id="index-0"></span><a class="cve reference external" '
'href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-10735#id1">'
'<strong>CVE 2020-10735#id1</strong></a></p>'
),
(
'\\sphinxAtStartPar\n'
'\\index{Common Vulnerabilities and Exposures@\\spxentry{Common Vulnerabilities and Exposures}'
'!CVE 2020\\sphinxhyphen{}10735\\#id1@\\spxentry{CVE 2020\\sphinxhyphen{}10735\\#id1}}'
'\\sphinxhref{https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-10735\\#id1}'
'{\\sphinxstylestrong{CVE 2020\\sphinxhyphen{}10735\\#id1}}'
),
),
(
# cwe role
'verify',
':cwe:`787`',
(
'<p><span class="target" id="index-0"></span><a class="cwe reference external" '
'href="https://cwe.mitre.org/data/definitions/787.html">'
'<strong>CWE 787</strong></a></p>'
),
(
'\\sphinxAtStartPar\n'
'\\index{Common Weakness Enumeration@\\spxentry{Common Weakness Enumeration}'
'!CWE 787@\\spxentry{CWE 787}}'
'\\sphinxhref{https://cwe.mitre.org/data/definitions/787.html}'
'{\\sphinxstylestrong{CWE 787}}'
),
),
(
# cwe role with anchor
'verify',
':cwe:`787#id1`',
(
'<p><span class="target" id="index-0"></span><a class="cwe reference external" '
'href="https://cwe.mitre.org/data/definitions/787.html#id1">'
'<strong>CWE 787#id1</strong></a></p>'
),
(
'\\sphinxAtStartPar\n'
'\\index{Common Weakness Enumeration@\\spxentry{Common Weakness Enumeration}'
'!CWE 787\\#id1@\\spxentry{CWE 787\\#id1}}'
'\\sphinxhref{https://cwe.mitre.org/data/definitions/787.html\\#id1}'
'{\\sphinxstylestrong{CWE 787\\#id1}}'
),
),
(
# pep role
'verify',
Expand Down