Skip to content

Commit

Permalink
Deprecate md5 and sha1 wrappers in sphinx.util (#11512)
Browse files Browse the repository at this point in the history
  • Loading branch information
AA-Turner committed Jul 25, 2023
1 parent f804da4 commit 5cf3dce
Show file tree
Hide file tree
Showing 9 changed files with 42 additions and 25 deletions.
3 changes: 3 additions & 0 deletions CHANGES
Expand Up @@ -12,6 +12,9 @@ Incompatible changes
Deprecated
----------

* #11512: Deprecate ``sphinx.util.md5`` and ``sphinx.util.sha1``.
Use ``hashlib`` instead.

Features added
--------------

Expand Down
10 changes: 10 additions & 0 deletions doc/extdev/deprecated.rst
Expand Up @@ -22,6 +22,16 @@ The following is a list of deprecated interfaces.
- Removed
- Alternatives

* - ``sphinx.util.md5``
- 7.2
- 9.0
- ``hashlib.md5``

* - ``sphinx.util.sha1``
- 7.2
- 9.0
- ``hashlib.sha1``

* - ``sphinx.util.osutil.cd``
- 6.2
- 8.0
Expand Down
5 changes: 3 additions & 2 deletions sphinx/builders/html/__init__.py
Expand Up @@ -2,6 +2,7 @@

from __future__ import annotations

import hashlib
import html
import os
import posixpath
Expand Down Expand Up @@ -37,7 +38,7 @@
from sphinx.locale import _, __
from sphinx.search import js_index
from sphinx.theming import HTMLThemeFactory
from sphinx.util import isurl, logging, md5
from sphinx.util import isurl, logging
from sphinx.util.display import progress_message, status_iterator
from sphinx.util.docutils import new_document
from sphinx.util.fileutil import copy_asset
Expand Down Expand Up @@ -80,7 +81,7 @@ def get_stable_hash(obj: Any) -> str:
return get_stable_hash(list(obj.items()))
elif isinstance(obj, (list, tuple)):
obj = sorted(get_stable_hash(o) for o in obj)
return md5(str(obj).encode()).hexdigest()
return hashlib.md5(str(obj).encode(), usedforsecurity=False).hexdigest()


def convert_locale_to_language_tag(locale: str | None) -> str | None:
Expand Down
7 changes: 4 additions & 3 deletions sphinx/ext/graphviz.py
Expand Up @@ -6,6 +6,7 @@
import posixpath
import re
import subprocess
from hashlib import sha1
from os import path
from subprocess import CalledProcessError
from typing import TYPE_CHECKING, Any
Expand All @@ -18,7 +19,7 @@
from sphinx.application import Sphinx
from sphinx.errors import SphinxError
from sphinx.locale import _, __
from sphinx.util import logging, sha1
from sphinx.util import logging
from sphinx.util.docutils import SphinxDirective, SphinxTranslator
from sphinx.util.i18n import search_image_for_language
from sphinx.util.nodes import set_source_info
Expand Down Expand Up @@ -62,7 +63,7 @@ def parse(self, dot: str) -> None:
if self.id == '%3':
# graphviz generates wrong ID if graph name not specified
# https://gitlab.com/graphviz/graphviz/issues/1327
hashed = sha1(dot.encode()).hexdigest()
hashed = sha1(dot.encode(), usedforsecurity=False).hexdigest()
self.id = 'grapviz%s' % hashed[-10:]
self.content[0] = self.content[0].replace('%3', self.id)

Expand Down Expand Up @@ -221,7 +222,7 @@ def render_dot(self: SphinxTranslator, code: str, options: dict, format: str,
hashkey = (code + str(options) + str(graphviz_dot) +
str(self.builder.config.graphviz_dot_args)).encode()

fname = f'{prefix}-{sha1(hashkey).hexdigest()}.{format}'
fname = f'{prefix}-{sha1(hashkey, usedforsecurity=False).hexdigest()}.{format}'
relfn = posixpath.join(self.builder.imgpath, fname)
outfn = path.join(self.builder.outdir, self.builder.imagedir, fname)

Expand Down
5 changes: 3 additions & 2 deletions sphinx/ext/imgmath.py
Expand Up @@ -7,6 +7,7 @@
import shutil
import subprocess
import tempfile
from hashlib import sha1
from os import path
from subprocess import CalledProcessError
from typing import Any
Expand All @@ -21,7 +22,7 @@
from sphinx.config import Config
from sphinx.errors import SphinxError
from sphinx.locale import _, __
from sphinx.util import logging, sha1
from sphinx.util import logging
from sphinx.util.math import get_node_equation_number, wrap_displaymath
from sphinx.util.osutil import ensuredir
from sphinx.util.png import read_png_depth, write_png_depth
Expand Down Expand Up @@ -239,7 +240,7 @@ def render_math(
self.builder.config,
self.builder.confdir)

filename = f"{sha1(latex.encode()).hexdigest()}.{image_format}"
filename = f"{sha1(latex.encode(), usedforsecurity=False).hexdigest()}.{image_format}"
generated_path = path.join(self.builder.outdir, self.builder.imagedir, 'math', filename)
ensuredir(path.dirname(generated_path))
if path.isfile(generated_path):
Expand Down
4 changes: 2 additions & 2 deletions sphinx/ext/inheritance_diagram.py
Expand Up @@ -31,6 +31,7 @@ class E(B): pass
from __future__ import annotations

import builtins
import hashlib
import inspect
import re
from collections.abc import Iterable
Expand All @@ -52,7 +53,6 @@ class E(B): pass
render_dot_latex,
render_dot_texinfo,
)
from sphinx.util import md5
from sphinx.util.docutils import SphinxDirective
from sphinx.util.typing import OptionSpec
from sphinx.writers.html import HTML5Translator
Expand Down Expand Up @@ -392,7 +392,7 @@ def run(self) -> list[Node]:

def get_graph_hash(node: inheritance_diagram) -> str:
encoded = (node['content'] + str(node['parts'])).encode()
return md5(encoded).hexdigest()[-10:]
return hashlib.md5(encoded, usedforsecurity=False).hexdigest()[-10:]


def html_visit_inheritance_diagram(self: HTML5Translator, node: inheritance_diagram) -> None:
Expand Down
9 changes: 5 additions & 4 deletions sphinx/transforms/post_transforms/images.py
Expand Up @@ -4,6 +4,7 @@

import os
import re
from hashlib import sha1
from math import ceil
from typing import Any

Expand All @@ -12,7 +13,7 @@
from sphinx.application import Sphinx
from sphinx.locale import __
from sphinx.transforms import SphinxTransform
from sphinx.util import logging, requests, sha1
from sphinx.util import logging, requests
from sphinx.util.http_date import epoch_to_rfc1123, rfc1123_to_epoch
from sphinx.util.images import get_image_extension, guess_mimetype, parse_data_uri
from sphinx.util.osutil import ensuredir
Expand Down Expand Up @@ -57,13 +58,13 @@ def handle(self, node: nodes.image) -> None:
basename = basename.split('?')[0]
if basename == '' or len(basename) > MAX_FILENAME_LEN:
filename, ext = os.path.splitext(node['uri'])
basename = sha1(filename.encode()).hexdigest() + ext
basename = sha1(filename.encode(), usedforsecurity=False).hexdigest() + ext
basename = re.sub(CRITICAL_PATH_CHAR_RE, "_", basename)

dirname = node['uri'].replace('://', '/').translate({ord("?"): "/",
ord("&"): "/"})
if len(dirname) > MAX_FILENAME_LEN:
dirname = sha1(dirname.encode()).hexdigest()
dirname = sha1(dirname.encode(), usedforsecurity=False).hexdigest()
ensuredir(os.path.join(self.imagedir, dirname))
path = os.path.join(self.imagedir, dirname, basename)

Expand Down Expand Up @@ -125,7 +126,7 @@ def handle(self, node: nodes.image) -> None:
return

ensuredir(os.path.join(self.imagedir, 'embeded'))
digest = sha1(image.data).hexdigest()
digest = sha1(image.data, usedforsecurity=False).hexdigest()
path = os.path.join(self.imagedir, 'embeded', digest + ext)
self.app.env.original_image_uri[path] = node['uri']

Expand Down
18 changes: 9 additions & 9 deletions sphinx/util/__init__.py
Expand Up @@ -104,21 +104,19 @@ def __setstate__(self, state: set[str]) -> None:
self._existing = state


def md5(data=b'', **kwargs):
"""Wrapper around hashlib.md5
def _md5(data=b'', **_kw):
"""Deprecated wrapper around hashlib.md5
Call with 'usedforsecurity=False'.
To be removed in Sphinx 9.0
"""

return hashlib.md5(data, usedforsecurity=False)


def sha1(data=b'', **kwargs):
"""Wrapper around hashlib.sha1
def _sha1(data=b'', **_kw):
"""Deprecated wrapper around hashlib.sha1
Call with 'usedforsecurity=False'.
To be removed in Sphinx 9.0
"""

return hashlib.sha1(data, usedforsecurity=False)


Expand All @@ -131,7 +129,7 @@ class DownloadFiles(dict):

def add_file(self, docname: str, filename: str) -> str:
if filename not in self:
digest = md5(filename.encode()).hexdigest()
digest = hashlib.md5(filename.encode(), usedforsecurity=False).hexdigest()
dest = f'{digest}/{os.path.basename(filename)}'
self[filename] = (set(), dest)

Expand Down Expand Up @@ -349,6 +347,8 @@ def _xml_name_checker():
'format_exception_cut_frames': (_exceptions.format_exception_cut_frames,
'sphinx.exceptions.format_exception_cut_frames'),
'xmlname_checker': (_xml_name_checker, 'sphinx.builders.epub3._XML_NAME_PATTERN'),
'md5': (_md5, ''),
'sha1': (_sha1, ''),
}


Expand Down
6 changes: 3 additions & 3 deletions tests/test_build_html.py
@@ -1,5 +1,6 @@
"""Test the HTML builder and check output against XPath."""

import hashlib
import os
import re
from itertools import chain, cycle
Expand All @@ -12,7 +13,6 @@
from sphinx.builders.html import validate_html_extra_path, validate_html_static_path
from sphinx.errors import ConfigError
from sphinx.testing.util import strip_escseq
from sphinx.util import md5
from sphinx.util.inventory import InventoryFile

FIGURE_CAPTION = ".//figure/figcaption/p"
Expand Down Expand Up @@ -474,9 +474,9 @@ def test_html_download(app):
@pytest.mark.sphinx('html', testroot='roles-download')
def test_html_download_role(app, status, warning):
app.build()
digest = md5(b'dummy.dat').hexdigest()
digest = hashlib.md5(b'dummy.dat', usedforsecurity=False).hexdigest()
assert (app.outdir / '_downloads' / digest / 'dummy.dat').exists()
digest_another = md5(b'another/dummy.dat').hexdigest()
digest_another = hashlib.md5(b'another/dummy.dat', usedforsecurity=False).hexdigest()
assert (app.outdir / '_downloads' / digest_another / 'dummy.dat').exists()

content = (app.outdir / 'index.html').read_text(encoding='utf8')
Expand Down

0 comments on commit 5cf3dce

Please sign in to comment.