@forge/api 2.18.6-next.3 → 2.19.0-next.4

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @forge/api
2
2
 
3
+ ## 2.19.0-next.4
4
+
5
+ ### Minor Changes
6
+
7
+ - 347359b: Better protection against path traversal attacks in product requests
8
+
3
9
  ## 2.18.6-next.3
4
10
 
5
11
  ### Patch Changes
package/out/safeUrl.d.ts CHANGED
@@ -3,7 +3,7 @@ export type Route = {
3
3
  };
4
4
  export declare function isRoute(x: unknown): x is Route;
5
5
  export declare function routeFromAbsolute(absolutePath: string): Route;
6
- export declare function route(path: TemplateStringsArray, ...parameters: (string | number | URLSearchParams | Route)[]): Route;
6
+ export declare function route(template: TemplateStringsArray, ...parameters: (string | number | URLSearchParams | Route)[]): Route;
7
7
  export declare function requireSafeUrl(url: unknown): Route;
8
8
  export declare function assumeTrustedRoute(route: string): Route;
9
9
  //# sourceMappingURL=safeUrl.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"safeUrl.d.ts","sourceRoot":"","sources":["../src/safeUrl.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,KAAK,GAAG;IAClB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,CAAC;AAcF,wBAAgB,OAAO,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,KAAK,CAE9C;AAED,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,KAAK,CAG7D;AAED,wBAAgB,KAAK,CAAC,IAAI,EAAE,oBAAoB,EAAE,GAAG,UAAU,EAAE,CAAC,MAAM,GAAG,MAAM,GAAG,eAAe,GAAG,KAAK,CAAC,EAAE,GAAG,KAAK,CAqBrH;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,KAAK,CASlD;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,CAEvD"}
1
+ {"version":3,"file":"safeUrl.d.ts","sourceRoot":"","sources":["../src/safeUrl.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,KAAK,GAAG;IAClB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,CAAC;AAcF,wBAAgB,OAAO,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,KAAK,CAE9C;AAED,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,KAAK,CAG7D;AAkCD,wBAAgB,KAAK,CACnB,QAAQ,EAAE,oBAAoB,EAC9B,GAAG,UAAU,EAAE,CAAC,MAAM,GAAG,MAAM,GAAG,eAAe,GAAG,KAAK,CAAC,EAAE,GAC3D,KAAK,CAoBP;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,KAAK,CASlD;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,CAEvD"}
package/out/safeUrl.js CHANGED
@@ -21,27 +21,46 @@ function routeFromAbsolute(absolutePath) {
21
21
  return assumeTrustedRoute(`${absoluteURL.pathname}${absoluteURL.search}`);
22
22
  }
23
23
  exports.routeFromAbsolute = routeFromAbsolute;
24
- function route(path, ...parameters) {
25
- let fullPath = '';
26
- for (let i = 0; i < path.length; i++) {
27
- fullPath += path[i];
28
- if (i < parameters.length) {
29
- const parameter = parameters[i];
30
- if (parameter === '..') {
31
- throw new Error('Disallowing path traversal attempt');
24
+ const DOUBLE_DOT = ['..', '.%2e', '%2e.', '%2e%2e', '.%2E', '%2E.', '%2E%2e'];
25
+ const ENDS_PATH = ['?', '#'];
26
+ function containsOneOf(needles, haystack) {
27
+ return needles.some((needle) => haystack.includes(needle));
28
+ }
29
+ function escapeParameter(parameter, mode) {
30
+ switch (mode) {
31
+ case 'path':
32
+ const parameterString = isRoute(parameter) ? parameter.value : String(parameter);
33
+ if (containsOneOf(DOUBLE_DOT, parameterString) || containsOneOf(ENDS_PATH, parameterString)) {
34
+ throw new Error('Disallowing path manipulation attempt');
32
35
  }
33
- else if (isRoute(parameter)) {
34
- fullPath += parameter.value;
36
+ return parameterString;
37
+ case 'query':
38
+ if (isRoute(parameter)) {
39
+ return encodeURIComponent(parameter.value);
35
40
  }
36
41
  else if (parameter instanceof URLSearchParams) {
37
- fullPath += parameter.toString();
42
+ return parameter.toString();
38
43
  }
39
44
  else {
40
- fullPath += encodeURIComponent(parameter);
45
+ return encodeURIComponent(parameter);
41
46
  }
47
+ }
48
+ }
49
+ function route(template, ...parameters) {
50
+ let mode = 'path';
51
+ let result = '';
52
+ for (let i = 0; i < template.length; i++) {
53
+ const templateFragment = template[i];
54
+ if (containsOneOf(ENDS_PATH, templateFragment)) {
55
+ mode = 'query';
56
+ }
57
+ result += templateFragment;
58
+ if (i >= parameters.length) {
59
+ break;
42
60
  }
61
+ result += escapeParameter(parameters[i], mode);
43
62
  }
44
- return new ReadonlyRoute(fullPath);
63
+ return new ReadonlyRoute(result);
45
64
  }
46
65
  exports.route = route;
47
66
  function requireSafeUrl(url) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forge/api",
3
- "version": "2.18.6-next.3",
3
+ "version": "2.19.0-next.4",
4
4
  "description": "Forge API methods",
5
5
  "author": "Atlassian",
6
6
  "license": "UNLICENSED",
@@ -12,7 +12,7 @@
12
12
  "compile": "tsc -b -v"
13
13
  },
14
14
  "devDependencies": {
15
- "@forge/runtime": "5.0.1-next.4",
15
+ "@forge/runtime": "5.0.1-next.5",
16
16
  "@types/node": "14.18.57",
17
17
  "jest-matcher-specific-error": "^1.0.0",
18
18
  "nock": "^10.0.6"