electron-updater 4.6.4 → 5.0.0-alpha.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"providerFactory.js","sourceRoot":"","sources":["../src/providerFactory.ts"],"names":[],"mappings":";;;AAAA,+DAY6B;AAE7B,iEAA6D;AAC7D,qEAAiE;AACjE,iEAA6D;AAC7D,+DAA2D;AAC3D,+DAA2D;AAC3D,6EAAyE;AAGzE,SAAgB,sCAAsC,CAAC,GAAW;IAChE,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAA;AAC1C,CAAC;AAFD,wFAEC;AAED,SAAgB,YAAY,CAAC,IAA8C,EAAE,OAAmB,EAAE,cAAsC;IACtI,qCAAqC;IACrC,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE;QAC5B,MAAM,+BAAQ,CAAC,yCAAyC,EAAE,4CAA4C,CAAC,CAAA;KACxG;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAA;IAC9B,QAAQ,QAAQ,EAAE;QAChB,KAAK,QAAQ,CAAC,CAAC;YACb,MAAM,aAAa,GAAG,IAAqB,CAAA;YAC3C,MAAM,KAAK,GAAG,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,aAAa,CAAC,KAAK,CAAA;YAC5H,IAAI,KAAK,IAAI,IAAI,EAAE;gBACjB,OAAO,IAAI,+BAAc,CAAC,aAAa,EAAE,OAAO,EAAE,cAAc,CAAC,CAAA;aAClE;iBAAM;gBACL,OAAO,IAAI,6CAAqB,CAAC,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,CAAC,CAAA;aAChF;SACF;QAED,KAAK,WAAW;YACd,OAAO,IAAI,qCAAiB,CAAC,IAAwB,EAAE,OAAO,EAAE,cAAc,CAAC,CAAA;QAEjF,KAAK,QAAQ;YACX,OAAO,IAAI,+BAAc,CAAC,IAAqB,EAAE,OAAO,EAAE,cAAc,CAAC,CAAA;QAE3E,KAAK,IAAI,CAAC;QACV,KAAK,QAAQ;YACX,OAAO,IAAI,iCAAe,CACxB;gBACE,QAAQ,EAAE,SAAS;gBACnB,GAAG,EAAE,+CAAwB,CAAC,IAAI,CAAC;gBACnC,OAAO,EAAG,IAAsB,CAAC,OAAO,IAAI,IAAI;aACjD,EACD,OAAO,EACP;gBACE,GAAG,cAAc;gBACjB,oEAAoE;gBACpE,yBAAyB,EAAE,KAAK;aACjC,CACF,CAAA;QAEH,KAAK,SAAS,CAAC,CAAC;YACd,MAAM,OAAO,GAAG,IAA4B,CAAA;YAC5C,OAAO,IAAI,iCAAe,CAAC,OAAO,EAAE,OAAO,EAAE;gBAC3C,GAAG,cAAc;gBACjB,yBAAyB,EAAE,OAAO,CAAC,uBAAuB,KAAK,KAAK,IAAI,sCAAsC,CAAC,OAAO,CAAC,GAAG,CAAC;aAC5H,CAAC,CAAA;SACH;QAED,KAAK,SAAS;YACZ,OAAO,IAAI,iCAAe,CAAC,IAAsB,EAAE,cAAc,CAAC,CAAA;QAEpE,KAAK,QAAQ,CAAC,CAAC;YACb,MAAM,OAAO,GAAG,IAA4B,CAAA;YAC5C,MAAM,WAAW,GAAG,OAAO,CAAC,cAAc,CAAA;YAC1C,IAAI,CAAC,WAAW,EAAE;gBAChB,MAAM,+BAAQ,CAAC,+BAA+B,EAAE,4CAA4C,CAAC,CAAA;aAC9F;YACD,OAAO,IAAI,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,CAAC,CAAA;SACzD;QAED;YACE,MAAM,+BAAQ,CAAC,yBAAyB,QAAQ,EAAE,EAAE,kCAAkC,CAAC,CAAA;KAC1F;AACH,CAAC;AA/DD,oCA+DC","sourcesContent":["import {\n AllPublishOptions,\n BaseS3Options,\n BintrayOptions,\n BitbucketOptions,\n CustomPublishOptions,\n GenericServerOptions,\n getS3LikeProviderBaseUrl,\n GithubOptions,\n KeygenOptions,\n newError,\n PublishConfiguration,\n} from \"builder-util-runtime\"\nimport { AppUpdater } from \"./AppUpdater\"\nimport { BintrayProvider } from \"./providers/BintrayProvider\"\nimport { BitbucketProvider } from \"./providers/BitbucketProvider\"\nimport { GenericProvider } from \"./providers/GenericProvider\"\nimport { GitHubProvider } from \"./providers/GitHubProvider\"\nimport { KeygenProvider } from \"./providers/KeygenProvider\"\nimport { PrivateGitHubProvider } from \"./providers/PrivateGitHubProvider\"\nimport { Provider, ProviderRuntimeOptions } from \"./providers/Provider\"\n\nexport function isUrlProbablySupportMultiRangeRequests(url: string): boolean {\n return !url.includes(\"s3.amazonaws.com\")\n}\n\nexport function createClient(data: PublishConfiguration | AllPublishOptions, updater: AppUpdater, runtimeOptions: ProviderRuntimeOptions): Provider<any> {\n // noinspection SuspiciousTypeOfGuard\n if (typeof data === \"string\") {\n throw newError(\"Please pass PublishConfiguration object\", \"ERR_UPDATER_INVALID_PROVIDER_CONFIGURATION\")\n }\n\n const provider = data.provider\n switch (provider) {\n case \"github\": {\n const githubOptions = data as GithubOptions\n const token = (githubOptions.private ? process.env[\"GH_TOKEN\"] || process.env[\"GITHUB_TOKEN\"] : null) || githubOptions.token\n if (token == null) {\n return new GitHubProvider(githubOptions, updater, runtimeOptions)\n } else {\n return new PrivateGitHubProvider(githubOptions, updater, token, runtimeOptions)\n }\n }\n\n case \"bitbucket\":\n return new BitbucketProvider(data as BitbucketOptions, updater, runtimeOptions)\n\n case \"keygen\":\n return new KeygenProvider(data as KeygenOptions, updater, runtimeOptions)\n\n case \"s3\":\n case \"spaces\":\n return new GenericProvider(\n {\n provider: \"generic\",\n url: getS3LikeProviderBaseUrl(data),\n channel: (data as BaseS3Options).channel || null,\n },\n updater,\n {\n ...runtimeOptions,\n // https://github.com/minio/minio/issues/5285#issuecomment-350428955\n isUseMultipleRangeRequest: false,\n }\n )\n\n case \"generic\": {\n const options = data as GenericServerOptions\n return new GenericProvider(options, updater, {\n ...runtimeOptions,\n isUseMultipleRangeRequest: options.useMultipleRangeRequest !== false && isUrlProbablySupportMultiRangeRequests(options.url),\n })\n }\n\n case \"bintray\":\n return new BintrayProvider(data as BintrayOptions, runtimeOptions)\n\n case \"custom\": {\n const options = data as CustomPublishOptions\n const constructor = options.updateProvider\n if (!constructor) {\n throw newError(\"Custom provider not specified\", \"ERR_UPDATER_INVALID_PROVIDER_CONFIGURATION\")\n }\n return new constructor(options, updater, runtimeOptions)\n }\n\n default:\n throw newError(`Unsupported provider: ${provider}`, \"ERR_UPDATER_UNSUPPORTED_PROVIDER\")\n }\n}\n"]}
1
+ {"version":3,"file":"providerFactory.js","sourceRoot":"","sources":["../src/providerFactory.ts"],"names":[],"mappings":";;;AAAA,+DAW6B;AAE7B,qEAAiE;AACjE,iEAA6D;AAC7D,+DAA2D;AAC3D,+DAA2D;AAC3D,6EAAyE;AAGzE,SAAgB,sCAAsC,CAAC,GAAW;IAChE,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAA;AAC1C,CAAC;AAFD,wFAEC;AAED,SAAgB,YAAY,CAAC,IAA8C,EAAE,OAAmB,EAAE,cAAsC;IACtI,qCAAqC;IACrC,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE;QAC5B,MAAM,+BAAQ,CAAC,yCAAyC,EAAE,4CAA4C,CAAC,CAAA;KACxG;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAA;IAC9B,QAAQ,QAAQ,EAAE;QAChB,KAAK,QAAQ,CAAC,CAAC;YACb,MAAM,aAAa,GAAG,IAAqB,CAAA;YAC3C,MAAM,KAAK,GAAG,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,aAAa,CAAC,KAAK,CAAA;YAC5H,IAAI,KAAK,IAAI,IAAI,EAAE;gBACjB,OAAO,IAAI,+BAAc,CAAC,aAAa,EAAE,OAAO,EAAE,cAAc,CAAC,CAAA;aAClE;iBAAM;gBACL,OAAO,IAAI,6CAAqB,CAAC,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,CAAC,CAAA;aAChF;SACF;QAED,KAAK,WAAW;YACd,OAAO,IAAI,qCAAiB,CAAC,IAAwB,EAAE,OAAO,EAAE,cAAc,CAAC,CAAA;QAEjF,KAAK,QAAQ;YACX,OAAO,IAAI,+BAAc,CAAC,IAAqB,EAAE,OAAO,EAAE,cAAc,CAAC,CAAA;QAE3E,KAAK,IAAI,CAAC;QACV,KAAK,QAAQ;YACX,OAAO,IAAI,iCAAe,CACxB;gBACE,QAAQ,EAAE,SAAS;gBACnB,GAAG,EAAE,+CAAwB,CAAC,IAAI,CAAC;gBACnC,OAAO,EAAG,IAAsB,CAAC,OAAO,IAAI,IAAI;aACjD,EACD,OAAO,EACP;gBACE,GAAG,cAAc;gBACjB,oEAAoE;gBACpE,yBAAyB,EAAE,KAAK;aACjC,CACF,CAAA;QAEH,KAAK,SAAS,CAAC,CAAC;YACd,MAAM,OAAO,GAAG,IAA4B,CAAA;YAC5C,OAAO,IAAI,iCAAe,CAAC,OAAO,EAAE,OAAO,EAAE;gBAC3C,GAAG,cAAc;gBACjB,yBAAyB,EAAE,OAAO,CAAC,uBAAuB,KAAK,KAAK,IAAI,sCAAsC,CAAC,OAAO,CAAC,GAAG,CAAC;aAC5H,CAAC,CAAA;SACH;QAED,KAAK,QAAQ,CAAC,CAAC;YACb,MAAM,OAAO,GAAG,IAA4B,CAAA;YAC5C,MAAM,WAAW,GAAG,OAAO,CAAC,cAAc,CAAA;YAC1C,IAAI,CAAC,WAAW,EAAE;gBAChB,MAAM,+BAAQ,CAAC,+BAA+B,EAAE,4CAA4C,CAAC,CAAA;aAC9F;YACD,OAAO,IAAI,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,CAAC,CAAA;SACzD;QAED;YACE,MAAM,+BAAQ,CAAC,yBAAyB,QAAQ,EAAE,EAAE,kCAAkC,CAAC,CAAA;KAC1F;AACH,CAAC;AA5DD,oCA4DC","sourcesContent":["import {\n AllPublishOptions,\n BaseS3Options,\n BitbucketOptions,\n CustomPublishOptions,\n GenericServerOptions,\n getS3LikeProviderBaseUrl,\n GithubOptions,\n KeygenOptions,\n newError,\n PublishConfiguration,\n} from \"builder-util-runtime\"\nimport { AppUpdater } from \"./AppUpdater\"\nimport { BitbucketProvider } from \"./providers/BitbucketProvider\"\nimport { GenericProvider } from \"./providers/GenericProvider\"\nimport { GitHubProvider } from \"./providers/GitHubProvider\"\nimport { KeygenProvider } from \"./providers/KeygenProvider\"\nimport { PrivateGitHubProvider } from \"./providers/PrivateGitHubProvider\"\nimport { Provider, ProviderRuntimeOptions } from \"./providers/Provider\"\n\nexport function isUrlProbablySupportMultiRangeRequests(url: string): boolean {\n return !url.includes(\"s3.amazonaws.com\")\n}\n\nexport function createClient(data: PublishConfiguration | AllPublishOptions, updater: AppUpdater, runtimeOptions: ProviderRuntimeOptions): Provider<any> {\n // noinspection SuspiciousTypeOfGuard\n if (typeof data === \"string\") {\n throw newError(\"Please pass PublishConfiguration object\", \"ERR_UPDATER_INVALID_PROVIDER_CONFIGURATION\")\n }\n\n const provider = data.provider\n switch (provider) {\n case \"github\": {\n const githubOptions = data as GithubOptions\n const token = (githubOptions.private ? process.env[\"GH_TOKEN\"] || process.env[\"GITHUB_TOKEN\"] : null) || githubOptions.token\n if (token == null) {\n return new GitHubProvider(githubOptions, updater, runtimeOptions)\n } else {\n return new PrivateGitHubProvider(githubOptions, updater, token, runtimeOptions)\n }\n }\n\n case \"bitbucket\":\n return new BitbucketProvider(data as BitbucketOptions, updater, runtimeOptions)\n\n case \"keygen\":\n return new KeygenProvider(data as KeygenOptions, updater, runtimeOptions)\n\n case \"s3\":\n case \"spaces\":\n return new GenericProvider(\n {\n provider: \"generic\",\n url: getS3LikeProviderBaseUrl(data),\n channel: (data as BaseS3Options).channel || null,\n },\n updater,\n {\n ...runtimeOptions,\n // https://github.com/minio/minio/issues/5285#issuecomment-350428955\n isUseMultipleRangeRequest: false,\n }\n )\n\n case \"generic\": {\n const options = data as GenericServerOptions\n return new GenericProvider(options, updater, {\n ...runtimeOptions,\n isUseMultipleRangeRequest: options.useMultipleRangeRequest !== false && isUrlProbablySupportMultiRangeRequests(options.url),\n })\n }\n\n case \"custom\": {\n const options = data as CustomPublishOptions\n const constructor = options.updateProvider\n if (!constructor) {\n throw newError(\"Custom provider not specified\", \"ERR_UPDATER_INVALID_PROVIDER_CONFIGURATION\")\n }\n return new constructor(options, updater, runtimeOptions)\n }\n\n default:\n throw newError(`Unsupported provider: ${provider}`, \"ERR_UPDATER_UNSUPPORTED_PROVIDER\")\n }\n}\n"]}
@@ -22,7 +22,7 @@ class BaseGitHubProvider extends Provider_1.Provider {
22
22
  computeGithubBasePath(result) {
23
23
  // https://github.com/electron-userland/electron-builder/issues/1903#issuecomment-320881211
24
24
  const host = this.options.host;
25
- return host != null && host !== "github.com" && host !== "api.github.com" ? `/api/v3${result}` : result;
25
+ return host !== null && host !== "github.com" && host !== "api.github.com" ? `/api/v3${result}` : result;
26
26
  }
27
27
  }
28
28
  exports.BaseGitHubProvider = BaseGitHubProvider;
@@ -33,6 +33,7 @@ class GitHubProvider extends BaseGitHubProvider {
33
33
  this.updater = updater;
34
34
  }
35
35
  async getLatestVersion() {
36
+ var _a, _b, _c, _d;
36
37
  const cancellationToken = new builder_util_runtime_1.CancellationToken();
37
38
  const feedXml = (await this.httpRequest(util_1.newUrlFromBase(`${this.basePath}.atom`, this.baseUrl), {
38
39
  accept: "application/xml, application/atom+xml, text/xml, */*",
@@ -40,11 +41,34 @@ class GitHubProvider extends BaseGitHubProvider {
40
41
  const feed = builder_util_runtime_1.parseXml(feedXml);
41
42
  // noinspection TypeScriptValidateJSTypes
42
43
  let latestRelease = feed.element("entry", false, `No published versions on GitHub`);
43
- let tag;
44
+ let tag = null;
44
45
  try {
45
46
  if (this.updater.allowPrerelease) {
46
- // noinspection TypeScriptValidateJSTypes
47
- tag = hrefRegExp.exec(latestRelease.element("link").attribute("href"))[1];
47
+ const currentChannel = ((_a = this.updater) === null || _a === void 0 ? void 0 : _a.channel) || String((_b = semver.prerelease(this.updater.currentVersion)) === null || _b === void 0 ? void 0 : _b[0]) || null;
48
+ for (const element of feed.getElements("entry")) {
49
+ // noinspection TypeScriptValidateJSTypes
50
+ const hrefElement = hrefRegExp.exec(element.element("link").attribute("href"));
51
+ // If this is null then something is wrong and skip this release
52
+ if (hrefElement === null)
53
+ continue;
54
+ // This Release's Tag
55
+ const hrefTag = hrefElement[1];
56
+ //Get Channel from this release's tag
57
+ const hrefChannel = ((_c = semver.prerelease(hrefTag)) === null || _c === void 0 ? void 0 : _c[0]) || null;
58
+ const shouldFetchVersion = !currentChannel || ["alpha", "beta"].includes(currentChannel);
59
+ const isCustomChannel = !["alpha", "beta"].includes(String(hrefChannel));
60
+ // Allow moving from alpha to beta but not down
61
+ const channelMismatch = currentChannel === "beta" && hrefChannel === "alpha";
62
+ if (shouldFetchVersion && !isCustomChannel && !channelMismatch) {
63
+ tag = hrefTag;
64
+ break;
65
+ }
66
+ const isNextPreRelease = hrefChannel && hrefChannel === currentChannel;
67
+ if (isNextPreRelease) {
68
+ tag = hrefTag;
69
+ break;
70
+ }
71
+ }
48
72
  }
49
73
  else {
50
74
  tag = await this.getLatestTagName(cancellationToken);
@@ -63,18 +87,35 @@ class GitHubProvider extends BaseGitHubProvider {
63
87
  if (tag == null) {
64
88
  throw builder_util_runtime_1.newError(`No published versions on GitHub`, "ERR_UPDATER_NO_PUBLISHED_VERSIONS");
65
89
  }
66
- const channelFile = util_1.getChannelFilename(this.getDefaultChannelName());
67
- const channelFileUrl = util_1.newUrlFromBase(this.getBaseDownloadPath(tag, channelFile), this.baseUrl);
68
- const requestOptions = this.createRequestOptions(channelFileUrl);
69
90
  let rawData;
91
+ let channelFile = "";
92
+ let channelFileUrl = "";
93
+ const fetchData = async (channelName) => {
94
+ channelFile = util_1.getChannelFilename(channelName);
95
+ channelFileUrl = util_1.newUrlFromBase(this.getBaseDownloadPath(String(tag), channelFile), this.baseUrl);
96
+ const requestOptions = this.createRequestOptions(channelFileUrl);
97
+ try {
98
+ return (await this.executor.request(requestOptions, cancellationToken));
99
+ }
100
+ catch (e) {
101
+ if (e instanceof builder_util_runtime_1.HttpError && e.statusCode === 404) {
102
+ throw builder_util_runtime_1.newError(`Cannot find ${channelFile} in the latest release artifacts (${channelFileUrl}): ${e.stack || e.message}`, "ERR_UPDATER_CHANNEL_FILE_NOT_FOUND");
103
+ }
104
+ throw e;
105
+ }
106
+ };
70
107
  try {
71
- rawData = (await this.executor.request(requestOptions, cancellationToken));
108
+ const channel = this.updater.allowPrerelease ? this.getCustomChannelName(String(((_d = semver.prerelease(tag)) === null || _d === void 0 ? void 0 : _d[0]) || "latest")) : this.getDefaultChannelName();
109
+ rawData = await fetchData(channel);
72
110
  }
73
111
  catch (e) {
74
- if (!this.updater.allowPrerelease && e instanceof builder_util_runtime_1.HttpError && e.statusCode === 404) {
75
- throw builder_util_runtime_1.newError(`Cannot find ${channelFile} in the latest release artifacts (${channelFileUrl}): ${e.stack || e.message}`, "ERR_UPDATER_CHANNEL_FILE_NOT_FOUND");
112
+ if (this.updater.allowPrerelease) {
113
+ // Allow fallback to `latest.yml`
114
+ rawData = await fetchData(this.getDefaultChannelName());
115
+ }
116
+ else {
117
+ throw e;
76
118
  }
77
- throw e;
78
119
  }
79
120
  const result = Provider_1.parseUpdateInfo(rawData, channelFile, channelFileUrl);
80
121
  if (result.releaseName == null) {
@@ -1 +1 @@
1
- {"version":3,"file":"GitHubProvider.js","sourceRoot":"","sources":["../../src/providers/GitHubProvider.ts"],"names":[],"mappings":"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","sourcesContent":["import { CancellationToken, GithubOptions, githubUrl, HttpError, newError, parseXml, ReleaseNoteInfo, UpdateInfo, XElement } from \"builder-util-runtime\"\nimport * as semver from \"semver\"\nimport { URL } from \"url\"\nimport { AppUpdater } from \"../AppUpdater\"\nimport { ResolvedUpdateFileInfo } from \"../main\"\nimport { getChannelFilename, newBaseUrl, newUrlFromBase } from \"../util\"\nimport { parseUpdateInfo, Provider, ProviderRuntimeOptions, resolveFiles } from \"./Provider\"\n\nconst hrefRegExp = /\\/tag\\/([^/]+)$/\n\ninterface GithubUpdateInfo extends UpdateInfo {\n tag: string\n}\nexport abstract class BaseGitHubProvider<T extends UpdateInfo> extends Provider<T> {\n // so, we don't need to parse port (because node http doesn't support host as url does)\n protected readonly baseUrl: URL\n protected readonly baseApiUrl: URL\n\n protected constructor(protected readonly options: GithubOptions, defaultHost: string, runtimeOptions: ProviderRuntimeOptions) {\n super({\n ...runtimeOptions,\n /* because GitHib uses S3 */\n isUseMultipleRangeRequest: false,\n })\n\n this.baseUrl = newBaseUrl(githubUrl(options, defaultHost))\n const apiHost = defaultHost === \"github.com\" ? \"api.github.com\" : defaultHost\n this.baseApiUrl = newBaseUrl(githubUrl(options, apiHost))\n }\n\n protected computeGithubBasePath(result: string): string {\n // https://github.com/electron-userland/electron-builder/issues/1903#issuecomment-320881211\n const host = this.options.host\n return host != null && host !== \"github.com\" && host !== \"api.github.com\" ? `/api/v3${result}` : result\n }\n}\n\nexport class GitHubProvider extends BaseGitHubProvider<GithubUpdateInfo> {\n constructor(protected readonly options: GithubOptions, private readonly updater: AppUpdater, runtimeOptions: ProviderRuntimeOptions) {\n super(options, \"github.com\", runtimeOptions)\n }\n\n async getLatestVersion(): Promise<GithubUpdateInfo> {\n const cancellationToken = new CancellationToken()\n\n const feedXml: string = (await this.httpRequest(\n newUrlFromBase(`${this.basePath}.atom`, this.baseUrl),\n {\n accept: \"application/xml, application/atom+xml, text/xml, */*\",\n },\n cancellationToken\n ))!\n\n const feed = parseXml(feedXml)\n // noinspection TypeScriptValidateJSTypes\n let latestRelease = feed.element(\"entry\", false, `No published versions on GitHub`)\n let tag: string | null\n try {\n if (this.updater.allowPrerelease) {\n // noinspection TypeScriptValidateJSTypes\n tag = hrefRegExp.exec(latestRelease.element(\"link\").attribute(\"href\"))![1]\n } else {\n tag = await this.getLatestTagName(cancellationToken)\n for (const element of feed.getElements(\"entry\")) {\n // noinspection TypeScriptValidateJSTypes\n if (hrefRegExp.exec(element.element(\"link\").attribute(\"href\"))![1] === tag) {\n latestRelease = element\n break\n }\n }\n }\n } catch (e) {\n throw newError(`Cannot parse releases feed: ${e.stack || e.message},\\nXML:\\n${feedXml}`, \"ERR_UPDATER_INVALID_RELEASE_FEED\")\n }\n\n if (tag == null) {\n throw newError(`No published versions on GitHub`, \"ERR_UPDATER_NO_PUBLISHED_VERSIONS\")\n }\n\n const channelFile = getChannelFilename(this.getDefaultChannelName())\n const channelFileUrl = newUrlFromBase(this.getBaseDownloadPath(tag, channelFile), this.baseUrl)\n const requestOptions = this.createRequestOptions(channelFileUrl)\n let rawData: string\n try {\n rawData = (await this.executor.request(requestOptions, cancellationToken))!\n } catch (e) {\n if (!this.updater.allowPrerelease && e instanceof HttpError && e.statusCode === 404) {\n throw newError(`Cannot find ${channelFile} in the latest release artifacts (${channelFileUrl}): ${e.stack || e.message}`, \"ERR_UPDATER_CHANNEL_FILE_NOT_FOUND\")\n }\n throw e\n }\n\n const result = parseUpdateInfo(rawData, channelFile, channelFileUrl)\n if (result.releaseName == null) {\n result.releaseName = latestRelease.elementValueOrEmpty(\"title\")\n }\n\n if (result.releaseNotes == null) {\n result.releaseNotes = computeReleaseNotes(this.updater.currentVersion, this.updater.fullChangelog, feed, latestRelease)\n }\n return {\n tag: tag,\n ...result,\n }\n }\n\n private async getLatestTagName(cancellationToken: CancellationToken): Promise<string | null> {\n const options = this.options\n // do not use API for GitHub to avoid limit, only for custom host or GitHub Enterprise\n const url =\n options.host == null || options.host === \"github.com\"\n ? newUrlFromBase(`${this.basePath}/latest`, this.baseUrl)\n : new URL(`${this.computeGithubBasePath(`/repos/${options.owner}/${options.repo}/releases`)}/latest`, this.baseApiUrl)\n try {\n const rawData = await this.httpRequest(url, { Accept: \"application/json\" }, cancellationToken)\n if (rawData == null) {\n return null\n }\n\n const releaseInfo: GithubReleaseInfo = JSON.parse(rawData)\n return releaseInfo.tag_name\n } catch (e) {\n throw newError(`Unable to find latest version on GitHub (${url}), please ensure a production release exists: ${e.stack || e.message}`, \"ERR_UPDATER_LATEST_VERSION_NOT_FOUND\")\n }\n }\n\n private get basePath(): string {\n return `/${this.options.owner}/${this.options.repo}/releases`\n }\n\n resolveFiles(updateInfo: GithubUpdateInfo): Array<ResolvedUpdateFileInfo> {\n // still replace space to - due to backward compatibility\n return resolveFiles(updateInfo, this.baseUrl, p => this.getBaseDownloadPath(updateInfo.tag, p.replace(/ /g, \"-\")))\n }\n\n private getBaseDownloadPath(tag: string, fileName: string): string {\n return `${this.basePath}/download/${tag}/${fileName}`\n }\n}\n\ninterface GithubReleaseInfo {\n readonly tag_name: string\n}\n\nfunction getNoteValue(parent: XElement): string {\n const result = parent.elementValueOrEmpty(\"content\")\n // GitHub reports empty notes as <content>No content.</content>\n return result === \"No content.\" ? \"\" : result\n}\n\nexport function computeReleaseNotes(currentVersion: semver.SemVer, isFullChangelog: boolean, feed: XElement, latestRelease: any): string | Array<ReleaseNoteInfo> | null {\n if (!isFullChangelog) {\n return getNoteValue(latestRelease)\n }\n\n const releaseNotes: Array<ReleaseNoteInfo> = []\n for (const release of feed.getElements(\"entry\")) {\n // noinspection TypeScriptValidateJSTypes\n const versionRelease = /\\/tag\\/v?([^/]+)$/.exec(release.element(\"link\").attribute(\"href\"))![1]\n if (semver.lt(currentVersion, versionRelease)) {\n releaseNotes.push({\n version: versionRelease,\n note: getNoteValue(release),\n })\n }\n }\n return releaseNotes.sort((a, b) => semver.rcompare(a.version, b.version))\n}\n"]}
1
+ {"version":3,"file":"GitHubProvider.js","sourceRoot":"","sources":["../../src/providers/GitHubProvider.ts"],"names":[],"mappings":"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","sourcesContent":["import { CancellationToken, GithubOptions, githubUrl, HttpError, newError, parseXml, ReleaseNoteInfo, UpdateInfo, XElement } from \"builder-util-runtime\"\nimport * as semver from \"semver\"\nimport { URL } from \"url\"\nimport { AppUpdater } from \"../AppUpdater\"\nimport { ResolvedUpdateFileInfo } from \"../main\"\nimport { getChannelFilename, newBaseUrl, newUrlFromBase } from \"../util\"\nimport { parseUpdateInfo, Provider, ProviderRuntimeOptions, resolveFiles } from \"./Provider\"\n\nconst hrefRegExp = /\\/tag\\/([^/]+)$/\n\ninterface GithubUpdateInfo extends UpdateInfo {\n tag: string\n}\nexport abstract class BaseGitHubProvider<T extends UpdateInfo> extends Provider<T> {\n // so, we don't need to parse port (because node http doesn't support host as url does)\n protected readonly baseUrl: URL\n protected readonly baseApiUrl: URL\n\n protected constructor(protected readonly options: GithubOptions, defaultHost: string, runtimeOptions: ProviderRuntimeOptions) {\n super({\n ...runtimeOptions,\n /* because GitHib uses S3 */\n isUseMultipleRangeRequest: false,\n })\n\n this.baseUrl = newBaseUrl(githubUrl(options, defaultHost))\n const apiHost = defaultHost === \"github.com\" ? \"api.github.com\" : defaultHost\n this.baseApiUrl = newBaseUrl(githubUrl(options, apiHost))\n }\n\n protected computeGithubBasePath(result: string): string {\n // https://github.com/electron-userland/electron-builder/issues/1903#issuecomment-320881211\n const host = this.options.host\n return host !== null && host !== \"github.com\" && host !== \"api.github.com\" ? `/api/v3${result}` : result\n }\n}\n\nexport class GitHubProvider extends BaseGitHubProvider<GithubUpdateInfo> {\n constructor(protected readonly options: GithubOptions, private readonly updater: AppUpdater, runtimeOptions: ProviderRuntimeOptions) {\n super(options, \"github.com\", runtimeOptions)\n }\n\n async getLatestVersion(): Promise<GithubUpdateInfo> {\n const cancellationToken = new CancellationToken()\n\n const feedXml: string = (await this.httpRequest(\n newUrlFromBase(`${this.basePath}.atom`, this.baseUrl),\n {\n accept: \"application/xml, application/atom+xml, text/xml, */*\",\n },\n cancellationToken\n ))!\n\n const feed = parseXml(feedXml)\n // noinspection TypeScriptValidateJSTypes\n let latestRelease = feed.element(\"entry\", false, `No published versions on GitHub`)\n let tag: string | null = null\n try {\n if (this.updater.allowPrerelease) {\n const currentChannel = this.updater?.channel || String(semver.prerelease(this.updater.currentVersion)?.[0]) || null\n for (const element of feed.getElements(\"entry\")) {\n // noinspection TypeScriptValidateJSTypes\n const hrefElement = hrefRegExp.exec(element.element(\"link\").attribute(\"href\"))!\n\n // If this is null then something is wrong and skip this release\n if (hrefElement === null) continue\n\n // This Release's Tag\n const hrefTag = hrefElement[1]\n //Get Channel from this release's tag\n const hrefChannel = semver.prerelease(hrefTag)?.[0] || null\n\n const shouldFetchVersion = !currentChannel || [\"alpha\", \"beta\"].includes(currentChannel)\n const isCustomChannel = ![\"alpha\", \"beta\"].includes(String(hrefChannel))\n // Allow moving from alpha to beta but not down\n const channelMismatch = currentChannel === \"beta\" && hrefChannel === \"alpha\"\n\n if (shouldFetchVersion && !isCustomChannel && !channelMismatch) {\n tag = hrefTag\n break\n }\n\n const isNextPreRelease = hrefChannel && hrefChannel === currentChannel\n if (isNextPreRelease) {\n tag = hrefTag\n break\n }\n }\n } else {\n tag = await this.getLatestTagName(cancellationToken)\n for (const element of feed.getElements(\"entry\")) {\n // noinspection TypeScriptValidateJSTypes\n if (hrefRegExp.exec(element.element(\"link\").attribute(\"href\"))![1] === tag) {\n latestRelease = element\n break\n }\n }\n }\n } catch (e) {\n throw newError(`Cannot parse releases feed: ${e.stack || e.message},\\nXML:\\n${feedXml}`, \"ERR_UPDATER_INVALID_RELEASE_FEED\")\n }\n\n if (tag == null) {\n throw newError(`No published versions on GitHub`, \"ERR_UPDATER_NO_PUBLISHED_VERSIONS\")\n }\n\n let rawData: string\n let channelFile = \"\"\n let channelFileUrl: any = \"\"\n const fetchData = async (channelName: string) => {\n channelFile = getChannelFilename(channelName)\n channelFileUrl = newUrlFromBase(this.getBaseDownloadPath(String(tag), channelFile), this.baseUrl)\n const requestOptions = this.createRequestOptions(channelFileUrl)\n try {\n return (await this.executor.request(requestOptions, cancellationToken))!\n } catch (e) {\n if (e instanceof HttpError && e.statusCode === 404) {\n throw newError(`Cannot find ${channelFile} in the latest release artifacts (${channelFileUrl}): ${e.stack || e.message}`, \"ERR_UPDATER_CHANNEL_FILE_NOT_FOUND\")\n }\n throw e\n }\n }\n\n try {\n const channel = this.updater.allowPrerelease ? this.getCustomChannelName(String(semver.prerelease(tag)?.[0] || \"latest\")) : this.getDefaultChannelName()\n rawData = await fetchData(channel)\n } catch (e) {\n if (this.updater.allowPrerelease) {\n // Allow fallback to `latest.yml`\n rawData = await fetchData(this.getDefaultChannelName())\n } else {\n throw e\n }\n }\n\n const result = parseUpdateInfo(rawData, channelFile, channelFileUrl)\n if (result.releaseName == null) {\n result.releaseName = latestRelease.elementValueOrEmpty(\"title\")\n }\n\n if (result.releaseNotes == null) {\n result.releaseNotes = computeReleaseNotes(this.updater.currentVersion, this.updater.fullChangelog, feed, latestRelease)\n }\n return {\n tag: tag,\n ...result,\n }\n }\n\n private async getLatestTagName(cancellationToken: CancellationToken): Promise<string | null> {\n const options = this.options\n // do not use API for GitHub to avoid limit, only for custom host or GitHub Enterprise\n const url =\n options.host == null || options.host === \"github.com\"\n ? newUrlFromBase(`${this.basePath}/latest`, this.baseUrl)\n : new URL(`${this.computeGithubBasePath(`/repos/${options.owner}/${options.repo}/releases`)}/latest`, this.baseApiUrl)\n try {\n const rawData = await this.httpRequest(url, { Accept: \"application/json\" }, cancellationToken)\n if (rawData == null) {\n return null\n }\n\n const releaseInfo: GithubReleaseInfo = JSON.parse(rawData)\n return releaseInfo.tag_name\n } catch (e) {\n throw newError(`Unable to find latest version on GitHub (${url}), please ensure a production release exists: ${e.stack || e.message}`, \"ERR_UPDATER_LATEST_VERSION_NOT_FOUND\")\n }\n }\n\n private get basePath(): string {\n return `/${this.options.owner}/${this.options.repo}/releases`\n }\n\n resolveFiles(updateInfo: GithubUpdateInfo): Array<ResolvedUpdateFileInfo> {\n // still replace space to - due to backward compatibility\n return resolveFiles(updateInfo, this.baseUrl, p => this.getBaseDownloadPath(updateInfo.tag, p.replace(/ /g, \"-\")))\n }\n\n private getBaseDownloadPath(tag: string, fileName: string): string {\n return `${this.basePath}/download/${tag}/${fileName}`\n }\n}\n\ninterface GithubReleaseInfo {\n readonly tag_name: string\n}\n\nfunction getNoteValue(parent: XElement): string {\n const result = parent.elementValueOrEmpty(\"content\")\n // GitHub reports empty notes as <content>No content.</content>\n return result === \"No content.\" ? \"\" : result\n}\n\nexport function computeReleaseNotes(currentVersion: semver.SemVer, isFullChangelog: boolean, feed: XElement, latestRelease: any): string | Array<ReleaseNoteInfo> | null {\n if (!isFullChangelog) {\n return getNoteValue(latestRelease)\n }\n\n const releaseNotes: Array<ReleaseNoteInfo> = []\n for (const release of feed.getElements(\"entry\")) {\n // noinspection TypeScriptValidateJSTypes\n const versionRelease = /\\/tag\\/v?([^/]+)$/.exec(release.element(\"link\").attribute(\"href\"))![1]\n if (semver.lt(currentVersion, versionRelease)) {\n releaseNotes.push({\n version: versionRelease,\n note: getNoteValue(release),\n })\n }\n }\n return releaseNotes.sort((a, b) => semver.rcompare(a.version, b.version))\n}\n"]}
@@ -37,7 +37,7 @@ function verifySignature(publisherNames, unescapedTempUpdateFile, logger) {
37
37
  "-InputFormat",
38
38
  "None",
39
39
  "-Command",
40
- `Get-AuthenticodeSignature '${tempUpdateFile}' | ConvertTo-Json -Compress | ForEach-Object { [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($_)) }`,
40
+ `Get-AuthenticodeSignature -LiteralPath '${tempUpdateFile}' | ConvertTo-Json -Compress | ForEach-Object { [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($_)) }`,
41
41
  ], {
42
42
  timeout: 20 * 1000,
43
43
  }, (error, stdout, stderr) => {
@@ -49,10 +49,25 @@ function verifySignature(publisherNames, unescapedTempUpdateFile, logger) {
49
49
  }
50
50
  const data = parseOut(Buffer.from(stdout, "base64").toString("utf-8"));
51
51
  if (data.Status === 0) {
52
- const name = builder_util_runtime_1.parseDn(data.SignerCertificate.Subject).get("CN");
53
- if (publisherNames.includes(name)) {
54
- resolve(null);
55
- return;
52
+ const subject = builder_util_runtime_1.parseDn(data.SignerCertificate.Subject);
53
+ let match = false;
54
+ for (const name of publisherNames) {
55
+ const dn = builder_util_runtime_1.parseDn(name);
56
+ if (dn.size) {
57
+ // if we have a full DN, compare all values
58
+ const allKeys = Array.from(dn.keys());
59
+ match = allKeys.every(key => {
60
+ return dn.get(key) === subject.get(key);
61
+ });
62
+ }
63
+ else if (name === subject.get("CN")) {
64
+ logger.warn(`Signature validated using only CN ${name}. Please add your full Distinguished Name (DN) to publisherNames configuration`);
65
+ match = true;
66
+ }
67
+ if (match) {
68
+ resolve(null);
69
+ return;
70
+ }
56
71
  }
57
72
  }
58
73
  const result = `publisherNames: ${publisherNames.join(" | ")}, raw info: ` + JSON.stringify(data, (name, value) => (name === "RawData" ? undefined : value), 2);
@@ -60,7 +75,7 @@ function verifySignature(publisherNames, unescapedTempUpdateFile, logger) {
60
75
  resolve(result);
61
76
  }
62
77
  catch (e) {
63
- logger.warn(`Cannot execute Get-AuthenticodeSignature: ${error}. Ignoring signature validation due to unknown error.`);
78
+ handleError(logger, e, null);
64
79
  resolve(null);
65
80
  return;
66
81
  }
@@ -101,8 +116,7 @@ function handleError(logger, error, stderr) {
101
116
  throw error;
102
117
  }
103
118
  if (stderr) {
104
- logger.warn(`Cannot execute Get-AuthenticodeSignature, stderr: ${stderr}. Ignoring signature validation due to unknown stderr.`);
105
- return;
119
+ throw new Error(`Cannot execute Get-AuthenticodeSignature, stderr: ${stderr}. Failing signature validation due to unknown stderr.`);
106
120
  }
107
121
  }
108
122
  function isOldWin6() {
@@ -1 +1 @@
1
- {"version":3,"file":"windowsExecutableCodeSignatureVerifier.js","sourceRoot":"","sources":["../src/windowsExecutableCodeSignatureVerifier.ts"],"names":[],"mappings":"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","sourcesContent":["import { parseDn } from \"builder-util-runtime\"\nimport { execFile, execFileSync } from \"child_process\"\nimport * as os from \"os\"\nimport { Logger } from \"./main\"\n\n// $certificateInfo = (Get-AuthenticodeSignature 'xxx\\yyy.exe'\n// | where {$_.Status.Equals([System.Management.Automation.SignatureStatus]::Valid) -and $_.SignerCertificate.Subject.Contains(\"CN=siemens.com\")})\n// | Out-String ; if ($certificateInfo) { exit 0 } else { exit 1 }\nexport function verifySignature(publisherNames: Array<string>, unescapedTempUpdateFile: string, logger: Logger): Promise<string | null> {\n return new Promise<string | null>(resolve => {\n // Escape quotes and backticks in filenames to prevent user from breaking the\n // arguments and perform a remote command injection.\n //\n // Consider example powershell command:\n // ```powershell\n // Get-AuthenticodeSignature 'C:\\\\path\\\\my-bad-';calc;'filename.exe'\n // ```\n // The above would work expected and find the file name, however, it will also execute `;calc;`\n // command and start the calculator app.\n //\n // From Powershell quoting rules:\n // https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_quoting_rules?view=powershell-7\n // * Double quotes `\"` are treated literally within single-quoted strings;\n // * Single quotes can be escaped by doubling them: 'don''t' -> don't;\n // * Backticks can be escaped by doubling them: 'A backtick (``) character';\n //\n // Also note that at this point the file has already been written to the disk, thus we are\n // guaranteed that the path will not contain any illegal characters like <>:\"/\\|?*\n // https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file\n const tempUpdateFile = unescapedTempUpdateFile.replace(/'/g, \"''\").replace(/`/g, \"``\")\n\n // https://github.com/electron-userland/electron-builder/issues/2421\n // https://github.com/electron-userland/electron-builder/issues/2535\n execFile(\n \"powershell.exe\",\n [\n \"-NoProfile\",\n \"-NonInteractive\",\n \"-InputFormat\",\n \"None\",\n \"-Command\",\n `Get-AuthenticodeSignature '${tempUpdateFile}' | ConvertTo-Json -Compress | ForEach-Object { [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($_)) }`,\n ],\n {\n timeout: 20 * 1000,\n },\n (error, stdout, stderr) => {\n try {\n if (error != null || stderr) {\n handleError(logger, error, stderr)\n resolve(null)\n return\n }\n\n const data = parseOut(Buffer.from(stdout, \"base64\").toString(\"utf-8\"))\n if (data.Status === 0) {\n const name = parseDn(data.SignerCertificate.Subject).get(\"CN\")!\n if (publisherNames.includes(name)) {\n resolve(null)\n return\n }\n }\n\n const result = `publisherNames: ${publisherNames.join(\" | \")}, raw info: ` + JSON.stringify(data, (name, value) => (name === \"RawData\" ? undefined : value), 2)\n logger.warn(`Sign verification failed, installer signed with incorrect certificate: ${result}`)\n resolve(result)\n } catch (e) {\n logger.warn(`Cannot execute Get-AuthenticodeSignature: ${error}. Ignoring signature validation due to unknown error.`)\n resolve(null)\n return\n }\n }\n )\n })\n}\n\nfunction parseOut(out: string): any {\n const data = JSON.parse(out)\n delete data.PrivateKey\n delete data.IsOSBinary\n delete data.SignatureType\n const signerCertificate = data.SignerCertificate\n if (signerCertificate != null) {\n delete signerCertificate.Archived\n delete signerCertificate.Extensions\n delete signerCertificate.Handle\n delete signerCertificate.HasPrivateKey\n // duplicates data.SignerCertificate (contains RawData)\n delete signerCertificate.SubjectName\n }\n delete data.Path\n return data\n}\n\nfunction handleError(logger: Logger, error: Error | null, stderr: string | null): void {\n if (isOldWin6()) {\n logger.warn(\n `Cannot execute Get-AuthenticodeSignature: ${error || stderr}. Ignoring signature validation due to unsupported powershell version. Please upgrade to powershell 3 or higher.`\n )\n return\n }\n\n try {\n execFileSync(\"powershell.exe\", [\"-NoProfile\", \"-NonInteractive\", \"-Command\", \"ConvertTo-Json test\"], { timeout: 10 * 1000 } as any)\n } catch (testError) {\n logger.warn(\n `Cannot execute ConvertTo-Json: ${testError.message}. Ignoring signature validation due to unsupported powershell version. Please upgrade to powershell 3 or higher.`\n )\n return\n }\n\n if (error != null) {\n throw error\n }\n\n if (stderr) {\n logger.warn(`Cannot execute Get-AuthenticodeSignature, stderr: ${stderr}. Ignoring signature validation due to unknown stderr.`)\n return\n }\n}\n\nfunction isOldWin6(): boolean {\n const winVersion = os.release()\n return winVersion.startsWith(\"6.\") && !winVersion.startsWith(\"6.3\")\n}\n"]}
1
+ {"version":3,"file":"windowsExecutableCodeSignatureVerifier.js","sourceRoot":"","sources":["../src/windowsExecutableCodeSignatureVerifier.ts"],"names":[],"mappings":"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","sourcesContent":["import { parseDn } from \"builder-util-runtime\"\nimport { execFile, execFileSync } from \"child_process\"\nimport * as os from \"os\"\nimport { Logger } from \"./main\"\n\n// $certificateInfo = (Get-AuthenticodeSignature 'xxx\\yyy.exe'\n// | where {$_.Status.Equals([System.Management.Automation.SignatureStatus]::Valid) -and $_.SignerCertificate.Subject.Contains(\"CN=siemens.com\")})\n// | Out-String ; if ($certificateInfo) { exit 0 } else { exit 1 }\nexport function verifySignature(publisherNames: Array<string>, unescapedTempUpdateFile: string, logger: Logger): Promise<string | null> {\n return new Promise<string | null>(resolve => {\n // Escape quotes and backticks in filenames to prevent user from breaking the\n // arguments and perform a remote command injection.\n //\n // Consider example powershell command:\n // ```powershell\n // Get-AuthenticodeSignature 'C:\\\\path\\\\my-bad-';calc;'filename.exe'\n // ```\n // The above would work expected and find the file name, however, it will also execute `;calc;`\n // command and start the calculator app.\n //\n // From Powershell quoting rules:\n // https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_quoting_rules?view=powershell-7\n // * Double quotes `\"` are treated literally within single-quoted strings;\n // * Single quotes can be escaped by doubling them: 'don''t' -> don't;\n // * Backticks can be escaped by doubling them: 'A backtick (``) character';\n //\n // Also note that at this point the file has already been written to the disk, thus we are\n // guaranteed that the path will not contain any illegal characters like <>:\"/\\|?*\n // https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file\n const tempUpdateFile = unescapedTempUpdateFile.replace(/'/g, \"''\").replace(/`/g, \"``\")\n\n // https://github.com/electron-userland/electron-builder/issues/2421\n // https://github.com/electron-userland/electron-builder/issues/2535\n execFile(\n \"powershell.exe\",\n [\n \"-NoProfile\",\n \"-NonInteractive\",\n \"-InputFormat\",\n \"None\",\n \"-Command\",\n `Get-AuthenticodeSignature -LiteralPath '${tempUpdateFile}' | ConvertTo-Json -Compress | ForEach-Object { [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($_)) }`,\n ],\n {\n timeout: 20 * 1000,\n },\n (error, stdout, stderr) => {\n try {\n if (error != null || stderr) {\n handleError(logger, error, stderr)\n resolve(null)\n return\n }\n\n const data = parseOut(Buffer.from(stdout, \"base64\").toString(\"utf-8\"))\n if (data.Status === 0) {\n const subject = parseDn(data.SignerCertificate.Subject)\n let match = false\n for (const name of publisherNames) {\n const dn = parseDn(name)\n if (dn.size) {\n // if we have a full DN, compare all values\n const allKeys = Array.from(dn.keys())\n match = allKeys.every(key => {\n return dn.get(key) === subject.get(key)\n })\n } else if (name === subject.get(\"CN\")!) {\n logger.warn(`Signature validated using only CN ${name}. Please add your full Distinguished Name (DN) to publisherNames configuration`)\n match = true\n }\n if (match) {\n resolve(null)\n return\n }\n }\n }\n\n const result = `publisherNames: ${publisherNames.join(\" | \")}, raw info: ` + JSON.stringify(data, (name, value) => (name === \"RawData\" ? undefined : value), 2)\n logger.warn(`Sign verification failed, installer signed with incorrect certificate: ${result}`)\n resolve(result)\n } catch (e: any) {\n handleError(logger, e, null)\n resolve(null)\n return\n }\n }\n )\n })\n}\n\nfunction parseOut(out: string): any {\n const data = JSON.parse(out)\n delete data.PrivateKey\n delete data.IsOSBinary\n delete data.SignatureType\n const signerCertificate = data.SignerCertificate\n if (signerCertificate != null) {\n delete signerCertificate.Archived\n delete signerCertificate.Extensions\n delete signerCertificate.Handle\n delete signerCertificate.HasPrivateKey\n // duplicates data.SignerCertificate (contains RawData)\n delete signerCertificate.SubjectName\n }\n delete data.Path\n return data\n}\n\nfunction handleError(logger: Logger, error: Error | null, stderr: string | null): void {\n if (isOldWin6()) {\n logger.warn(\n `Cannot execute Get-AuthenticodeSignature: ${error || stderr}. Ignoring signature validation due to unsupported powershell version. Please upgrade to powershell 3 or higher.`\n )\n return\n }\n\n try {\n execFileSync(\"powershell.exe\", [\"-NoProfile\", \"-NonInteractive\", \"-Command\", \"ConvertTo-Json test\"], { timeout: 10 * 1000 } as any)\n } catch (testError: any) {\n logger.warn(\n `Cannot execute ConvertTo-Json: ${testError.message}. Ignoring signature validation due to unsupported powershell version. Please upgrade to powershell 3 or higher.`\n )\n return\n }\n\n if (error != null) {\n throw error\n }\n\n if (stderr) {\n throw new Error(`Cannot execute Get-AuthenticodeSignature, stderr: ${stderr}. Failing signature validation due to unknown stderr.`)\n }\n}\n\nfunction isOldWin6(): boolean {\n const winVersion = os.release()\n return winVersion.startsWith(\"6.\") && !winVersion.startsWith(\"6.3\")\n}\n"]}
package/package.json CHANGED
@@ -1,38 +1,39 @@
1
1
  {
2
- "name": "electron-updater",
3
- "version": "4.6.4",
4
- "description": "Cross platform updater for electron applications",
5
- "main": "out/main.js",
6
- "author": "Vladimir Krivosheev",
7
- "license": "MIT",
8
- "repository": {
9
- "type": "git",
10
- "url": "git+https://github.com/electron-userland/electron-builder.git",
11
- "directory": "packages/electron-updater"
12
- },
13
- "bugs": "https://github.com/electron-userland/electron-builder/issues",
14
- "homepage": "https://github.com/electron-userland/electron-builder",
15
- "files": [
16
- "out"
17
- ],
18
- "dependencies": {
19
- "@types/semver": "^7.3.6",
20
- "builder-util-runtime": "8.9.2",
21
- "fs-extra": "^10.0.0",
22
- "js-yaml": "^4.1.0",
23
- "lazy-val": "^1.0.5",
24
- "lodash.escaperegexp": "^4.1.2",
25
- "lodash.isequal": "^4.5.0",
26
- "semver": "^7.3.5"
27
- },
28
- "devDependencies": {
29
- "@types/fs-extra": "9.0.12",
30
- "@types/js-yaml": "4.0.3",
31
- "@types/lodash.escaperegexp": "4.1.6",
32
- "@types/lodash.isequal": "4.5.5"
33
- },
34
- "typings": "./out/main.d.ts",
35
- "publishConfig": {
36
- "tag": "next"
37
- }
38
- }
2
+ "name": "electron-updater",
3
+ "version": "5.0.0-alpha.2",
4
+ "description": "Cross platform updater for electron applications",
5
+ "main": "out/main.js",
6
+ "author": "Vladimir Krivosheev",
7
+ "license": "MIT",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/electron-userland/electron-builder.git",
11
+ "directory": "packages/electron-updater"
12
+ },
13
+ "bugs": "https://github.com/electron-userland/electron-builder/issues",
14
+ "homepage": "https://github.com/electron-userland/electron-builder",
15
+ "files": [
16
+ "out"
17
+ ],
18
+ "dependencies": {
19
+ "@types/semver": "^7.3.6",
20
+ "builder-util-runtime": "9.0.0-alpha.0",
21
+ "fs-extra": "^10.0.0",
22
+ "js-yaml": "^4.1.0",
23
+ "lazy-val": "^1.0.5",
24
+ "lodash.escaperegexp": "^4.1.2",
25
+ "lodash.isequal": "^4.5.0",
26
+ "semver": "^7.3.5"
27
+ },
28
+ "devDependencies": {
29
+ "@types/fs-extra": "9.0.13",
30
+ "@types/js-yaml": "4.0.3",
31
+ "@types/lodash.escaperegexp": "4.1.6",
32
+ "@types/lodash.isequal": "4.5.5"
33
+ },
34
+ "typings": "./out/main.d.ts",
35
+ "publishConfig": {
36
+ "tag": "next"
37
+ },
38
+ "readme": "# electron-updater\n\nThis module allows you to automatically update your application. You only need to install this module and write two lines of code!\nTo publish your updates you just need simple file hosting, it does not require a dedicated server.\n\nSee [Auto Update](https://electron.build/auto-update) for more information.\n\nSupported OS:\n - macOS ([Squirrel.Mac](https://github.com/Squirrel/Squirrel.Mac)).\n - Windows (NSIS).\n - Linux (AppImage).\n\n## Credits\n\nThanks to [Evolve Labs](https://www.evolvehq.com) for donating the npm package name.\n"
39
+ }