@openmrs/esm-navigation 10.0.1-pre.5501 → 10.0.1-pre.5505

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,3 +1,3 @@
1
- [0] Successfully compiled: 8 files with swc (192.85ms)
1
+ [0] Successfully compiled: 8 files with swc (295.7ms)
2
2
  [0] swc --strip-leading-paths src -d dist exited with code 0
3
3
  [1] tsc --project tsconfig.build.json exited with code 0
@@ -1 +1 @@
1
- {"version":3,"file":"interpolate-string.d.ts","sourceRoot":"","sources":["../../src/navigation/interpolate-string.ts"],"names":[],"mappings":"AAAA,mCAAmC;AAMnC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,GAAG,MAAM,CAOrG;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,GAAG,MAAM,CAG7F"}
1
+ {"version":3,"file":"interpolate-string.d.ts","sourceRoot":"","sources":["../../src/navigation/interpolate-string.ts"],"names":[],"mappings":"AAAA,mCAAmC;AAMnC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,GAAG,MAAM,CAWrG;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,GAAG,MAAM,CAG7F"}
@@ -32,11 +32,14 @@
32
32
  * @returns The interpolated string with all template parameters replaced.
33
33
  */ export function interpolateUrl(template, additionalParams) {
34
34
  const openmrsSpaBase = trimTrailingSlash(window.getOpenmrsSpaBase());
35
- return interpolateString(template, {
35
+ const result = interpolateString(template, {
36
36
  openmrsBase: window.openmrsBase,
37
37
  openmrsSpaBase: openmrsSpaBase,
38
38
  ...additionalParams
39
- }).replace(/^\/\//, '/'); // remove extra initial slash if present
39
+ });
40
+ // remove a additional / at the beginning if added by interpolation
41
+ // e.g. /${openmrsSpaBase} -> //openmrs/spa
42
+ return template.startsWith('//') ? result : result.replace(/^\/\//, '/');
40
43
  }
41
44
  /**
42
45
  * Interpolates values of `params` into the `template` string.
@@ -1 +1 @@
1
- {"version":3,"file":"navigate.d.ts","sourceRoot":"","sources":["../../src/navigation/navigate.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,cAAc,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAC;AAEvD,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,QAAQ,CAAC,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,eAAe,GAAG,IAAI,CAUtE"}
1
+ {"version":3,"file":"navigate.d.ts","sourceRoot":"","sources":["../../src/navigation/navigate.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,cAAc,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAC;AAEvD,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,QAAQ,CAAC,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,eAAe,GAAG,IAAI,CActE"}
@@ -1,4 +1,5 @@
1
1
  /** @module @category Navigation */ import { navigateToUrl } from "single-spa";
2
+ import { getCoreTranslation } from "@openmrs/esm-translations";
2
3
  import { interpolateUrl } from "./interpolate-string.js";
3
4
  function trimTrailingSlash(str) {
4
5
  return str.replace(/\/$/, '');
@@ -37,8 +38,12 @@ function trimTrailingSlash(str) {
37
38
  */ export function navigate({ to, templateParams }) {
38
39
  const openmrsSpaBase = trimTrailingSlash(window.getOpenmrsSpaBase());
39
40
  const target = interpolateUrl(to, templateParams).replace(window.location.origin, '');
40
- const isSpaPath = target.startsWith(openmrsSpaBase);
41
- if (isSpaPath) {
41
+ const targetURL = new URL(target, window.location.origin);
42
+ if (targetURL.protocol != 'http:' && targetURL.protocol != 'https:') {
43
+ console.error(`Received request to navigate to non-http URL: ${targetURL.toString()}`);
44
+ throw new Error(getCoreTranslation('navigateNonHttp'));
45
+ }
46
+ if (target.startsWith(openmrsSpaBase)) {
42
47
  navigateToUrl(target);
43
48
  } else {
44
49
  window.location.assign(target);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmrs/esm-navigation",
3
- "version": "10.0.1-pre.5501",
3
+ "version": "10.0.1-pre.5505",
4
4
  "license": "MPL-2.0",
5
5
  "description": "OpenMRS library providing tools for breadcrumbs, navigation, and history.",
6
6
  "type": "module",
@@ -53,10 +53,12 @@
53
53
  "path-to-regexp": "^8.3.0"
54
54
  },
55
55
  "peerDependencies": {
56
- "@openmrs/esm-state": "^10.0.1-pre.5501"
56
+ "@openmrs/esm-state": "^10.0.1-pre.5505",
57
+ "@openmrs/esm-translations": "^10.0.1-pre.5505"
57
58
  },
58
59
  "devDependencies": {
59
- "@openmrs/esm-state": "10.0.1-pre.5501",
60
+ "@openmrs/esm-state": "10.0.1-pre.5505",
61
+ "@openmrs/esm-translations": "10.0.1-pre.5505",
60
62
  "@swc/cli": "0.8.1",
61
63
  "@swc/core": "1.15.21",
62
64
  "@vitest/coverage-v8": "^4.1.2",
@@ -18,6 +18,10 @@ describe('interpolateUrl', () => {
18
18
  const result = interpolateUrl('test ok');
19
19
  expect(result).toBe('test ok');
20
20
  });
21
+
22
+ it('preserves protocol-relative URLs', () => {
23
+ expect(interpolateUrl('//evil.example/${openmrsSpaBase}')).toBe('//evil.example//openmrs/spa');
24
+ });
21
25
  });
22
26
 
23
27
  describe('interpolateString', () => {
@@ -36,11 +36,15 @@ function trimTrailingSlash(str: string) {
36
36
  */
37
37
  export function interpolateUrl(template: string, additionalParams?: { [key: string]: string }): string {
38
38
  const openmrsSpaBase = trimTrailingSlash(window.getOpenmrsSpaBase());
39
- return interpolateString(template, {
39
+ const result = interpolateString(template, {
40
40
  openmrsBase: window.openmrsBase,
41
41
  openmrsSpaBase: openmrsSpaBase,
42
42
  ...additionalParams,
43
- }).replace(/^\/\//, '/'); // remove extra initial slash if present
43
+ });
44
+
45
+ // remove a additional / at the beginning if added by interpolation
46
+ // e.g. /${openmrsSpaBase} -> //openmrs/spa
47
+ return template.startsWith('//') ? result : result.replace(/^\/\//, '/');
44
48
  }
45
49
 
46
50
  /**
@@ -66,4 +66,18 @@ describe('navigate', () => {
66
66
  expect(navigateToUrl).toHaveBeenCalledWith('/openmrs/spa/qux/page');
67
67
  expect(window.location.assign).not.toHaveBeenCalled();
68
68
  });
69
+
70
+ it('rejects non-http URLs', () => {
71
+ expect(() => navigate({ to: 'ftp://${openmrsSpaBase}/qux/page' })).toThrow(Error);
72
+ expect(() => navigate({ to: 'javascript:alert(1)' })).toThrow(Error);
73
+ expect(navigateToUrl).not.toHaveBeenCalled();
74
+ expect(window.location.assign).not.toHaveBeenCalled();
75
+ });
76
+
77
+ it('rejects non-http URLs when the SPA base is the root', () => {
78
+ const getOpenmrsSpaBase = vi.spyOn(window, 'getOpenmrsSpaBase').mockReturnValue('/');
79
+ expect(() => navigate({ to: 'javascript:alert(1)' })).toThrow(Error);
80
+ expect(navigateToUrl).not.toHaveBeenCalled();
81
+ getOpenmrsSpaBase.mockRestore();
82
+ });
69
83
  });
@@ -1,5 +1,6 @@
1
1
  /** @module @category Navigation */
2
2
  import { navigateToUrl } from 'single-spa';
3
+ import { getCoreTranslation } from '@openmrs/esm-translations';
3
4
  import { interpolateUrl } from './interpolate-string';
4
5
  import type {} from '@openmrs/esm-globals';
5
6
 
@@ -49,9 +50,13 @@ export interface NavigateOptions {
49
50
  export function navigate({ to, templateParams }: NavigateOptions): void {
50
51
  const openmrsSpaBase = trimTrailingSlash(window.getOpenmrsSpaBase());
51
52
  const target = interpolateUrl(to, templateParams).replace(window.location.origin, '');
52
- const isSpaPath = target.startsWith(openmrsSpaBase);
53
+ const targetURL = new URL(target, window.location.origin);
54
+ if (targetURL.protocol != 'http:' && targetURL.protocol != 'https:') {
55
+ console.error(`Received request to navigate to non-http URL: ${targetURL.toString()}`);
56
+ throw new Error(getCoreTranslation('navigateNonHttp'));
57
+ }
53
58
 
54
- if (isSpaPath) {
59
+ if (target.startsWith(openmrsSpaBase)) {
55
60
  navigateToUrl(target);
56
61
  } else {
57
62
  window.location.assign(target);