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

Deprecate md5 and sha1 wrappers in sphinx.util #11512

Merged
merged 2 commits into from Jul 25, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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