Handle unresolved Maven credential placeholders gracefully#6845
Handle unresolved Maven credential placeholders gracefully#6845
Conversation
When Maven settings.xml uses ${env.MAVEN_USER} placeholders for server
credentials and those env vars are not set, the literal placeholder
string is sent as the username, causing 403 errors on artifact downloads.
These tests verify:
- Unresolved credential placeholders are nulled out by the Interpolator
- MavenArtifactDownloader falls back to anonymous on 403 with credentials
When Maven settings.xml uses ${env.MAVEN_USER} / ${env.MAVEN_PASSWORD}
placeholders and those env vars are not set, the PropertyPlaceholderHelper
preserves the literal "${env.FOO}" string. This was then sent as actual
credentials, causing 403 errors.
Now the Interpolator checks for remaining "${" in server credentials
after interpolation and nulls them out, so no auth header is sent and
the download proceeds anonymously — matching Apache Maven behavior.
Also adds @nullable to Server.username and Server.password since
credentials can genuinely be absent.
Fixes moderneinc/customer-requests#1928
When credentials are applied and the server responds with a 4xx client error, retry the download without authentication. This mirrors the existing behavior in MavenPomDownloader.requestAsAuthenticatedOrAnonymous() and matches how Apache Maven handles credential failures gracefully. This is defense-in-depth alongside the Interpolator fix — it also protects against other credential mismatch scenarios.
|
Hi Nick, thanks for the helpful link to the earlier work in #1859 ; guess we had missed to do the same for artifacts at the time, which is a separate downloader. Now made consistent here to hopefully resolve your immediate issue. |
| if (!response.isSuccessful() || body == null) { | ||
| int code = response.getCode(); | ||
| // If credentials caused a client-side error, retry anonymously | ||
| MavenRepository repository = dependency.getRepository(); |
There was a problem hiding this comment.
Every artifact downloaded is going to cause a client-side error. That may trigger anomaly detection on that server.
There was a problem hiding this comment.
An alternative I'd considered was to not make an authenticated request when the username or password starts with ${, but that rules out some very special passwords.
The pattern to repeat with an unauthenticated request is taken from MavenPomDownloader, where we had added that four years ago
I'll restore skipping authenticated requests when there's still a likely placeholder left over, unless you'd wanted to nudge in a different direction still.
There was a problem hiding this comment.
I suppose we could also make this a bit stateful -- toggle between trying authenticated or anonymous requests first, based on success of previous attempts.
if we land on a really good pattern, maybe also worth refactoring so the Pom and Artifact downloaders both share the same impl
There was a problem hiding this comment.
Adjusted to logic to now only make a single unauthenticated request when the username/password still contain a placeholder. I've not yet replicated the same to the pom downloader, but could if desired.
What's changed
When a Maven
settings.xmluses environment variable placeholders for server credentials (e.g.${env.MAVEN_USER}) and those variables are not set, thePropertyPlaceholderHelperpreserves the literal"${env.MAVEN_USER}"string. This was then sent as actual Basic Auth credentials, causing 403 errors when downloading artifact JARs.POM downloads were unaffected because
MavenPomDownloaderhasrequestAsAuthenticatedOrAnonymous()fallback logic (added for #1859), butMavenArtifactDownloaderlacked this fallback.In
MavenArtifactDownloader, when credentials are applied and the server responds with a 4xx client error, retry the download anonymously — matching the existingMavenPomDownloaderbehavior.