<NME> windowsExecutableCodeSignatureVerifier.ts
<BEF> import BluebirdPromise from "bluebird-lst"
import { parseDn } from "builder-util-runtime"
import { execFile, execFileSync } from "child_process"
import * as os from "os"
import { Logger } from "./main"

// $certificateInfo = (Get-AuthenticodeSignature 'xxx\yyy.exe'
// | where {$_.Status.Equals([System.Management.Automation.SignatureStatus]::Valid) -and $_.SignerCertificate.Subject.Contains("CN=siemens.com")})
// | Out-String ; if ($certificateInfo) { exit 0 } else { exit 1 }
export function verifySignature(publisherNames: Array<string>, tempUpdateFile: string, logger: Logger): Promise<string | null> {
  return new BluebirdPromise<string | null>((resolve, reject) => {
    // https://github.com/electron-userland/electron-builder/issues/2421
    // https://github.com/electron-userland/electron-builder/issues/2535
    execFile("powershell.exe", ["-NoProfile", "-NonInteractive", "-InputFormat", "None", "-Command", `Get-AuthenticodeSignature '${tempUpdateFile}' | ConvertTo-Json -Compress`], {
      timeout: 30 * 1000
    }, (error, stdout, stderr) => {
      if (error != null || stderr) {
        if (isOldWin6()) {
          logger.warn(`Cannot execute Get-AuthenticodeSignature: ${error || stderr}. Ignoring signature validation due to unsupported powershell version. Please upgrade to powershell 3 or higher.`)
          resolve(null)
          return
        }

        try {
          execFileSync("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", "ConvertTo-Json test"], {timeout: 10 * 1000})
        }
        catch (testError) {
          logger.warn(`Cannot execute ConvertTo-Json: ${testError.message}. Ignoring signature validation due to unsupported powershell version. Please upgrade to powershell 3 or higher.`)
          resolve(null)
          return
        }

        if (error != null) {
          reject(error)
          return
        }

        if (stderr) {
          reject(new Error(`Cannot execute Get-AuthenticodeSignature: ${stderr}`))
          return
        }
      }

      const data = JSON.parse(stdout)
      delete data.PrivateKey
      delete data.IsOSBinary
      delete data.SignatureType
      const signerCertificate = data.SignerCertificate
      if (signerCertificate != null) {
        delete signerCertificate.Archived
        delete signerCertificate.Extensions
        delete signerCertificate.Handle
        delete signerCertificate.HasPrivateKey
        // duplicates data.SignerCertificate (contains RawData)
        delete signerCertificate.SubjectName
      }
      delete data.Path

      if (data.Status === 0) {
        const name = parseDn(data.SignerCertificate.Subject).get("CN")!
        if (publisherNames.includes(name)) {
          resolve(null)
          return
        }
      }

      const result = `publisherNames: ${publisherNames.join(" | ")}, raw info: ` + JSON.stringify(data, (name, value) => name === "RawData" ? undefined : value, 2)
      logger.info(`Sign verification failed, installer signed with incorrect certificate: ${result}`)
      resolve(result)
    })
  })
}

function isOldWin6() {
  const winVersion = os.release()
  return winVersion.startsWith("6.") && !winVersion.startsWith("6.3")
  if (signerCertificate != null) {
    delete signerCertificate.Archived
    delete signerCertificate.Extensions
    delete signerCertificate.Handle
    delete signerCertificate.HasPrivateKey
    // duplicates data.SignerCertificate (contains RawData)
    delete signerCertificate.SubjectName
  }
  delete data.Path
  return data
}

function handleError(logger: Logger, error: Error | null, stderr: string | null): void {
  if (isOldWin6()) {
    logger.warn(
      `Cannot execute Get-AuthenticodeSignature: ${error || stderr}. Ignoring signature validation due to unsupported powershell version. Please upgrade to powershell 3 or higher.`
    )
    return
  }

  try {
    execFileSync("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", "ConvertTo-Json test"], { timeout: 10 * 1000 } as any)
  } catch (testError: any) {
    logger.warn(
      `Cannot execute ConvertTo-Json: ${testError.message}. Ignoring signature validation due to unsupported powershell version. Please upgrade to powershell 3 or higher.`
    )
    return
  }

  if (error != null) {
    throw error
  }

  if (stderr) {
    throw new Error(`Cannot execute Get-AuthenticodeSignature, stderr: ${stderr}. Failing signature validation due to unknown stderr.`)
  }
}

function isOldWin6(): boolean {
  const winVersion = os.release()
  return winVersion.startsWith("6.") && !winVersion.startsWith("6.3")
}

<MSG> fix(electron-updater): ignore unknown powershell errors

Related to #2949

Close #2589

<DFF> @@ -1,4 +1,3 @@
-import BluebirdPromise from "bluebird-lst"
 import { parseDn } from "builder-util-runtime"
 import { execFile, execFileSync } from "child_process"
 import * as os from "os"
@@ -8,69 +7,83 @@ import { Logger } from "./main"
 // | where {$_.Status.Equals([System.Management.Automation.SignatureStatus]::Valid) -and $_.SignerCertificate.Subject.Contains("CN=siemens.com")})
 // | Out-String ; if ($certificateInfo) { exit 0 } else { exit 1 }
 export function verifySignature(publisherNames: Array<string>, tempUpdateFile: string, logger: Logger): Promise<string | null> {
-  return new BluebirdPromise<string | null>((resolve, reject) => {
+  return new Promise<string | null>(resolve => {
     // https://github.com/electron-userland/electron-builder/issues/2421
     // https://github.com/electron-userland/electron-builder/issues/2535
     execFile("powershell.exe", ["-NoProfile", "-NonInteractive", "-InputFormat", "None", "-Command", `Get-AuthenticodeSignature '${tempUpdateFile}' | ConvertTo-Json -Compress`], {
-      timeout: 30 * 1000
+      timeout: 20 * 1000
     }, (error, stdout, stderr) => {
-      if (error != null || stderr) {
-        if (isOldWin6()) {
-          logger.warn(`Cannot execute Get-AuthenticodeSignature: ${error || stderr}. Ignoring signature validation due to unsupported powershell version. Please upgrade to powershell 3 or higher.`)
+      try {
+        if (error != null || stderr) {
+          handleError(logger, error, stderr)
           resolve(null)
           return
         }
 
-        try {
-          execFileSync("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", "ConvertTo-Json test"], {timeout: 10 * 1000})
-        }
-        catch (testError) {
-          logger.warn(`Cannot execute ConvertTo-Json: ${testError.message}. Ignoring signature validation due to unsupported powershell version. Please upgrade to powershell 3 or higher.`)
-          resolve(null)
-          return
-        }
-
-        if (error != null) {
-          reject(error)
-          return
+        const data = parseOut(stdout)
+        if (data.Status === 0) {
+          const name = parseDn(data.SignerCertificate.Subject).get("CN")!
+          if (publisherNames.includes(name)) {
+            resolve(null)
+            return
+          }
         }
 
-        if (stderr) {
-          reject(new Error(`Cannot execute Get-AuthenticodeSignature: ${stderr}`))
-          return
-        }
+        const result = `publisherNames: ${publisherNames.join(" | ")}, raw info: ` + JSON.stringify(data, (name, value) => name === "RawData" ? undefined : value, 2)
+        logger.warn(`Sign verification failed, installer signed with incorrect certificate: ${result}`)
+        resolve(result)
       }
-
-      const data = JSON.parse(stdout)
-      delete data.PrivateKey
-      delete data.IsOSBinary
-      delete data.SignatureType
-      const signerCertificate = data.SignerCertificate
-      if (signerCertificate != null) {
-        delete signerCertificate.Archived
-        delete signerCertificate.Extensions
-        delete signerCertificate.Handle
-        delete signerCertificate.HasPrivateKey
-        // duplicates data.SignerCertificate (contains RawData)
-        delete signerCertificate.SubjectName
-      }
-      delete data.Path
-
-      if (data.Status === 0) {
-        const name = parseDn(data.SignerCertificate.Subject).get("CN")!
-        if (publisherNames.includes(name)) {
-          resolve(null)
-          return
-        }
+      catch (e) {
+        logger.warn(`Cannot execute Get-AuthenticodeSignature: ${error}. Ignoring signature validation due to unknown error.`)
+        resolve(null)
+        return
       }
-
-      const result = `publisherNames: ${publisherNames.join(" | ")}, raw info: ` + JSON.stringify(data, (name, value) => name === "RawData" ? undefined : value, 2)
-      logger.info(`Sign verification failed, installer signed with incorrect certificate: ${result}`)
-      resolve(result)
     })
   })
 }
 
+function parseOut(out: string): any {
+  const data = JSON.parse(out)
+  delete data.PrivateKey
+  delete data.IsOSBinary
+  delete data.SignatureType
+  const signerCertificate = data.SignerCertificate
+  if (signerCertificate != null) {
+    delete signerCertificate.Archived
+    delete signerCertificate.Extensions
+    delete signerCertificate.Handle
+    delete signerCertificate.HasPrivateKey
+    // duplicates data.SignerCertificate (contains RawData)
+    delete signerCertificate.SubjectName
+  }
+  delete data.Path
+  return data
+}
+
+function handleError(logger: Logger, error: Error | null, stderr: string | null) {
+  if (isOldWin6()) {
+    logger.warn(`Cannot execute Get-AuthenticodeSignature: ${error || stderr}. Ignoring signature validation due to unsupported powershell version. Please upgrade to powershell 3 or higher.`)
+    return
+  }
+
+  try {
+    execFileSync("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", "ConvertTo-Json test"], {timeout: 10 * 1000})
+  }
+  catch (testError) {
+    logger.warn(`Cannot execute ConvertTo-Json: ${testError.message}. Ignoring signature validation due to unsupported powershell version. Please upgrade to powershell 3 or higher.`)
+    return
+  }
+
+  if (error != null) {
+    throw error
+  }
+
+  if (stderr) {
+    logger.warn(`Cannot execute Get-AuthenticodeSignature, stderr: ${stderr}. Ignoring signature validation due to unknown stderr.`)
+    return
+  }
+}
+
 function isOldWin6() {
   const winVersion = os.release()
   return winVersion.startsWith("6.") && !winVersion.startsWith("6.3")

{'addition_count': 61, 'commit_subject': 'fix(electron-updater): ignore unknown powershell errors', 'deletion_count': 48, 'file_extension': '.ts', 'lang': 'ts', 'license': 'mit', 'repo_name': 'electron-userland/electron-builder'}