15815213711
2024-08-26 67b8b6731811983447e053d4396b3708c14dfe3c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.verifySignature = void 0;
const builder_util_runtime_1 = require("builder-util-runtime");
const child_process_1 = require("child_process");
const os = require("os");
// $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 }
function verifySignature(publisherNames, unescapedTempUpdateFile, logger) {
    return new Promise(resolve => {
        // Escape quotes and backticks in filenames to prevent user from breaking the
        // arguments and perform a remote command injection.
        //
        // Consider example powershell command:
        // ```powershell
        // Get-AuthenticodeSignature 'C:\\path\\my-bad-';calc;'filename.exe'
        // ```
        // The above would work expected and find the file name, however, it will also execute `;calc;`
        // command and start the calculator app.
        //
        // From Powershell quoting rules:
        // https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_quoting_rules?view=powershell-7
        // * Double quotes `"` are treated literally within single-quoted strings;
        // * Single quotes can be escaped by doubling them: 'don''t' -> don't;
        //
        // Also note that at this point the file has already been written to the disk, thus we are
        // guaranteed that the path will not contain any illegal characters like <>:"/\|?*
        // https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
        const tempUpdateFile = unescapedTempUpdateFile.replace(/'/g, "''");
        // https://github.com/electron-userland/electron-builder/issues/2421
        // https://github.com/electron-userland/electron-builder/issues/2535
        child_process_1.execFile("powershell.exe", [
            "-NoProfile",
            "-NonInteractive",
            "-InputFormat",
            "None",
            "-Command",
            `Get-AuthenticodeSignature -LiteralPath '${tempUpdateFile}' | ConvertTo-Json -Compress | ForEach-Object { [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($_)) }`,
        ], {
            timeout: 20 * 1000,
        }, (error, stdout, stderr) => {
            try {
                if (error != null || stderr) {
                    handleError(logger, error, stderr);
                    resolve(null);
                    return;
                }
                const data = parseOut(Buffer.from(stdout, "base64").toString("utf-8"));
                if (data.Status === 0) {
                    const subject = builder_util_runtime_1.parseDn(data.SignerCertificate.Subject);
                    let match = false;
                    for (const name of publisherNames) {
                        const dn = builder_util_runtime_1.parseDn(name);
                        if (dn.size) {
                            // if we have a full DN, compare all values
                            const allKeys = Array.from(dn.keys());
                            match = allKeys.every(key => {
                                return dn.get(key) === subject.get(key);
                            });
                        }
                        else if (name === subject.get("CN")) {
                            logger.warn(`Signature validated using only CN ${name}. Please add your full Distinguished Name (DN) to publisherNames configuration`);
                            match = true;
                        }
                        if (match) {
                            resolve(null);
                            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);
            }
            catch (e) {
                handleError(logger, e, null);
                resolve(null);
                return;
            }
        });
    });
}
exports.verifySignature = verifySignature;
function parseOut(out) {
    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, error, 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.`);
        return;
    }
    try {
        child_process_1.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) {
        throw new Error(`Cannot execute Get-AuthenticodeSignature, stderr: ${stderr}. Failing signature validation due to unknown stderr.`);
    }
}
function isOldWin6() {
    const winVersion = os.release();
    return winVersion.startsWith("6.") && !winVersion.startsWith("6.3");
}
//# sourceMappingURL=windowsExecutableCodeSignatureVerifier.js.map