@ampsec/platform-client 84.40.0 → 84.42.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 (49) hide show
  1. package/build/src/dto/browserCoachingSubject.dto.d.ts +2 -1
  2. package/build/src/dto/browserCoachingSubject.dto.js +1 -0
  3. package/build/src/dto/browserCoachingSubject.dto.js.map +1 -1
  4. package/build/src/dto/findings.dto.d.ts +97 -5
  5. package/build/src/dto/findings.dto.js +26 -1
  6. package/build/src/dto/findings.dto.js.map +1 -1
  7. package/build/src/dto/findingsInsights.dto.d.ts +2 -2
  8. package/build/src/dto/index.d.ts +1 -0
  9. package/build/src/dto/index.js +1 -0
  10. package/build/src/dto/index.js.map +1 -1
  11. package/build/src/dto/platform/platform.findings.dto.d.ts +5 -5
  12. package/build/src/dto/platform/platform.webhookProviders.dto.d.ts +5 -5
  13. package/build/src/dto/rules.dto.d.ts +69 -0
  14. package/build/src/dto/rules.dto.js +18 -0
  15. package/build/src/dto/rules.dto.js.map +1 -0
  16. package/build/src/dto/tagSpecs.dto.d.ts +5 -4
  17. package/build/src/dto/tagSpecs.dto.js +1 -1
  18. package/build/src/dto/tagSpecs.dto.js.map +1 -1
  19. package/build/src/dto/webhookProviders.dto.d.ts +5 -5
  20. package/build/src/services/AmpSdk.d.ts +2 -0
  21. package/build/src/services/AmpSdk.js +2 -0
  22. package/build/src/services/AmpSdk.js.map +1 -1
  23. package/build/src/services/ContentTemplateService.d.ts +10 -0
  24. package/build/src/services/ContentTemplateService.js +50 -24
  25. package/build/src/services/ContentTemplateService.js.map +1 -1
  26. package/build/src/services/constants.d.ts +1 -0
  27. package/build/src/services/constants.js +1 -0
  28. package/build/src/services/constants.js.map +1 -1
  29. package/build/src/services/index.d.ts +1 -0
  30. package/build/src/services/index.js +1 -0
  31. package/build/src/services/index.js.map +1 -1
  32. package/build/src/services/rules.service.d.ts +14 -0
  33. package/build/src/services/rules.service.js +50 -0
  34. package/build/src/services/rules.service.js.map +1 -0
  35. package/build/src/settings.d.ts +1 -0
  36. package/build/src/settings.js +1 -0
  37. package/build/src/settings.js.map +1 -1
  38. package/package.json +1 -1
  39. package/src/dto/browserCoachingSubject.dto.ts +1 -0
  40. package/src/dto/findings.dto.ts +28 -0
  41. package/src/dto/index.ts +1 -0
  42. package/src/dto/rules.dto.ts +66 -0
  43. package/src/dto/tagSpecs.dto.ts +3 -2
  44. package/src/services/AmpSdk.ts +3 -0
  45. package/src/services/ContentTemplateService.ts +54 -31
  46. package/src/services/constants.ts +1 -0
  47. package/src/services/index.ts +1 -0
  48. package/src/services/rules.service.ts +59 -0
  49. package/src/settings.ts +2 -0
@@ -1,6 +1,6 @@
1
1
  import _ from 'lodash';
2
- import {DYNAMIC_VARIABLES} from './constants';
3
2
  import {FindingDto} from '../dto';
3
+
4
4
  const JSON_PATH_PATTERN = /(\$\.(?:(?:[a-zA-Z0-9_]+)(?:\.[a-zA-Z0-9_]+)*)?)/g;
5
5
  const DYNAMIC_PROPERTY_PATTERN = /\{\{([^}]+)\}\}/g;
6
6
 
@@ -21,6 +21,18 @@ export const fillJsonPathTemplate = (raw: string, meta: unknown) => {
21
21
  return result;
22
22
  };
23
23
 
24
+ /**
25
+ * Resolves a value from meta at the given path. When the resolved value is an array,
26
+ * returns the first element (for backwards compatibility with paths like user.emails[0]).
27
+ */
28
+ const getTemplateValue = (meta: unknown, path: string): unknown => {
29
+ const value: unknown = _.get(meta, path, undefined);
30
+ if (Array.isArray(value) && value.length > 0) {
31
+ return value[0];
32
+ }
33
+ return value;
34
+ };
35
+
24
36
  /**
25
37
  * @param rawTemplateString Template string with placeholders in the format of {{path.to.value}}
26
38
  * @param meta object containing the values to replace the placeholders
@@ -33,10 +45,10 @@ export const fillDynamicPropertyTemplate = (rawTemplateString: string, meta: unk
33
45
  if (matches) {
34
46
  matches.forEach(placeholder => {
35
47
  const path = placeholder.slice(2, -2).trim();
36
- const value = _.get(meta, path, undefined);
48
+ const value = getTemplateValue(meta, path);
37
49
  if (_.isString(value) || _.isNumber(value) || _.isBoolean(value)) {
38
50
  // primitive value type is replaced as is
39
- result = result.replace(placeholder, value);
51
+ result = result.replace(placeholder, String(value));
40
52
  } else if (_.isObject(value) || _.isArray(value)) {
41
53
  // object and array values are stringified
42
54
  result = result.replace(placeholder, JSON.stringify(value, null, 2));
@@ -49,39 +61,50 @@ export const fillDynamicPropertyTemplate = (rawTemplateString: string, meta: unk
49
61
  return result;
50
62
  };
51
63
 
52
- const populateDynamicVariablesEntityData = (
53
- entityProperties: Record<string, string>,
54
- baseObject: Record<string, unknown> | undefined,
55
- fallbackObject: Record<string, unknown> | undefined = undefined
56
- ): Record<string, unknown> => {
57
- const entityData: Record<string, unknown> = {};
58
- for (const key in entityProperties) {
59
- const findingKeyPath = entityProperties[key];
60
- entityData[key] = _.get(baseObject, findingKeyPath) || (fallbackObject ? _.get(fallbackObject, findingKeyPath) : undefined);
61
- }
62
-
63
- return entityData;
64
- };
65
-
64
+ /**
65
+ * Builds the context for dynamic template variables. Exposes the finding and its sub-entities
66
+ * (user, asset, provider, meta) so that any property can be referenced in templates, e.g.
67
+ * {{user.firstName}}, {{finding.displayValue}}, {{meta._vulnerability.cve}}.
68
+ *
69
+ * - Sub-entities can be used without the "finding." prefix (e.g. {{user.firstName}}).
70
+ * - Legacy aliases are preserved: user.email (from user.emails[0]), asset.serialNumber
71
+ * (from meta._asset.sn), asset.macAddress (from meta._asset.macs[0]).
72
+ * - Arrays resolved in templates use the first element (see getTemplateValue).
73
+ */
66
74
  export const buildDynamicVariablesContext = (finding: FindingDto): Map<string, unknown> => {
67
- const entityMap: Map<string, unknown> = new Map();
75
+ const entityMap = new Map<string, unknown>();
68
76
 
69
- for (const entityName in DYNAMIC_VARIABLES.ALLOWED_ENTITIES) {
70
- const entityKey = DYNAMIC_VARIABLES.ALLOWED_ENTITIES[entityName as keyof typeof DYNAMIC_VARIABLES.ALLOWED_ENTITIES];
71
- const entityProperties = DYNAMIC_VARIABLES.ALLOWED_ENTITY_KEYS[entityKey as keyof typeof DYNAMIC_VARIABLES.ALLOWED_ENTITY_KEYS];
77
+ // Full finding at "finding" so {{finding.displayValue}}, {{finding.discoveredAt}}, etc. work
78
+ entityMap.set('finding', finding);
72
79
 
73
- let entityData: Record<string, unknown> = {};
80
+ // User: full object + legacy alias "email" from user.emails[0]
81
+ const userEntity = finding.user
82
+ ? {
83
+ ...finding.user,
84
+ ...(Array.isArray((finding.user as {emails?: string[]}).emails) && {
85
+ email: (finding.user as {emails: string[]}).emails[0],
86
+ }),
87
+ }
88
+ : {};
89
+ entityMap.set('user', userEntity);
74
90
 
75
- if (entityName === 'finding') {
76
- entityData = populateDynamicVariablesEntityData(entityProperties, finding);
77
- } else if (entityName === 'user') {
78
- entityData = populateDynamicVariablesEntityData(entityProperties, finding.user, finding);
79
- } else if (entityName === 'asset') {
80
- entityData = populateDynamicVariablesEntityData(entityProperties, finding.asset, finding);
81
- }
91
+ // Asset: full object + legacy keys from meta (serialNumber, macAddress)
92
+ const assetEntity = finding.asset
93
+ ? {
94
+ ...finding.asset,
95
+ ...(_.get(finding, 'meta._asset.sn') !== undefined && {
96
+ serialNumber: _.get(finding, 'meta._asset.sn'),
97
+ }),
98
+ ...(_.get(finding, 'meta._asset.macs[0]') !== undefined && {
99
+ macAddress: _.get(finding, 'meta._asset.macs[0]'),
100
+ }),
101
+ }
102
+ : {};
103
+ entityMap.set('asset', assetEntity);
82
104
 
83
- entityMap.set(entityName, entityData);
84
- }
105
+ // Provider and meta: pass through when present
106
+ entityMap.set('provider', finding.provider ?? {});
107
+ entityMap.set('meta', finding.meta ?? {});
85
108
 
86
109
  return entityMap;
87
110
  };
@@ -39,6 +39,7 @@ export const KIND = {
39
39
  PROVIDERS: 'providers',
40
40
  REPORT_RESULTS: 'report_results',
41
41
  RISK_CONTRIBUTORS: 'risk_contributors',
42
+ RULES: 'rules',
42
43
  SAAS_ASSETS: 'saas_assets',
43
44
  SAAS_COMPONENTS: 'saas_components',
44
45
  SAAS_USERS: 'saas_users',
@@ -19,3 +19,4 @@ export * from './contentful.service';
19
19
  export * from './prediction.service';
20
20
  export * from './customActionsService';
21
21
  export * from './userExtension.service';
22
+ export * from './rules.service';
@@ -0,0 +1,59 @@
1
+ import {CreateRuleDtoSchema, RuleUpsertDtoSchema} from '../dto/rules.dto';
2
+ import {TargetApi} from './constants';
3
+ import {RestClient, RestResponse} from './rest';
4
+
5
+ export class RulesService {
6
+ protected readonly rest: RestClient;
7
+ protected readonly targetApi: string;
8
+ protected readonly kind: string;
9
+
10
+ constructor(rest: RestClient, kind: string, targetApi: TargetApi) {
11
+ this.rest = rest;
12
+ this.kind = kind;
13
+ this.targetApi = targetApi;
14
+ }
15
+
16
+ createRuleSet = async (rulesDto: CreateRuleDtoSchema): Promise<RestResponse> => {
17
+ return await this.rest.call({
18
+ url: `/${this.targetApi}/v1/${this.kind}`,
19
+ method: 'POST',
20
+ data: rulesDto,
21
+ });
22
+ };
23
+
24
+ updateRuleSet = async (id: string, rulesDto: RuleUpsertDtoSchema): Promise<RestResponse> => {
25
+ return await this.rest.call({
26
+ url: `/${this.targetApi}/v1/${this.kind}/${id}`,
27
+ method: 'PUT',
28
+ data: rulesDto,
29
+ });
30
+ };
31
+
32
+ getRulesByTenant = async (tid: string, cid?: string): Promise<RestResponse> => {
33
+ const params: Record<string, any> = {tid, limit: 200};
34
+ if (cid) {
35
+ params.cid = cid;
36
+ }
37
+
38
+ return await this.rest.call({
39
+ url: `/${this.targetApi}/v1/${this.kind}`,
40
+ method: 'GET',
41
+ params,
42
+ });
43
+ };
44
+
45
+ getRuleSet = async (cid: string, forceRefresh = false): Promise<RestResponse> => {
46
+ return await this.rest.call({
47
+ url: `/${this.targetApi}/v1/${this.kind}/ruleset`,
48
+ method: 'GET',
49
+ params: {cid, forceRefresh},
50
+ });
51
+ };
52
+
53
+ deleteRuleSet = async (id: string): Promise<RestResponse> => {
54
+ return await this.rest.call({
55
+ url: `/${this.targetApi}/v1/${this.kind}/${id}`,
56
+ method: 'DELETE',
57
+ });
58
+ };
59
+ }
package/src/settings.ts CHANGED
@@ -125,6 +125,8 @@ const TENANT_SETTINGS = {
125
125
 
126
126
  BROWSER_COACHING_AI_SERVICES_CONFIG: AmpSettingsMap.asKey<BrowserCoachingAiServicesConfig>('browsercoaching.ai.services.config', {services: []}),
127
127
 
128
+ BROWSER_AI_SERVICE_SIGNALS_ENABLED: AmpSettingsMap.asKey<boolean>('browser.ai.service.signals.enabled', false),
129
+
128
130
  /** Compliance Training Feature Flag */
129
131
  COMPLIANCE_TRAINING_ENABLED: AmpSettingsMap.asKey<boolean>('compliance.training.enabled', false),
130
132