javaguard.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. const cp = require('child_process')
  2. const fs = require('fs')
  3. const path = require('path')
  4. const Registry = require('winreg')
  5. /**
  6. * Attempts to find a valid x64 installation of Java on Windows machines.
  7. * Possible paths will be pulled from the registry and the JAVA_HOME environment
  8. * variable. The paths will be sorted with higher versions preceeding lower, and
  9. * JREs preceeding JDKs. The binaries at the sorted paths will then be validated.
  10. * The first validated is returned.
  11. *
  12. * Higher versions > Lower versions
  13. * If versions are equal, JRE > JDK.
  14. *
  15. * @returns {string} The root path of a valid x64 Java installation. If none are
  16. * found, null is returned.
  17. */
  18. async function _win32Validate(){
  19. // Get possible paths from the registry.
  20. const pathSet = await _scanRegistry()
  21. console.log(Array.from(pathSet)) // DEBUGGING
  22. // Validate JAVA_HOME
  23. const jHome = _scanJavaHome()
  24. if(jHome != null && jHome.indexOf('(x86)') === -1){
  25. pathSet.add(jHome)
  26. }
  27. // Convert path set to an array for processing.
  28. let pathArr = Array.from(pathSet)
  29. console.log(pathArr) // DEBUGGING
  30. // Sorts array. Higher version numbers preceed lower. JRE preceeds JDK.
  31. pathArr = pathArr.sort((a, b) => {
  32. // Note that Java 9+ uses semver and that will need to be accounted for in
  33. // the future.
  34. const aVer = parseInt(a.split('_')[1])
  35. const bVer = parseInt(b.split('_')[1])
  36. if(bVer === aVer){
  37. return a.indexOf('jdk') > -1 ? 1 : 0
  38. } else {
  39. return bVer - aVer
  40. }
  41. })
  42. console.log(pathArr) // DEBUGGING
  43. // Validate that the binary is actually x64.
  44. for(let i=0; i<pathArr.length; i++) {
  45. let res = await _validateBinary(pathArr[i])
  46. if(res){
  47. return pathArr[i]
  48. }
  49. }
  50. // No suitable candidates found.
  51. return null;
  52. }
  53. /**
  54. * Validates that a Java binary is at least 64 bit. This makes use of the non-standard
  55. * command line option -XshowSettings:properties. The output of this contains a property,
  56. * sun.arch.data.model = ARCH, in which ARCH is either 32 or 64. This option is supported
  57. * in Java 8 and 9. Since this is a non-standard option. This will resolve to true if
  58. * the function's code throws errors. That would indicate that the option is changed or
  59. * removed.
  60. *
  61. * @param {string} binaryPath Path to the root of the java binary we wish to validate.
  62. *
  63. * @returns {Promise.<boolean>} Resolves to false only if the test is successful and the result
  64. * is less than 64.
  65. */
  66. function _validateBinary(binaryPath){
  67. return new Promise((resolve, reject) => {
  68. const fBp = path.join(binaryPath, 'bin', 'java.exe')
  69. cp.exec('"' + fBp + '" -XshowSettings:properties', (err, stdout, stderr) => {
  70. try {
  71. // Output is stored in stderr?
  72. const res = stderr
  73. const props = res.split('\n')
  74. for(let i=0; i<props.length; i++){
  75. if(props[i].indexOf('sun.arch.data.model') > -1){
  76. let arch = props[i].split('=')[1].trim()
  77. console.log(props[i].trim() + ' for ' + binaryPath)
  78. resolve(parseInt(arch) >= 64)
  79. }
  80. }
  81. // sun.arch.data.model not found?
  82. // Disregard this test.
  83. resolve(true)
  84. } catch (err){
  85. // Output format might have changed, validation cannot be completed.
  86. // Disregard this test in that case.
  87. resolve(true)
  88. }
  89. })
  90. })
  91. }
  92. /**
  93. * Checks for the presence of the environment variable JAVA_HOME. If it exits, we will check
  94. * to see if the value points to a path which exists. If the path exits, the path is returned.
  95. *
  96. * @returns {string} The path defined by JAVA_HOME, if it exists. Otherwise null.
  97. */
  98. function _scanJavaHome(){
  99. const jHome = process.env.JAVA_HOME
  100. try {
  101. let res = fs.existsSync(jHome)
  102. return res ? jHome : null
  103. } catch (err) {
  104. // Malformed JAVA_HOME property.
  105. return null
  106. }
  107. }
  108. /**
  109. * Scans the registry for 64-bit Java entries. The paths of each entry are added to
  110. * a set and returned. Currently, only Java 8 (1.8) is supported.
  111. *
  112. * @returns {Promise.<Set.<string>>} A promise which resolves to a set of 64-bit Java root
  113. * paths found in the registry.
  114. */
  115. function _scanRegistry(){
  116. return new Promise((resolve, reject) => {
  117. // Keys for Java v9.0.0 and later:
  118. // 'SOFTWARE\\JavaSoft\\JRE'
  119. // 'SOFTWARE\\JavaSoft\\JDK'
  120. // Forge does not yet support Java 9, therefore we do not.
  121. let cbTracker = 0
  122. let cbAcc = 0
  123. // Keys for Java 1.8 and prior:
  124. const regKeys = [
  125. '\\SOFTWARE\\JavaSoft\\Java Runtime Environment',
  126. '\\SOFTWARE\\JavaSoft\\Java Development Kit'
  127. ]
  128. const candidates = new Set()
  129. for(let i=0; i<regKeys.length; i++){
  130. const key = new Registry({
  131. hive: Registry.HKLM,
  132. key: regKeys[i],
  133. arch: 'x64'
  134. })
  135. key.keys((err, javaVers) => {
  136. if(err){
  137. console.error(err)
  138. if(i === regKeys.length-1){
  139. resolve(candidates)
  140. }
  141. } else {
  142. cbTracker += javaVers.length
  143. if(i === regKeys.length-1 && cbTracker === cbAcc){
  144. resolve(candidates)
  145. }
  146. for(let j=0; j<javaVers.length; j++){
  147. const javaVer = javaVers[j]
  148. const vKey = javaVer.key.substring(javaVer.key.lastIndexOf('\\')+1)
  149. // Only Java 8 is supported currently.
  150. if(parseFloat(vKey) === 1.8){
  151. javaVer.get('JavaHome', (err, res) => {
  152. const jHome = res.value
  153. if(jHome.indexOf('(x86)') === -1){
  154. candidates.add(jHome)
  155. cbAcc++
  156. }
  157. if(cbAcc === cbTracker){
  158. resolve(candidates)
  159. }
  160. })
  161. }
  162. }
  163. }
  164. })
  165. }
  166. })
  167. }
  168. /**
  169. * WIP -> get a valid x64 Java path on macOS.
  170. */
  171. function _darwinValidate(){
  172. return null
  173. }
  174. /**
  175. * WIP -> get a valid x64 Java path on linux.
  176. */
  177. function _linuxValidate(){
  178. return null
  179. }
  180. // This will eventually return something.
  181. async function validate(){
  182. let res = null
  183. const opSys = process.platform
  184. if(opSys === 'win32'){
  185. res = await _win32Validate()
  186. } else if(opSys === 'darwin'){
  187. res = _darwinValidate()
  188. } else if(opSys === 'linux'){
  189. res = _linuxValidate()
  190. }
  191. return res;
  192. }
  193. async function test(){
  194. console.log(await validate())
  195. }
  196. test()
  197. module.exports = {
  198. validate
  199. }