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

Improve file-details with certificate claims #17145

Merged
merged 18 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from 6 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
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
set -x \
&& apt-get update \
&& apt-get install --no-install-recommends -y \
libpq5 libxml2 libxslt1.1 libcurl4 \
DarkaMaul marked this conversation as resolved.
Show resolved Hide resolved
libpq5 libxml2 libxslt1.1 libcurl4 git \
$(if [ "$DEVEL" = "yes" ]; then echo 'bash libjpeg62 postgresql-client build-essential libffi-dev libxml2-dev libxslt-dev libpq-dev libcurl4-openssl-dev libssl-dev vim oathtool'; fi) \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
Expand All @@ -238,4 +238,5 @@ COPY . /opt/warehouse/src/
RUN tldextract --update
# Load our module to pre-compile as much bytecode as we can easily.
# Saves time collectively on container boot!
RUN pip install --force pypi-attestations@git+https://github.com/trailofbits/pypi-attestations/@dm/extensions
RUN python -m warehouse
130 changes: 130 additions & 0 deletions tests/unit/packaging/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@ def test_detail_rendered(self, db_request):
],
"maintainers": sorted(users, key=lambda u: u.username.lower()),
"license": None,
"get_publication_details": views.get_publication_details,
"format_url": views.format_url,
}

def test_detail_renders_files_natural_sort(self, db_request):
Expand Down Expand Up @@ -323,6 +325,134 @@ def test_long_singleline_license(self, db_request):
"characters, it's really so lo..."
)

@pytest.mark.parametrize(
("base_url", "reference", "expected"),
[
(
"https://github.com/pypi/warehouse",
"2ec275dc2a05cd8ed2f0a9a0adadfcff9096d982",
(
"https://github.com/pypi/warehouse/tree/"
"2ec275dc2a05cd8ed2f0a9a0adadfcff9096d982"
),
),
(
"https://gitlab.com/sample/sample",
"refs/heads/main",
"https://gitlab.com/sample/sample/tree/refs/heads/main",
),
],
)
def test_format_url(self, base_url, reference, expected):
assert views.format_url(base_url, reference) == expected

@pytest.mark.parametrize(
("publisher_name", "claims"),
[
(
"GitLab",
{
"1.3.6.1.4.1.57264.1.8": "https://gitlab.com",
"1.3.6.1.4.1.57264.1.9": (
"https://gitlab.com/facutuesca/gitlab-oidc-project//"
DarkaMaul marked this conversation as resolved.
Show resolved Hide resolved
".gitlab-ci.yml@refs/heads/main"
),
"1.3.6.1.4.1.57264.1.10": (
"72f7c63b75eb55ea80864962f0e645c93414da34"
),
"1.3.6.1.4.1.57264.1.11": "gitlab-hosted",
"1.3.6.1.4.1.57264.1.12": (
"https://gitlab.com/facutuesca/gitlab-oidc-project"
),
"1.3.6.1.4.1.57264.1.13": (
"72f7c63b75eb55ea80864962f0e645c93414da34"
),
"1.3.6.1.4.1.57264.1.14": "refs/heads/main",
"1.3.6.1.4.1.57264.1.15": "55235664",
"1.3.6.1.4.1.57264.1.16": "https://gitlab.com/facutuesca",
"1.3.6.1.4.1.57264.1.17": "12885801",
"1.3.6.1.4.1.57264.1.18": (
"https://gitlab.com/facutuesca/gitlab-oidc-project//"
".gitlab-ci.yml@refs/heads/main"
),
"1.3.6.1.4.1.57264.1.19": (
"72f7c63b75eb55ea80864962f0e645c93414da34"
),
"1.3.6.1.4.1.57264.1.20": "push",
"1.3.6.1.4.1.57264.1.21": (
"https://gitlab.com/facutuesca/gitlab-oidc-project/-/jobs/"
"8415754949"
),
"1.3.6.1.4.1.57264.1.22": "private",
},
),
(
"GitHub",
{
"1.3.6.1.4.1.57264.1.8": (
DarkaMaul marked this conversation as resolved.
Show resolved Hide resolved
"https://token.actions.githubusercontent.com"
),
"1.3.6.1.4.1.57264.1.9": (
"https://github.com/trailofbits/pypi-attestations/"
".github/workflows/release.yml@refs/tags/v0.0.16"
),
"1.3.6.1.4.1.57264.1.10": (
"58c872e67c03c9c031ba71b1654ff542ff290cd7"
),
"1.3.6.1.4.1.57264.1.11": "github-hosted",
"1.3.6.1.4.1.57264.1.12": (
"https://github.com/trailofbits/pypi-attestations"
),
"1.3.6.1.4.1.57264.1.13": (
"58c872e67c03c9c031ba71b1654ff542ff290cd7"
),
"1.3.6.1.4.1.57264.1.14": "refs/tags/v0.0.16",
"1.3.6.1.4.1.57264.1.15": "772247423",
"1.3.6.1.4.1.57264.1.16": "https://github.com/trailofbits",
"1.3.6.1.4.1.57264.1.17": "2314423",
"1.3.6.1.4.1.57264.1.18": (
"https://github.com/trailofbits/pypi-attestations/"
".github/workflows/release.yml@refs/tags/v0.0.16"
),
"1.3.6.1.4.1.57264.1.19": (
"58c872e67c03c9c031ba71b1654ff542ff290cd7"
),
"1.3.6.1.4.1.57264.1.20": "release",
"1.3.6.1.4.1.57264.1.21": (
"https://github.com/trailofbits/pypi-attestations/actions/"
"runs/11732568384/attempts/1"
),
"1.3.6.1.4.1.57264.1.22": "public",
},
),
],
)
def test_get_publication_details(self, publisher_name, claims):
if publisher_name == "GitLab":
publisher = pretend.stub(
kind="GitLab",
workflow_filepath="subfolder/example.yml",
)
elif publisher_name == "GitHub":
publisher = pretend.stub(
kind="GitHub",
workflow="example.yml",
)

views.get_publication_details(
DarkaMaul marked this conversation as resolved.
Show resolved Hide resolved
claims,
publisher,
)

def test_get_publication_details_unknown(self):
results = views.get_publication_details(
claims={},
publisher=pretend.stub(kind="unknown-publisher"),
)

assert not results["workflow_filename"]
assert results["workflow_url"] == "/blob//"


class TestReportMalwareButton:
def test_report_malware_button(self):
Expand Down
34 changes: 17 additions & 17 deletions warehouse/locale/messages.pot
Original file line number Diff line number Diff line change
Expand Up @@ -826,7 +826,7 @@ msgstr ""
#: warehouse/templates/base.html:321 warehouse/templates/base.html:331
#: warehouse/templates/base.html:344
#: warehouse/templates/includes/accounts/profile-callout.html:18
#: warehouse/templates/includes/file-details.html:101
#: warehouse/templates/includes/file-details.html:121
#: warehouse/templates/index.html:100 warehouse/templates/index.html:104
#: warehouse/templates/manage/account.html:228
#: warehouse/templates/manage/account.html:234
Expand Down Expand Up @@ -2707,51 +2707,51 @@ msgstr ""
msgid "Public profile"
msgstr ""

#: warehouse/templates/includes/file-details.html:34
#: warehouse/templates/includes/file-details.html:54
msgid "File details"
msgstr ""

#: warehouse/templates/includes/file-details.html:45
#: warehouse/templates/includes/file-details.html:65
#, python-format
msgid "Upload date: %(upload_time)s"
msgstr ""

#: warehouse/templates/includes/file-details.html:46
#: warehouse/templates/includes/file-details.html:66
#, python-format
msgid "Size: %(size)s"
msgstr ""

#: warehouse/templates/includes/file-details.html:47
#: warehouse/templates/includes/file-details.html:67
#, python-format
msgid "Tags: %(tags)s"
msgstr ""

#: warehouse/templates/includes/file-details.html:49
#: warehouse/templates/includes/file-details.html:69
#, python-format
msgid "Uploaded using Trusted Publishing? %(is_tp)s"
msgstr ""

#: warehouse/templates/includes/file-details.html:54
#: warehouse/templates/includes/file-details.html:74
#, python-format
msgid "Uploaded via: %(uploaded_via)s"
msgstr ""

#: warehouse/templates/includes/file-details.html:62
#: warehouse/templates/includes/file-details.html:82
#, python-format
msgid "Hashes for %(filename)s"
msgstr ""

#: warehouse/templates/includes/file-details.html:65
#: warehouse/templates/includes/file-details.html:85
msgid "Algorithm"
msgstr ""

#: warehouse/templates/includes/file-details.html:66
#: warehouse/templates/includes/file-details.html:86
msgid "Hash digest"
msgstr ""

#: warehouse/templates/includes/file-details.html:75
#: warehouse/templates/includes/file-details.html:84
#: warehouse/templates/includes/file-details.html:93
#: warehouse/templates/includes/file-details.html:95
#: warehouse/templates/includes/file-details.html:104
#: warehouse/templates/includes/file-details.html:113
#: warehouse/templates/manage/account.html:206
#: warehouse/templates/manage/account/recovery_codes-provision.html:58
#: warehouse/templates/manage/account/totp-provision.html:57
Expand All @@ -2761,9 +2761,9 @@ msgstr ""
msgid "Copy to clipboard"
msgstr ""

#: warehouse/templates/includes/file-details.html:76
#: warehouse/templates/includes/file-details.html:85
#: warehouse/templates/includes/file-details.html:94
#: warehouse/templates/includes/file-details.html:96
#: warehouse/templates/includes/file-details.html:105
#: warehouse/templates/includes/file-details.html:114
#: warehouse/templates/manage/account.html:207
#: warehouse/templates/manage/account/recovery_codes-provision.html:59
#: warehouse/templates/manage/account/totp-provision.html:58
Expand All @@ -2772,7 +2772,7 @@ msgstr ""
msgid "Copy"
msgstr ""

#: warehouse/templates/includes/file-details.html:101
#: warehouse/templates/includes/file-details.html:121
#, python-format
msgid ""
"<a href=\"%(href)s\" title=\"%(title)s\" target=\"_blank\" "
Expand Down
62 changes: 62 additions & 0 deletions warehouse/packaging/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import typing

from natsort import natsorted
from pyramid.httpexceptions import HTTPMovedPermanently, HTTPNotFound
Expand All @@ -22,6 +23,9 @@
from warehouse.packaging.forms import SubmitMalwareObservationForm
from warehouse.packaging.models import Description, File, Project, Release, Role

if typing.TYPE_CHECKING:
import pypi_attestations


@view_config(
route_name="packaging.project",
Expand Down Expand Up @@ -56,6 +60,61 @@ def project_detail(project, request):
return release_detail(release, request)


def format_url(base_url: str, reference: str):
"""Format a URL to create a permalink to a repository.

Works on both GitHub and GitLab.
Reference can either be a hash or a named revision.
"""
return f"{base_url}/tree/{reference}"


def get_publication_details(
claims: dict[str, str], publisher: pypi_attestations.Publisher
):
"""Helper function for Jinja to format the claims present in the attestation."""
workflow_filename = ""
if publisher.kind == "GitHub":
workflow_filename = publisher.workflow
elif publisher.kind == "GitLab":
workflow_filename = publisher.workflow_filepath

# Source Repository URI
repo_url = claims.get("1.3.6.1.4.1.57264.1.12", "")
# Build Config URI
workflow_url = claims.get("1.3.6.1.4.1.57264.1.18", "")
# Build Config Digest
build_hash = claims.get("1.3.6.1.4.1.57264.1.19", "")

# The claim `workflow_url` does not return a valid link to the repository,
# but we can use it to retrieve the path towards the workflow file.
workflow_file_path = workflow_url.split("@")[0].replace(repo_url + "/", "")

workflow_permalink = f"{repo_url}/blob/{build_hash}/{workflow_file_path}"

return {
"workflow_url": workflow_permalink,
"workflow_filename": workflow_filename,
"build_hash": build_hash,
# Issuer
"issuer": claims.get("1.3.6.1.4.1.57264.1.8"),
# Runner Environment
"environment": claims.get("1.3.6.1.4.1.57264.1.11"),
# Source Repository URI
"source": claims.get("1.3.6.1.4.1.57264.1.12"),
# Source Repository Digest
"source_digest": claims.get("1.3.6.1.4.1.57264.1.13"),
# Source Repository Ref
"source_ref": claims.get("1.3.6.1.4.1.57264.1.14"),
# Source Repository Owner URI
"owner": claims.get("1.3.6.1.4.1.57264.1.16"),
# Build Trigger
"trigger": claims.get("1.3.6.1.4.1.57264.1.20"),
# Source Repository Visibility At Signing
"access": claims.get("1.3.6.1.4.1.57264.1.22"),
}


@view_config(
route_name="packaging.release",
context=Release,
Expand Down Expand Up @@ -148,6 +207,9 @@ def release_detail(release, request):
"all_versions": project.all_versions,
"maintainers": maintainers,
"license": license,
# Additional function to format the attestations
"get_publication_details": get_publication_details,
"format_url": format_url,
}


Expand Down
7 changes: 7 additions & 0 deletions warehouse/static/sass/blocks/_files.scss
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,12 @@
&__card {
display: block;
margin: $quarter-spacing-unit 0;

small {
display: block;
margin-top: 4px;
margin-bottom: 6px;
}

}
}
Loading
Loading