@forge/csp 3.2.1 → 3.2.2-next.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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @forge/csp
2
2
 
3
+ ## 3.2.2-next.0
4
+
5
+ ### Patch Changes
6
+
7
+ - add374d: Change FedRAMP avatar urls to the correct URL
8
+
3
9
  ## 3.2.1
4
10
 
5
11
  ### Patch Changes
@@ -14,73 +14,16 @@ const atlassianImageHosts = {
14
14
  stg: ['https://avatar-management--avatars.us-west-2.staging.public.atl-paas.net', apiGatewayHost['stg']],
15
15
  prod: ['https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net', apiGatewayHost['prod']],
16
16
  'fedramp-stg': [
17
- 'https://avatar-management--avatars.us-east-1.stg.public.atl-paas-us-gov-mod.net',
17
+ 'https://avatar-management--avatars.us-east-1.staging.cdn.atlassian-us-gov-mod.com',
18
18
  apiGatewayHost['fedramp-stg']
19
19
  ],
20
20
  'fedramp-prod': [
21
- 'https://avatar-management--avatars.us-east-1.prod.public.atl-paas-us-gov-mod.net',
21
+ 'https://avatar-management--avatars.us-east-1.prod.cdn.atlassian-us-gov-mod.com',
22
22
  apiGatewayHost['fedramp-prod']
23
23
  ]
24
24
  };
25
25
  const gravatarUrl = 'https://secure.gravatar.com';
26
26
  class CSPInjectionService {
27
- constructor() {
28
- this.getInjectableCSP = ({ existingCSPDetails, microsEnv, tunnelCSPReporterUri, hostname, isFedRAMP }) => {
29
- const reportUri = tunnelCSPReporterUri || this.getCSPReportUri(microsEnv);
30
- const defaultSrc = `'self'`;
31
- const frameAncestors = ["'self'", ...this.getFrameAncestors(microsEnv, hostname)].join(' ');
32
- const frameSrc = ["'self'", ...this.getExistingCSPDetails(types_1.ExternalCspType.FRAME_SRC, existingCSPDetails)].join(' ');
33
- const fontSrc = ["'self'", ...this.getExistingCSPDetails(types_1.ExternalCspType.FONT_SRC, existingCSPDetails)].join(' ');
34
- const imgSrc = [
35
- "'self'",
36
- 'data:',
37
- 'blob:',
38
- hostname,
39
- gravatarUrl,
40
- ...atlassianImageHosts[microsEnv],
41
- ...this.getExistingCSPDetails(types_1.ExternalCspType.IMG_SRC, existingCSPDetails)
42
- ]
43
- .filter((a) => a)
44
- .join(' ');
45
- const mediaSrc = [
46
- "'self'",
47
- 'data:',
48
- 'blob:',
49
- ...this.getExistingCSPDetails(types_1.ExternalCspType.MEDIA_SRC, existingCSPDetails)
50
- ].join(' ');
51
- const connectSrc = [
52
- "'self'",
53
- ...this.getConnectSrc(microsEnv, !!tunnelCSPReporterUri),
54
- ...this.getExistingCSPDetails(types_1.ExternalCspType.CONNECT_SRC, existingCSPDetails)
55
- ].join(' ');
56
- const scriptSrc = [
57
- "'self'",
58
- this.getForgeGlobalCSP(microsEnv, isFedRAMP),
59
- ...this.getExistingCSPDetails(types_1.ExternalCspType.SCRIPT_SRC, existingCSPDetails)
60
- ].join(' ');
61
- const styleSrc = [
62
- "'self'",
63
- this.getForgeGlobalCSP(microsEnv, isFedRAMP),
64
- ...this.getExistingCSPDetails(types_1.ExternalCspType.STYLE_SRC, existingCSPDetails)
65
- ].join(' ');
66
- const navigateTo = ["'self'"];
67
- return [
68
- `default-src ${defaultSrc}`,
69
- `frame-ancestors ${frameAncestors}`,
70
- `frame-src ${frameSrc}`,
71
- `font-src ${fontSrc}`,
72
- `img-src ${imgSrc}`,
73
- `media-src ${mediaSrc}`,
74
- `connect-src ${connectSrc}`,
75
- `script-src ${scriptSrc}`,
76
- `navigate-to ${navigateTo}`,
77
- `style-src ${styleSrc}`,
78
- `form-action 'self'`,
79
- `sandbox allow-downloads allow-forms allow-modals allow-pointer-lock allow-same-origin allow-scripts`,
80
- `report-uri ${reportUri}`
81
- ];
82
- };
83
- }
84
27
  getCSPReportUri(microsEnv) {
85
28
  if (microsEnv === 'dev' || microsEnv === 'stg')
86
29
  return 'https://web-security-reports.stg.services.atlassian.com/csp-report/forge-cdn';
@@ -92,8 +35,7 @@ class CSPInjectionService {
92
35
  : `https://forge.cdn.${microsEnv}.atlassian-dev.net`;
93
36
  }
94
37
  getExistingCSPDetails(cspType, cspDetails) {
95
- var _a;
96
- return (_a = cspDetails[cspType]) !== null && _a !== void 0 ? _a : [];
38
+ return cspDetails[cspType] ?? [];
97
39
  }
98
40
  getConnectSrc(microsEnv, isTunnelling) {
99
41
  const allowed = [];
@@ -130,5 +72,60 @@ class CSPInjectionService {
130
72
  }
131
73
  return frameAncestors;
132
74
  }
75
+ getInjectableCSP = ({ existingCSPDetails, microsEnv, tunnelCSPReporterUri, hostname, isFedRAMP }) => {
76
+ const reportUri = tunnelCSPReporterUri || this.getCSPReportUri(microsEnv);
77
+ const defaultSrc = `'self'`;
78
+ const frameAncestors = ["'self'", ...this.getFrameAncestors(microsEnv, hostname)].join(' ');
79
+ const frameSrc = ["'self'", ...this.getExistingCSPDetails(types_1.ExternalCspType.FRAME_SRC, existingCSPDetails)].join(' ');
80
+ const fontSrc = ["'self'", ...this.getExistingCSPDetails(types_1.ExternalCspType.FONT_SRC, existingCSPDetails)].join(' ');
81
+ const imgSrc = [
82
+ "'self'",
83
+ 'data:',
84
+ 'blob:',
85
+ hostname,
86
+ gravatarUrl,
87
+ ...atlassianImageHosts[microsEnv],
88
+ ...this.getExistingCSPDetails(types_1.ExternalCspType.IMG_SRC, existingCSPDetails)
89
+ ]
90
+ .filter((a) => a)
91
+ .join(' ');
92
+ const mediaSrc = [
93
+ "'self'",
94
+ 'data:',
95
+ 'blob:',
96
+ ...this.getExistingCSPDetails(types_1.ExternalCspType.MEDIA_SRC, existingCSPDetails)
97
+ ].join(' ');
98
+ const connectSrc = [
99
+ "'self'",
100
+ ...this.getConnectSrc(microsEnv, !!tunnelCSPReporterUri),
101
+ ...this.getExistingCSPDetails(types_1.ExternalCspType.CONNECT_SRC, existingCSPDetails)
102
+ ].join(' ');
103
+ const scriptSrc = [
104
+ "'self'",
105
+ this.getForgeGlobalCSP(microsEnv, isFedRAMP),
106
+ ...this.getExistingCSPDetails(types_1.ExternalCspType.SCRIPT_SRC, existingCSPDetails)
107
+ ].join(' ');
108
+ const styleSrc = [
109
+ "'self'",
110
+ this.getForgeGlobalCSP(microsEnv, isFedRAMP),
111
+ ...this.getExistingCSPDetails(types_1.ExternalCspType.STYLE_SRC, existingCSPDetails)
112
+ ].join(' ');
113
+ const navigateTo = ["'self'"];
114
+ return [
115
+ `default-src ${defaultSrc}`,
116
+ `frame-ancestors ${frameAncestors}`,
117
+ `frame-src ${frameSrc}`,
118
+ `font-src ${fontSrc}`,
119
+ `img-src ${imgSrc}`,
120
+ `media-src ${mediaSrc}`,
121
+ `connect-src ${connectSrc}`,
122
+ `script-src ${scriptSrc}`,
123
+ `navigate-to ${navigateTo}`,
124
+ `style-src ${styleSrc}`,
125
+ `form-action 'self'`,
126
+ `sandbox allow-downloads allow-forms allow-modals allow-pointer-lock allow-same-origin allow-scripts`,
127
+ `report-uri ${reportUri}`
128
+ ];
129
+ };
133
130
  }
134
131
  exports.CSPInjectionService = CSPInjectionService;
@@ -12,36 +12,39 @@ class InvalidConnectSrc extends Error {
12
12
  }
13
13
  exports.InvalidConnectSrc = InvalidConnectSrc;
14
14
  class CSPProcessingService {
15
+ logger;
16
+ STYLE_SRC_ALLOWLIST = [`'unsafe-inline'`];
17
+ QUOTED_SCRIPT_SRC_ALLOWLIST = ['unsafe-inline', 'unsafe-eval', 'unsafe-hashes'];
18
+ UNQUOTED_SCRIPT_SRC_ALLOWLIST = ['blob:'];
19
+ SCRIPT_SRC_ALLOWLIST = [...this.QUOTED_SCRIPT_SRC_ALLOWLIST, ...this.UNQUOTED_SCRIPT_SRC_ALLOWLIST];
20
+ BASE_64_HASH_PATTERNS = [
21
+ /^sha256-[a-zA-Z0-9=+/]{44}$/,
22
+ /^sha384-[a-zA-Z0-9=+/]{64}$/,
23
+ /^sha512-[a-zA-Z0-9=+/]{88}$/
24
+ ];
15
25
  constructor(logger) {
16
26
  this.logger = logger;
17
- this.STYLE_SRC_ALLOWLIST = [`'unsafe-inline'`];
18
- this.QUOTED_SCRIPT_SRC_ALLOWLIST = ['unsafe-inline', 'unsafe-eval', 'unsafe-hashes'];
19
- this.UNQUOTED_SCRIPT_SRC_ALLOWLIST = ['blob:'];
20
- this.SCRIPT_SRC_ALLOWLIST = [...this.QUOTED_SCRIPT_SRC_ALLOWLIST, ...this.UNQUOTED_SCRIPT_SRC_ALLOWLIST];
21
- this.BASE_64_HASH_PATTERNS = [
22
- /^sha256-[a-zA-Z0-9=+/]{44}$/,
23
- /^sha384-[a-zA-Z0-9=+/]{64}$/,
24
- /^sha512-[a-zA-Z0-9=+/]{88}$/
25
- ];
26
27
  }
27
28
  getCspDetails(body, permissions) {
28
- var _a, _b;
29
- const { scripts, styles } = (_a = permissions === null || permissions === void 0 ? void 0 : permissions.content) !== null && _a !== void 0 ? _a : { scripts: [], styles: [] };
30
- const external = (_b = permissions === null || permissions === void 0 ? void 0 : permissions.external) !== null && _b !== void 0 ? _b : {};
29
+ const { scripts, styles } = permissions?.content ?? { scripts: [], styles: [] };
30
+ const external = permissions?.external ?? {};
31
31
  const $ = cheerio_1.default.load(body);
32
- const _c = this.mapExternalPermissionsToCsp(external), { 'script-src': scriptSrc, 'style-src': styleSrc } = _c, mappedExternalCsp = tslib_1.__rest(_c, ['script-src', 'style-src']);
33
- return Object.assign({ 'style-src': [...this.getStyleSrc($, styles), ...styleSrc], 'script-src': [...this.getScriptSrc($, scripts), ...scriptSrc] }, mappedExternalCsp);
32
+ const { 'script-src': scriptSrc, 'style-src': styleSrc, ...mappedExternalCsp } = this.mapExternalPermissionsToCsp(external);
33
+ return {
34
+ 'style-src': [...this.getStyleSrc($, styles), ...styleSrc],
35
+ 'script-src': [...this.getScriptSrc($, scripts), ...scriptSrc],
36
+ ...mappedExternalCsp
37
+ };
34
38
  }
35
39
  getInvalidCspPermissions(contentPermissions) {
36
- var _a, _b;
37
40
  const { styles, scripts } = contentPermissions;
38
- const invalidStyles = (_a = styles === null || styles === void 0 ? void 0 : styles.filter((styleSrc) => !this.isValidUserStyleSrc(`'${styleSrc}'`))) !== null && _a !== void 0 ? _a : [];
39
- const invalidScripts = (_b = scripts === null || scripts === void 0 ? void 0 : scripts.filter((scriptSrc) => !this.isValidUserScriptSrc(scriptSrc))) !== null && _b !== void 0 ? _b : [];
41
+ const invalidStyles = styles?.filter((styleSrc) => !this.isValidUserStyleSrc(`'${styleSrc}'`)) ?? [];
42
+ const invalidScripts = scripts?.filter((scriptSrc) => !this.isValidUserScriptSrc(scriptSrc)) ?? [];
40
43
  return [...invalidStyles, ...invalidScripts];
41
44
  }
42
45
  assertValidFetchClient(fetch) {
43
- if (fetch === null || fetch === void 0 ? void 0 : fetch.client) {
44
- for (const client of fetch === null || fetch === void 0 ? void 0 : fetch.client) {
46
+ if (fetch?.client) {
47
+ for (const client of fetch?.client) {
45
48
  if (typeof client !== 'string') {
46
49
  throw new InvalidConnectSrc();
47
50
  }
@@ -49,43 +52,39 @@ class CSPProcessingService {
49
52
  }
50
53
  }
51
54
  mapExternalPermissionsToCsp(externalPermissions) {
52
- var _a;
53
55
  const { images, media, scripts, fetch, styles, fonts, frames } = externalPermissions;
54
56
  this.assertValidFetchClient(fetch);
55
57
  return {
56
- 'img-src': images !== null && images !== void 0 ? images : [],
57
- 'media-src': media !== null && media !== void 0 ? media : [],
58
- 'script-src': scripts !== null && scripts !== void 0 ? scripts : [],
59
- 'style-src': styles !== null && styles !== void 0 ? styles : [],
60
- 'connect-src': (_a = fetch === null || fetch === void 0 ? void 0 : fetch.client) !== null && _a !== void 0 ? _a : [],
61
- 'font-src': fonts !== null && fonts !== void 0 ? fonts : [],
62
- 'frame-src': frames !== null && frames !== void 0 ? frames : []
58
+ 'img-src': images ?? [],
59
+ 'media-src': media ?? [],
60
+ 'script-src': scripts ?? [],
61
+ 'style-src': styles ?? [],
62
+ 'connect-src': fetch?.client ?? [],
63
+ 'font-src': fonts ?? [],
64
+ 'frame-src': frames ?? []
63
65
  };
64
66
  }
65
67
  getStyleSrc($, userStyleSrc) {
66
- var _a, _b;
67
- const quotedUserStyleSrc = (_a = userStyleSrc === null || userStyleSrc === void 0 ? void 0 : userStyleSrc.map((x) => `'${x}'`)) !== null && _a !== void 0 ? _a : [];
68
- const deprecatedUserStyleSrc = (_b = this.getDeprecatedUserCsp($)['style-src']) !== null && _b !== void 0 ? _b : [];
68
+ const quotedUserStyleSrc = userStyleSrc?.map((x) => `'${x}'`) ?? [];
69
+ const deprecatedUserStyleSrc = this.getDeprecatedUserCsp($)['style-src'] ?? [];
69
70
  const uniqueStyleSrc = [...new Set([...deprecatedUserStyleSrc, ...quotedUserStyleSrc])];
70
71
  return uniqueStyleSrc.filter((x) => this.isValidUserStyleSrc(x));
71
72
  }
72
73
  getScriptSrc($, userScriptSrc) {
73
- var _a;
74
- const validUserScriptSrc = (_a = userScriptSrc === null || userScriptSrc === void 0 ? void 0 : userScriptSrc.filter((x) => this.isValidUserScriptSrc(x))) !== null && _a !== void 0 ? _a : [];
74
+ const validUserScriptSrc = userScriptSrc?.filter((x) => this.isValidUserScriptSrc(x)) ?? [];
75
75
  const generatedScriptHashes = validUserScriptSrc.includes('unsafe-inline') ? [] : this.getInlineScriptHashes($);
76
76
  const { scriptSrc, userScriptHashes } = this.extractUniqueHashes(validUserScriptSrc, generatedScriptHashes);
77
77
  return [...scriptSrc, ...generatedScriptHashes, ...userScriptHashes].map((x) => this.formatScriptSrc(x));
78
78
  }
79
79
  extractUniqueHashes(userScriptSrc, existingScriptHashes) {
80
- var _a;
81
80
  const userScriptHashes = [];
82
- const scriptSrc = (_a = userScriptSrc === null || userScriptSrc === void 0 ? void 0 : userScriptSrc.filter((scriptSrc) => {
81
+ const scriptSrc = userScriptSrc?.filter((scriptSrc) => {
83
82
  const isValidHash = this.isValidHash(scriptSrc);
84
83
  if (isValidHash && !existingScriptHashes.includes(scriptSrc)) {
85
84
  userScriptHashes.push(scriptSrc);
86
85
  }
87
86
  return !isValidHash;
88
- })) !== null && _a !== void 0 ? _a : [];
87
+ }) ?? [];
89
88
  return { scriptSrc, userScriptHashes };
90
89
  }
91
90
  getInlineScriptHashes($) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forge/csp",
3
- "version": "3.2.1",
3
+ "version": "3.2.2-next.0",
4
4
  "description": "Contains the CSP configuration for Custom UI resources in Forge",
5
5
  "main": "out/index.js",
6
6
  "author": "Atlassian",
@@ -11,8 +11,8 @@
11
11
  "clean": "rm -rf ./out && rm -f tsconfig.tsbuildinfo"
12
12
  },
13
13
  "devDependencies": {
14
- "@forge/cli-shared": "3.25.2",
15
- "@forge/manifest": "6.3.1",
14
+ "@forge/cli-shared": "5.1.1-next.1",
15
+ "@forge/manifest": "7.5.0-next.1",
16
16
  "@types/jest": "^29.5.12",
17
17
  "@types/node": "14.18.63"
18
18
  },