@ohos-ports/vscode-vsce-sign 2.1.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE.txt ADDED
@@ -0,0 +1,41 @@
1
+ MICROSOFT SOFTWARE LICENSE TERMS
2
+ MICROSOFT VSCE-SIGN
3
+
4
+ These license terms are an agreement between Microsoft Corporation (or based on where you live, one of its affiliates) and you. They apply to the software named above. The terms also apply to any Microsoft services or updates for the software, except to the extent those have different terms.
5
+
6
+ IF YOU COMPLY WITH THESE LICENSE TERMS, YOU HAVE THE RIGHTS BELOW.
7
+
8
+ 1. INSTALLATION AND USE RIGHTS. You may install and use any number of copies of the software only with Microsoft Visual Studio, Visual Studio for Mac, Visual Studio Code, Azure DevOps, Team Foundation Server, Code Spaces and successor Microsoft products and services (collectively, the "Visual Studio Products and Services") to develop and test your applications.
9
+
10
+ 2. TERMS FOR SPECIFIC COMPONENTS. The software may include third party components with separate legal notices or governed by other agreements, as may be described in the notices file(s) accompanying the software.
11
+
12
+ 3. SCOPE OF LICENSE. The software is licensed, not sold. This agreement only gives you some rights to use the software. Microsoft reserves all other rights. Unless applicable law gives you more rights despite this limitation, you may use the software only as expressly permitted in this agreement. In doing so, you must comply with any technical limitations in the software that only allow you to use it in certain ways. You may not
13
+ * work around any technical limitations in the software;
14
+ * reverse engineer, decompile or disassemble the software, or otherwise attempt to derive the source code for the software except, and only to the extent required by third party licensing terms governing the use of certain open source components that may be included in the software;
15
+ * remove, minimize, block or modify any notices of Microsoft or its suppliers in the software;
16
+ * use the software in any way that is against the law;
17
+ * share, publish, rent or lease the software; or
18
+ * provide the software as a stand-alone offering or combined with any of your applications for others to use, or transfer the software or this agreement to any third party.
19
+
20
+ 4. EXPORT RESTRICTIONS. You must comply with all domestic and international export laws and regulations that apply to the software, which include restrictions on destinations, end users, and end use. For further information on export restrictions, visit www.microsoft.com/exporting.
21
+
22
+ 5. SUPPORT SERVICES. Because this software is "as is," we may not provide support services for it.
23
+
24
+ 6. ENTIRE AGREEMENT. This agreement, and the terms for supplements, updates, Internet-based services and support services that you use, are the entire agreement for the software and support services.
25
+
26
+ 7. APPLICABLE LAW. If you acquired the software in the United States, Washington law applies to interpretation of and claims for breach of this agreement, and the laws of the state where you live apply to all other claims. If you acquired the software in any other country, its laws apply.
27
+
28
+ 8. CONSUMER RIGHTS; REGIONAL VARIATIONS. This agreement describes certain legal rights. You may have other rights, including consumer rights, under the laws of your state or country. Separate and apart from your relationship with Microsoft, you may also have rights with respect to the party from which you acquired the software. This agreement does not change those other rights if the laws of your state or country do not permit it to do so. For example, if you acquired the software in one of the below regions, or mandatory country law applies, then the following provisions apply to you:
29
+ a. Australia. You have statutory guarantees under the Australian Consumer Law and nothing in this agreement is intended to affect those rights.
30
+
31
+ b. Canada. If you acquired this software in Canada, you may stop receiving updates by turning off the automatic update feature, disconnecting your device from the Internet (if and when you re-connect to the Internet, however, the software will resume checking for and installing updates), or uninstalling the software. The product documentation, if any, may also specify how to turn off updates for your specific device or software.
32
+
33
+ c. Germany and Austria.
34
+ (i) Warranty. The properly licensed software will perform substantially as described in any Microsoft materials that accompany the software. However, Microsoft gives no contractual guarantee in relation to the licensed software.
35
+ (ii) Limitation of Liability. In case of intentional conduct, gross negligence, claims based on the Product Liability Act, as well as, in case of death or personal or physical injury, Microsoft is liable according to the statutory law.
36
+ Subject to the foregoing clause (ii), Microsoft will only be liable for slight negligence if Microsoft is in breach of such material contractual obligations, the fulfillment of which facilitate the due performance of this agreement, the breach of which would endanger the purpose of this agreement and the compliance with which a party may constantly trust in (so-called "cardinal obligations"). In other cases of slight negligence, Microsoft will not be liable for slight negligence.
37
+
38
+ 9. DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED "AS-IS." YOU BEAR THE RISK OF USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES OR CONDITIONS. TO THE EXTENT PERMITTED UNDER YOUR LOCAL LAWS, MICROSOFT EXCLUDES THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
39
+ 10. LIMITATION ON AND EXCLUSION OF DAMAGES. YOU CAN RECOVER FROM MICROSOFT AND ITS SUPPLIERS ONLY DIRECT DAMAGES UP TO U.S. $5.00. YOU CANNOT RECOVER ANY OTHER DAMAGES, INCLUDING CONSEQUENTIAL, LOST PROFITS, SPECIAL, INDIRECT OR INCIDENTAL DAMAGES.
40
+ This limitation applies to (a) anything related to the software, services, content (including code) on third party Internet sites, or third party applications; and (b) claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the extent permitted by applicable law.
41
+ It also applies even if Microsoft knew or should have known about the possibility of the damages. The above limitation or exclusion may not apply to you because your country may not allow the exclusion or limitation of incidental, consequential or other damages.
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@ohos-ports/vscode-vsce-sign",
3
+ "description": "HarmonyOS (OpenHarmony) port of @vscode/vsce-sign - Visual Studio Code extension package signing and verification module",
4
+ "version": "2.1.0-beta.0",
5
+ "author": {
6
+ "name": "Microsoft"
7
+ },
8
+ "contributors": [
9
+ {
10
+ "name": "@ohos-ports"
11
+ }
12
+ ],
13
+ "license": "SEE LICENSE IN LICENSE.txt",
14
+ "homepage": "https://github.com/Microsoft/node-vsce-sign",
15
+ "main": "src/main.js",
16
+ "ohos_ports": {
17
+ "upstream": "@vscode/vsce-sign@2.1.0",
18
+ "platform": "openharmony",
19
+ "arch": "arm64",
20
+ "notes": "Upstream node-vsce-sign repository (Rust source) was deleted and is unavailable; native binary is provisioned via the package bin/ directory at packaging time. Without a bundled binary, install succeeds and sign/verify APIs degrade gracefully (ENOENT)."
21
+ },
22
+ "scripts": {
23
+ "postinstall": "node ./src/postinstall.js"
24
+ },
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/ohos-ports/ohos-ports.git",
28
+ "directory": "ports/vscode-vsce-sign/2.1.0"
29
+ },
30
+ "bugs": {
31
+ "url": "https://github.com/ohos-ports/ohos-ports/issues"
32
+ },
33
+ "files": [
34
+ "src"
35
+ ]
36
+ }
package/src/main.d.ts ADDED
@@ -0,0 +1,46 @@
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License. See License.txt in the project root for
4
+ * license information.
5
+ *--------------------------------------------------------------------------------------------*/
6
+
7
+ export function verify(vsixFilePath: string, signatureArchiveFilePath: string, verbose?: boolean): Promise<ExtensionSignatureVerificationResult>;
8
+ export function generateManifest(vsixFilePath: string, manifestFilePath?: string, verbose?: boolean): Promise<string>;
9
+ export function zip(manifestFilePath: string, signatureFilePath: string, signatureArchiveFilePath?: string, verbose?: boolean): Promise<string>;
10
+
11
+ export enum ExtensionSignatureVerificationCode {
12
+ 'Success' = 'Success',
13
+ 'RequiredArgumentMissing' = 'RequiredArgumentMissing',
14
+ 'InvalidArgument' = 'InvalidArgument',
15
+ 'PackageIsUnreadable' = 'PackageIsUnreadable',
16
+ 'UnhandledException' = 'UnhandledException',
17
+ 'SignatureManifestIsMissing' = 'SignatureManifestIsMissing',
18
+ 'SignatureManifestIsUnreadable' = 'SignatureManifestIsUnreadable',
19
+ 'SignatureIsMissing' = 'SignatureIsMissing',
20
+ 'SignatureIsUnreadable' = 'SignatureIsUnreadable',
21
+ 'CertificateIsUnreadable' = 'CertificateIsUnreadable',
22
+ 'SignatureArchiveIsUnreadable' = 'SignatureArchiveIsUnreadable',
23
+ 'FileAlreadyExists' = 'FileAlreadyExists',
24
+ 'SignatureArchiveIsInvalidZip' = 'SignatureArchiveIsInvalidZip',
25
+ 'SignatureArchiveHasSameSignatureFile' = 'SignatureArchiveHasSameSignatureFile',
26
+
27
+ 'PackageIntegrityCheckFailed' = 'PackageIntegrityCheckFailed',
28
+ 'SignatureIsInvalid' = 'SignatureIsInvalid',
29
+ 'SignatureManifestIsInvalid' = 'SignatureManifestIsInvalid',
30
+ 'SignatureIntegrityCheckFailed' = 'SignatureIntegrityCheckFailed',
31
+ 'EntryIsMissing' = 'EntryIsMissing',
32
+ 'EntryIsTampered' = 'EntryIsTampered',
33
+ 'Untrusted' = 'Untrusted',
34
+ 'CertificateRevoked' = 'CertificateRevoked',
35
+ 'SignatureIsNotValid' = 'SignatureIsNotValid',
36
+ 'UnknownError' = 'UnknownError',
37
+ 'PackageIsInvalidZip' = 'PackageIsInvalidZip',
38
+ 'SignatureArchiveHasTooManyEntries' = 'SignatureArchiveHasTooManyEntries',
39
+ }
40
+
41
+ export class ExtensionSignatureVerificationResult {
42
+ readonly code: ExtensionSignatureVerificationCode;
43
+ readonly didExecute: boolean;
44
+ readonly internalCode?: number;
45
+ readonly output?: string;
46
+ }
package/src/main.js ADDED
@@ -0,0 +1,194 @@
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * See LICENSE.txt in the project root for license information.
4
+ *--------------------------------------------------------------------------------------------*/
5
+
6
+ // @ts-check
7
+ 'use strict';
8
+
9
+ const { execFile } = require('child_process');
10
+ const Path = require('path');
11
+
12
+ const command = process.platform === 'win32' ? 'vsce-sign.exe' : 'vsce-sign';
13
+ const vsceSignFilePath = Path.join(__dirname, '..', 'bin', command);
14
+
15
+ // If this module is in an .asar file, then the binary is unpacked.
16
+ const vsceSignOnDiskFilePath = vsceSignFilePath.replace(/\bnode_modules\.asar\b/, 'node_modules.asar.unpacked');
17
+
18
+ const ExtensionSignatureVerificationCode = {
19
+ 'Success': 'Success',
20
+ 'RequiredArgumentMissing': 'RequiredArgumentMissing',
21
+ 'InvalidArgument': 'InvalidArgument',
22
+ 'PackageIsUnreadable': 'PackageIsUnreadable',
23
+ 'UnhandledException': 'UnhandledException',
24
+ 'SignatureManifestIsMissing': 'SignatureManifestIsMissing',
25
+ 'SignatureManifestIsUnreadable': 'SignatureManifestIsUnreadable',
26
+ 'SignatureIsMissing': 'SignatureIsMissing',
27
+ 'SignatureIsUnreadable': 'SignatureIsUnreadable',
28
+ 'CertificateIsUnreadable': 'CertificateIsUnreadable',
29
+ 'SignatureArchiveIsUnreadable': 'SignatureArchiveIsUnreadable',
30
+ 'FileAlreadyExists': 'FileAlreadyExists',
31
+ 'SignatureArchiveIsInvalidZip': 'SignatureArchiveIsInvalidZip',
32
+ 'SignatureArchiveHasSameSignatureFile': 'SignatureArchiveHasSameSignatureFile',
33
+
34
+ 'PackageIntegrityCheckFailed': 'PackageIntegrityCheckFailed',
35
+ 'SignatureIsInvalid': 'SignatureIsInvalid',
36
+ 'SignatureManifestIsInvalid': 'SignatureManifestIsInvalid',
37
+ 'SignatureIntegrityCheckFailed': 'SignatureIntegrityCheckFailed',
38
+ 'EntryIsMissing': 'EntryIsMissing',
39
+ 'EntryIsTampered': 'EntryIsTampered',
40
+ 'Untrusted': 'Untrusted',
41
+ 'CertificateRevoked': 'CertificateRevoked',
42
+ 'SignatureIsNotValid': 'SignatureIsNotValid',
43
+ 'UnknownError': 'UnknownError',
44
+ 'PackageIsInvalidZip': 'PackageIsInvalidZip',
45
+ 'SignatureArchiveHasTooManyEntries': 'SignatureArchiveHasTooManyEntries',
46
+ };
47
+
48
+ // Must stay in sync with Microsoft.VisualStudio.Extensions.Signing.ReturnCode (https://github.com/microsoft/vsce-sign/blob/main/src/Core/ReturnCode.cs).
49
+ const ReturnCode = {};
50
+
51
+ ReturnCode[ReturnCode[ExtensionSignatureVerificationCode.Success] = 0] = ExtensionSignatureVerificationCode.Success; // The operation succeeded.
52
+ ReturnCode[ReturnCode[ExtensionSignatureVerificationCode.RequiredArgumentMissing] = 3] = ExtensionSignatureVerificationCode.RequiredArgumentMissing; // A required argument is missing.
53
+ ReturnCode[ReturnCode[ExtensionSignatureVerificationCode.InvalidArgument] = 4] = ExtensionSignatureVerificationCode.InvalidArgument; // An argument is invalid.
54
+ ReturnCode[ReturnCode[ExtensionSignatureVerificationCode.PackageIsUnreadable] = 5] = ExtensionSignatureVerificationCode.PackageIsUnreadable; // The extension package is unreadable.
55
+ ReturnCode[ReturnCode[ExtensionSignatureVerificationCode.UnhandledException] = 6] = ExtensionSignatureVerificationCode.UnhandledException; // An unhandled exception occurred.
56
+ ReturnCode[ReturnCode[ExtensionSignatureVerificationCode.SignatureManifestIsMissing] = 7] = ExtensionSignatureVerificationCode.SignatureManifestIsMissing; // The extension is missing a signature manifest file (.signature.manifest).
57
+ ReturnCode[ReturnCode[ExtensionSignatureVerificationCode.SignatureManifestIsUnreadable] = 8] = ExtensionSignatureVerificationCode.SignatureManifestIsUnreadable; // The signature manifest is unreadable.
58
+ ReturnCode[ReturnCode[ExtensionSignatureVerificationCode.SignatureIsMissing] = 9] = ExtensionSignatureVerificationCode.SignatureIsMissing; // The extension is missing a signature file (.signature.p7s).
59
+ ReturnCode[ReturnCode[ExtensionSignatureVerificationCode.SignatureIsUnreadable] = 10] = ExtensionSignatureVerificationCode.SignatureIsUnreadable; // The signature is unreadable.
60
+ ReturnCode[ReturnCode[ExtensionSignatureVerificationCode.CertificateIsUnreadable] = 11] = ExtensionSignatureVerificationCode.CertificateIsUnreadable; // The certificate is unreadable.
61
+ ReturnCode[ReturnCode[ExtensionSignatureVerificationCode.SignatureArchiveIsUnreadable] = 12] = ExtensionSignatureVerificationCode.SignatureArchiveIsUnreadable; // The signature archive is unreadable.
62
+ ReturnCode[ReturnCode[ExtensionSignatureVerificationCode.FileAlreadyExists] = 13] = ExtensionSignatureVerificationCode.FileAlreadyExists; // The output file already exists.
63
+ ReturnCode[ReturnCode[ExtensionSignatureVerificationCode.SignatureArchiveIsInvalidZip] = 14] = ExtensionSignatureVerificationCode.SignatureArchiveIsInvalidZip; // The signature archive is not valid ZIP format.
64
+ ReturnCode[ReturnCode[ExtensionSignatureVerificationCode.SignatureArchiveHasSameSignatureFile] = 15] = ExtensionSignatureVerificationCode.SignatureArchiveHasSameSignatureFile; // The signature archive has the same signature file.
65
+
66
+ // Space reserved for additional CLI-specific return codes.
67
+ ReturnCode[ReturnCode[ExtensionSignatureVerificationCode.PackageIntegrityCheckFailed] = 30] = ExtensionSignatureVerificationCode.PackageIntegrityCheckFailed; // The package integrity check failed.
68
+ ReturnCode[ReturnCode[ExtensionSignatureVerificationCode.SignatureIsInvalid] = 31] = ExtensionSignatureVerificationCode.SignatureIsInvalid; // The extension has an invalid signature file (.signature.p7s).
69
+ ReturnCode[ReturnCode[ExtensionSignatureVerificationCode.SignatureManifestIsInvalid] = 32] = ExtensionSignatureVerificationCode.SignatureManifestIsInvalid; // The extension has an invalid signature manifest file (.signature.manifest).
70
+ ReturnCode[ReturnCode[ExtensionSignatureVerificationCode.SignatureIntegrityCheckFailed] = 33] = ExtensionSignatureVerificationCode.SignatureIntegrityCheckFailed; // The extension's signature integrity check failed. Extension integrity is suspect.
71
+ ReturnCode[ReturnCode[ExtensionSignatureVerificationCode.EntryIsMissing] = 34] = ExtensionSignatureVerificationCode.EntryIsMissing; // An entry referenced in the signature manifest was not found in the extension.
72
+ ReturnCode[ReturnCode[ExtensionSignatureVerificationCode.EntryIsTampered] = 35] = ExtensionSignatureVerificationCode.EntryIsTampered; // The integrity check for an entry referenced in the signature manifest failed.
73
+ ReturnCode[ReturnCode[ExtensionSignatureVerificationCode.Untrusted] = 36] = ExtensionSignatureVerificationCode.Untrusted; // An X.509 certificate in the extension signature is untrusted.
74
+ ReturnCode[ReturnCode[ExtensionSignatureVerificationCode.CertificateRevoked] = 37] = ExtensionSignatureVerificationCode.CertificateRevoked; // An X.509 certificate in the extension signature has been revoked.
75
+ ReturnCode[ReturnCode[ExtensionSignatureVerificationCode.SignatureIsNotValid] = 38] = ExtensionSignatureVerificationCode.SignatureIsNotValid; // The extension signature is invalid.
76
+ ReturnCode[ReturnCode[ExtensionSignatureVerificationCode.UnknownError] = 39] = ExtensionSignatureVerificationCode.UnknownError; // An unknown error occurred.
77
+ ReturnCode[ReturnCode[ExtensionSignatureVerificationCode.PackageIsInvalidZip] = 40] = ExtensionSignatureVerificationCode.PackageIsInvalidZip; // The extension package is not valid ZIP format.
78
+ ReturnCode[ReturnCode[ExtensionSignatureVerificationCode.SignatureArchiveHasTooManyEntries] = 41] = ExtensionSignatureVerificationCode.SignatureArchiveHasTooManyEntries; // The signature archive has too many entries.
79
+
80
+ class ExtensionSignatureVerificationResult {
81
+ /**
82
+ * @param {string} code
83
+ * @param {boolean} didExecute
84
+ * @param {number | undefined} internalCode
85
+ * @param {string | undefined} output
86
+ */
87
+ constructor(code, didExecute, internalCode, output) {
88
+ this.code = code;
89
+ this.internalCode = internalCode;
90
+ this.didExecute = didExecute;
91
+ this.output = output;
92
+ }
93
+ }
94
+
95
+ /**
96
+ * @param {string} vsixFilePath
97
+ * @param {string} signatureArchiveFilePath
98
+ * @param {boolean} verbose
99
+ * @returns {Promise<ExtensionSignatureVerificationResult>}
100
+ */
101
+ async function verify(vsixFilePath, signatureArchiveFilePath, verbose) {
102
+ const args = ['verify', '--package', vsixFilePath, '--signaturearchive', signatureArchiveFilePath];
103
+ return await execCommand(args, verbose, false);
104
+ }
105
+
106
+ /**
107
+ * @param {string} vsixFilePath
108
+ * @param {string | undefined} manifestFilePath
109
+ * @param {boolean} verbose
110
+ * @returns {Promise<string>}
111
+ */
112
+ async function generateManifest(vsixFilePath, manifestFilePath, verbose) {
113
+ const args = ['generatemanifest', '--package', vsixFilePath];
114
+
115
+ if (typeof manifestFilePath === 'string') {
116
+ args.push('--output');
117
+ args.push(manifestFilePath);
118
+ }
119
+
120
+ await execCommand(args, verbose, true);
121
+
122
+ return manifestFilePath ?? Path.join(Path.dirname(vsixFilePath), '.signature.manifest');
123
+ }
124
+
125
+ /**
126
+ * @param {string} manifestFilePath
127
+ * @param {string} signatureFilePath
128
+ * @param {string | undefined} signatureArchiveFilePath
129
+ * @param {boolean} verbose
130
+ * @returns {Promise<string>}
131
+ */
132
+ async function zip(manifestFilePath, signatureFilePath, signatureArchiveFilePath, verbose) {
133
+ const args = ['zip', '--manifest', manifestFilePath, '--signature', signatureFilePath];
134
+
135
+ if (typeof signatureArchiveFilePath === 'string') {
136
+ args.push('--output');
137
+ args.push(signatureArchiveFilePath);
138
+ }
139
+
140
+ await execCommand(args, verbose, true);
141
+
142
+ return signatureArchiveFilePath ?? Path.join(Path.dirname(manifestFilePath), '.signature.zip');
143
+ }
144
+
145
+ /**
146
+ * @param {string[]} args
147
+ * @param {boolean} verbose
148
+ * @param {boolean} throwIfNotSuccess
149
+ * @returns {Promise<ExtensionSignatureVerificationResult>}
150
+ */
151
+ function execCommand(args, verbose, throwIfNotSuccess) {
152
+ return new Promise((resolve, reject) => {
153
+
154
+ if (verbose === true) {
155
+ args.push('--verbose');
156
+ }
157
+
158
+ execFile(vsceSignOnDiskFilePath, args, (error, stdout) => {
159
+ let internalCode = undefined;
160
+ let returnCode;
161
+ let didExecute = false;
162
+
163
+ const code = error === null ? 0 : error?.code;
164
+ const output = verbose ? stdout : undefined;
165
+
166
+ // vsce-sign returns exit codes (numbers), whereas execFile(...), in the absence of an exit code, returns an error code (string) (e.g.: ENOENT) to indicate failure.
167
+ if (typeof code === 'number') {
168
+ didExecute = true;
169
+ internalCode = code;
170
+ returnCode = ReturnCode[code];
171
+ } else if (typeof code === 'string') {
172
+ returnCode = code;
173
+ }
174
+
175
+ returnCode = returnCode ?? ExtensionSignatureVerificationCode.UnknownError;
176
+
177
+ if (throwIfNotSuccess && returnCode !== ExtensionSignatureVerificationCode.Success) {
178
+ reject(new ExtensionSignatureVerificationResult(returnCode, didExecute, internalCode, output));
179
+ return;
180
+ }
181
+
182
+ resolve(new ExtensionSignatureVerificationResult(returnCode, didExecute, internalCode, output));
183
+ });
184
+ });
185
+ }
186
+
187
+ module.exports = {
188
+ verify,
189
+ generateManifest,
190
+ zip,
191
+ ReturnCode,
192
+ ExtensionSignatureVerificationCode,
193
+ ExtensionSignatureVerificationResult
194
+ };
@@ -0,0 +1,42 @@
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * See LICENSE.txt in the project root for license information.
4
+ *--------------------------------------------------------------------------------------------*/
5
+
6
+ // @ts-check
7
+ 'use strict';
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+
12
+ /**
13
+ * See https://github.com/nodejs/node-v0.x-archive/issues/3045#issuecomment-4862588
14
+ * mode is a combination of file type and permissions.
15
+ * @param {number} mode
16
+ * @returns {string}
17
+ */
18
+ function getPermissions(mode) {
19
+ // Get 3 smallest bytes in octal
20
+ return (mode & 0o777).toString(8);
21
+ }
22
+
23
+ /**
24
+ * @param {string} filePath
25
+ */
26
+ function setPermissions(filePath) {
27
+ let stats = fs.statSync(filePath);
28
+ const oldMode = stats.mode;
29
+
30
+ console.log(`Existing file permissions: ${getPermissions(oldMode)} (octal)`);
31
+
32
+ // 755 (octal) = -rwxr-xr-x
33
+ const newMode = 0o755;
34
+
35
+ fs.chmodSync(filePath, newMode);
36
+
37
+ stats = fs.statSync(filePath);
38
+
39
+ console.log(`New file permissions: ${getPermissions(stats.mode)} (octal)`);
40
+ }
41
+
42
+ module.exports = { setPermissions };
@@ -0,0 +1,224 @@
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * See LICENSE.txt in the project root for license information.
4
+ *--------------------------------------------------------------------------------------------*/
5
+
6
+ // @ts-check
7
+ 'use strict';
8
+
9
+ const https = require('https');
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+ const cp = require("child_process");
13
+ const zlib = require("zlib");
14
+ const { setPermissions } = require('./permissions');
15
+ const { getTarget } = require('./target');
16
+
17
+ const IS_WINDOWS = process.platform === 'win32';
18
+
19
+ async function main() {
20
+ const architecture = process.env['npm_config_arch'] ?? process.arch;
21
+ const target = getTarget(process.platform, architecture);
22
+
23
+ if (!target) {
24
+ throw new Error(`The current platform (${process.platform}) and architecture (${architecture}) is not supported.`);
25
+ }
26
+
27
+ const exeName = IS_WINDOWS ? 'vsce-sign.exe' : 'vsce-sign';
28
+ const binDir = path.join(__dirname, '..', 'bin');
29
+ const binPath = path.join(binDir, exeName);
30
+
31
+ // @ohos-ports port: on HarmonyOS there are no @vscode/vsce-sign-<target>
32
+ // binary packages on npm. The port provisions the binary either bundled in
33
+ // the package's own bin/ directory (added at packaging time) or not at all;
34
+ // in the latter case installation must still succeed and signing/verification
35
+ // APIs degrade gracefully (execFile returns ENOENT), matching the behaviour
36
+ // of the upstream module when its binary is missing.
37
+ if (target.startsWith('openharmony')) {
38
+ if (!fs.existsSync(binDir)) {
39
+ fs.mkdirSync(binDir);
40
+ }
41
+ if (fs.existsSync(binPath)) {
42
+ if (!IS_WINDOWS) {
43
+ setPermissions(binPath);
44
+ }
45
+ console.log(`[@ohos-ports/vscode-vsce-sign] Using bundled binary: ${binPath}`);
46
+ } else {
47
+ console.warn(`[@ohos-ports/vscode-vsce-sign] No vsce-sign binary provisioned for ${target}; sign/verify APIs will return ENOENT until a binary is bundled in ${binDir}.`);
48
+ }
49
+ return;
50
+ }
51
+
52
+ const subPath = `bin/${exeName}`;
53
+ const pkgName = `@vscode/vsce-sign-${target}`;
54
+
55
+ try {
56
+ setup(pkgName, subPath, binPath);
57
+ } catch (err) {
58
+ const version = require(path.join(__dirname, '..', 'package.json')).optionalDependencies?.[pkgName];
59
+ if (!version) {
60
+ console.warn(`[@ohos-ports/vscode-vsce-sign] Binary package "${pkgName}" is not declared (port build without optionalDependencies); skipping binary provisioning for ${target}.`);
61
+ return;
62
+ }
63
+ await fallbackSetup(pkgName, subPath, binPath, version);
64
+ }
65
+ }
66
+
67
+ /**
68
+ * @param {string} pkgName
69
+ * @param {string} subPath
70
+ * @param {string} binPath
71
+ */
72
+ function setup(pkgName, subPath, binPath) {
73
+ const targetBinPath = require.resolve(`${pkgName}/${subPath}`);
74
+ fs.copyFileSync(targetBinPath, binPath);
75
+ }
76
+
77
+ /**
78
+ * @param {string} pkgName
79
+ * @param {string} subPath
80
+ * @param {string} binPath
81
+ * @param {string} version
82
+ */
83
+ async function fallbackSetup(pkgName, subPath, binPath, version) {
84
+ console.error(`[@vscode/vsce-sign] Failed to find package "${pkgName}" on the file system
85
+
86
+ This can happen if you use the "--no-optional" flag. The "optionalDependencies"
87
+ package.json feature is used to install the correct binary executable
88
+ for your current platform. This install script will now attempt to work around
89
+ this. If that fails, you need to remove the "--no-optional" flag to use @vscode/vsce-sign.
90
+ `);
91
+
92
+ try {
93
+ console.error(`[@vscode/vsce-sign] Trying to install package "${pkgName}" using npm`);
94
+ installUsingNPM(pkgName, subPath, binPath, version);
95
+ return;
96
+ } catch (err2) {
97
+ console.error(`[@vscode/vsce-sign] Failed to install package "${pkgName}" using npm: ${err2 && err2.message || err2}`);
98
+ }
99
+
100
+ try {
101
+ await downloadDirectlyFromNPM(pkgName, subPath, binPath, version);
102
+ return;
103
+ } catch (e3) {
104
+ throw new Error(`Failed to install package "${pkgName}"`);
105
+ }
106
+ }
107
+
108
+ /**
109
+ * @param {string} pkgName
110
+ * @param {string} subpath
111
+ * @param {string} binPath
112
+ * @param {string} version
113
+ */
114
+ function installUsingNPM(pkgName, subpath, binPath, version) {
115
+ const env = { ...process.env, npm_config_global: void 0 };
116
+ const vsceSignLibDir = path.dirname(require.resolve('@vscode/vsce-sign'));
117
+ const installDir = path.join(vsceSignLibDir, "npm-install");
118
+ fs.mkdirSync(installDir);
119
+ try {
120
+ fs.writeFileSync(path.join(installDir, "package.json"), "{}");
121
+ // Pass --force so npm installs this deliberately-selected, platform-specific
122
+ // package even when the host os/cpu differ from the target (e.g. building a
123
+ // Linux arm64 or alpine server on an x64 host). npm otherwise rejects it with
124
+ // EBADPLATFORM and we fall through to a hardcoded public download, which fails
125
+ // in network-isolated / air-gapped environments. The install still uses the
126
+ // configured (authenticated) registry, so this resolves from a private feed.
127
+ cp.execSync(
128
+ `npm install --loglevel=error --prefer-offline --no-audit --progress=false --force ${pkgName}@${version}`,
129
+ { cwd: installDir, stdio: "pipe", env }
130
+ );
131
+ const installedBinPath = path.join(installDir, "node_modules", pkgName, subpath);
132
+ fs.renameSync(installedBinPath, binPath);
133
+ } finally {
134
+ try {
135
+ fs.rmSync(installDir, { recursive: true, force: true, maxRetries: 3 });
136
+ } catch {
137
+ }
138
+ }
139
+ }
140
+
141
+ /**
142
+ *
143
+ * @param {string} pkgName
144
+ * @param {string} subpath
145
+ * @param {string} binPath
146
+ * @param {string} version
147
+ */
148
+ async function downloadDirectlyFromNPM(pkgName, subpath, binPath, version) {
149
+ const url = `https://registry.npmjs.org/${pkgName}/-/${pkgName.replace("@vscode/", "")}-${version}.tgz`;
150
+ console.error(`[@vscode/vsce-sign] Trying to download ${JSON.stringify(url)}`);
151
+
152
+ let buffer;
153
+ try {
154
+ buffer = await fetch(url);
155
+ } catch (e) {
156
+ console.error(`[@vscode/vsce-sign] Failed to download ${JSON.stringify(url)}: ${e && e.message || e}`);
157
+ throw e;
158
+ }
159
+
160
+ fs.writeFileSync(binPath, extractFileFromTarGzip(buffer, subpath));
161
+ if (!IS_WINDOWS) {
162
+ setPermissions(binPath);
163
+ }
164
+ }
165
+
166
+ /**
167
+ * @param {string} url
168
+ * @returns {Promise<Buffer>}
169
+ */
170
+ function fetch(url) {
171
+ return new Promise((resolve, reject) => {
172
+ https.get(url, (res) => {
173
+ if ((res.statusCode === 301 || res.statusCode === 302) && res.headers.location) {
174
+ return fetch(res.headers.location).then(resolve, reject);
175
+ }
176
+ if (res.statusCode !== 200) {
177
+ console.error(`[@vscode/vsce-sign] Failed to download ${JSON.stringify(url)}: Server responded with ${res.statusCode}`);
178
+ return reject(new Error(`Server responded with ${res.statusCode}`));
179
+ }
180
+ let chunks = [];
181
+ res.on("data", (chunk) => chunks.push(chunk));
182
+ res.on("end", () => resolve(Buffer.concat(chunks)));
183
+ }).on("error", reject);
184
+ });
185
+ }
186
+
187
+ /**
188
+ * @param {Buffer} buffer
189
+ * @param {string} subpath
190
+ * @returns
191
+ */
192
+ function extractFileFromTarGzip(buffer, subpath) {
193
+ try {
194
+ buffer = zlib.unzipSync(buffer);
195
+ } catch (err) {
196
+ const message = `[@vscode/vsce-sign] Invalid gzip data in archive: ${err && err.message || err}`;
197
+ console.error(message);
198
+ throw new Error(message);
199
+ }
200
+ let str = (/** @type {number} */ i, /** @type {number} */ n) => String.fromCharCode(...buffer.subarray(i, i + n)).replace(/\0.*$/, "");
201
+ let offset = 0;
202
+ subpath = `package/${subpath}`;
203
+ while (offset < buffer.length) {
204
+ let name = str(offset, 100);
205
+ let size = parseInt(str(offset + 124, 12), 8);
206
+ offset += 512;
207
+ if (!isNaN(size)) {
208
+ if (name === subpath)
209
+ return buffer.subarray(offset, offset + size);
210
+ offset += size + 511 & ~511;
211
+ }
212
+ }
213
+
214
+ const message = `Could not find ${JSON.stringify(subpath)} in archive`;
215
+ console.error(message);
216
+ throw new Error(message);
217
+ }
218
+
219
+ main()
220
+ .then(() => process.exit(0))
221
+ .catch((err) => {
222
+ console.error(err && err.message || err);
223
+ process.exit(1);
224
+ });
package/src/target.js ADDED
@@ -0,0 +1,148 @@
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * See LICENSE.txt in the project root for license information.
4
+ *--------------------------------------------------------------------------------------------*/
5
+
6
+ // @ts-check
7
+ 'use strict';
8
+
9
+ const fs = require('fs');
10
+
11
+ /**
12
+ * @typedef {Function} BooleanFunction
13
+ * @returns {boolean}
14
+ */
15
+
16
+ /**
17
+ * @param {string} platform
18
+ * @param {string} architecture
19
+ * @param {BooleanFunction=} isAlpineLinuxFunc
20
+ * @returns {string | null}
21
+ */
22
+ function getTarget(platform, architecture, isAlpineLinuxFunc = isAlpineLinux) {
23
+ switch (platform) {
24
+ case 'darwin':
25
+ return getMacOsRuntimeIdentifier(architecture);
26
+
27
+ case 'linux':
28
+ if (isAlpineLinuxFunc()) {
29
+ return getAlpineLinuxRuntimeIdentifier(architecture);
30
+ }
31
+
32
+ return getLinuxRuntimeIdentifier(architecture);
33
+
34
+ case 'win32':
35
+ return getWindowsRuntimeIdentifier(architecture);
36
+
37
+ // HarmonyOS (OpenHarmony): mapped by the @ohos-ports port of @vscode/vsce-sign.
38
+ // The port provisions its binary through the package's own bin/ directory
39
+ // (see postinstall.js), not through @vscode/vsce-sign-<target> npm packages.
40
+ case 'openharmony':
41
+ return getOpenHarmonyRuntimeIdentifier(architecture);
42
+
43
+ default:
44
+ return null;
45
+ }
46
+ }
47
+
48
+ /**
49
+ * @param {string} architecture
50
+ * @returns {string | null}
51
+ */
52
+ function getOpenHarmonyRuntimeIdentifier(architecture) {
53
+ switch (architecture) {
54
+ case 'arm64':
55
+ return `openharmony-arm64`;
56
+
57
+ default:
58
+ return null;
59
+ }
60
+ }
61
+
62
+ /**
63
+ * @param {string} architecture
64
+ * @returns {string | null}
65
+ */
66
+ function getAlpineLinuxRuntimeIdentifier(architecture) {
67
+ switch (architecture) {
68
+ case 'arm64':
69
+ case 'x64':
70
+ return `alpine-${architecture}`;
71
+
72
+ default:
73
+ return null;
74
+ }
75
+ }
76
+
77
+ /**
78
+ * @param {string} architecture
79
+ * @returns {string | null}
80
+ */
81
+ function getLinuxRuntimeIdentifier(architecture) {
82
+ switch (architecture) {
83
+ case 'arm':
84
+ case 'arm64':
85
+ case 'x64':
86
+ return `linux-${architecture}`;
87
+
88
+ default:
89
+ return null;
90
+ }
91
+ }
92
+
93
+ /**
94
+ * @param {string} architecture
95
+ * @returns {string | null}
96
+ */
97
+ function getMacOsRuntimeIdentifier(architecture) {
98
+ switch (architecture) {
99
+ case 'arm64':
100
+ case 'x64':
101
+ return `darwin-${architecture}`;
102
+
103
+ default:
104
+ return null;
105
+ }
106
+ }
107
+
108
+ /**
109
+ * @param {string} architecture
110
+ * @returns {string | null}
111
+ */
112
+ function getWindowsRuntimeIdentifier(architecture) {
113
+ switch (architecture) {
114
+ case 'arm':
115
+ case 'arm64':
116
+ case 'x64':
117
+ return `win32-${architecture}`;
118
+
119
+ case 'ia32':
120
+ return `win32-x86`;
121
+
122
+ default:
123
+ return null;
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Borrowed from {@link https://github.com/microsoft/vscode/blob/49e696c011a868b283f7bc7f208378a5d450b5d8/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts#L171-L189}
129
+ * @returns {boolean}
130
+ */
131
+ function isAlpineLinux() {
132
+ let content;
133
+
134
+ try {
135
+ const fileContent = fs.readFileSync('/etc/os-release');
136
+ content = fileContent.toString();
137
+ } catch (error) {
138
+ try {
139
+ const fileContent = fs.readFileSync('/usr/lib/os-release');
140
+ content = fileContent.toString();
141
+ } catch (error) {
142
+ }
143
+ }
144
+
145
+ return !!content && (content.match(/^ID=([^\u001b\r\n]*)/m) || [])[1] === 'alpine';
146
+ }
147
+
148
+ module.exports = { getTarget };