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

Reapply "Store attestations for PEP740 (#16302)" (#16545) #16546

Merged
merged 39 commits into from
Sep 3, 2024

Conversation

woodruffw
Copy link
Member

@woodruffw woodruffw commented Aug 21, 2024

WIP, needs full backstop tests.

This reverts commit da7e1ed.

Signed-off-by: William Woodruff <william@trailofbits.com>
Signed-off-by: William Woodruff <william@trailofbits.com>
IFileStorage requires a name to disambiguate it.

Signed-off-by: William Woodruff <william@trailofbits.com>
Signed-off-by: William Woodruff <william@trailofbits.com>
tests/conftest.py Outdated Show resolved Hide resolved
This removes two mocked `db_request`s from the simple
index tests. These mocks were masking larger architectural
issues with both attestations and our test scaffolding
for attestations.

This isn't quite complete yet, since it does a nasty thing
(uses a file storage with a tmpdir) to get IntegrityService
initialization working.

Signed-off-by: William Woodruff <william@trailofbits.com>
tests/conftest.py Outdated Show resolved Hide resolved
@woodruffw woodruffw self-assigned this Aug 21, 2024
@woodruffw
Copy link
Member Author

woodruffw commented Aug 21, 2024

Listing some action items that need to be accomplished before undrafting:

  • The current TestSimpleDetail tests should be extended to be include a case where provenance is actually present, not just None
  • Reapply "Store attestations for PEP740 (#16302)" (#16545) #16546 (review)
  • More generally, go through TestAttestationsService (which should be renamed TestIntegrityService) with a fine-toothed comb and ensure we aren't mocking overly agressively

woodruffw and others added 11 commits August 21, 2024 18:41
Signed-off-by: William Woodruff <william@trailofbits.com>
This reduces the overall API surface for IIntegrityService implementers,
and adds an initial NullIntegrityService to make unit-level testing
simpler.

Signed-off-by: William Woodruff <william@trailofbits.com>
Signed-off-by: William Woodruff <william@trailofbits.com>
Signed-off-by: William Woodruff <william@trailofbits.com>
Signed-off-by: William Woodruff <william@trailofbits.com>
Remove more ad-hoc stubs as well.

Signed-off-by: William Woodruff <william@trailofbits.com>
Signed-off-by: William Woodruff <william@trailofbits.com>
Signed-off-by: William Woodruff <william@trailofbits.com>
Signed-off-by: William Woodruff <william@trailofbits.com>
@woodruffw woodruffw marked this pull request as ready for review August 22, 2024 17:58
@woodruffw woodruffw requested a review from a team as a code owner August 22, 2024 17:58
Copy link
Member

@di di left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a functional test for this in 17e9d45 but it's currently failing trying to refresh the TUF metadata. @woodruffw @DarkaMaul thoughts on what we should do about TUF here?

warehouse/attestations/services.py Show resolved Hide resolved
@di di force-pushed the ww/pep740-persistence-take-2 branch from 6a55e5c to b58fa0b Compare August 29, 2024 21:23
@di di force-pushed the ww/pep740-persistence-take-2 branch from b58fa0b to 866b0a7 Compare August 29, 2024 21:45
@di
Copy link
Member

di commented Aug 29, 2024

Oh interesting, the TUF failure seems to have been an intermittent thing, this now works. Will have to look into why the attestation hash digest is different.

@di
Copy link
Member

di commented Aug 29, 2024

Here's what I was getting, for context:

============================================= short test summary info =============================================
FAILED tests/functional/api/test_simple.py::test_simple_attestations_from_upload - webtest.app.AppError: Bad response: 400 Unknown error while trying to verify included attestations: Failed to refresh TUF metadata (not 200)
400 Unknown error while trying to verify included attestations: Failed to refresh TUF metadata

The server could not comply with the request since it is either malformed or otherwise incorrect.


Unknown error while trying to verify included attestations: Failed to refresh TUF metadata

Results (16.29s):
       1 failed
         - tests/functional/api/test_simple.py:48 test_simple_attestations_from_upload
make: *** [Makefile:79: tests] Error 1

@DarkaMaul
Copy link
Contributor

I added a functional test for this in 17e9d45 but it's currently failing trying to refresh the TUF metadata. @woodruffw @DarkaMaul thoughts on what we should do about TUF here?

I've also started writing some tests for this at trail-of-forks#3063, and my solution there was to mock the verification.

However, I did not assert that the hashes were correct (and they are probably not).

response = webtest.get("/simple/sampleproject/", status=HTTPStatus.OK)
link = response.html.find("a", text="sampleproject-3.0.0.tar.gz")
assert "data-provenance" in link.attrs
assert link.get("data-provenance") == expected_hash
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test fails here because the hash we get here is

hashlib.sha256(
    b"sampleproject-3.0.0.tar.gz:2"   # notice the 2 here
).hexdigest()

However, the len(attestations) should be 1.

The problem lies in generate_provenance of NullService that creates the DatabaseAttestation and adds it to the file.attestations (like in _persist_attestations )

Because the relationship is noted as back_populates, sqlalchemy automatically adds the newly created DatabaseAttestation to the file instance.

The statement below is thus redundant and creates add a second time the attestation to the file instance.

file.attestations.append(database_attestation)

However, if we look at test_persist_attestations_succeeds, the test passes with the following statements :

        assert len(attestations_db) == 1
        assert len(file.attestations) == 1

I've observed the state in a debugger using sqlalchemy tools and they looked appropriate.

from sqlachemy import inspect
state = inspect(database_attestation)

Any idea here of the reason for this behavior ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue is that the Attestation objects weren't actually being added to the session, the test was emitting a warning like:

tests/unit/attestations/test_services.py::TestIntegrityService::test_generate_provenance_succeeds[GitHubPublisherFactory]
  /opt/warehouse/src/warehouse/attestations/models.py:53: SAWarning: Object of type <Attestation> not in session, add operation along 'File.attestations' will not proceed (This warning originated from the Session 'autoflush' process, which was invoked automatically in response to a user-initiated operation.)
    f"{Path(self.file.path).name}.attestation",

I think f7e277e and fa1bd58 should resolve this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for sorting this out - I missed the warning in the logs.

Copy link
Member

@di di left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, will wait to merge until next week though.

@woodruffw woodruffw mentioned this pull request Sep 3, 2024
23 tasks
@ewdurbin ewdurbin merged commit d1c1161 into pypi:main Sep 3, 2024
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants