Skip to content

Commit c711123

Browse files
committed
2.0.4 - Harden JVM discovery logic.
Only check standard java.version property, do not collect build number. If a JVM version is invalid, warn and skip. Do not throw. Add error handling around -XshowSettings invocation.
1 parent e70e27c commit c711123

File tree

4 files changed

+167
-142
lines changed

4 files changed

+167
-142
lines changed

lib/java/JavaGuard.ts

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ export interface JavaVersion {
1919
major: number
2020
minor: number
2121
patch: number
22-
build?: number
2322
}
2423

2524
export interface AdoptiumJdk {
@@ -319,13 +318,26 @@ export interface HotSpotSettings {
319318
* @param execPath The path to the Java executable.
320319
* @returns The parsed HotSpot VM properties.
321320
*/
322-
export async function getHotSpotSettings(execPath: string): Promise<HotSpotSettings> {
321+
export async function getHotSpotSettings(execPath: string): Promise<HotSpotSettings | null> {
323322

324323
const javaExecutable = execPath.includes('javaw.exe') ? execPath.replace('javaw.exe', 'java.exe') : execPath
325324

325+
if(!await pathExists(execPath)) {
326+
log.warn(`Candidate JVM path does not exist, skipping. ${execPath}`)
327+
return null
328+
}
329+
326330
const execAsync = promisify(exec)
327331

328-
const { stderr } = await execAsync(`"${javaExecutable}" -XshowSettings:properties -version`)
332+
let stderr
333+
try {
334+
stderr = (await execAsync(`"${javaExecutable}" -XshowSettings:properties -version`)).stderr
335+
} catch(error) {
336+
log.error(`Failed to resolve JVM settings for '${execPath}'`)
337+
log.error(error)
338+
return null
339+
}
340+
329341

330342
const listProps = [
331343
'java.library.path'
@@ -368,7 +380,11 @@ export async function resolveJvmSettings(paths: string[]): Promise<{ [path: stri
368380

369381
for(const path of paths) {
370382
const settings = await getHotSpotSettings(javaExecFromRoot(path))
371-
ret[path] = settings
383+
if(settings != null) {
384+
ret[path] = settings
385+
} else {
386+
log.warn(`Skipping invalid JVM candidate: ${path}`)
387+
}
372388
}
373389

374390
return ret
@@ -389,14 +405,19 @@ export function filterApplicableJavaPaths(resolvedSettings: { [path: string]: Ho
389405
.filter(([, settings ]) => parseInt(settings['sun.arch.data.model']) === 64) // Only allow 64-bit.
390406
.filter(([, settings ]) => arm ? settings['os.arch'] === 'aarch64' : true) // Only allow arm on arm architecture (disallow rosetta on m2 mac)
391407
.map(([ path, settings ]) => {
392-
const parsedVersion = parseJavaRuntimeVersion(settings['java.runtime.version'])
408+
const parsedVersion = parseJavaRuntimeVersion(settings['java.version'])
409+
if(parsedVersion == null) {
410+
log.error(`Failed to parse JDK version at location '${path}' (Vendor: ${settings['java.vendor']})`)
411+
return null!
412+
}
393413
return {
394414
semver: parsedVersion,
395415
semverStr: javaVersionToString(parsedVersion),
396416
vendor: settings['java.vendor'],
397417
path
398418
}
399419
})
420+
.filter(x => x != null)
400421

401422
// Now filter by options.
402423
const jvmDetails = jvmDetailsUnfiltered
@@ -701,7 +722,7 @@ export async function loadMojangLauncherData(): Promise<LauncherJson | null> {
701722
* @param {string} verString Full version string to parse.
702723
* @returns Object containing the version information.
703724
*/
704-
export function parseJavaRuntimeVersion(verString: string): JavaVersion {
725+
export function parseJavaRuntimeVersion(verString: string): JavaVersion | null {
705726
if(verString.startsWith('1.')){
706727
return parseJavaRuntimeVersionLegacy(verString)
707728
} else {
@@ -716,21 +737,21 @@ export function parseJavaRuntimeVersion(verString: string): JavaVersion {
716737
* @param {string} verString Full version string to parse.
717738
* @returns Object containing the version information.
718739
*/
719-
export function parseJavaRuntimeVersionLegacy(verString: string): JavaVersion {
740+
export function parseJavaRuntimeVersionLegacy(verString: string): JavaVersion | null {
720741
// 1.{major}.0_{update}-b{build}
721742
// ex. 1.8.0_152-b16
722743
const regex = /1.(\d+).(\d+)_(\d+)(?:-b(\d+))?/
723744
const match = regex.exec(verString)!
724745

725746
if(match == null) {
726-
throw new Error(`Failed to parse legacy Java version: ${verString}`)
747+
log.error(`Failed to parse legacy Java version: ${verString}`)
748+
return null
727749
}
728750

729751
return {
730752
major: parseInt(match[1]),
731753
minor: parseInt(match[2]),
732-
patch: parseInt(match[3]),
733-
build: match[4] != undefined ? parseInt(match[4]) : undefined
754+
patch: parseInt(match[3])
734755
}
735756
}
736757

@@ -741,26 +762,26 @@ export function parseJavaRuntimeVersionLegacy(verString: string): JavaVersion {
741762
* @param {string} verString Full version string to parse.
742763
* @returns Object containing the version information.
743764
*/
744-
export function parseJavaRuntimeVersionSemver(verString: string): JavaVersion {
765+
export function parseJavaRuntimeVersionSemver(verString: string): JavaVersion | null {
745766
// {major}.{minor}.{patch}+{build}
746767
// ex. 10.0.2+13 or 10.0.2.13
747768
const regex = /(\d+)\.(\d+).(\d+)(?:[+.](\d+))?/
748769
const match = regex.exec(verString)!
749770

750771
if(match == null) {
751-
throw new Error(`Failed to parse semver Java version: ${verString}`)
772+
log.error(`Failed to parse semver Java version: ${verString}`)
773+
return null
752774
}
753775

754776
return {
755777
major: parseInt(match[1]),
756778
minor: parseInt(match[2]),
757-
patch: parseInt(match[3]),
758-
build: match[4] != undefined ? parseInt(match[4]) : undefined
779+
patch: parseInt(match[3])
759780
}
760781
}
761782

762-
export function javaVersionToString({ major, minor, patch, build }: JavaVersion): string {
763-
return `${major}.${minor}.${patch}${build != null ? `+${build}` : ''}`
783+
export function javaVersionToString({ major, minor, patch }: JavaVersion): string {
784+
return `${major}.${minor}.${patch}`
764785
}
765786

766787
export interface JavaDiscoverer {
@@ -898,7 +919,7 @@ export class Win32RegistryJavaDiscoverer implements JavaDiscoverer {
898919
// eslint-disable-next-line @typescript-eslint/no-explicit-any
899920
if(isNaN(vKey as any)) {
900921
// Should be a semver key.
901-
major = parseJavaRuntimeVersion(vKey).major
922+
major = parseJavaRuntimeVersion(vKey)?.major ?? -1
902923
} else {
903924
// This is an abbreviated version, ie 1.8 or 17.
904925
const asNum = parseFloat(vKey)

0 commit comments

Comments
 (0)