From 05d2fcf4d0ccf1f042b0252d75e61d2f30a6bec6 Mon Sep 17 00:00:00 2001 From: Sli Date: Sun, 29 Mar 2026 13:13:57 +0200 Subject: [PATCH] Upgrade xapian to 1.4.31 and add sha256 check to avoid supply chain attack --- core/management/commands/install_xapian.py | 17 +++++++++++++---- core/management/commands/install_xapian.sh | 12 +++++++++++- pyproject.toml | 7 ++++++- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/core/management/commands/install_xapian.py b/core/management/commands/install_xapian.py index 4be1d907..3f7fe3cc 100644 --- a/core/management/commands/install_xapian.py +++ b/core/management/commands/install_xapian.py @@ -39,12 +39,16 @@ class Command(BaseCommand): return None return xapian.version_string() - def _desired_version(self) -> str: + def _desired_version(self) -> tuple[str, str, str]: with open( Path(__file__).parent.parent.parent.parent / "pyproject.toml", "rb" ) as f: pyproject = tomli.load(f) - return pyproject["tool"]["xapian"]["version"] + return ( + pyproject["tool"]["xapian"]["version"], + pyproject["tool"]["xapian"]["core-sha256"], + pyproject["tool"]["xapian"]["bindings-sha256"], + ) def handle(self, *args, force: bool, **options): if not os.environ.get("VIRTUAL_ENV", None): @@ -53,7 +57,7 @@ class Command(BaseCommand): ) return - desired = self._desired_version() + desired, core_checksum, bindings_checksum = self._desired_version() if desired == self._current_version(): if not force: self.stdout.write( @@ -65,7 +69,12 @@ class Command(BaseCommand): f"Installing xapian version {desired} at {os.environ['VIRTUAL_ENV']}" ) subprocess.run( - [str(Path(__file__).parent / "install_xapian.sh"), desired], + [ + str(Path(__file__).parent / "install_xapian.sh"), + desired, + core_checksum, + bindings_checksum, + ], env=dict(os.environ), check=True, ) diff --git a/core/management/commands/install_xapian.sh b/core/management/commands/install_xapian.sh index 2c97f120..5bfeef07 100755 --- a/core/management/commands/install_xapian.sh +++ b/core/management/commands/install_xapian.sh @@ -1,7 +1,11 @@ #!/usr/bin/env bash # Originates from https://gist.github.com/jorgecarleitao/ab6246c86c936b9c55fd # first argument of the script is Xapian version (e.g. 1.2.19) +# second argument of the script is core sha256 +# second argument of the script is binding sha256 VERSION="$1" +CORE_SHA256="$2" +BINDINGS_SHA256="$3" # Cleanup env vars for auto discovery mechanism unset CPATH @@ -21,9 +25,15 @@ BINDINGS=xapian-bindings-$VERSION # download echo "Downloading source..." -curl -O "https://oligarchy.co.uk/xapian/$VERSION/${CORE}.tar.xz" +curl -O "https://oligarchy.co.uk/xapian/$VERSION/${CORE}.tar.xz" || exit 1 + +echo "${CORE_SHA256} ${CORE}.tar.xz" | sha256sum -c - || exit 1 + curl -O "https://oligarchy.co.uk/xapian/$VERSION/${BINDINGS}.tar.xz" +echo "${BINDINGS_SHA256} ${BINDINGS}.tar.xz" | sha256sum -c - || exit 1 + + # extract echo "Extracting source..." tar xf "${CORE}.tar.xz" diff --git a/pyproject.toml b/pyproject.toml index 94273bf9..18ac2e07 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -92,7 +92,12 @@ docs = [ default-groups = ["dev", "tests", "docs"] [tool.xapian] -version = "1.4.29" +version = "1.4.31" +# Those hashes are here to protect against supply chains attacks +# They are obtained by downloawing xapian-core and xapian-bindings from xapian.org +# and running `sha256sum` on the downloaded compressed files +core-sha256 = "fecf609ea2efdc8a64be369715aac733336a11f7480a6545244964ae6bc80811" +bindings-sha256 = "a38cc7ba4188cc0bd27dc7369f03906772047087a1c54f1b93355d5e9103c304" [tool.ruff] output-format = "concise" # makes ruff error logs easier to read