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 2 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
23 changes: 23 additions & 0 deletions doc/usage/restructuredtext/roles.rst
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,29 @@ 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; in the HTML output, this text is a hyperlink to an online copy
of the specified CVE. You can link to a specific section by saying
``:cve:`number#anchor```.

For example: :cve:`2020-10735`

.. versionadded:: 7.3

.. 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,
this text is a hyperlink to an online copy of the specified CWE. You can
link to a specific section by saying ``:cwe:`number#anchor```.

For example: :cwe:`787`

.. versionadded:: 7.3

.. 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 @@ -43,6 +43,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
74 changes: 74 additions & 0 deletions sphinx/roles.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,78 @@ def process_link(self, env: BuildEnvironment, refnode: Element, has_explicit_tit
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 = "CVE " + self.title
hugovk marked this conversation as resolved.
Show resolved Hide resolved
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]}"
else:
return f"{base_url}{ret[0]}"
hugovk marked this conversation as resolved.
Show resolved Hide resolved


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 = "CWE " + self.title
hugovk marked this conversation as resolved.
Show resolved Hide resolved
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]}"
else:
return f"{base_url}{int(ret[0])}.html"
hugovk marked this conversation as resolved.
Show resolved Hide resolved


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 @@ -401,6 +473,8 @@ def code_role(name: str, rawtext: str, text: str, lineno: int,
# links to anything
'any': AnyXRefRole(warn_dangling=True),

'cve': CVE(),
'cwe': CWE(),
'pep': PEP(),
'rfc': RFC(),
'guilabel': GUILabel(),
Expand Down
52 changes: 52 additions & 0 deletions tests/test_markup.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,58 @@ 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
Loading