Adapt per review

This commit is contained in:
Reinier Criel 2026-04-02 13:00:01 -07:00
parent 0aabba668e
commit 1a2805ba56
2 changed files with 74 additions and 22 deletions

View file

@ -6,6 +6,11 @@ export { parsePipMetadataUrl, isPipPackageInfoUrl } from "./parsePipPackageUrl.j
import { getPipMetadataContentType, logSuppressedVersion } from "./pipMetadataResponseUtils.js";
import { modifyPipJsonResponse } from "./modifyPipJsonResponse.js";
// Match simple-index anchor tags and capture their href so we can suppress
// individual distribution links from PyPI HTML metadata responses.
const HTML_ANCHOR_HREF_RE =
/<a\b[^>]*href\s*=\s*(["'])([^"']+)\1[^>]*>[\s\S]*?<\/a>/gi;
/**
* @param {Buffer} body
* @param {NodeJS.Dict<string | string[]> | undefined} headers
@ -80,10 +85,36 @@ function modifyHtmlSimpleResponse(
) {
const html = body.toString("utf8");
let modified = false;
const rewriteHtmlAnchor = createHtmlAnchorRewriter(
metadataUrl,
isNewlyReleasedPackage,
packageName,
() => {
modified = true;
}
);
const updatedHtml = html.replace(HTML_ANCHOR_HREF_RE, rewriteHtmlAnchor);
const updatedHtml = html.replace(
/<a\b[^>]*href\s*=\s*(["'])([^"']+)\1[^>]*>[\s\S]*?<\/a>/gi,
(anchor, _quote, href) => {
if (!modified) return body;
const modifiedBuffer = Buffer.from(updatedHtml);
clearCachingHeaders(headers);
return modifiedBuffer;
}
/**
* @param {string} metadataUrl
* @param {(packageName: string | undefined, version: string | undefined) => boolean} isNewlyReleasedPackage
* @param {string} packageName
* @param {() => void} onModified
* @returns {(anchor: string, quote: string, href: string) => string}
*/
function createHtmlAnchorRewriter(
metadataUrl,
isNewlyReleasedPackage,
packageName,
onModified
) {
return (anchor, _quote, href) => {
const resolvedHref = new URL(href, metadataUrl).toString();
const { packageName: hrefPackageName, version } = parsePipPackageFromUrl(
resolvedHref,
@ -92,23 +123,18 @@ function modifyHtmlSimpleResponse(
if (
hrefPackageName &&
normalizePipPackageName(hrefPackageName) === normalizePipPackageName(packageName) &&
normalizePipPackageName(hrefPackageName) ===
normalizePipPackageName(packageName) &&
version &&
isNewlyReleasedPackage(packageName, version)
) {
modified = true;
onModified();
logSuppressedVersion(packageName, version);
return "";
}
return anchor;
}
);
if (!modified) return body;
const modifiedBuffer = Buffer.from(updatedHtml);
clearCachingHeaders(headers);
return modifiedBuffer;
};
}
/**

View file

@ -134,6 +134,32 @@ describe("modifyPipInfo", async () => {
assert.ok(modified.includes("foo_bar-1.0.0.tar.gz"));
});
it("matches anchor href regex with single quotes and extra attributes", () => {
const headers = { "content-type": "application/vnd.pypi.simple.v1+html" };
const body = Buffer.from(`
<a
data-requires-python="&gt;=3.9"
class="pkg"
href='https://files.pythonhosted.org/packages/xx/yy/foo_bar-2.0.0.tar.gz'
>
foo_bar-2.0.0.tar.gz
</a>
<a href="https://files.pythonhosted.org/packages/xx/yy/foo_bar-1.0.0.tar.gz">foo_bar-1.0.0.tar.gz</a>
`);
const modified = modifyPipInfoResponse(
body,
headers,
"https://pypi.org/simple/foo-bar/",
(_packageName, version) => version === "2.0.0",
"foo-bar"
).toString("utf8");
assert.ok(!modified.includes("foo_bar-2.0.0.tar.gz"));
assert.ok(modified.includes("foo_bar-1.0.0.tar.gz"));
});
it("removes too-young files from simple JSON metadata", () => {
const headers = {
"content-type": "application/vnd.pypi.simple.v1+json",