electron-builder icon indicating copy to clipboard operation
electron-builder copied to clipboard

Auto update failed while executing the powershell command

Open davidcgi opened this issue 2 years ago • 1 comments

  • Electron-Builder Version: 22.2.0
  • Node Version: 14.16.0
  • Electron Version: 17.4.9
  • Electron Type (current, beta, nightly): current
  • Electron Updater: 5.2.1
  • Target: nsis

Using Electron updater and git hub provider for auto update. After the update downloaded from github, A JavaScript error message displayed.

[Window Title] Error

[Main Instruction] A JavaScript error occurred in the main process

[Content] Uncaught Exception: Error: Command failed: powershell.exe -NoProfile -NonInteractive -InputFormat None -Command Get-AuthenticodeSignature -LiteralPath 'C:\Users\Administrator\AppData\Local\testApp-updater\pending\temp-update.exe' | ConvertTo-Json -Compress | ForEach-Object { [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($_)) }

   at ChildProcess.exithandler (node:child_process:406:12)    at ChildProcess.emit (node:events:390:28)    at maybeClose (node:internal/child_process:1064:16)    at Process.ChildProcess._handle.onexit (node:internal/child_process:301:5)

[OK]

This is occurring only in Windows thin clients. I couldn't able to reproduce in my windows laptop. I tried few other Windows laptops to test, but I couldn't able to reproduce it other than thin clients.

I tried to manually run this command to see, if there is any issue. but the command is executed fine without any error.

davidcgi avatar Sep 06 '22 17:09 davidcgi

We've had 16k reports of this error in Evernote's updater.

Error: Command failed: powershell.exe -NoProfile -NonInteractive -InputFormat None -Command Get-AuthenticodeSignature -LiteralPath '***********AppData\Local\evernote-client-updater\pending\temp-Evernote-10.43.7-win-ddl-ga-3598-a51c0cce94-setup.exe' | ... File "node:child_process", line 406, in ChildProcess.exithandler File "node:events", line 390, in ChildProcess.emit File "node:domain", line 475, in ChildProcess.emit File "node:internal/child_process", line 1064, in maybeClose File "node:internal/child_process", line 301, in Process.ChildProcess._handle.onexit

RMorgan-Evernote avatar Sep 13 '22 15:09 RMorgan-Evernote

Our error logs are also filled with this error:

Unhandled Error Error: Command failed: powershell.exe -NoProfile -NonInteractive -InputFormat None -Command Get-AuthenticodeSignature -LiteralPath 'C:/Users/User/AppData/Local/appname-updater/pending/temp-AppName-Setup-1.0.99.exe' | ConvertTo-Json -Compress | ForEach-Object { [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($_)) } at ChildProcess.exithandler (node:child_process:407:12) at ChildProcess.emit (node:events:527:28) at maybeClose (node:internal/child_process:1092:16) at Socket. (node:internal/child_process:451:11) at Socket.emit (node:events:527:28) at Pipe. (node:net:709:12)

Any clues how to fix or silence this error? It seems to occur only the first time the app is started according to one user report we got.

gpronet avatar Nov 01 '22 07:11 gpronet

Hi! We are also experiencing the issue mentioned above. A lot of our users can't perform the auto update properly and this is a major issue for us as some of them are not up to date with the current version. Is there any updates on this issue? Did someone find a workaround or manage to figure out where this was coming from?

Here is the command that is failing on Windows if it can be helpful : Command failed: powershell.exe -NoProfile -NonInteractive -InputFormat None -Command Get-AuthenticodeSignature -LiteralPath 'C:\Users\username\AppData\Local\appname-updater\pending\temp-appname-setup-X.X.X.exe' | ConvertTo-Json -Compress | ForEach-Object { [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($_)) }

apayet974 avatar Nov 09 '22 16:11 apayet974

We have the same issue, and not sure under what condition it happens. The code fails here https://github.com/electron-userland/electron-builder/blob/346af1d470ebbf12733a9619a2389bcfdf452bc6/packages/electron-updater/src/windowsExecutableCodeSignatureVerifier.ts#L33

quolpr avatar Nov 17 '22 11:11 quolpr

We had the issue that appears to occur when the machine doesn't have enough CPU/memory. It was timing out before the certification verification could complete.

ruthieballenger avatar Nov 17 '22 14:11 ruthieballenger

I've also been running into this issue -- but intermittently, even on the same machine. I haven't really noticed a pattern but sometimes it'll resolve itself with a restart of the application in question. Worth noting it doesn't just happen on thin clients for me, reproducible intermittently on my lappy.

Powershell version I'm working with

PS C:\code> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      5.1.19041.2364
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.19041.2364
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

Attempt to run command manually

I attempted to run the command in it's entirety manually, and was immediately confronted with:

powershell.exe -NoProfile -NonInteractive -InputFormat None -Command Get-AuthenticodeSignature -LiteralPath './electron/output/MyApp Setup 9.1.52.exe' | ConvertTo-Json -Compress | ForEach-Object { [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($_)) }

WyJHZXQtQXV0aGVudGljb2RlU2lnbmF0dXJlIDogQSBwb3NpdGlvbmFsIHBhcmFtZXRlciBjYW5ub3QgYmUgZm91bmQgdGhhdCBhY2NlcHRzIGFyZ3VtZW50IFx1MDAyN1NldHVwXHUwMDI3LiIsIkF0IGxpbmU6MSBjaGFyOjEiLCIrIEdldC1BdXRoZW50aWNvZGVTaWduYXR1cmUgLUxpdGVyYWxQYXRoIC4vZWxlY3Ryb24vb3V0cHV0L015QXBwIFNldHVwICAuLi4iLCIrIH5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fiIsIiAgICArIENhdGVnb3J5SW5mbyAgICAgICAgICA6IEludmFsaWRBcmd1bWVudDogKDopIFtHZXQtQXV0aGVudGljb2RlU2lnbmF0dXJlXSwgUGFyYW1ldGVyQmluZGluZ0V4Y2VwdGlvbiIsIiAgICArIEZ1bGx5UXVhbGlmaWVkRXJyb3JJZCA6IFBvc2l0aW9uYWxQYXJhbWV0ZXJOb3RGb3VuZCxNaWNyb3NvZnQuUG93ZXJTaGVsbC5Db21tYW5kcy5HZXRBdXRoZW50aWNvZGVTaWduYXR1cmVDb21tYW5kIiwiICJd

The base64 decodes to:

powershell.exe -NoProfile -NonInteractive -InputFormat None -Command Get-AuthenticodeSignature -LiteralPath './electron/output/MyApp Setup 9.1.52.exe' | ConvertTo-Json -Compress | ForEach-Object { [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($_)) }

["Get-AuthenticodeSignature : A positional parameter cannot be found that accepts argument \u0027Setup\u0027.","At line:1 char:1","+ Get-AuthenticodeSignature -LiteralPath ./electron/output/MyApp Setup  ...","+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~","    + CategoryInfo          : InvalidArgument: (:) [Get-AuthenticodeSignature], ParameterBindingException","    + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.GetAuthenticodeSignatureCommand"," "]

This lead me down to reading a bit more about the -Command parameter. It looks like you can make use of it like: powershell.exe -Command { Get-Authenticode -LiteralPath './path/to/file with spaces.exe' }

Attempt to run with braces

powershell.exe -NoProfile -NonInteractive -InputFormat None -Command {Get-AuthenticodeSignature -LiteralPath './electron/output/MyApp Setup 9.1.52.exe'}
powershell.exe : A value that is not valid (None) was specified for the inputFormat parameter. Valid values are Text and Xml.
At line:1 char:1
+ powershell.exe -NoProfile -NonInteractive -InputFormat None -Command  ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [], ParameterBindingException
    + FullyQualifiedErrorId : IncorrectValueForFormatParameter

Seems to be that valid inputs are only Text and Xml. So I switched it to Text. (I'm not entirely sure the implications of this)

powershell.exe -NoProfile -NonInteractive -InputFormat Text -Command {Get-AuthenticodeSignature -LiteralPath './electron/output/MyApp Setup 9.1.52.exe'}


    Directory: C:\code\myapp\electron\output


SignerCertificate                         Status                                                                              Path
-----------------                         ------                                                                              ----
CF4XXXXBB52627BXXXXXXXXDFDBAB67DA         Valid                                                                               MyApp Setup 9.1.52.exe

Testing the changes

To double check my results I edited node_modules/electron_updater/out/windowsExecutableCodeSignatureVerifier.js directly as follows:

child_process_1.execFile("powershell.exe", [
            "-NoProfile",
            "-NonInteractive",
            "-InputFormat",
            "Text",
            "-Command",
            `{Get-AuthenticodeSignature -LiteralPath '${tempUpdateFile}'} | ConvertTo-Json -Compress | ForEach-Object { [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($_)) }`,
            ]

It does seem to not exhibit the issue anymore with my local testing but I'm unsure if this has introduced other issues. I'll update this as and when I test on a few different devices.

I'll escalate it up to a proper PR if it seems to be fixed wholesale.

M3ales avatar Dec 22 '22 15:12 M3ales

Any update on this issue? Thank you!

gujinku avatar Jan 16 '23 07:01 gujinku

Unfortunately this doesn't work, it resolves the powershell error but seems to cause publisher verification errors when checking the signatures later in the process.

I've also been running into this issue -- but intermittently, even on the same machine. I haven't really noticed a pattern but sometimes it'll resolve itself with a restart of the application in question. Worth noting it doesn't just happen on thin clients for me, reproducible intermittently on my lappy.

Powershell version I'm working with

PS C:\code> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      5.1.19041.2364
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.19041.2364
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

Attempt to run command manually

I attempted to run the command in it's entirety manually, and was immediately confronted with:

powershell.exe -NoProfile -NonInteractive -InputFormat None -Command Get-AuthenticodeSignature -LiteralPath './electron/output/MyApp Setup 9.1.52.exe' | ConvertTo-Json -Compress | ForEach-Object { [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($_)) }

WyJHZXQtQXV0aGVudGljb2RlU2lnbmF0dXJlIDogQSBwb3NpdGlvbmFsIHBhcmFtZXRlciBjYW5ub3QgYmUgZm91bmQgdGhhdCBhY2NlcHRzIGFyZ3VtZW50IFx1MDAyN1NldHVwXHUwMDI3LiIsIkF0IGxpbmU6MSBjaGFyOjEiLCIrIEdldC1BdXRoZW50aWNvZGVTaWduYXR1cmUgLUxpdGVyYWxQYXRoIC4vZWxlY3Ryb24vb3V0cHV0L015QXBwIFNldHVwICAuLi4iLCIrIH5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fiIsIiAgICArIENhdGVnb3J5SW5mbyAgICAgICAgICA6IEludmFsaWRBcmd1bWVudDogKDopIFtHZXQtQXV0aGVudGljb2RlU2lnbmF0dXJlXSwgUGFyYW1ldGVyQmluZGluZ0V4Y2VwdGlvbiIsIiAgICArIEZ1bGx5UXVhbGlmaWVkRXJyb3JJZCA6IFBvc2l0aW9uYWxQYXJhbWV0ZXJOb3RGb3VuZCxNaWNyb3NvZnQuUG93ZXJTaGVsbC5Db21tYW5kcy5HZXRBdXRoZW50aWNvZGVTaWduYXR1cmVDb21tYW5kIiwiICJd

The base64 decodes to:

powershell.exe -NoProfile -NonInteractive -InputFormat None -Command Get-AuthenticodeSignature -LiteralPath './electron/output/MyApp Setup 9.1.52.exe' | ConvertTo-Json -Compress | ForEach-Object { [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($_)) }

["Get-AuthenticodeSignature : A positional parameter cannot be found that accepts argument \u0027Setup\u0027.","At line:1 char:1","+ Get-AuthenticodeSignature -LiteralPath ./electron/output/MyApp Setup  ...","+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~","    + CategoryInfo          : InvalidArgument: (:) [Get-AuthenticodeSignature], ParameterBindingException","    + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.GetAuthenticodeSignatureCommand"," "]

This lead me down to reading a bit more about the -Command parameter. It looks like you can make use of it like: powershell.exe -Command { Get-Authenticode -LiteralPath './path/to/file with spaces.exe' }

Attempt to run with braces

powershell.exe -NoProfile -NonInteractive -InputFormat None -Command {Get-AuthenticodeSignature -LiteralPath './electron/output/MyApp Setup 9.1.52.exe'}
powershell.exe : A value that is not valid (None) was specified for the inputFormat parameter. Valid values are Text and Xml.
At line:1 char:1
+ powershell.exe -NoProfile -NonInteractive -InputFormat None -Command  ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [], ParameterBindingException
    + FullyQualifiedErrorId : IncorrectValueForFormatParameter

Seems to be that valid inputs are only Text and Xml. So I switched it to Text. (I'm not entirely sure the implications of this)

powershell.exe -NoProfile -NonInteractive -InputFormat Text -Command {Get-AuthenticodeSignature -LiteralPath './electron/output/MyApp Setup 9.1.52.exe'}


    Directory: C:\code\myapp\electron\output


SignerCertificate                         Status                                                                              Path
-----------------                         ------                                                                              ----
CF4XXXXBB52627BXXXXXXXXDFDBAB67DA         Valid                                                                               MyApp Setup 9.1.52.exe

Testing the changes

To double check my results I edited node_modules/electron_updater/out/windowsExecutableCodeSignatureVerifier.js directly as follows:

child_process_1.execFile("powershell.exe", [
            "-NoProfile",
            "-NonInteractive",
            "-InputFormat",
            "Text",
            "-Command",
            `{Get-AuthenticodeSignature -LiteralPath '${tempUpdateFile}'} | ConvertTo-Json -Compress | ForEach-Object { [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($_)) }`,
            ]

It does seem to not exhibit the issue anymore with my local testing but I'm unsure if this has introduced other issues. I'll update this as and when I test on a few different devices.

I'll escalate it up to a proper PR if it seems to be fixed wholesale.

M3ales avatar Jan 18 '23 07:01 M3ales

@M3ales You can replace the PowerShell with a native verify signature module to resolve all powershell problems.

https://github.com/electron-userland/electron-builder/pull/7337

beyondkmp avatar Jan 29 '23 03:01 beyondkmp

This issue might be fixed by https://github.com/electron-userland/electron-builder/pull/7230 which is included since [email protected]

juwonjung-hdj avatar Feb 23 '23 04:02 juwonjung-hdj

I saw this issue with the recently-released electron-updater 6.1.0

NoahAndrews avatar Apr 28 '23 19:04 NoahAndrews

We are also still seeing this issue in 6.0.0

mfranzs avatar May 19 '23 22:05 mfranzs

This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 30 days.

github-actions[bot] avatar Jul 19 '23 02:07 github-actions[bot]

Still issue: 6.1.4

Maybe need add some delay for check...

panther7 avatar Aug 16 '23 07:08 panther7

Duplicate issue #7051

panther7 avatar Aug 16 '23 07:08 panther7

Seeing this pretty frequently in 6.0.3. Haven't discovered any kind of pattern yet.

Error: Command failed: chcp 65001 >NUL & powershell.exe -NoProfile -NonInteractive -InputFormat None -Command "Get-AuthenticodeSignature -LiteralPath 'C:\Users\<user>\AppData\Local\<updater_dir>\pending\temp-<application_name>.exe' | ConvertTo...

seanssel avatar Sep 20 '23 17:09 seanssel

+1

We're seeing it since updated from electron-updater 4.3.5 -> 5.2.1. Right now we're at 6.1.4 and don't see any improvement.

Haven't figured out any pattern in it other than the fact that most of the cases (but not all of them) are happening when performing full download due to auto-update performed between multiple releases (eg. 1.0.0 -> 1.3.0 skipping 1.2.0 and anything else in between) :

Cannot download differentially, fallback to full download: Error: Cannot download "<scrubbed>.exe.blockmap", status 404: 
    at ClientRequest.<anonymous> (C:\Program Files\<scrubbed>\resources\app.asar\node_modules\electron-updater\node_modules\builder-util-runtime\out\httpExecutor.js:204:34)
    at ClientRequest.emit (node:events:525:35)
    at ClientRequest.emit (node:domain:489:12)
    at SimpleURLLoaderWrapper.<anonymous> (node:electron/js2c/browser_init:2:49128)
    at SimpleURLLoaderWrapper.emit (node:events:513:28)
    at SimpleURLLoaderWrapper.emit (node:domain:489:12)

Any chance it may get resolved soon? Happy to perform some tests if anyone figured out reproduction steps and/or fix

piotr-chowaniec avatar Oct 18 '23 17:10 piotr-chowaniec

Also hitting this one. Even though I can't reproduce this myself (through the updater), this is what I get when I run this on my machine (both with Powershell 5 as well as 7)

➜ powershell.exe -NoProfile -NonInteractive -InputFormat None -Command Get-AuthenticodeSignature -LiteralPath 'C:/Users/<redacted>/AppData/Local/<redacted>-updater/pending/temp-<redacted>.exe'
Get-AuthenticodeSignature : A positional parameter cannot be found that accepts argument 'Setup'.
At line:1 char:1
+ Get-AuthenticodeSignature -LiteralPath C:/Users/<redacted>/AppData/Loca ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Get-AuthenticodeSignature], ParameterBindingException
    + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.GetAuthenticodeSignatureComman
   d

So, a different error than @M3ales got. I got a similar error when trying to release the app on a Powershell 7 terminal though (as described on https://github.com/electron-userland/electron-builder/issues/7729)

Could this info be useful? Any suggestions what could be done here?

Edit: The only way I'm able to run the above command is on the "Windows Command Prompt" (so, cmd, not powershell). I'm on Windows 11 and "cmd" version is:

C:\Users\<redacted>\ver

Microsoft Windows [Version 10.0.22621.2428]

rasgo-cc avatar Nov 10 '23 15:11 rasgo-cc

This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 30 days.

github-actions[bot] avatar Jan 10 '24 00:01 github-actions[bot]

Hello, We are also facing same issue on clients machines. Issue is only replicable at client end. We, developers of our organization have ADMIN access to their allotted machines, so we are not facing this issue, hence its becoming impossible to replicate this issue at our end. And our clients have machines are in which they have non-admin permissions. When they are trying to update our application, they are getting this error - image

Its been very long time, still we haven't found any solution yet. Is anyone else facing same issue?

ShubhamSurya06 avatar Jan 15 '24 09:01 ShubhamSurya06

Hey there!

We've managed to create a setup for which it's failing with 100% repeatability.

Context

It was purely coincidental as it surfaced while we were working on e2e tests for auto-update. Why do we bother to do this? Well, it's crucial for us to ensure that our app can auto-update without any issues. The challenge is, you wouldn't discover it's faulty until it's already on users' machines, and you released a newer version. From that point, there's no turning back other than asking people to install it manually.

How to reproduce

Create a GitHub Action which should run your app assuming it should auto-update. You most likely need to point it to a different update source where you host the test version eg v99.0.0-e2e

name: E2E auto-update

on:
  workflow_call:

env:
  NPM_READ_TOKEN: ${{ secrets.GITHUB_TOKEN }}

jobs:
  windows:
    runs-on: windows-latest

    steps:
      - uses: actions/checkout@v3
      - uses: actions/download-artifact@v3
        with:
          name: windows-binary
          path: installer

      - name: List downloaded installer
        run: ls -R installer

      - name: Find installer name
        run: |
          $EXE_INSTALLER = Get-ChildItem -Path 'installer' -Filter '*.exe' -Recurse | ForEach-Object { $_.FullName }
          echo "EXE_INSTALLER=$EXE_INSTALLER" >> $env:GITHUB_ENV

      - name: Test Get-AuthenticodeSignature on cmd
        shell: cmd
        run: powershell -Command "Get-AuthenticodeSignature -LiteralPath '${{ env.EXE_INSTALLER }}' | ConvertTo-Json -Compress"

      - name: Test Get-AuthenticodeSignature on powershell
        shell: powershell
        run: Get-AuthenticodeSignature -LiteralPath '${{ env.EXE_INSTALLER }}' | ConvertTo-Json -Compress

      - name: Check verifySignature cmd
        shell: cmd
        run: chcp 65001 >NUL & powershell.exe -NoProfile -NonInteractive -InputFormat None -Command "Get-AuthenticodeSignature -LiteralPath '${{ env.EXE_INSTALLER }}' | ConvertTo-Json -Compress"

      - name: Run copy of electron-builder verifySignature
        run: node ./verifySignature.js

verifySignature.js script that is nothing more than extracted electron-builder windowsExecutableCodeSignatureVerifier.ts -> verifySignature which is causing problems

const { execFile } = require("child_process");

function verifySignature(unescapedTempUpdateFile) {
    return new Promise((resolve, reject) => {
        const tempUpdateFile = unescapedTempUpdateFile.replace(/'/g, "''");

        execFile(
            "chcp 65001 >NUL & powershell.exe",
            [
                "-NoProfile",
                "-NonInteractive",
                "-InputFormat",
                "None",
                "-Command",
                `"Get-AuthenticodeSignature -LiteralPath '${tempUpdateFile}' | ConvertTo-Json -Compress"`,
            ],
            {
                shell: true,
                timeout: 20 * 1000,
            },
            (error, stdout, stderr) => {
                try {
                    if (error != null || stderr) {
                        console.log(error, stderr);
                        reject(error);
                    }

                    console.log(stdout);
                    resolve(JSON.parse(stdout));
                } catch (e) {
                    reject(e);
                }
            },
        );
    });
}

function importModule() {
    return new Promise((resolve, reject) => {
        execFile(
            "chcp 65001 >NUL & powershell.exe",
            [
                "-NoProfile",
                "-NonInteractive",
                "-InputFormat",
                "None",
                "-Command",
                "Import-Module Microsoft.PowerShell.Security",
            ],
            {
                shell: true,
                timeout: 20 * 1000,
            },
            (error, stdout, stderr) => {
                try {
                    if (error != null || stderr) {
                        console.log(error, stderr);
                        reject(error);
                    }

                    console.log(stdout);
                    resolve(stdout);
                } catch (e) {
                    reject(e);
                }
            },
        );
    });
}

(async () => {
    try {
        console.log("[verifySignature] starting...");

        await importModule();
        await verifySignature("path_to_installer_at_gh_worker.exe");

        console.log("[verifySignature] done...");
        process.exit(0);
    } catch (error) {
        console.error("[verifySignature] failed", error);
        process.exit(1);
    }
})();

Findings

verifySignature fails with error

Error: Command failed: chcp 65001 >NUL & powershell.exe -NoProfile -NonInteractive -InputFormat None -Command "Get-AuthenticodeSignature -LiteralPath PATH_TO_INSTALLER | ConvertTo-Json -Compress"
Get-AuthenticodeSignature : The 'Get-AuthenticodeSignature' command was found in the module 
'Microsoft.PowerShell.Security', but the module could not be loaded. For more information, run 'Import-Module 
[verifySignature] failed Error: Command failed: chcp 65001 >NUL & powershell.exe -NoProfile -NonInteractive -InputFormat None -Command "Get-AuthenticodeSignature -LiteralPath PATH_TO_INSTALLER | ConvertTo-Json -Compress"
Microsoft.PowerShell.Security'.
Get-AuthenticodeSignature : The 'Get-AuthenticodeSignature' command was found in the module 
'Microsoft.PowerShell.Security', but the module could not be loaded. For more information, run 'Import-Module 
Microsoft.PowerShell.Security'.
At line:1 char:1
+ Get-AuthenticodeSignature -LiteralPath PATH_TO_INSTALLER ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (Get-AuthenticodeSignature:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CouldNotAutoloadMatchingModule

Given the error message:

Get-AuthenticodeSignature : The 'Get-AuthenticodeSignature' command was found in the module 'Microsoft.PowerShell.Security', but the module could not be loaded. For more information, run 'Import-Module Microsoft.PowerShell.Security'.

I did a test with manually importing it - see importModule()

Which fails with:

Command failed: chcp 65001 >NUL & powershell.exe -NoProfile -NonInteractive -InputFormat None -Command Import-Module Microsoft.PowerShell.Security
"System.Security.AccessControl.ObjectSecurity": The member AuditToString is already present.
Import-Module : The following error occurred while loading the extended type data file: Error in TypeData 
"System.Security.AccessControl.ObjectSecurity": The member AuditToString is already present.
Error in TypeData "System.Security.AccessControl.ObjectSecurity": The member AccessToString is already present.
Error in TypeData "System.Security.AccessControl.ObjectSecurity": The member Sddl is already present.
Error in TypeData "System.Security.AccessControl.ObjectSecurity": The member Access is already present.
Error in TypeData "System.Security.AccessControl.ObjectSecurity": The member Group is already present.
Error in TypeData "System.Security.AccessControl.ObjectSecurity": The member Owner is already present.
Error in TypeData "System.Security.AccessControl.ObjectSecurity": The member Path is already present.
At line:1 char:1
+ Import-Module Microsoft.PowerShell.Security
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [Import-Module], RuntimeException
    + FullyQualifiedErrorId : FormatXmlUpdateException,Microsoft.PowerShell.Commands.ImportModuleCommand
 
}

⚠️ What's interesting, when running the verifySignature command directly in the workflow - it does not fail. It fails only when it's executed by node script which later on runs:

execFile("chcp 65001 >NUL & powershell.exe")

I did a bit of research and found the following threads:

  • https://github.com/PowerShell/PowerShell/issues/18530
  • https://stackoverflow.com/a/74869395

However, with hours of try-and-error, I didn't manage to make it pass

Impact

As of today, we see ~40k of these errors a month which varies depending on how often we release. Due to this error, we can't auto-update app for certain group of users, hence they either are stuck at a version they already have or need to install it manually

piotr-chowaniec avatar Feb 07 '24 09:02 piotr-chowaniec

Hello @piotr-chowaniec, Yes, you are right. We were also facing the same issue on some clients machine. Below is the solution which we have applied, give it try at your end.

import win-verify-signature package in your appUpdater file. then add below code where autoUpdater's verifyUpdaterCodeSignature will get replace,

winVerifySign = require("win-verify-signature")


// Verify application after update downloaded successfully
autoUpdater.verifyUpdateCodeSignature = (publisherName, path) => {
    log.info('autoUpdaterService.ts::verifyUpdateCodeSignature::starting verification::-', path, publisherName);
    const result = winVerifySign.verifySignatureByPublishName(path, publisherName);
    if (result.signed) return Promise.resolve(null);
    log.info('autoUpdaterService.ts::verifyUpdateCodeSignature::225::-', result, result.message);
    return Promise.resolve(result.message);
};

With this change, we are not facing same issue which we were facing earlier. Give it a try.

ShubhamSurya06 avatar Feb 08 '24 06:02 ShubhamSurya06

Same issue here.

Jaifroid avatar Feb 09 '24 11:02 Jaifroid

Thank you @piotr-chowaniec for the very detailed writeup https://github.com/electron-userland/electron-builder/issues/7127#issuecomment-1931610372. I'll look into that

As @ShubhamSurya06 pointed out, there's an additional hook you can use to write your own code-verifying process or leverage a native module. https://www.electron.build/configuration/win#how-do-use-a-custom-verify-function-to-enable-nsis-signature-verification-alternatives-instead-of-powershell I would recommend the native module approach that leverages https://www.npmjs.com/package/win-verify-signature. It unfortunately can't be the default implementation of electron-builder though as there's no prebuild of the repo's native binaries, so it requires a Windows build machine to be integrated.

mmaietta avatar Feb 10 '24 18:02 mmaietta

@piotr-chowaniec, quick Q. What happens for you if you set the first exec in your verifySignature.js to be

set "PSModulePath="; chcp 65001 >NUL & powershell.exe

The suggestion came from one of the docs/posts that you linked. I was receiving signature validation errors on the CI tests on a windows machine, but don't seem to be any longer with that change. https://github.com/electron-userland/electron-builder/actions/runs/7874990028/job/21485844914

  ● valid signature using DN
    Command failed: chcp 65001 >NUL & powershell.exe -NoProfile -NonInteractive -InputFormat None -Command "Get-AuthenticodeSignature -LiteralPath 'C:\Users\RUNNER~1\AppData\Local\Temp\et-d8d6360ca6c99632f01b203b451e295c\t-8kecNa\1h\test-updater-app\pending\temp-TestApp-Setup-1.1.0.exe' | ConvertTo-Json -Compress"
    Get-AuthenticodeSignature : The 'Get-AuthenticodeSignature' command was found in the module 
    'Microsoft.PowerShell.Security', but the module could not be loaded. For more information, run 'Import-Module 
    Microsoft.PowerShell.Security'.
    At line:1 char:1
    + Get-AuthenticodeSignature -LiteralPath 'C:\Users\RUNNER~1\AppData\Loc ...
    + ~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : ObjectNotFound: (Get-AuthenticodeSignature:String) [], CommandNotFoundException
        + FullyQualifiedErrorId : CouldNotAutoloadMatchingModule

Versus having set "PSModulePath=" in command https://github.com/electron-userland/electron-builder/actions/runs/7874884860/job/21485522063 Signature tests are passing

I'm still trying to get WSL working in my Windows VM, getting other odd errors like chcp not found and could use someone more experienced in Windows Development than I am to help here 🙃

mmaietta avatar Feb 12 '24 16:02 mmaietta

@mmaietta Thanks for taking a look at it!

Following @ShubhamSurya06 advice I ended up with a custom verify signature implementation, but I took a bit more defensive approach trying to use win-verify-signature only if the default one fails:

const customVerifyUpdateCodeSignature = async (publisherName: string[], path: string) => {
    try {
        const nsisUpdater = new NsisUpdater();
        nsisUpdater.logger = autoUpdaterLogger;
        const defaultSignatureVerification = nsisUpdater.verifyUpdateCodeSignature;

        try {
            const defaultStatus = await defaultSignatureVerification(publisherName, path);
            if (defaultStatus !== null) {
                throw new Error(defaultStatus);
            }

            return defaultStatus;
        } catch (error) {
            // If the default verification fails, we will try to verify the signature using win-verify-signature
        }

        const winVerifySign = await import("win-verify-signature");
        const customStatus = winVerifySign.verifySignatureByPublishName(
            path,
            publisherName,
        );
        if (!customStatus.signed) {
            return customStatus.message;
        }

        return null;
    } catch (error) {
        return "Error while verifying update code signature with custom verifyUpdateCodeSignature implementation.";
    }
};

(autoUpdater as NsisUpdater).verifyUpdateCodeSignature = customVerifyUpdateCodeSignature;

Considering the fact we're building not only for windows. I had to add win-verify-signature as an optionalDependency so it can fail preinstall silently on macOS. Later on, it's imported only on Windows. With that change, our tests work as expected. For now, I can't tell much about how it behaves in prod as we haven't released it yet.

@mmaietta speaking about set "PSModulePath="; It works 🥳 Before posting a summary, I tried on my own with such command:

chcp 65001 >NUL & PSModulePath= & powershell.exe

But without success so I dropped it.

Regarding chpc an Windows WSL - please correct me if I'm wrong but isn't chpc available only on Windows hence you won't be able to access it on Windows WSL?

piotr-chowaniec avatar Feb 12 '24 19:02 piotr-chowaniec

Wonderful news! I'll open a PR fixing that and getting more unit tests up and running.

Regarding chpc an Windows WSL - please correct me if I'm wrong but isn't chpc available only on Windows hence you won't be able to access it on Windows WSL?

You're right, that makes sense. It's working in powershell, but my pnpm Windows dev environment is completely wrecked outside of WSL, so I need to figure that out as well.

mmaietta avatar Feb 12 '24 23:02 mmaietta

@mmaietta I'm a bit hesitant about whether that "small" change won't introduce regression issues. Do you have a way to test it heavily beforehand? If there is any issue caused by it, it'll be very hard to recover other than forcing people to manually install the app.

piotr-chowaniec avatar Feb 13 '24 08:02 piotr-chowaniec

@piotr-chowaniec the windows signing verification tests were only running on linux for some reason, so the moment I added them to the windows-latest github runner, the tests started failing and matching the error log you had provided previously. Now it passes successfully.

In the PR, the unit tests can validate up until the point of installing the app as on windows, the exe can't clean up the process due to the parent test process, and on linux, the rpm/deb changes require an authentication agent (for sudo) for the install to go through. I also tried adding the native signing method to the unit tests, but that caused test failures on mac even with win-verify-signature as an optional dependency.

That all being said, I'm am still trying to create a dev self-signed cert on windows to test further with, but it's proving to be quite difficult. electron-builder's create-self-signed-cert CLI option isn't working for me and I can't tell why.

mmaietta avatar Feb 13 '24 15:02 mmaietta

@piotr-chowaniec following up back here. I was able to get windows auto-update on a local minio server working on my windows Parallels VM with a self-signed certificate. I wasn't able to reproduce the original error here where signature verification fails on my VM though, only could via the CI unit tests on a windows-latest GH runner.

With my code changes in PR #8051, the update flow still works on my VM.

As with all electron-updater releases, this will be a next pre-release. Please make sure to test with your own processes as usual

mmaietta avatar Feb 17 '24 19:02 mmaietta