mirror of
https://github.com/AikidoSec/safe-chain.git
synced 2026-05-26 12:10:49 +00:00
Adapt per review
This commit is contained in:
parent
0aabba668e
commit
1a2805ba56
2 changed files with 74 additions and 22 deletions
|
|
@ -6,6 +6,11 @@ export { parsePipMetadataUrl, isPipPackageInfoUrl } from "./parsePipPackageUrl.j
|
||||||
import { getPipMetadataContentType, logSuppressedVersion } from "./pipMetadataResponseUtils.js";
|
import { getPipMetadataContentType, logSuppressedVersion } from "./pipMetadataResponseUtils.js";
|
||||||
import { modifyPipJsonResponse } from "./modifyPipJsonResponse.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 {Buffer} body
|
||||||
* @param {NodeJS.Dict<string | string[]> | undefined} headers
|
* @param {NodeJS.Dict<string | string[]> | undefined} headers
|
||||||
|
|
@ -80,10 +85,36 @@ function modifyHtmlSimpleResponse(
|
||||||
) {
|
) {
|
||||||
const html = body.toString("utf8");
|
const html = body.toString("utf8");
|
||||||
let modified = false;
|
let modified = false;
|
||||||
|
const rewriteHtmlAnchor = createHtmlAnchorRewriter(
|
||||||
|
metadataUrl,
|
||||||
|
isNewlyReleasedPackage,
|
||||||
|
packageName,
|
||||||
|
() => {
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const updatedHtml = html.replace(HTML_ANCHOR_HREF_RE, rewriteHtmlAnchor);
|
||||||
|
|
||||||
const updatedHtml = html.replace(
|
if (!modified) return body;
|
||||||
/<a\b[^>]*href\s*=\s*(["'])([^"']+)\1[^>]*>[\s\S]*?<\/a>/gi,
|
const modifiedBuffer = Buffer.from(updatedHtml);
|
||||||
(anchor, _quote, href) => {
|
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 resolvedHref = new URL(href, metadataUrl).toString();
|
||||||
const { packageName: hrefPackageName, version } = parsePipPackageFromUrl(
|
const { packageName: hrefPackageName, version } = parsePipPackageFromUrl(
|
||||||
resolvedHref,
|
resolvedHref,
|
||||||
|
|
@ -92,23 +123,18 @@ function modifyHtmlSimpleResponse(
|
||||||
|
|
||||||
if (
|
if (
|
||||||
hrefPackageName &&
|
hrefPackageName &&
|
||||||
normalizePipPackageName(hrefPackageName) === normalizePipPackageName(packageName) &&
|
normalizePipPackageName(hrefPackageName) ===
|
||||||
|
normalizePipPackageName(packageName) &&
|
||||||
version &&
|
version &&
|
||||||
isNewlyReleasedPackage(packageName, version)
|
isNewlyReleasedPackage(packageName, version)
|
||||||
) {
|
) {
|
||||||
modified = true;
|
onModified();
|
||||||
logSuppressedVersion(packageName, version);
|
logSuppressedVersion(packageName, version);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return anchor;
|
return anchor;
|
||||||
}
|
};
|
||||||
);
|
|
||||||
|
|
||||||
if (!modified) return body;
|
|
||||||
const modifiedBuffer = Buffer.from(updatedHtml);
|
|
||||||
clearCachingHeaders(headers);
|
|
||||||
return modifiedBuffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -134,6 +134,32 @@ describe("modifyPipInfo", async () => {
|
||||||
assert.ok(modified.includes("foo_bar-1.0.0.tar.gz"));
|
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=">=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", () => {
|
it("removes too-young files from simple JSON metadata", () => {
|
||||||
const headers = {
|
const headers = {
|
||||||
"content-type": "application/vnd.pypi.simple.v1+json",
|
"content-type": "application/vnd.pypi.simple.v1+json",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue