@ampsec/platform-client 62.23.0 → 63.0.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.
Files changed (60) hide show
  1. package/build/src/dto/enums/category.d.ts +2 -1
  2. package/build/src/dto/enums/category.js +1 -0
  3. package/build/src/dto/enums/category.js.map +1 -1
  4. package/build/src/dto/enums/saasComponentKind.d.ts +2 -1
  5. package/build/src/dto/enums/saasComponentKind.js +2 -0
  6. package/build/src/dto/enums/saasComponentKind.js.map +1 -1
  7. package/build/src/dto/findings.dto.d.ts +27 -1
  8. package/build/src/dto/findings.dto.js +9 -1
  9. package/build/src/dto/findings.dto.js.map +1 -1
  10. package/build/src/dto/index.d.ts +1 -0
  11. package/build/src/dto/index.js +1 -0
  12. package/build/src/dto/index.js.map +1 -1
  13. package/build/src/dto/platform/index.d.ts +1 -0
  14. package/build/src/dto/platform/index.js +1 -0
  15. package/build/src/dto/platform/index.js.map +1 -1
  16. package/build/src/dto/platform/platform.findings.dto.d.ts +6 -0
  17. package/build/src/dto/platform/platform.tokens.dto.d.ts +53 -0
  18. package/build/src/dto/platform/platform.tokens.dto.js +8 -0
  19. package/build/src/dto/platform/platform.tokens.dto.js.map +1 -0
  20. package/build/src/dto/saasComponents.dto.d.ts +1 -1
  21. package/build/src/dto/tokens.dto.d.ts +47 -0
  22. package/build/src/dto/tokens.dto.js +11 -0
  23. package/build/src/dto/tokens.dto.js.map +1 -0
  24. package/build/src/services/AmpApi.d.ts +2 -1
  25. package/build/src/services/AmpApi.js +1 -0
  26. package/build/src/services/AmpApi.js.map +1 -1
  27. package/build/src/services/AmpSdk.d.ts +2 -1
  28. package/build/src/services/AmpSdk.js +1 -0
  29. package/build/src/services/AmpSdk.js.map +1 -1
  30. package/build/src/services/ContentTemplateService.d.ts +1 -0
  31. package/build/src/services/ContentTemplateService.js +25 -0
  32. package/build/src/services/ContentTemplateService.js.map +1 -0
  33. package/build/src/services/constants.d.ts +1 -0
  34. package/build/src/services/constants.js +1 -0
  35. package/build/src/services/constants.js.map +1 -1
  36. package/build/src/services/contentful.service.d.ts +272 -1
  37. package/build/src/services/contentful.service.js +197 -2
  38. package/build/src/services/contentful.service.js.map +1 -1
  39. package/build/src/services/index.d.ts +1 -0
  40. package/build/src/services/index.js +1 -0
  41. package/build/src/services/index.js.map +1 -1
  42. package/build/src/services/rest/RestClient.d.ts +1 -1
  43. package/build/src/services/rest/RestClient.js +1 -1
  44. package/build/src/services/rest/RestClient.js.map +1 -1
  45. package/package.json +2 -2
  46. package/src/dto/enums/category.ts +1 -0
  47. package/src/dto/enums/saasComponentKind.ts +3 -0
  48. package/src/dto/findings.dto.ts +11 -1
  49. package/src/dto/index.ts +1 -0
  50. package/src/dto/platform/index.ts +1 -0
  51. package/src/dto/platform/platform.tokens.dto.ts +9 -0
  52. package/src/dto/saasComponents.dto.ts +1 -1
  53. package/src/dto/tokens.dto.ts +11 -0
  54. package/src/services/AmpApi.ts +3 -0
  55. package/src/services/AmpSdk.ts +4 -0
  56. package/src/services/ContentTemplateService.ts +18 -0
  57. package/src/services/constants.ts +1 -0
  58. package/src/services/contentful.service.ts +238 -1
  59. package/src/services/index.ts +1 -0
  60. package/src/services/rest/RestClient.ts +2 -2
@@ -17,5 +17,6 @@ export * from './platform.saasComponents.dto';
17
17
  export * from './platform.saasUsers.dto';
18
18
  export * from './platform.ampSetting.dto';
19
19
  export * from './platform.tenants.dto';
20
+ export * from './platform.tokens.dto';
20
21
  export * from './platform.users.dto';
21
22
  export * from './tenant.based.dto';
@@ -0,0 +1,9 @@
1
+ import {z} from 'zod';
2
+ import {_TenantBased, _UpsertTenantBased} from './tenant.based.dto';
3
+ import {_TokenDto, _TokenUpsertDto} from '../tokens.dto';
4
+
5
+ export const _PlatformTokenDto = _TokenDto.merge(_TenantBased);
6
+ export type PlatformTokenDto = z.infer<typeof _PlatformTokenDto>;
7
+
8
+ export const _PlatformTokenUpsertDto = _TokenUpsertDto.merge(_UpsertTenantBased);
9
+ export type PlatformTokenUpsertDto = z.infer<typeof _PlatformTokenUpsertDto>;
@@ -89,7 +89,7 @@ export type SaasComponentUpsertDto = ChangeAwareUpsertDto & {
89
89
  /** Connector Id */
90
90
  cid: string;
91
91
  /** */
92
- kind: SaasComponentKind;
92
+ kind: SaasComponentKind | string;
93
93
  /** Context specific information related to the Saas Component */
94
94
  meta: SaasComponentMeta;
95
95
  /** User Id of the affected user */
@@ -0,0 +1,11 @@
1
+ import {z} from 'zod';
2
+ import {UPSERT_DTO_MASK, _BaseDto} from './base.dto';
3
+
4
+ export const _TokenDto = _BaseDto.extend({
5
+ displayValue: z.string().optional(),
6
+ key: z.string(),
7
+ });
8
+ export type TokenDto = z.infer<typeof _TokenDto>;
9
+
10
+ export const _TokenUpsertDto = _TokenDto.partial(UPSERT_DTO_MASK);
11
+ export type TokenUpsertDto = z.infer<typeof _TokenUpsertDto>;
@@ -17,6 +17,7 @@ import {
17
17
  SaasUserDto,
18
18
  TenantDto,
19
19
  TenantUpsertDto,
20
+ TokenDto,
20
21
  UserDto,
21
22
  } from '../dto';
22
23
  import {
@@ -77,6 +78,7 @@ export class AmpApi {
77
78
  readonly saasUsers: AmpDataService<SaasUserDto>;
78
79
  readonly settings: AmpSettingsService;
79
80
  readonly tenants: AmpEntityService<TenantUpsertDto, TenantDto>;
81
+ readonly tokens: AmpDataService<TokenDto>;
80
82
  readonly users: AmpDataService<UserDto>;
81
83
  readonly usersInsights: UsersInsightsService;
82
84
  readonly prediction: PredictionService;
@@ -105,6 +107,7 @@ export class AmpApi {
105
107
  this.saasUsers = new AmpDataServiceImpl<SaasUserDto>(rest, KIND.SAAS_USERS);
106
108
  this.settings = new AmpSettingsService(rest);
107
109
  this.tenants = new AmpEntityServiceImpl<TenantUpsertDto, TenantDto>(rest, KIND.TENANTS);
110
+ this.tokens = new AmpDataServiceImpl<TokenDto>(rest, KIND.TOKENS);
108
111
  this.users = new AmpDataServiceImpl<UserDto>(rest, KIND.USERS);
109
112
  this.usersInsights = new UsersInsightsService(rest);
110
113
  this.prediction = new PredictionService(rest, KIND.FINDINGS);
@@ -36,6 +36,8 @@ import {
36
36
  PlatformStagedSaasComponentUpsertDto,
37
37
  PlatformStagedSaasUserDto,
38
38
  PlatformStagedSaasUserUpsertDto,
39
+ PlatformTokenDto,
40
+ PlatformTokenUpsertDto,
39
41
  ProviderDto,
40
42
  TenantDto,
41
43
  TenantUpsertDto,
@@ -96,6 +98,7 @@ export class AmpSdkServices {
96
98
  readonly stagedSaaSUsers: TruncatableAmpEntityService<PlatformStagedSaasUserUpsertDto, PlatformStagedSaasUserDto>;
97
99
  readonly settings: AmpSdkSettingsService;
98
100
  readonly tenants: AmpSdkTenantService<TenantUpsertDto, TenantDto>;
101
+ readonly tokens: AmpSdkTenantService<PlatformTokenUpsertDto, PlatformTokenDto>;
99
102
  readonly users: AmpSdkUserService;
100
103
 
101
104
  constructor(rest: RestClient) {
@@ -131,6 +134,7 @@ export class AmpSdkServices {
131
134
 
132
135
  this.settings = new AmpSdkSettingsService(rest);
133
136
  this.tenants = new AmpEntityServiceImpl<TenantUpsertDto, TenantDto>(rest, KIND.TENANTS, TARGET_API_PLATFORM);
137
+ this.tokens = new AmpEntityServiceImpl<PlatformTokenUpsertDto, PlatformTokenDto>(rest, KIND.TOKENS, TARGET_API_PLATFORM);
134
138
  this.users = new AmpSdkUserService(rest, TARGET_API_PLATFORM);
135
139
  }
136
140
 
@@ -0,0 +1,18 @@
1
+ import _ from 'lodash';
2
+ const JSON_PATH_PATTERN = /(\$\.(?:(?:[a-zA-Z0-9_]+)(?:\.[a-zA-Z0-9_]+)*)?)/g;
3
+
4
+ export const fillJsonPathTemplate = (raw: string, meta: unknown) => {
5
+ const jsonPathMatches = raw.match(JSON_PATH_PATTERN);
6
+ let result = raw;
7
+ if (jsonPathMatches) {
8
+ jsonPathMatches.forEach(jsonPath => {
9
+ const path = jsonPath.replace('$.', '');
10
+ let value = _.get(meta, path, '?');
11
+ if (!_.isString(value)) {
12
+ value = JSON.stringify(value);
13
+ }
14
+ result = result.replace(jsonPath, value);
15
+ });
16
+ }
17
+ return result;
18
+ };
@@ -37,6 +37,7 @@ export const KIND = {
37
37
  STAGED_SAAS_COMPONENTS: 'staged_saas_components',
38
38
  STAGED_SAAS_USERS: 'staged_saas_users',
39
39
  TENANTS: 'tenants',
40
+ TOKENS: 'tokens',
40
41
  USERS: 'users',
41
42
  USERS_INSIGHTS: 'coverage',
42
43
  WORKFLOW_PROGRESS: 'workflow_progress',
@@ -1,6 +1,11 @@
1
+ import {z} from 'zod';
1
2
  import type {Entry, EntrySkeletonType, EntryFieldTypes, EntryCollection, ContentfulClientApi} from 'contentful/dist/types';
2
3
  import * as contentful from 'contentful';
4
+ import _ from 'lodash';
3
5
 
6
+ /**
7
+ * @deprecated Use ContentService and MessageTemplateContent instead
8
+ */
4
9
  export type MessageTemplate = EntrySkeletonType & {
5
10
  contentTypeId: 'messageTemplate';
6
11
  fields: {
@@ -12,6 +17,8 @@ export type MessageTemplate = EntrySkeletonType & {
12
17
  };
13
18
 
14
19
  /**
20
+ * @deprecated Use ContentService instead
21
+ *
15
22
  * ContentfulService
16
23
  *
17
24
  * This client is a wrapper around the Contentful REST API meant to be used by
@@ -47,12 +54,14 @@ export class ContentfulService {
47
54
  return messageTemplates;
48
55
  };
49
56
 
50
- getContentfulEntries = async <T extends EntrySkeletonType>(contentType: string, include?: number): Promise<EntryCollection<T>> => {
57
+ getContentfulEntries = async <T extends EntrySkeletonType>(contentType: string, include?: number, skip?: number, limit?: number): Promise<EntryCollection<T>> => {
51
58
  if (!this.contentfulClient) {
52
59
  throw new Error('Contenful client not initialized because of missing configurations: CONTENTFUL_SPACE_ID and CONTENTFUL_ACCESS_TOKEN must be set in the environment');
53
60
  }
54
61
  const entries = await this.contentfulClient.getEntries<T>({
55
62
  content_type: contentType,
63
+ skip,
64
+ limit,
56
65
  ...(include ? {include} : {}),
57
66
  });
58
67
  return entries;
@@ -66,3 +75,231 @@ export class ContentfulService {
66
75
  return entry;
67
76
  };
68
77
  }
78
+
79
+ export enum ContentType {
80
+ MESSAGE_TEMPLATE = 'messageTemplate',
81
+ BACKEND_CONTENT = 'backendContent',
82
+ SERVICE_PROFILE = 'serviceProfile',
83
+ INTEGRATION = 'integration',
84
+ INTEGRATION_DOMAIN = 'integrationDomain',
85
+ FINDING = 'finding',
86
+ SECURITY_HUB_I18N = 'securityHubI18n',
87
+ }
88
+
89
+ const ContentfulRichText: z.ZodType<EntryFieldTypes.RichText> = z.any();
90
+
91
+ export type ContentOptions = {
92
+ include?: number;
93
+ serviceFilter?: string | string[];
94
+ useCaseFilter?: string | string[];
95
+ };
96
+
97
+ const _BackendContent = z.object({
98
+ key: z.string(),
99
+ value: z.string(),
100
+ serviceFilter: z.array(z.string()),
101
+ useCaseFilter: z.array(z.string()),
102
+ });
103
+ export type BackendContent = z.infer<typeof _BackendContent>;
104
+
105
+ const _ServiceProfile = z.object({
106
+ serviceKey: z.string(),
107
+ displayValue: z.string(),
108
+ logo: z.string(),
109
+ aliases: z.array(z.string()),
110
+ });
111
+ export type ServiceProfile = z.infer<typeof _ServiceProfile>;
112
+
113
+ const _IntegrationDomain = z.object({
114
+ name: z.string(),
115
+ domain: z.string(),
116
+ });
117
+ export type IntegrationDomain = z.infer<typeof _IntegrationDomain>;
118
+
119
+ const _FindingContent = z.object({
120
+ kind: z.string(),
121
+ title: z.string(),
122
+ displayText: z.string().optional(),
123
+ timeToComplete: z.string().optional(),
124
+ instructions: ContentfulRichText,
125
+ });
126
+ export type FindingContent = z.infer<typeof _FindingContent>;
127
+
128
+ const _Integration = z.object({
129
+ name: z.string(),
130
+ providerKey: z.string(),
131
+ logo: z.string(),
132
+ icon: z.string().optional(),
133
+ tileDescription: z.string(),
134
+ overview: z.string(),
135
+ installationInstructions: ContentfulRichText,
136
+ termsOfUseLink: z.string().optional(),
137
+ privacyStatementLink: z.string().optional(),
138
+ supportLink: z.string().optional(),
139
+ formTextKeys: z.unknown(),
140
+ tags: z.array(z.string().optional()).default([]),
141
+ overviewLong: z.string(),
142
+ installModalDynamicTabs: z.unknown(),
143
+ overviewRichText: ContentfulRichText,
144
+ findings: _FindingContent,
145
+ domains: z.array(_IntegrationDomain),
146
+ });
147
+ export type Integration = z.infer<typeof _Integration>;
148
+
149
+ const _I18nContent = z.object({
150
+ key: z.string(),
151
+ value: z.string(),
152
+ useCaseFilter: z.array(z.string()),
153
+ });
154
+ export type I18nContent = z.infer<typeof _I18nContent>;
155
+
156
+ const _MessageTemplateContent = z.object({
157
+ id: z.string(),
158
+ body: z.string(),
159
+ subject: z.string().optional(),
160
+ });
161
+ export type MessageTemplateContent = z.infer<typeof _MessageTemplateContent>;
162
+
163
+ export class ContentService {
164
+ private constructor(private readonly contentfulService: ContentfulService) {}
165
+
166
+ getEntries = async (contentType: ContentType, options: ContentOptions): Promise<unknown[]> => {
167
+ const entries = [] as unknown[];
168
+ let hasMore = true;
169
+ let skip = 0;
170
+ while (hasMore) {
171
+ const rawEntries = await this.contentfulService.getContentfulEntries(contentType, options.include, skip, 100);
172
+ entries.push(...rawEntries.items.map(entry => entry.fields));
173
+ skip += 100;
174
+ hasMore = rawEntries.skip + rawEntries.limit < rawEntries.total;
175
+ }
176
+ return entries;
177
+ };
178
+
179
+ getBackendContent = async (options?: ContentOptions): Promise<BackendContent[]> => {
180
+ const rawEntries = await this.getEntries(ContentType.BACKEND_CONTENT, options || {});
181
+ const entries = rawEntries.map(entry => _BackendContent.parse(entry));
182
+ return entries;
183
+ };
184
+
185
+ getMessageTemplates = async (): Promise<MessageTemplateContent[]> => {
186
+ const rawEntries = await this.contentfulService.getMessageTemplates();
187
+ const entries = rawEntries.items.map(entry => _MessageTemplateContent.parse(entry.fields));
188
+ return entries;
189
+ };
190
+
191
+ getIntegrationDomains = async (options?: ContentOptions): Promise<IntegrationDomain[]> => {
192
+ const rawEntries = await this.getEntries(ContentType.INTEGRATION_DOMAIN, options || {});
193
+ const entries = rawEntries.map(this.mapIntegrationDomain);
194
+ return entries;
195
+ };
196
+ private mapIntegrationDomain = (entry: unknown): IntegrationDomain => _IntegrationDomain.parse(entry);
197
+
198
+ getFindings = async (options?: ContentOptions): Promise<FindingContent[]> => {
199
+ const rawEntries = await this.getEntries(ContentType.FINDING, options || {});
200
+ const entries = rawEntries.map(this.mapFindings);
201
+ return entries;
202
+ };
203
+ private mapFindings = (entry: unknown): FindingContent => _FindingContent.parse(entry);
204
+
205
+ getIntegrations = async (options?: ContentOptions): Promise<Integration[]> => {
206
+ const rawEntries = await this.getEntries(ContentType.INTEGRATION, options || {});
207
+ const entries = rawEntries.map(entry => {
208
+ const logoUrl = _.get(entry, 'logo.fields.file.url');
209
+ _.set(entry as object, 'logo', logoUrl ? 'https:' + logoUrl : '');
210
+ const iconUrl = _.get(entry, 'icon.fields.file.url');
211
+ _.set(entry as object, 'icon', iconUrl ? 'https:' + iconUrl : '');
212
+ _.set(
213
+ entry as object,
214
+ 'domains',
215
+ _.get(entry, 'domains', []).map(e => this.mapIntegrationDomain(_.get(e, 'fields')))
216
+ );
217
+ _.set(
218
+ entry as object,
219
+ 'findings',
220
+ _.get(entry, 'findings', []).map(e => this.mapFindings(_.get(e, 'fields')))
221
+ );
222
+ return _Integration.parse(entry);
223
+ });
224
+ return entries;
225
+ };
226
+
227
+ getServiceProfiles = async (options?: ContentOptions): Promise<ServiceProfile[]> => {
228
+ const rawEntries = await this.getEntries(ContentType.SERVICE_PROFILE, options || {});
229
+ const entries = rawEntries.map(entry => {
230
+ const logoUrl = _.get(entry, 'logo.fields.file.url');
231
+ _.set(entry as object, 'logo', logoUrl ? 'https:' + logoUrl : '');
232
+ return _ServiceProfile.parse(entry);
233
+ });
234
+ return entries;
235
+ };
236
+
237
+ getI18nContent = async (options?: ContentOptions): Promise<I18nContent[]> => {
238
+ const rawEntries = await this.getEntries(ContentType.SECURITY_HUB_I18N, options || {});
239
+ const entries = rawEntries.map(entry => _I18nContent.parse(entry));
240
+ return entries;
241
+ };
242
+
243
+ static instance(): ContentService {
244
+ return new ContentService(new ContentfulService());
245
+ }
246
+ }
247
+
248
+ export class I18nContentService {
249
+ private content = {} as Record<string, I18nContent>;
250
+ private isInitialized: Promise<I18nContentService>;
251
+ private constructor(private readonly contentService: ContentService) {
252
+ this.isInitialized = this.refresh().then(service => {
253
+ console.debug('i18nContentService initialized');
254
+ return service;
255
+ });
256
+ }
257
+
258
+ init = async (): Promise<I18nContentService> => this.isInitialized;
259
+
260
+ refresh = async (): Promise<I18nContentService> => {
261
+ const content = await this.contentService.getI18nContent();
262
+ this.content = _.keyBy(content, 'key');
263
+ return this;
264
+ };
265
+
266
+ /**
267
+ * Example Usage:
268
+ * ```ts
269
+ * const i18n = await i18nContentService.instance();
270
+ * await i18n.init();
271
+ * const value = i18n.g('LOADING', 'Loading...');
272
+ * ```
273
+ * @param key
274
+ * @param defaultValue
275
+ * @returns
276
+ */
277
+ g = (key: string, defaultValue?: string): string => {
278
+ return _.get(this.content, [key, 'value'], defaultValue || key);
279
+ };
280
+
281
+ /**
282
+ * Example Usage:
283
+ * ```ts
284
+ * const i18n = await I18nContentService.instance();
285
+ * await i18n.init();
286
+ * const t = i18n.getLookupFunction();
287
+ * const value = t('LOADING', 'Loading...');
288
+ * // or more simply
289
+ * return <div>{t('Loading...')}</div>
290
+ * // Note this can be use to extend functionality of a Typography component, e.g.
291
+ * const MyTypography = (props: {children: string}) => <Typography>{t(props.children)}</Typography>
292
+ * return <MyTypography>Loading...</MyTypography>
293
+ * ```
294
+ * @returns
295
+ */
296
+ getLookupFunction = () => this.g;
297
+
298
+ private static _instance: I18nContentService | undefined;
299
+ static instance(_options?: {locale?: 'en'; useCaseFilter?: string | string[]}) {
300
+ if (!I18nContentService._instance) {
301
+ I18nContentService._instance = new I18nContentService(ContentService.instance());
302
+ }
303
+ return I18nContentService._instance;
304
+ }
305
+ }
@@ -2,6 +2,7 @@ export * from './AmpApi';
2
2
  export * from './EngageApi';
3
3
  export * from './AmpSdk';
4
4
  export * from './constants';
5
+ export * from './ContentTemplateService';
5
6
  export * from './data.service';
6
7
  export * from './entity.service';
7
8
  export * from './findingsInsights.service';
@@ -16,7 +16,7 @@ export type AmpRestClientOptions = {
16
16
  rateLimitStrategy?: RestClientRateLimitStrategy;
17
17
  logger?: AmpLogger;
18
18
  client?: AxiosStatic;
19
- /** Throws errors on non-200 responses if `true`. Default `false` */
19
+ /** Throws errors on non-200 responses if `true`*/
20
20
  strict?: boolean;
21
21
  };
22
22
 
@@ -123,7 +123,7 @@ export const getAmpRestClient = (options: AmpRestClientOptions): RestClient => {
123
123
  rateLimitStrategy: new StaticRestClientRateLimitStrategy(50),
124
124
  logger: options.logger,
125
125
  client: options.client,
126
- strict: options.strict,
126
+ strict: _.isUndefined(options.strict) ? true : options.strict,
127
127
  });
128
128
  return ampClient;
129
129
  };