Skip to content

Commit a8e5a7e

Browse files
authored
Merge pull request #1076 from tspascoal/fix-version-matching-for-non-string-semver-advisories
fix: patched version display for advisories with non-strict semver ranges (e.g. Maven beta versions)
2 parents 55d3e75 + 15a4986 commit a8e5a7e

2 files changed

Lines changed: 108 additions & 1 deletion

File tree

__tests__/summary.test.ts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,49 @@ test('addChangeVulnerabilitiesToSummary() - handles RestSharp GHSA-4rr6-2v9v-wcp
696696
})
697697
})
698698

699+
test('addChangeVulnerabilitiesToSummary() - handles GHSA-vc5p-v9hr-52mj maven range', async () => {
700+
const pkg = createTestChange({
701+
ecosystem: 'maven',
702+
name: 'org.apache.logging.log4j:log4j-core',
703+
version: '2.12.4',
704+
vulnerabilities: [
705+
createTestVulnerability({
706+
advisory_ghsa_id: 'GHSA-vc5p-v9hr-52mj',
707+
advisory_summary: 'Apache Log4j vulnerable to XXE in JDBC Appender',
708+
severity: 'high'
709+
})
710+
]
711+
})
712+
713+
// Mock API response from https://api.github.com/advisories/GHSA-vc5p-v9hr-52mj
714+
mockOctokitRequest.mockResolvedValueOnce({
715+
data: {
716+
vulnerabilities: [
717+
{
718+
package: {
719+
ecosystem: 'maven',
720+
name: 'org.apache.logging.log4j:log4j-core'
721+
},
722+
vulnerable_version_range: '>= 2.0-beta9, < 2.25.3',
723+
first_patched_version: '2.25.3'
724+
}
725+
]
726+
}
727+
})
728+
729+
const changes = [pkg]
730+
await summary.addChangeVulnerabilitiesToSummary(changes, 'low', true)
731+
732+
const text = core.summary.stringify()
733+
734+
// Should show the correct patched version for this advisory range
735+
expect(text).toContain('2.25.3')
736+
expect(text).not.toContain('N/A')
737+
expect(mockOctokitRequest).toHaveBeenCalledWith('GET /advisories/{ghsa_id}', {
738+
ghsa_id: 'GHSA-vc5p-v9hr-52mj'
739+
})
740+
})
741+
699742
test('addChangeVulnerabilitiesToSummary() - handles version coercion for non-strict semver versions', async () => {
700743
// Test that versions like "8.0" (without patch version) can be coerced to "8.0.0"
701744
// for successful range matching in fail-open mode (patch selection)
@@ -778,6 +821,50 @@ test('addChangeVulnerabilitiesToSummary() - handles invalid versions in fail-ope
778821
expect(text).toContain('N/A')
779822
})
780823

824+
test('addChangeVulnerabilitiesToSummary() - handles GHSA-r9w3-57w2-gch2 go pseudo-version range', async () => {
825+
const pkg = createTestChange({
826+
ecosystem: 'go',
827+
name: 'github.com/ory/hydra',
828+
version: '2.1.0-pre.2',
829+
vulnerabilities: [
830+
createTestVulnerability({
831+
advisory_ghsa_id: 'GHSA-r9w3-57w2-gch2',
832+
advisory_summary:
833+
'Ory Hydra has a SQL injection via forged pagination tokens',
834+
severity: 'high'
835+
})
836+
]
837+
})
838+
839+
// Mock API response from https://api.github.com/advisories/GHSA-r9w3-57w2-gch2
840+
mockOctokitRequest.mockResolvedValueOnce({
841+
data: {
842+
vulnerabilities: [
843+
{
844+
package: {
845+
ecosystem: 'go',
846+
name: 'github.com/ory/hydra'
847+
},
848+
vulnerable_version_range: '< 2.3.1-0.20260320110106-0b84568fffcc',
849+
first_patched_version: '2.3.1-0.20260320110106-0b84568fffcc'
850+
}
851+
]
852+
}
853+
})
854+
855+
const changes = [pkg]
856+
await summary.addChangeVulnerabilitiesToSummary(changes, 'low', true)
857+
858+
const text = core.summary.stringify()
859+
860+
// Should show the correct patched version for this advisory range
861+
expect(text).toContain('2.3.1-0.20260320110106-0b84568fffcc')
862+
expect(text).not.toContain('N/A')
863+
expect(mockOctokitRequest).toHaveBeenCalledWith('GET /advisories/{ghsa_id}', {
864+
ghsa_id: 'GHSA-r9w3-57w2-gch2'
865+
})
866+
})
867+
781868
test('addChangeVulnerabilitiesToSummary() - respects concurrency limit for API calls', async () => {
782869
// Create 15 packages with different vulnerabilities to test concurrency limiting
783870
const packages = Array.from({length: 15}, (_, i) =>

src/summary.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ function versionInRange(
7474
// Validate version and range explicitly to enforce fail-closed semantics
7575
// semver.satisfies() typically returns false for invalid inputs without throwing
7676
let validVersion = semver.valid(trimmedVersion)
77-
const validRange = semver.validRange(semverRange)
77+
let validRange = semver.validRange(semverRange)
7878

7979
// For fail-open mode (patch selection), try coercing invalid versions
8080
// to handle common real-world formats like "8.0", date-based versions, etc.
@@ -88,6 +88,26 @@ function versionInRange(
8888
}
8989
}
9090

91+
// For fail-open mode, try coercing invalid version components within the range string
92+
// to handle formats like "2.0-beta9" which should be "2.0.0-beta9" for strict semver
93+
if (!validRange && !failClosed) {
94+
const coercedRangeStr = semverRange.replace(
95+
/(?<=[><=~^\s]|^)(\d+\.\d+(?:\.\d+)?(?:-[^\s]*)?)(?=\s|$)/g,
96+
match => {
97+
const coerced = semver.coerce(match)
98+
return coerced ? coerced.version : match
99+
}
100+
)
101+
if (coercedRangeStr !== semverRange) {
102+
validRange = semver.validRange(coercedRangeStr)
103+
if (validRange) {
104+
core.debug(
105+
`Coerced range "${semverRange}" to "${coercedRangeStr}" for range matching`
106+
)
107+
}
108+
}
109+
}
110+
91111
if (!validVersion || !validRange) {
92112
if (failClosed) {
93113
const issues: string[] = []

0 commit comments

Comments
 (0)