@forge/csp 4.1.0 → 4.2.0-experimental-a6c1d53

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,24 @@
1
1
  # @forge/csp
2
2
 
3
+ ## 4.2.0-experimental-a6c1d53
4
+
5
+ ### Patch Changes
6
+
7
+ - aebd633: Patch @forge/csp IC frame ancestors csp bug
8
+ - abf0bb1: Add support for custom getICDomain option in CSPInjectionService
9
+
10
+ ## 4.2.0
11
+
12
+ ### Minor Changes
13
+
14
+ - cfde21e: Add CSP Urls for IC environment
15
+
16
+ ## 4.2.0-next.0
17
+
18
+ ### Minor Changes
19
+
20
+ - cfde21e: Add CSP Urls for IC environment
21
+
3
22
  ## 4.1.0
4
23
 
5
24
  ### Minor Changes
@@ -1,21 +1,31 @@
1
1
  import type { LambdaEnvironment } from '@forge/cli-shared';
2
2
  import { CSPDetails } from '../types';
3
- export declare const ATLASSIAN_IMAGES_HOSTS: {
4
- [microsEnv in LambdaEnvironment]: string[];
3
+ declare type StandardIcOptions = {
4
+ icLabel: string;
5
+ serviceName: string;
5
6
  };
7
+ declare type GetICDomainIcOptions = {
8
+ serviceName?: string;
9
+ getICDomain: () => string;
10
+ };
11
+ declare type IcOptions = StandardIcOptions | GetICDomainIcOptions;
12
+ export declare const getAtlassianImageHost: (microsEnv: LambdaEnvironment, icOptions?: IcOptions) => string[];
6
13
  export declare const EXTERNAL_ALLOW_LISTED_IMAGES_HOSTS: string[];
7
14
  export declare class CSPInjectionService {
8
15
  private getCSPReportUri;
9
16
  private getForgeGlobalCSP;
17
+ private getMetalClientCSP;
10
18
  private getExistingCSPDetails;
11
19
  private getConnectSrc;
12
20
  private getFrameAncestors;
13
- getInjectableCSP: ({ existingCSPDetails, microsEnv, tunnelCSPReporterUri, hostname, isFedRAMP }: {
21
+ getInjectableCSP: ({ existingCSPDetails, microsEnv, tunnelCSPReporterUri, hostname, isFedRAMP, icOptions }: {
14
22
  existingCSPDetails: CSPDetails;
15
23
  microsEnv: LambdaEnvironment;
16
24
  tunnelCSPReporterUri?: string | undefined;
17
25
  hostname?: string | undefined;
18
26
  isFedRAMP?: boolean | undefined;
27
+ icOptions?: IcOptions | undefined;
19
28
  }) => string[];
20
29
  }
30
+ export {};
21
31
  //# sourceMappingURL=csp-injection-service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"csp-injection-service.d.ts","sourceRoot":"","sources":["../../src/csp/csp-injection-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE3D,OAAO,EAAE,UAAU,EAAmB,MAAM,UAAU,CAAC;AA2DvD,eAAO,MAAM,sBAAsB,EAAE;KAAG,SAAS,IAAI,iBAAiB,GAAG,MAAM,EAAE;CA8ChF,CAAC;AAMF,eAAO,MAAM,kCAAkC,UAAiE,CAAC;AAEjH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,qBAAqB;IAI7B,OAAO,CAAC,aAAa;IAsBrB,OAAO,CAAC,iBAAiB;IAoClB,gBAAgB;4BAOD,UAAU;mBACnB,iBAAiB;;;;UAI1B,MAAM,EAAE,CA8DV;CACH"}
1
+ {"version":3,"file":"csp-injection-service.d.ts","sourceRoot":"","sources":["../../src/csp/csp-injection-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE3D,OAAO,EAAE,UAAU,EAAmB,MAAM,UAAU,CAAC;AAEvD,aAAK,iBAAiB,GAAG;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAIF,aAAK,oBAAoB,GAAG;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,MAAM,CAAC;CAC3B,CAAC;AAEF,aAAK,SAAS,GAAG,iBAAiB,GAAG,oBAAoB,CAAC;AAyG1D,eAAO,MAAM,qBAAqB,cAAe,iBAAiB,cAAc,SAAS,KAAG,MAAM,EAUjG,CAAC;AAMF,eAAO,MAAM,kCAAkC,UAAiE,CAAC;AAEjH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,iBAAiB;IASzB,OAAO,CAAC,iBAAiB;IASzB,OAAO,CAAC,qBAAqB;IAI7B,OAAO,CAAC,aAAa;IAqBrB,OAAO,CAAC,iBAAiB;IAiDlB,gBAAgB;4BAQD,UAAU;mBACnB,iBAAiB;;;;;UAK1B,MAAM,EAAE,CA8DV;CACH"}
@@ -1,211 +1,233 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CSPInjectionService = exports.EXTERNAL_ALLOW_LISTED_IMAGES_HOSTS = exports.ATLASSIAN_IMAGES_HOSTS = void 0;
3
+ exports.CSPInjectionService = exports.EXTERNAL_ALLOW_LISTED_IMAGES_HOSTS = exports.getAtlassianImageHost = void 0;
4
4
  const types_1 = require("../types");
5
- const ATLASSIAN_API_GATEWAY_HOST = {
6
- dev: 'https://api.dev.atlassian.com',
7
- stg: 'https://api.stg.atlassian.com',
8
- prod: 'https://api.atlassian.com',
9
- 'fedramp-stg': 'https://api.stg.atlassian-us-gov-mod.com',
10
- 'fedramp-prod': 'https://api.atlassian-us-gov-mod.com'
11
- };
12
- const ATLASSIAN_MEDIA_GATEWAY_HOST = {
13
- dev: 'https://media.dev.atl-paas.net',
14
- stg: 'https://media.staging.atl-paas.net',
15
- prod: 'https://api.media.atlassian.com',
16
- 'fedramp-stg': 'https://api-media.stg.atlassian-us-gov-mod.com',
17
- 'fedramp-prod': 'https://api-media.atlassian-us-gov-mod.com'
18
- };
19
- const ATLASSIAN_AVATAR_HOST = {
20
- dev: 'avatar-management--avatars.us-west-2.staging.public.atl-paas.net',
21
- stg: 'avatar-management--avatars.us-west-2.staging.public.atl-paas.net',
22
- prod: 'avatar-management--avatars.us-west-2.prod.public.atl-paas.net',
23
- 'fedramp-stg': 'avatar-management--avatars.us-east-1.staging.cdn.atlassian-us-gov-mod.com',
24
- 'fedramp-prod': 'avatar-management--avatars.us-east-1.prod.cdn.atlassian-us-gov-mod.com'
5
+ const isICEnvKey = (env) => env === 'ic-prod' || env === 'ic-stg';
6
+ const makeICDomain = (env, icLabel) => `${icLabel}.${env === 'ic-prod' ? 'atlassian-isolated.net' : 'oasis-stg.com'}`;
7
+ const getICDomain = (env, icOptions) => {
8
+ if ('getICDomain' in icOptions) {
9
+ return icOptions.getICDomain();
10
+ }
11
+ return makeICDomain(env, icOptions.icLabel);
25
12
  };
26
- const ATLASSIAN_TEAM_HEADER_HOST = {
27
- dev: 'https://ptc-directory-sited-static.us-east-1.staging.public.atl-paas.net/gradients/',
28
- stg: 'https://ptc-directory-sited-static.us-east-1.staging.public.atl-paas.net/gradients/',
29
- prod: 'https://ptc-directory-sited-static.us-east-1.prod.public.atl-paas.net/gradients/',
30
- 'fedramp-stg': 'https://teams-directory-frontend.frontend.cdn.atlassian-us-gov-mod.com/assets/',
31
- 'fedramp-prod': 'https://teams-directory-frontend.frontend.cdn.atlassian-us-gov-mod.com/assets/'
13
+ const makeICHosts = (targetHostFunction) => {
14
+ return {
15
+ 'ic-stg': (icOptions) => targetHostFunction('ic-stg', icOptions),
16
+ 'ic-prod': (icOptions) => targetHostFunction('ic-prod', icOptions)
17
+ };
32
18
  };
33
- const ATLASSIAN_TEAM_AVATAR_HOST = {
34
- dev: 'https://teams-directory-frontend.stg-east.frontend.public.atl-paas.net/assets/',
35
- stg: 'https://teams-directory-frontend.stg-east.frontend.public.atl-paas.net/assets/',
36
- prod: 'https://teams-directory-frontend.prod-east.frontend.public.atl-paas.net/assets/',
37
- 'fedramp-stg': 'https://teams-directory-frontend.frontend.cdn.atlassian-us-gov-mod.com/assets/',
38
- 'fedramp-prod': 'https://teams-directory-frontend.frontend.cdn.atlassian-us-gov-mod.com/assets/'
19
+ const ATLASSIAN_HOST = {
20
+ ATLASSIAN_API_GATEWAY_HOST: {
21
+ dev: 'https://api.dev.atlassian.com',
22
+ stg: 'https://api.stg.atlassian.com',
23
+ prod: 'https://api.atlassian.com',
24
+ 'fedramp-stg': 'https://api.stg.atlassian-us-gov-mod.com',
25
+ 'fedramp-prod': 'https://api.atlassian-us-gov-mod.com',
26
+ ...makeICHosts((env, icOptions) => `https://api.${getICDomain(env, icOptions)}`)
27
+ },
28
+ ATLASSIAN_MEDIA_GATEWAY_HOST: {
29
+ dev: 'https://media.dev.atl-paas.net',
30
+ stg: 'https://media.staging.atl-paas.net',
31
+ prod: 'https://api.media.atlassian.com',
32
+ 'fedramp-stg': 'https://api-media.stg.atlassian-us-gov-mod.com',
33
+ 'fedramp-prod': 'https://api-media.atlassian-us-gov-mod.com',
34
+ ...makeICHosts((env, icOptions) => `https://media-api.${getICDomain(env, icOptions)}`)
35
+ },
36
+ ATLASSIAN_AVATAR_HOST: {
37
+ dev: 'avatar-management--avatars.us-west-2.staging.public.atl-paas.net',
38
+ stg: 'avatar-management--avatars.us-west-2.staging.public.atl-paas.net',
39
+ prod: 'avatar-management--avatars.us-west-2.prod.public.atl-paas.net',
40
+ 'fedramp-stg': 'avatar-management--avatars.us-east-1.staging.cdn.atlassian-us-gov-mod.com',
41
+ 'fedramp-prod': 'avatar-management--avatars.us-east-1.prod.cdn.atlassian-us-gov-mod.com',
42
+ 'ic-stg': (_icOptions) => 'avatar-management--avatars.us-west-2.staging.public.atl-paas.net',
43
+ 'ic-prod': (_icOptions) => 'avatar-management--avatars.us-west-2.prod.public.atl-paas.net'
44
+ },
45
+ ATLASSIAN_TEAM_HEADER_HOST: {
46
+ dev: 'https://ptc-directory-sited-static.us-east-1.staging.public.atl-paas.net/gradients/',
47
+ stg: 'https://ptc-directory-sited-static.us-east-1.staging.public.atl-paas.net/gradients/',
48
+ prod: 'https://ptc-directory-sited-static.us-east-1.prod.public.atl-paas.net/gradients/',
49
+ 'fedramp-stg': 'https://teams-directory-frontend.frontend.cdn.atlassian-us-gov-mod.com/assets/',
50
+ 'fedramp-prod': 'https://teams-directory-frontend.frontend.cdn.atlassian-us-gov-mod.com/assets/',
51
+ ...makeICHosts((env, icOptions) => `https://teams-directory-frontend.services.${getICDomain(env, icOptions)}/bfa/`)
52
+ },
53
+ ATLASSIAN_TEAM_AVATAR_HOST: {
54
+ dev: 'https://teams-directory-frontend.stg-east.frontend.public.atl-paas.net/assets/',
55
+ stg: 'https://teams-directory-frontend.stg-east.frontend.public.atl-paas.net/assets/',
56
+ prod: 'https://teams-directory-frontend.prod-east.frontend.public.atl-paas.net/assets/',
57
+ 'fedramp-stg': 'https://teams-directory-frontend.frontend.cdn.atlassian-us-gov-mod.com/assets/',
58
+ 'fedramp-prod': 'https://teams-directory-frontend.frontend.cdn.atlassian-us-gov-mod.com/assets/',
59
+ ...makeICHosts((env, icOptions) => `https://teams-directory-frontend.services.${getICDomain(env, icOptions)}/bfa/`)
60
+ },
61
+ ATLASSIAN_EMOJIS_HOST: {
62
+ dev: 'https://pf-emoji-service--cdn.ap-southeast-2.dev.public.atl-paas.net',
63
+ stg: 'https://pf-emoji-service--cdn.us-east-1.staging.public.atl-paas.net',
64
+ prod: 'https://pf-emoji-service--cdn.us-east-1.prod.public.atl-paas.net',
65
+ 'fedramp-stg': 'https://pf-emoji-service--cdn.us-east-1.staging.cdn.atlassian-us-gov-mod.com',
66
+ 'fedramp-prod': 'https://pf-emoji-service--cdn.us-east-1.prod.cdn.atlassian-us-gov-mod.com',
67
+ ...makeICHosts((env, icOptions) => `https://pf-emoji-service.${getICDomain(env, icOptions)}`)
68
+ }
39
69
  };
40
- const ATLASSIAN_EMOJIS_HOST = {
41
- dev: 'https://pf-emoji-service--cdn.ap-southeast-2.dev.public.atl-paas.net',
42
- stg: 'https://pf-emoji-service--cdn.us-east-1.staging.public.atl-paas.net',
43
- prod: 'https://pf-emoji-service--cdn.us-east-1.prod.public.atl-paas.net',
44
- 'fedramp-stg': 'https://pf-emoji-service--cdn.us-east-1.staging.cdn.atlassian-us-gov-mod.com',
45
- 'fedramp-prod': 'https://pf-emoji-service--cdn.us-east-1.prod.cdn.atlassian-us-gov-mod.com'
70
+ const getAtlassianHost = (hostType, microsEnv, icOptions) => {
71
+ const hostMap = ATLASSIAN_HOST[hostType];
72
+ if (isICEnvKey(microsEnv)) {
73
+ if (!icOptions) {
74
+ throw new Error('Missing IC label');
75
+ }
76
+ return hostMap[microsEnv](icOptions);
77
+ }
78
+ return hostMap[microsEnv];
46
79
  };
47
- exports.ATLASSIAN_IMAGES_HOSTS = {
48
- dev: [
49
- `https://${ATLASSIAN_AVATAR_HOST['dev']}`,
50
- `https://*.wp.com/${ATLASSIAN_AVATAR_HOST['dev']}/`,
51
- ATLASSIAN_API_GATEWAY_HOST['dev'],
52
- ATLASSIAN_MEDIA_GATEWAY_HOST['dev'],
53
- ATLASSIAN_EMOJIS_HOST['dev'],
54
- ATLASSIAN_TEAM_AVATAR_HOST['dev'],
55
- ATLASSIAN_TEAM_HEADER_HOST['dev']
56
- ],
57
- stg: [
58
- `https://${ATLASSIAN_AVATAR_HOST['stg']}`,
59
- `https://*.wp.com/${ATLASSIAN_AVATAR_HOST['stg']}/`,
60
- ATLASSIAN_API_GATEWAY_HOST['stg'],
61
- ATLASSIAN_MEDIA_GATEWAY_HOST['stg'],
62
- ATLASSIAN_EMOJIS_HOST['stg'],
63
- ATLASSIAN_TEAM_AVATAR_HOST['stg'],
64
- ATLASSIAN_TEAM_HEADER_HOST['stg']
65
- ],
66
- prod: [
67
- `https://${ATLASSIAN_AVATAR_HOST['prod']}`,
68
- `https://*.wp.com/${ATLASSIAN_AVATAR_HOST['prod']}/`,
69
- ATLASSIAN_API_GATEWAY_HOST['prod'],
70
- ATLASSIAN_MEDIA_GATEWAY_HOST['prod'],
71
- ATLASSIAN_EMOJIS_HOST['prod'],
72
- ATLASSIAN_TEAM_AVATAR_HOST['prod'],
73
- ATLASSIAN_TEAM_HEADER_HOST['prod']
74
- ],
75
- 'fedramp-stg': [
76
- `https://${ATLASSIAN_AVATAR_HOST['fedramp-stg']}`,
77
- `https://*.wp.com/${ATLASSIAN_AVATAR_HOST['fedramp-stg']}/`,
78
- ATLASSIAN_API_GATEWAY_HOST['fedramp-stg'],
79
- ATLASSIAN_MEDIA_GATEWAY_HOST['fedramp-stg'],
80
- ATLASSIAN_EMOJIS_HOST['fedramp-stg'],
81
- ATLASSIAN_TEAM_AVATAR_HOST['fedramp-stg'],
82
- ATLASSIAN_TEAM_HEADER_HOST['fedramp-stg']
83
- ],
84
- 'fedramp-prod': [
85
- `https://${ATLASSIAN_AVATAR_HOST['fedramp-prod']}`,
86
- `https://*.wp.com/${ATLASSIAN_AVATAR_HOST['fedramp-prod']}/`,
87
- ATLASSIAN_API_GATEWAY_HOST['fedramp-prod'],
88
- ATLASSIAN_MEDIA_GATEWAY_HOST['fedramp-prod'],
89
- ATLASSIAN_EMOJIS_HOST['fedramp-prod'],
90
- ATLASSIAN_TEAM_AVATAR_HOST['fedramp-prod'],
91
- ATLASSIAN_TEAM_HEADER_HOST['fedramp-prod']
92
- ]
80
+ const getAtlassianImageHost = (microsEnv, icOptions) => {
81
+ return [
82
+ `https://${getAtlassianHost('ATLASSIAN_AVATAR_HOST', microsEnv, icOptions)}`,
83
+ `https://*.wp.com/${getAtlassianHost('ATLASSIAN_AVATAR_HOST', microsEnv, icOptions)}/`,
84
+ getAtlassianHost('ATLASSIAN_API_GATEWAY_HOST', microsEnv, icOptions),
85
+ getAtlassianHost('ATLASSIAN_MEDIA_GATEWAY_HOST', microsEnv, icOptions),
86
+ getAtlassianHost('ATLASSIAN_EMOJIS_HOST', microsEnv, icOptions),
87
+ getAtlassianHost('ATLASSIAN_TEAM_AVATAR_HOST', microsEnv, icOptions),
88
+ getAtlassianHost('ATLASSIAN_TEAM_HEADER_HOST', microsEnv, icOptions)
89
+ ];
93
90
  };
91
+ exports.getAtlassianImageHost = getAtlassianImageHost;
94
92
  exports.EXTERNAL_ALLOW_LISTED_IMAGES_HOSTS = ['https://secure.gravatar.com', 'https://images.unsplash.com'];
95
93
  class CSPInjectionService {
96
- getCSPReportUri(microsEnv) {
94
+ constructor() {
95
+ this.getInjectableCSP = ({ existingCSPDetails, microsEnv, tunnelCSPReporterUri, hostname, isFedRAMP, icOptions }) => {
96
+ const reportUri = tunnelCSPReporterUri || this.getCSPReportUri(microsEnv, icOptions);
97
+ const defaultSrc = `'self'`;
98
+ const frameAncestors = ["'self'", ...this.getFrameAncestors(microsEnv, hostname, icOptions)].join(' ');
99
+ const frameSrc = ["'self'", hostname, ...this.getExistingCSPDetails(types_1.ExternalCspType.FRAME_SRC, existingCSPDetails)]
100
+ .filter((a) => a)
101
+ .join(' ');
102
+ const fontSrc = ["'self'", ...this.getExistingCSPDetails(types_1.ExternalCspType.FONT_SRC, existingCSPDetails)].join(' ');
103
+ const imgSrc = [
104
+ "'self'",
105
+ 'data:',
106
+ 'blob:',
107
+ hostname,
108
+ ...exports.EXTERNAL_ALLOW_LISTED_IMAGES_HOSTS,
109
+ ...(0, exports.getAtlassianImageHost)(microsEnv, icOptions),
110
+ ...this.getExistingCSPDetails(types_1.ExternalCspType.IMG_SRC, existingCSPDetails)
111
+ ]
112
+ .filter((a) => a)
113
+ .join(' ');
114
+ const mediaSrc = [
115
+ "'self'",
116
+ 'data:',
117
+ 'blob:',
118
+ hostname,
119
+ getAtlassianHost('ATLASSIAN_MEDIA_GATEWAY_HOST', microsEnv, icOptions),
120
+ ...this.getExistingCSPDetails(types_1.ExternalCspType.MEDIA_SRC, existingCSPDetails)
121
+ ]
122
+ .filter((a) => a)
123
+ .join(' ');
124
+ const connectSrc = [
125
+ "'self'",
126
+ ...this.getConnectSrc(microsEnv, !!tunnelCSPReporterUri, icOptions),
127
+ ...this.getExistingCSPDetails(types_1.ExternalCspType.CONNECT_SRC, existingCSPDetails)
128
+ ].join(' ');
129
+ const scriptSrc = [
130
+ "'self'",
131
+ this.getForgeGlobalCSP(microsEnv, isFedRAMP, icOptions),
132
+ ...this.getExistingCSPDetails(types_1.ExternalCspType.SCRIPT_SRC, existingCSPDetails)
133
+ ].join(' ');
134
+ const styleSrc = [
135
+ "'self'",
136
+ this.getForgeGlobalCSP(microsEnv, isFedRAMP, icOptions),
137
+ ...this.getExistingCSPDetails(types_1.ExternalCspType.STYLE_SRC, existingCSPDetails)
138
+ ].join(' ');
139
+ return [
140
+ `default-src ${defaultSrc}`,
141
+ `frame-ancestors ${frameAncestors}`,
142
+ `frame-src ${frameSrc}`,
143
+ `font-src ${fontSrc}`,
144
+ `img-src ${imgSrc}`,
145
+ `media-src ${mediaSrc}`,
146
+ `connect-src ${connectSrc}`,
147
+ `script-src ${scriptSrc}`,
148
+ `style-src ${styleSrc}`,
149
+ `form-action 'self'`,
150
+ `sandbox allow-downloads allow-forms allow-modals allow-pointer-lock allow-same-origin allow-scripts`,
151
+ `report-uri ${reportUri}`
152
+ ];
153
+ };
154
+ }
155
+ getCSPReportUri(microsEnv, icOptions) {
156
+ const serviceName = isICEnvKey(microsEnv) && icOptions ? icOptions.serviceName : 'forge-cdn';
97
157
  if (microsEnv === 'dev' || microsEnv === 'stg')
98
- return 'https://web-security-reports.stg.services.atlassian.com/csp-report/forge-cdn';
99
- return 'https://web-security-reports.services.atlassian.com/csp-report/forge-cdn';
158
+ return `https://web-security-reports.stg.services.atlassian.com/csp-report/${serviceName}`;
159
+ return `https://web-security-reports.services.atlassian.com/csp-report/${serviceName}`;
100
160
  }
101
- getForgeGlobalCSP(microsEnv, isFedRAMP = false) {
161
+ getForgeGlobalCSP(microsEnv, isFedRAMP = false, icOptions) {
162
+ if (isICEnvKey(microsEnv) && icOptions) {
163
+ return `https://forge.forge-cdn.${getICDomain(microsEnv, icOptions)}`;
164
+ }
102
165
  return isFedRAMP
103
166
  ? `https://forge.cdn.${microsEnv.split('-')[1]}.atlassian-dev-us-gov-mod.net`
104
167
  : `https://forge.cdn.${microsEnv}.atlassian-dev.net`;
105
168
  }
169
+ getMetalClientCSP(microsEnv, icOptions) {
170
+ if (isICEnvKey(microsEnv) && icOptions) {
171
+ return `https://api.${getICDomain(microsEnv, icOptions)}/metal/ingest`;
172
+ }
173
+ return `https://api.${microsEnv === 'prod' ? '' : 'stg.'}atlassian.com/metal/ingest`;
174
+ }
106
175
  getExistingCSPDetails(cspType, cspDetails) {
107
- return cspDetails[cspType] ?? [];
176
+ var _a;
177
+ return (_a = cspDetails[cspType]) !== null && _a !== void 0 ? _a : [];
108
178
  }
109
- getConnectSrc(microsEnv, isTunnelling) {
179
+ getConnectSrc(microsEnv, isTunnelling, icOptions) {
110
180
  const allowed = [];
111
181
  if (isTunnelling) {
112
182
  allowed.push(...['ws://localhost:*', 'http://localhost:*']);
113
183
  }
114
- allowed.push(`https://api.${microsEnv === 'prod' ? '' : 'stg.'}atlassian.com/metal/ingest`);
115
- allowed.push(`${ATLASSIAN_API_GATEWAY_HOST[microsEnv]}/gateway/api/emoji/`);
116
- allowed.push(ATLASSIAN_MEDIA_GATEWAY_HOST[microsEnv]);
184
+ const metalClientCSP = this.getMetalClientCSP(microsEnv, icOptions);
185
+ allowed.push(metalClientCSP);
186
+ allowed.push(`${getAtlassianHost('ATLASSIAN_API_GATEWAY_HOST', microsEnv, icOptions)}/gateway/api/emoji/`);
187
+ allowed.push(getAtlassianHost('ATLASSIAN_MEDIA_GATEWAY_HOST', microsEnv, icOptions));
117
188
  return allowed;
118
189
  }
119
- getFrameAncestors(microsEnv, hostname) {
190
+ getFrameAncestors(microsEnv, hostname, icOptions) {
120
191
  let frameAncestors = [];
121
- if (microsEnv === 'dev' || microsEnv === 'stg') {
122
- frameAncestors = [
123
- '*.jira-dev.com',
124
- 'http://localhost:*',
125
- 'http://devbucket.localhost',
126
- 'https://integration.bb-inf.net',
127
- '*.atl-paas.net',
128
- '*.stg.atlassian.com'
129
- ];
130
- }
131
- else if (microsEnv === 'fedramp-stg') {
132
- frameAncestors = ['*.atlassian-stg-fedm.net'];
133
- }
134
- else if (microsEnv === 'fedramp-prod') {
135
- frameAncestors = ['*.atlassian-us-gov-mod.net'];
136
- }
137
- else {
138
- frameAncestors = [
139
- '*.atlassian.net',
140
- 'bitbucket.org',
141
- '*.jira.com',
142
- '*.atlassian.com',
143
- '*.frontend.public.atl-paas.net'
144
- ];
192
+ switch (microsEnv) {
193
+ case 'dev':
194
+ case 'stg':
195
+ frameAncestors = [
196
+ '*.jira-dev.com',
197
+ 'http://localhost:*',
198
+ 'http://devbucket.localhost',
199
+ 'https://integration.bb-inf.net',
200
+ '*.atl-paas.net',
201
+ '*.stg.atlassian.com'
202
+ ];
203
+ break;
204
+ case 'fedramp-stg':
205
+ frameAncestors = ['*.atlassian-stg-fedm.net'];
206
+ break;
207
+ case 'fedramp-prod':
208
+ frameAncestors = ['*.atlassian-us-gov-mod.net'];
209
+ break;
210
+ case 'ic-stg':
211
+ case 'ic-prod':
212
+ if (icOptions) {
213
+ frameAncestors = [`*.${getICDomain(microsEnv, icOptions)}`];
214
+ }
215
+ break;
216
+ case 'prod':
217
+ default:
218
+ frameAncestors = [
219
+ '*.atlassian.net',
220
+ 'bitbucket.org',
221
+ '*.jira.com',
222
+ '*.atlassian.com',
223
+ '*.frontend.public.atl-paas.net'
224
+ ];
225
+ break;
145
226
  }
146
227
  if (hostname) {
147
228
  frameAncestors.push(hostname);
148
229
  }
149
230
  return frameAncestors;
150
231
  }
151
- getInjectableCSP = ({ existingCSPDetails, microsEnv, tunnelCSPReporterUri, hostname, isFedRAMP }) => {
152
- const reportUri = tunnelCSPReporterUri || this.getCSPReportUri(microsEnv);
153
- const defaultSrc = `'self'`;
154
- const frameAncestors = ["'self'", ...this.getFrameAncestors(microsEnv, hostname)].join(' ');
155
- const frameSrc = ["'self'", hostname, ...this.getExistingCSPDetails(types_1.ExternalCspType.FRAME_SRC, existingCSPDetails)]
156
- .filter((a) => a)
157
- .join(' ');
158
- const fontSrc = ["'self'", ...this.getExistingCSPDetails(types_1.ExternalCspType.FONT_SRC, existingCSPDetails)].join(' ');
159
- const imgSrc = [
160
- "'self'",
161
- 'data:',
162
- 'blob:',
163
- hostname,
164
- ...exports.EXTERNAL_ALLOW_LISTED_IMAGES_HOSTS,
165
- ...exports.ATLASSIAN_IMAGES_HOSTS[microsEnv],
166
- ...this.getExistingCSPDetails(types_1.ExternalCspType.IMG_SRC, existingCSPDetails)
167
- ]
168
- .filter((a) => a)
169
- .join(' ');
170
- const mediaSrc = [
171
- "'self'",
172
- 'data:',
173
- 'blob:',
174
- hostname,
175
- ATLASSIAN_MEDIA_GATEWAY_HOST[microsEnv],
176
- ...this.getExistingCSPDetails(types_1.ExternalCspType.MEDIA_SRC, existingCSPDetails)
177
- ]
178
- .filter((a) => a)
179
- .join(' ');
180
- const connectSrc = [
181
- "'self'",
182
- ...this.getConnectSrc(microsEnv, !!tunnelCSPReporterUri),
183
- ...this.getExistingCSPDetails(types_1.ExternalCspType.CONNECT_SRC, existingCSPDetails)
184
- ].join(' ');
185
- const scriptSrc = [
186
- "'self'",
187
- this.getForgeGlobalCSP(microsEnv, isFedRAMP),
188
- ...this.getExistingCSPDetails(types_1.ExternalCspType.SCRIPT_SRC, existingCSPDetails)
189
- ].join(' ');
190
- const styleSrc = [
191
- "'self'",
192
- this.getForgeGlobalCSP(microsEnv, isFedRAMP),
193
- ...this.getExistingCSPDetails(types_1.ExternalCspType.STYLE_SRC, existingCSPDetails)
194
- ].join(' ');
195
- return [
196
- `default-src ${defaultSrc}`,
197
- `frame-ancestors ${frameAncestors}`,
198
- `frame-src ${frameSrc}`,
199
- `font-src ${fontSrc}`,
200
- `img-src ${imgSrc}`,
201
- `media-src ${mediaSrc}`,
202
- `connect-src ${connectSrc}`,
203
- `script-src ${scriptSrc}`,
204
- `style-src ${styleSrc}`,
205
- `form-action 'self'`,
206
- `sandbox allow-downloads allow-forms allow-modals allow-pointer-lock allow-same-origin allow-scripts`,
207
- `report-uri ${reportUri}`
208
- ];
209
- };
210
232
  }
211
233
  exports.CSPInjectionService = CSPInjectionService;
@@ -12,22 +12,22 @@ 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
- ];
25
15
  constructor(logger) {
26
16
  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
+ ];
27
26
  }
28
27
  getCspDetails(body, permissions) {
29
- const { scripts, styles } = permissions?.content ?? { scripts: [], styles: [] };
30
- const external = permissions?.external ?? {};
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 : {};
31
31
  const $ = (0, cheerio_1.load)(body, { xml: { xmlMode: false } });
32
32
  const { 'script-src': scriptSrc, 'style-src': styleSrc, ...mappedExternalCsp } = this.mapExternalPermissionsToCsp(external);
33
33
  return {
@@ -37,14 +37,15 @@ class CSPProcessingService {
37
37
  };
38
38
  }
39
39
  getInvalidCspPermissions(contentPermissions) {
40
+ var _a, _b;
40
41
  const { styles, scripts } = contentPermissions;
41
- const invalidStyles = styles?.filter((styleSrc) => !this.isValidUserStyleSrc(`'${styleSrc}'`)) ?? [];
42
- const invalidScripts = scripts?.filter((scriptSrc) => !this.isValidUserScriptSrc(scriptSrc)) ?? [];
42
+ const invalidStyles = (_a = styles === null || styles === void 0 ? void 0 : styles.filter((styleSrc) => !this.isValidUserStyleSrc(`'${styleSrc}'`))) !== null && _a !== void 0 ? _a : [];
43
+ const invalidScripts = (_b = scripts === null || scripts === void 0 ? void 0 : scripts.filter((scriptSrc) => !this.isValidUserScriptSrc(scriptSrc))) !== null && _b !== void 0 ? _b : [];
43
44
  return [...invalidStyles, ...invalidScripts];
44
45
  }
45
46
  assertValidFetchClient(fetch) {
46
- if (fetch?.client) {
47
- for (const client of fetch?.client) {
47
+ if (fetch === null || fetch === void 0 ? void 0 : fetch.client) {
48
+ for (const client of fetch === null || fetch === void 0 ? void 0 : fetch.client) {
48
49
  if (typeof client !== 'string') {
49
50
  throw new InvalidConnectSrc();
50
51
  }
@@ -52,42 +53,46 @@ class CSPProcessingService {
52
53
  }
53
54
  }
54
55
  egressesToStringMap(externalPermissions) {
55
- return externalPermissions?.map((egress) => (typeof egress === 'object' ? egress.address : egress));
56
+ return externalPermissions === null || externalPermissions === void 0 ? void 0 : externalPermissions.map((egress) => (typeof egress === 'object' ? egress.address : egress));
56
57
  }
57
58
  mapExternalPermissionsToCsp(externalPermissions) {
59
+ var _a, _b, _c, _d, _e, _f, _g;
58
60
  const { images, media, scripts, fetch, styles, fonts, frames } = externalPermissions;
59
61
  this.assertValidFetchClient(fetch);
60
62
  return {
61
- 'img-src': this.egressesToStringMap(images) ?? [],
62
- 'media-src': this.egressesToStringMap(media) ?? [],
63
- 'script-src': this.egressesToStringMap(scripts) ?? [],
64
- 'style-src': this.egressesToStringMap(styles) ?? [],
65
- 'connect-src': fetch?.client ?? [],
66
- 'font-src': this.egressesToStringMap(fonts) ?? [],
67
- 'frame-src': this.egressesToStringMap(frames) ?? []
63
+ 'img-src': (_a = this.egressesToStringMap(images)) !== null && _a !== void 0 ? _a : [],
64
+ 'media-src': (_b = this.egressesToStringMap(media)) !== null && _b !== void 0 ? _b : [],
65
+ 'script-src': (_c = this.egressesToStringMap(scripts)) !== null && _c !== void 0 ? _c : [],
66
+ 'style-src': (_d = this.egressesToStringMap(styles)) !== null && _d !== void 0 ? _d : [],
67
+ 'connect-src': (_e = fetch === null || fetch === void 0 ? void 0 : fetch.client) !== null && _e !== void 0 ? _e : [],
68
+ 'font-src': (_f = this.egressesToStringMap(fonts)) !== null && _f !== void 0 ? _f : [],
69
+ 'frame-src': (_g = this.egressesToStringMap(frames)) !== null && _g !== void 0 ? _g : []
68
70
  };
69
71
  }
70
72
  getStyleSrc($, userStyleSrc) {
71
- const quotedUserStyleSrc = userStyleSrc?.map((x) => `'${x}'`) ?? [];
72
- const deprecatedUserStyleSrc = this.getDeprecatedUserCsp($)['style-src'] ?? [];
73
+ var _a, _b;
74
+ const quotedUserStyleSrc = (_a = userStyleSrc === null || userStyleSrc === void 0 ? void 0 : userStyleSrc.map((x) => `'${x}'`)) !== null && _a !== void 0 ? _a : [];
75
+ const deprecatedUserStyleSrc = (_b = this.getDeprecatedUserCsp($)['style-src']) !== null && _b !== void 0 ? _b : [];
73
76
  const uniqueStyleSrc = [...new Set([...deprecatedUserStyleSrc, ...quotedUserStyleSrc])];
74
77
  return uniqueStyleSrc.filter((x) => this.isValidUserStyleSrc(x));
75
78
  }
76
79
  getScriptSrc($, userScriptSrc) {
77
- const validUserScriptSrc = userScriptSrc?.filter((x) => this.isValidUserScriptSrc(x)) ?? [];
80
+ var _a;
81
+ const validUserScriptSrc = (_a = userScriptSrc === null || userScriptSrc === void 0 ? void 0 : userScriptSrc.filter((x) => this.isValidUserScriptSrc(x))) !== null && _a !== void 0 ? _a : [];
78
82
  const generatedScriptHashes = validUserScriptSrc.includes('unsafe-inline') ? [] : this.getInlineScriptHashes($);
79
83
  const { scriptSrc, userScriptHashes } = this.extractUniqueHashes(validUserScriptSrc, generatedScriptHashes);
80
84
  return [...scriptSrc, ...generatedScriptHashes, ...userScriptHashes].map((x) => this.formatScriptSrc(x));
81
85
  }
82
86
  extractUniqueHashes(userScriptSrc, existingScriptHashes) {
87
+ var _a;
83
88
  const userScriptHashes = [];
84
- const scriptSrc = userScriptSrc?.filter((scriptSrc) => {
89
+ const scriptSrc = (_a = userScriptSrc === null || userScriptSrc === void 0 ? void 0 : userScriptSrc.filter((scriptSrc) => {
85
90
  const isValidHash = this.isValidHash(scriptSrc);
86
91
  if (isValidHash && !existingScriptHashes.includes(scriptSrc)) {
87
92
  userScriptHashes.push(scriptSrc);
88
93
  }
89
94
  return !isValidHash;
90
- }) ?? [];
95
+ })) !== null && _a !== void 0 ? _a : [];
91
96
  return { scriptSrc, userScriptHashes };
92
97
  }
93
98
  getInlineScriptHashes($) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forge/csp",
3
- "version": "4.1.0",
3
+ "version": "4.2.0-experimental-a6c1d53",
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": "8.1.0",
15
- "@forge/manifest": "10.1.0",
14
+ "@forge/cli-shared": "8.2.0",
15
+ "@forge/manifest": "10.2.0",
16
16
  "@types/jest": "^29.5.14",
17
17
  "@types/node": "20.19.1"
18
18
  },