fix(core): reject relative path segments in repository hosts (#51577)

This commit is contained in:
Niels Kootstra
2026-09-28 06:44:35 +02:00
committed by GitHub
parent d9f54392ba
commit 39e1ce55bc
2 changed files with 20 additions and 1 deletions
+2 -1
View File
@@ -145,8 +145,9 @@ function parts(input: string) {
.filter(Boolean)
}
// cachePath makes each `:`-separated host part a directory.
function safeHost(input: string) {
return Boolean(input) && !input.startsWith("-") && !/[\s/\\]/.test(input)
return Boolean(input) && !input.startsWith("-") && input.split(":").every(safeSegment)
}
function safeSegment(input: string) {
+18
View File
@@ -41,6 +41,12 @@ describe("Repository", () => {
})
})
test("caches a host with a port under a directory per host part", () => {
expect(Repository.cachePath("/cache", Repository.parseRemote("ssh://git@example.com:2222/owner/repo"))).toBe(
path.join("/cache", "example.com", "2222", "owner", "repo"),
)
})
test("keeps local file repositories distinct from remote repositories", () => {
const localPath = path.resolve("repo.git")
const reference = Repository.parse(pathToFileURL(localPath).href)
@@ -62,6 +68,18 @@ describe("Repository", () => {
expect(() => Repository.validateBranch("bad branch")).toThrow(Repository.InvalidBranchError)
})
test.each([
"..:repo",
"git@..:repo",
"../owner/repo",
"ssh://../repo",
"ssh://..:22/repo",
"https://%2e%2e/owner/repo",
".:repo",
])("rejects %s because its host contains a relative path segment", (input) => {
expect(() => Repository.parseRemote(input)).toThrow(Repository.InvalidReferenceError)
})
test("compares cache identity independent of input spelling", () => {
const shorthand = Repository.parseRemote("owner/repo")