Skip to content

PEP-691 fingerprinting cannot authenticate to basic-auth indexes (AWS CodeArtifact): lock creation downloads every artifact and builds sdists #3224

Description

@jasonwbarnett

Summary

When a --style universal lock is created against an index that requires Basic authentication and serves the PEP-691 JSON Simple API, Pex cannot authenticate the follow-up requests made by its PEP-691 FingerprintService. Pex warns and then falls back to downloading every retained artifact for each selected locked version just to fingerprint it. Callers such as Pants suppress Pex warnings, making the fallback operationally silent.

Because pip download --no-deps <direct-sdist-url> can run PEP-517 metadata hooks, retained sdists may then fail the lock for reasons unrelated to dependency resolution—for example, psycopg2-binary without pg_config, or ddtrace==3.19.1 importing pkg_resources after build isolation installs setuptools 82, which removed it. Even when every retained sdist can prepare metadata, lock creation needlessly downloads and hashes a potentially large artifact set.

This surfaced when AWS CodeArtifact began serving PEP 691 in July 2026. Pip >=22.2 prefers JSON through content negotiation, so locks that had regenerated successfully against PEP-503 HTML began failing without a client-side configuration change. With HTML, artifact URLs include #sha256= fragments and Pex obtains hashes directly from Pip's log; with JSON, hashes are separate files[].hashes values and are not included in Pip's logged URLs.

Root causes

1. PEP-691 endpoints are scraped from Pip's credential-redacted log

Locker.analyze records endpoints from Pip lines of this form:

Fetched page <url> as application/vnd.pypi.simple.v1+json

For an index configured as https://user:secret@host/simple, Pip logs the endpoint as literal https://user:****@host/simple/<project>/. Before the fix, Pex attempted to open that URL:

pex: Fetching PEP-691 index metadata from https://aws:****@<host>/pypi/<repo>/simple/<project>/ for application/vnd.pypi.simple.v1+json

The stdlib opener cannot parse its authority:

PEXWarning: Failed to fetch project metadata, continuing: nonnumeric port: '****@<host>'

Pex separately retains the real repository credential, but the redacted userinfo in the endpoint prevents that credential from being used.

2. Clean endpoint URLs still fail against CodeArtifact's challenge

With the redacted userinfo removed and a matching machine-scoped password entry supplied, the pre-fix URLFetcher still starts unauthenticated. CodeArtifact accepts Basic authentication but answers an unauthenticated request with 401 and WWW-Authenticate: Bearer .... Python's stdlib Basic/Digest challenge handlers do not support that scheme and can raise:

ValueError: AbstractDigestAuthHandler does not support the following scheme: 'Bearer'

As a result, the known Basic credential is never sent. This is in the same family as #1803, which #1811 fixed for artifact downloads by delegating them to Pip; the independently implemented PEP-691 metadata client retained the problematic stdlib authentication path.

FingerprintService.fingerprint intentionally treats these request errors as non-fatal, emits Failed to fetch project metadata, continuing, and leaves artifacts without fingerprints. Higher-level lock creation then downloads each unresolved retained artifact and hashes it locally.

Reproduction

Against a CodeArtifact repository with a public PyPI upstream, from a lock host without pg_config:

TOKEN=$(aws codeartifact get-authorization-token ... --query authorizationToken --output text)
pex3 lock create \
  --pip-version 24.2 --style universal --no-pypi \
  --index "https://aws:${TOKEN}@<domain>-<acct>.d.codeartifact.<region>.amazonaws.com/pypi/<repo>/simple" \
  psycopg2-binary==2.9.12 -o lock.json

On v2.66.0 and main at bea9c2e, this fails with metadata-generation-failed / pg_config executable not found after downloading all 12 retained artifacts for the selected release.

The challenge failure can be reproduced without AWS by giving URLFetcher.get_body_stream a matching machine-scoped password entry and pointing it at a server that accepts Basic auth but returns 401 with WWW-Authenticate: Bearer when the initial request lacks authorization.

Expected behavior

PEP-691 metadata requests should authenticate using the known credential for the configured index, hashes should be recovered from the JSON files[].hashes mappings, and no retained artifacts should be downloaded solely for fingerprinting. This should produce the same lock artifact set and hashes as the equivalent PEP-503 HTML response with #sha256= fragments.

Fix

#3225 strips credentials from Pip-redacted endpoint URLs, sends known machine-scoped Basic credentials preemptively, safely handles unsupported challenge schemes, and adds composed regression coverage from the redacted Pip log line through authenticated PEP-691 SHA-256 recovery. It has been validated against the CodeArtifact repository that exposed the issue: the reproducer completes with zero fallback fingerprint downloads and produces hashes identical to the HTML-index lock.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions