Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def resolve_fields(self, purl: PackageURL) -> None:

logger.debug("From package JSON inferred Python constraints: %s", python_version_set)

self.data["has_binaries"] = not pypi_package_json.has_pure_wheel()
self.data["has_binaries"] = pypi_package_json.has_non_pure_wheel()

if self.data["has_binaries"]:
logger.debug("Can not find a pure wheel")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,14 @@ def gen_dockerfile(buildspec: BaseBuildSpecDict) -> str:
"else python -m build --wheel -n; fi"
)

wheel_url: str = ""
# Initialized empty so that the validation script can exit gracefully in the case we find no upstream wheel
wheel_name: str = ""

wheel_urls = buildspec["upstream_artifacts"]["wheels"]
# We currently only look for the pure wheel, if it exists
if wheel_urls:
wheel_url = list(wheel_urls)[0]
wheel_name = wheel_url.rsplit("/", 1)[-1]
wheel_url: str = ""
if "wheels" in buildspec["upstream_artifacts"]:
wheel_urls = buildspec["upstream_artifacts"]["wheels"]
if wheel_urls:
wheel_url = wheel_urls[0]
wheel_name = wheel_url.rsplit("/", 1)[-1]
else:
logger.debug("We could not find an upstream artifact, and therefore we cannot run validation")

Expand Down
13 changes: 5 additions & 8 deletions src/macaron/slsa_analyzer/package_registry/pypi_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -965,13 +965,13 @@ def download_wheel(self) -> bool:
logger.debug(error)
return False

def has_pure_wheel(self) -> bool:
"""Check whether the PURL has a pure wheel from its package json.
def has_non_pure_wheel(self) -> bool:
"""Check whether the PURL has any non-pure wheel from its package json.

Returns
-------
bool
Whether the PURL has a pure wheel or not.
Whether the PURL has any non-pure wheel or not.
"""
if self.component_version:
urls = json_extract(self.package_json, ["releases", self.component_version], list)
Expand All @@ -982,16 +982,13 @@ def has_pure_wheel(self) -> bool:
return False
for distribution in urls:
file_name: str = distribution.get("filename") or ""
# Parse out and check none and any
# Catch exceptions
try:
_, _, _, tags = parse_wheel_filename(file_name)
# Check if none and any are in the tags (i.e. the wheel is pure)
if all(tag.abi == "none" and tag.platform == "any" for tag in tags):
# A wheel is non-pure if any tag is not abi=none and platform=any
if any(tag.abi != "none" or tag.platform != "any" for tag in tags):
return True
except InvalidWheelFilename:
logger.debug("Could not parse wheel name.")
return False
return False

@contextmanager
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"macaron_version": "0.20.0",
"group_id": null,
"artifact_id": "pytesseract",
"version": "0.3.8",
"git_repo": "https://github.com/madmaze/pytesseract",
"git_tag": "805d3959496232edf2f0feb41af750d5702d85b7",
"newline": "lf",
"language_version": [
">=3.7"
],
"ecosystem": "pypi",
"purl": "pkg:pypi/pytesseract@0.3.8",
"language": "python",
"build_tools": [
"pip"
],
"build_commands": [
[
"python",
"-m",
"build",
"--wheel",
"-n"
]
],
"has_binaries": false,
"build_requires": {
"setuptools": "==67.7.2"
},
"build_backends": [
"setuptools.build_meta"
],
"upstream_artifacts": {
"sdist": [
"https://files.pythonhosted.org/packages/a3/c9/d6e8903482bd6fb994c32722831d15842dd8b614f94ad9ca735807252671/pytesseract-0.3.8.tar.gz"
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@

#syntax=docker/dockerfile:1.10
FROM oraclelinux:9

# Install core tools
RUN dnf -y install which wget tar unzip git

# Install compiler and make
RUN dnf -y install gcc make

# Download and unzip interpreter
RUN <<EOF
wget https://www.python.org/ftp/python/3.7.17/Python-3.7.17.tgz
tar -xf Python-3.7.17.tgz
EOF

# Install necessary libraries to build the interpreter
# From: https://devguide.python.org/getting-started/setup-building/
RUN dnf install \
gcc-c++ gdb lzma glibc-devel libstdc++-devel openssl-devel \
readline-devel zlib-devel libzstd-devel libffi-devel bzip2-devel \
xz-devel sqlite sqlite-devel sqlite-libs libuuid-devel gdbm-libs \
perf expat expat-devel mpdecimal python3-pip \
perl perl-File-Compare

# Build OpenSSL 1.1.1w
RUN <<EOF
wget https://www.openssl.org/source/old/1.1.1/openssl-1.1.1w.tar.gz
tar xzf openssl-1.1.1w.tar.gz
cd openssl-1.1.1w
./config --prefix=/opt/openssl --openssldir=/opt/openssl shared zlib
make -j"$(nproc)"
make install_sw
EOF

ENV LD_LIBRARY_PATH=/opt/openssl/lib
ENV CPPFLAGS=-I/opt/openssl/include
ENV LDFLAGS=-L/opt/openssl/lib

# Build interpreter and create venv
RUN <<EOF
cd Python-3.7.17
./configure --with-pydebug
make -s -j $(nproc)
make install
./python -m ensurepip
./python -m venv /deps
EOF

# Clone code to rebuild
RUN <<EOF
mkdir src
cd src
git clone https://github.com/madmaze/pytesseract .
git checkout --force 805d3959496232edf2f0feb41af750d5702d85b7
EOF

WORKDIR /src

# Install build and the build backends
RUN <<EOF
/deps/bin/pip install --upgrade setuptools
/deps/bin/pip install build
EOF

# Run the build
RUN source /deps/bin/activate && python -m build --wheel -n

# Validate script
RUN cat <<'EOF' >/validate
[ -n "" ] || { echo "No upstream artifact to validate against."; exit 1; }
# Capture artifacts generated
WHEELS=(/src/dist/*.whl)
# Ensure we only have one artifact
[ ${#WHEELS[@]} -eq 1 ] || { echo "Unexpected artifacts produced!"; exit 1; }
# BUILT_WHEEL is the artifact we built
BUILT_WHEEL=${WHEELS[0]}
# Ensure the artifact produced is not the literal returned by the glob
[ -e $BUILT_WHEEL ] || { echo "No wheels found!"; exit 1; }
# Download the wheel
wget -q
# Compare wheel names
[ $(basename $BUILT_WHEEL) == "" ] || { echo "Wheel name does not match!"; exit 1; }
# Compare file tree
(unzip -Z1 $BUILT_WHEEL | grep -v '\.dist-info' | sort) > built.tree
(unzip -Z1 "" | grep -v '\.dist-info' | sort ) > pypi_artifact.tree
diff -u built.tree pypi_artifact.tree || { echo "File trees do not match!"; exit 1; }
echo "Success!"
EOF

ENTRYPOINT ["/bin/bash","/validate"]
45 changes: 45 additions & 0 deletions tests/integration/cases/pypi_pytesseract/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Copyright (c) 2025 - 2026, Oracle and/or its affiliates. All rights reserved.
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/.

description: |
Integration test to ensure that has_binaries is not a false positive.

tags:
- macaron-python-package
- tutorial

steps:
- name: Run macaron analyze
kind: analyze
options:
command_args:
- -purl
- pkg:pypi/pytesseract@0.3.8
- name: Generate the buildspec
kind: gen-build-spec
options:
command_args:
- -purl
- pkg:pypi/pytesseract@0.3.8
- --output-format
- default-buildspec
- name: Compare Buildspec.
kind: compare
options:
kind: default_build_spec
result: output/buildspec/pypi/pytesseract/macaron.buildspec
expected: expected_default.buildspec
- name: Generate the buildspec
kind: gen-build-spec
options:
command_args:
- -purl
- pkg:pypi/pytesseract@0.3.8
- --output-format
- dockerfile
- name: Compare Dockerfile
kind: compare
options:
kind: dockerfile_build_spec
result: output/buildspec/pypi/pytesseract/dockerfile.buildspec
expected: expected_dockerfile.buildspec
Loading