Skip to content

Commit 627a7dc

Browse files
author
Michael Smith
committed
fix: avoid ReDoS in addGitSha committish stripping
1 parent 6c2555a commit 627a7dc

2 files changed

Lines changed: 25 additions & 7 deletions

File tree

lib/util/add-git-sha.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ const addGitSha = (spec, sha) => {
88
return `${base}#${sha}`
99
} else {
1010
// don't use new URL for this, because it doesn't handle scp urls
11-
return spec.rawSpec.replace(/#.*$/, '') + `#${sha}`
11+
// strip the committish with indexOf/slice to avoid a regexp redos
12+
const hashIndex = spec.rawSpec.indexOf('#')
13+
const base = hashIndex === -1 ? spec.rawSpec : spec.rawSpec.slice(0, hashIndex)
14+
return `${base}#${sha}`
1215
}
1316
}
1417

test/util/add-git-sha.js

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ const cases = [
66
// unknown host
77
['git+ssh://git@some-host:user/repo', 'sha', 'git+ssh://git@some-host:user/repo#sha'],
88
['git+ssh://git@some-host:user/repo#othersha', 'sha', 'git+ssh://git@some-host:user/repo#sha'],
9-
[
10-
'git+ssh://git@some-host:user/repo#othersha#otherothersha',
9+
['git+ssh://git@some-host:user/repo#othersha#otherothersha',
1110
'sha',
1211
'git+ssh://git@some-host:user/repo#sha'],
1312
['git+ssh://git@some-host/user/repo', 'sha', 'git+ssh://git@some-host/user/repo#sha'],
@@ -40,7 +39,23 @@ const cases = [
4039
['git+ssh://git@github.com:user/repo#othersha#otherothersha', 'sha', 'github:user/repo#sha'],
4140
]
4241

43-
t.plan(cases.length)
44-
for (const [spec, sha, result] of cases) {
45-
t.equal(addGitSha(npa(spec), sha), result, `${spec} + ${sha} = ${result}`)
46-
}
42+
t.test('matches expected committish-stripping results', t => {
43+
t.plan(cases.length)
44+
for (const [spec, sha, result] of cases) {
45+
t.equal(addGitSha(npa(spec), sha), result, `${spec} + ${sha} = ${result}`)
46+
}
47+
})
48+
49+
t.test('strips committish from a malicious rawSpec without catastrophic backtracking (CVE-2026-9496)', t => {
50+
// a plain object exercises the non-hosted branch directly. the `\n` + trailing
51+
// char make this rawSpec pathological for the old `/#.*$/` regex (O(n^2)
52+
// backtracking from every `#`), but it stays linear for indexOf/slice
53+
const base = 'git+ssh://git@some-host/user/repo'
54+
const spec = { hosted: null, rawSpec: `${base}${'#'.repeat(1e5)}\nx` }
55+
const start = process.hrtime.bigint()
56+
const result = addGitSha(spec, 'sha')
57+
const elapsedMs = Number(process.hrtime.bigint() - start) / 1e6
58+
t.equal(result, `${base}#sha`, 'strips everything from the first committish')
59+
t.ok(elapsedMs < 1000, `completes quickly (took ${elapsedMs.toFixed(1)}ms)`)
60+
t.end()
61+
})

0 commit comments

Comments
 (0)