Skip to content

Custom Windows signing script using signingManager.getToolPath() broken in [email protected] #9620

@lk101

Description

@lk101

I am packaging an Electron project with [email protected]. During the packaging process, I need to code-sign the .exe and .dll files in the installer to avoid false positives from antivirus software. However, for third‑party tools (.exe/.dllthat already have a valid digital signature, I want to preserve their original signature and only apply my own certificate to files that are not yet signed.

In [email protected], I used a custom signing script (configured via build.win.signtoolOptions.sign) that worked perfectly. It first checked whether a file was already signed and skipped it if so; otherwise it signed the file with my certificate. The script relied on packager.signingManager.getToolPath() to obtain the path to the signing tool.

After upgrading to 26.8.1, the script fails with the error: getToolPath() is not a function. It appears that the signingManager API has changed or been removed.

Here is the script that worked with 26.7.0:

import { spawn } from 'node:child_process'

/**
 * Path to the signing tool executable.
 */
let tool = ''

/**
 * If the file to be signed is already signed, do nothing;
 * otherwise sign it using the signing tool.
 *
 * @param {CustomWindowsSignTaskConfiguration} config signing configuration
 * @param {WinPackager} packager packager instance
 */
export default async (config, packager) => {
  if (!tool) {
    tool = (await (await packager.signingManager.value).getToolPath()).path
  }
  try {
    // Check if already signed
    await run(tool, ['verify', '/pa', '/q', '/hash', config.hash, config.path])
  } catch {
    // Not signed -> sign it
    await run(tool, config.computeSignToolArgs(true))
  }
}

/**
 * Execute an external command.
 *
 * @param {string} command command path
 * @param {string[]} args command arguments
 * @returns {Promise<string>} stdout output; rejects with error message on failure
 */
function run(command, args = []) {
  return new Promise((r, j) => {
    const child = spawn(command, args)
    let out = Buffer.alloc(0),
      error = Buffer.alloc(0)
    child.on('error', j)
    child.on('close', (code) =>
      code ? j(error.toString('utf-8')) : r(out.toString('utf-8')),
    )
    child.stdout.on('data', (data) => (out = Buffer.concat([out, data])))
    child.stderr.on('data', (data) => (error = Buffer.concat([error, data])))
  })
}

Expected Behavior:

I would like to achieve the same result in [email protected]: conditionally sign files only if they are not already signed, preserving third‑party signatures.

s there a new recommended way to implement such a custom signing script? Or has the signingManager API been moved/replaced? If the internal API is no longer exposed, could you suggest an alternative approach that does not rely on private methods? Ideally, I would prefer to keep the script simple and not have to replicate the argument building logic (which config.computeSignToolArgs() provided).

Thank you!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions