extension-create 4.1.18 → 4.1.19

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/README.md CHANGED
@@ -47,7 +47,7 @@ Creates a new extension project with the specified configuration.
47
47
 
48
48
  - `projectName` (string, required) - The name of your extension project
49
49
  - `options` (object) - Configuration options
50
- - `template` (string, optional) - Template name or URL. Defaults to `'javascript'` (`init` is an alias)
50
+ - `template` (string, optional) - Template name or URL. Defaults to `'javascript'` (`init` is an alias). A URL must use `https://`. A plain `http://` URL, and an `http://` value in `EXTENSION_CREATE_TEMPLATE_URL`, fails unless `EXTENSION_ALLOW_HTTP_TEMPLATE=true` is set
51
51
  - `install` (boolean, optional) - Whether to install dependencies after scaffolding. Defaults to `false` so project creation is fast and users see the familiar `npm install` output on their own.
52
52
  - `cliVersion` (string, optional) - CLI version for package.json
53
53
 
@@ -11,6 +11,7 @@ export declare function writingTypeDefinitionsError(error: unknown): string;
11
11
  export declare function installingFromTemplate(projectName: string, templateName: string): string;
12
12
  export declare function usingTemplate(templateName: string, source: string): string;
13
13
  export declare function installingFromTemplateError(template: string, error: unknown): string;
14
+ export declare function templateUrlNotHttps(url: string): string;
14
15
  export declare function templateFetchTimedOut(templateName: string, ms: number): string;
15
16
  export declare function templateNotFoundInCatalog(templateName: string, error?: unknown): string;
16
17
  export declare function templateDownloadFailed(templateName: string, error: unknown): string;
package/dist/module.cjs CHANGED
@@ -252,6 +252,9 @@ function usingTemplate(templateName, source) {
252
252
  function installingFromTemplateError(template, error) {
253
253
  return `${prefix('error')} Couldn't find the template ${external_pintor_default().blue(template)}.\n${fmt.label('REASON')} ${fmt.val(fmt.truncate(String(error)))}\n${external_pintor_default().red('Choose a template name from')} ${external_pintor_default().blue('extension create --help')}${external_pintor_default().red(', or pass a GitHub URL.')}`;
254
254
  }
255
+ function templateUrlNotHttps(url) {
256
+ return `${prefix('error')} Can't download a template over plain HTTP.\n${fmt.label('GOT')} ${fmt.val(fmt.truncate(url, 120))}\n${external_pintor_default().red('- Use the')} ${external_pintor_default().blue('https://')} ${external_pintor_default().red('address of the template.')}\n${external_pintor_default().red('- Set')} ${external_pintor_default().blue('EXTENSION_ALLOW_HTTP_TEMPLATE=true')} ${external_pintor_default().red('to allow plain HTTP.')}`;
257
+ }
255
258
  function templateFetchTimedOut(templateName, ms) {
256
259
  return `${prefix('error')} Couldn't fetch the template ${external_pintor_default().blue(templateName)} within ${Math.round(ms / 1000)}s.\n${external_pintor_default().red('- Check your network connection.')}\n${external_pintor_default().red('- Set')} ${external_pintor_default().blue('EXTENSION_CREATE_TIMEOUT_MS')} ${external_pintor_default().red('to allow more time.')}`;
257
260
  }
@@ -655,6 +658,29 @@ class TemplateNotFoundError extends Error {
655
658
  if (cause) this.cause = cause;
656
659
  }
657
660
  }
661
+ class InsecureTemplateUrlError extends Error {
662
+ constructor(url){
663
+ super(`template URL is not https: ${url}`), _define_property(this, "url", void 0);
664
+ this.name = 'InsecureTemplateUrlError';
665
+ this.url = url;
666
+ }
667
+ }
668
+ function isRefusedHttpTemplateUrl(url) {
669
+ if ('true' === process.env.EXTENSION_ALLOW_HTTP_TEMPLATE) return false;
670
+ return /^http:\/\//i.test(url);
671
+ }
672
+ function refuseHttpRedirect(options) {
673
+ const target = String(options.href || `${options.protocol || ''}//`);
674
+ if (isRefusedHttpTemplateUrl(target)) throw new InsecureTemplateUrlError(target);
675
+ }
676
+ function findInsecureTemplateUrlError(error) {
677
+ let current = error;
678
+ for(let depth = 0; current && depth < 4; depth++){
679
+ if (current instanceof InsecureTemplateUrlError) return current;
680
+ current = current.cause;
681
+ }
682
+ return null;
683
+ }
658
684
  class TemplateDownloadError extends Error {
659
685
  constructor(templateName, cause){
660
686
  const msg = cause?.message ?? String(cause);
@@ -674,11 +700,13 @@ async function downloadArchive(url, timeoutMs, attempts = 2) {
674
700
  timeout: timeoutMs,
675
701
  headers: {
676
702
  'User-Agent': 'extension-create'
677
- }
703
+ },
704
+ beforeRedirect: refuseHttpRedirect
678
705
  });
679
706
  return Buffer.from(data);
680
707
  } catch (error) {
681
708
  lastError = error;
709
+ if (findInsecureTemplateUrlError(error)) break;
682
710
  const status = error?.response?.status;
683
711
  const retriable = void 0 === status || 429 === status || status >= 500;
684
712
  if (attempt >= attempts || !retriable) break;
@@ -712,6 +740,7 @@ async function extractExamplesTemplateFromZip(zipBuffer, templateName, projectPa
712
740
  async function importFromExamplesCatalog(templateName, projectPath) {
713
741
  const ref = process.env.EXTENSION_CREATE_TEMPLATE_REF || DEFAULT_TEMPLATES_REF;
714
742
  const overrideUrl = process.env.EXTENSION_CREATE_TEMPLATE_URL || void 0;
743
+ if (overrideUrl && isRefusedHttpTemplateUrl(overrideUrl)) throw new InsecureTemplateUrlError(overrideUrl);
715
744
  const urls = resolveCatalogUrls(ref, overrideUrl);
716
745
  let buffer;
717
746
  let source;
@@ -721,6 +750,8 @@ async function importFromExamplesCatalog(templateName, projectPath) {
721
750
  source = candidate;
722
751
  break;
723
752
  } catch (error) {
753
+ const insecure = findInsecureTemplateUrlError(error);
754
+ if (insecure) throw insecure;
724
755
  lastError = error;
725
756
  }
726
757
  if (!buffer || !source) throw new TemplateDownloadError(templateName, lastError);
@@ -884,6 +915,7 @@ async function importExternalTemplate(projectPath, projectName, template, logger
884
915
  } catch {}
885
916
  const ownerGitignore = dirExistedBeforeImport ? await readOwnerGitignore(projectPath) : null;
886
917
  try {
918
+ if (isRefusedHttpTemplateUrl(template)) throw new InsecureTemplateUrlError(template);
887
919
  await promises_namespaceObject.mkdir(projectPath, {
888
920
  recursive: true
889
921
  });
@@ -931,7 +963,8 @@ async function importExternalTemplate(projectPath, projectName, template, logger
931
963
  const { data, headers } = await external_axios_default().get(template, {
932
964
  responseType: 'arraybuffer',
933
965
  maxRedirects: 5,
934
- timeout: NETWORK_TIMEOUT_MS
966
+ timeout: NETWORK_TIMEOUT_MS,
967
+ beforeRedirect: refuseHttpRedirect
935
968
  });
936
969
  const contentType = String(headers?.['content-type'] || '');
937
970
  const looksZip = /zip|octet-stream/i.test(contentType) || template.toLowerCase().endsWith('.zip');
@@ -974,7 +1007,9 @@ async function importExternalTemplate(projectPath, projectName, template, logger
974
1007
  return fallback;
975
1008
  }
976
1009
  }
977
- if (error instanceof TemplateNotFoundError) logger.error(templateNotFoundInCatalog(templateName, error.cause));
1010
+ const insecureUrl = findInsecureTemplateUrlError(error);
1011
+ if (insecureUrl) logger.error(templateUrlNotHttps(insecureUrl.url));
1012
+ else if (error instanceof TemplateNotFoundError) logger.error(templateNotFoundInCatalog(templateName, error.cause));
978
1013
  else if (error instanceof TemplateDownloadError) logger.error(templateDownloadFailed(templateName, error));
979
1014
  else logger.error(installingFromTemplateError(templateName, error));
980
1015
  await cleanupFailedImport(projectPath, ownsProjectDir, preExistingEntries);
@@ -14,6 +14,15 @@ export declare class TemplateNotFoundError extends Error {
14
14
  readonly templateName: string;
15
15
  constructor(templateName: string, cause?: unknown);
16
16
  }
17
+ export declare class InsecureTemplateUrlError extends Error {
18
+ readonly url: string;
19
+ constructor(url: string);
20
+ }
21
+ export declare function isRefusedHttpTemplateUrl(url: string): boolean;
22
+ export declare function refuseHttpRedirect(options: {
23
+ protocol?: string;
24
+ href?: string;
25
+ }): void;
17
26
  export declare class TemplateDownloadError extends Error {
18
27
  readonly templateName: string;
19
28
  constructor(templateName: string, cause: unknown);
@@ -1,5 +1,5 @@
1
1
  {
2
- "createdWith": "extension-create@4.1.17",
2
+ "createdWith": "extension-create@4.1.18",
3
3
  "template": "typescript",
4
4
  "source": "https://codeload.github.com/extension-js/examples/zip/e552f6db6f2862093ba9d7026e0d376f002d95cc",
5
5
  "ref": "e552f6db6f2862093ba9d7026e0d376f002d95cc"
@@ -9,7 +9,7 @@ Packaging your extension is local and free. Submitting the result to a
9
9
  store is what [extension.dev](https://docs.extension.dev/publish/overview?utm_source=store-md)
10
10
  does, and it sponsors Extension.js.
11
11
 
12
- Last updated: 2026-09-14
12
+ Last updated: 2026-09-15
13
13
 
14
14
  ## Listing
15
15
 
@@ -8,7 +8,7 @@
8
8
  "devDependencies": {
9
9
  "@types/chrome": "^0.0.287",
10
10
  "typescript": "7.0.2",
11
- "extension": "^4.1.17"
11
+ "extension": "^4.1.18"
12
12
  },
13
13
  "scripts": {
14
14
  "dev": "extension dev",
package/package.json CHANGED
@@ -25,7 +25,7 @@
25
25
  "templates"
26
26
  ],
27
27
  "name": "extension-create",
28
- "version": "4.1.18",
28
+ "version": "4.1.19",
29
29
  "description": "The standalone extension creation engine for Extension.js",
30
30
  "author": {
31
31
  "name": "Cezar Augusto",