perimeterx-js-core 0.23.3 → 0.24.1

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 (93) hide show
  1. package/lib/cjs/activities/utils.js +4 -3
  2. package/lib/cjs/config/ConfigurationBase.js +14 -0
  3. package/lib/cjs/config/defaults/DefaultConfigurationParams.js +2 -0
  4. package/lib/cjs/context/DefaultContext.js +2 -0
  5. package/lib/cjs/context/SerializedContext.js +1 -0
  6. package/lib/cjs/enforcer/utils.js +6 -1
  7. package/lib/cjs/index.js +1 -0
  8. package/lib/cjs/phase/flow/EndEnforcerFlow.js +4 -4
  9. package/lib/cjs/phase/flow/FilterFlow.js +1 -1
  10. package/lib/cjs/phase/flow/PostEnforceFlow.js +2 -2
  11. package/lib/cjs/phase/impl/IdentifyTelemetryRequestPhase.js +61 -0
  12. package/lib/cjs/phase/impl/ModifyOutgoingResponsePhase.js +32 -1
  13. package/lib/cjs/phase/impl/RiskApiPhase.js +1 -0
  14. package/lib/cjs/phase/impl/{TelemetryPhase.js → SendTelemetryActivityPhase.js} +12 -13
  15. package/lib/cjs/phase/impl/index.js +2 -1
  16. package/lib/cjs/risk_api/client/PostRiskApiClientBase.js +5 -4
  17. package/lib/cjs/risk_api/risk_response/RiskResponseBase.js +9 -0
  18. package/lib/cjs/risk_api/risk_response/serialize/SerializedRiskResponse.js +1 -0
  19. package/lib/cjs/snippet_injection/CustomSnippetFunction.js +2 -0
  20. package/lib/cjs/snippet_injection/index.js +20 -0
  21. package/lib/cjs/snippet_injection/snippet_injector/ISnippetInjector.js +2 -0
  22. package/lib/cjs/snippet_injection/snippet_injector/index.js +17 -0
  23. package/lib/cjs/snippet_injection/snippet_retriever/DefaultSnippetRetriever.js +95 -0
  24. package/lib/cjs/snippet_injection/snippet_retriever/ISnippetRetriever.js +2 -0
  25. package/lib/cjs/snippet_injection/snippet_retriever/index.js +18 -0
  26. package/lib/cjs/snippet_injection/utils.js +5 -0
  27. package/lib/cjs/utils/constants.js +1 -1
  28. package/lib/cjs/utils/request_id_generator/SimpleRequestIdGenerator.js +13 -0
  29. package/lib/cjs/utils/request_id_generator/index.js +1 -0
  30. package/lib/esm/activities/utils.js +4 -3
  31. package/lib/esm/config/ConfigurationBase.js +6 -0
  32. package/lib/esm/config/defaults/DefaultConfigurationParams.js +2 -0
  33. package/lib/esm/context/DefaultContext.js +3 -0
  34. package/lib/esm/context/SerializedContext.js +2 -0
  35. package/lib/esm/enforcer/utils.js +5 -0
  36. package/lib/esm/index.js +1 -0
  37. package/lib/esm/phase/flow/EndEnforcerFlow.js +3 -3
  38. package/lib/esm/phase/flow/FilterFlow.js +2 -2
  39. package/lib/esm/phase/flow/PostEnforceFlow.js +2 -2
  40. package/lib/esm/phase/impl/IdentifyTelemetryRequestPhase.js +13 -0
  41. package/lib/esm/phase/impl/ModifyOutgoingResponsePhase.js +17 -1
  42. package/lib/esm/phase/impl/RiskApiPhase.js +1 -0
  43. package/lib/esm/phase/impl/{TelemetryPhase.js → SendTelemetryActivityPhase.js} +3 -4
  44. package/lib/esm/phase/impl/index.js +2 -1
  45. package/lib/esm/risk_api/client/PostRiskApiClientBase.js +5 -4
  46. package/lib/esm/risk_api/risk_response/RiskResponseBase.js +4 -0
  47. package/lib/esm/risk_api/risk_response/serialize/SerializedRiskResponse.js +2 -0
  48. package/lib/esm/snippet_injection/CustomSnippetFunction.js +1 -0
  49. package/lib/esm/snippet_injection/index.js +4 -0
  50. package/lib/esm/snippet_injection/snippet_injector/ISnippetInjector.js +1 -0
  51. package/lib/esm/snippet_injection/snippet_injector/index.js +1 -0
  52. package/lib/esm/snippet_injection/snippet_retriever/DefaultSnippetRetriever.js +36 -0
  53. package/lib/esm/snippet_injection/snippet_retriever/ISnippetRetriever.js +1 -0
  54. package/lib/esm/snippet_injection/snippet_retriever/index.js +2 -0
  55. package/lib/esm/snippet_injection/utils.js +9 -0
  56. package/lib/esm/utils/constants.js +1 -1
  57. package/lib/esm/utils/request_id_generator/SimpleRequestIdGenerator.js +9 -0
  58. package/lib/esm/utils/request_id_generator/index.js +1 -0
  59. package/lib/types/activities/model/CommonActivityDetails.d.ts +1 -0
  60. package/lib/types/config/ConfigurationBase.d.ts +3 -0
  61. package/lib/types/config/ConfigurationBuilderBase.d.ts +1 -1
  62. package/lib/types/config/IConfiguration.d.ts +9 -0
  63. package/lib/types/config/params/CoreConfigurationParams.d.ts +6 -1
  64. package/lib/types/context/ContextJson.d.ts +1 -0
  65. package/lib/types/context/DefaultContext.d.ts +1 -0
  66. package/lib/types/context/SerializedContext.d.ts +1 -0
  67. package/lib/types/context/interfaces/IContext.d.ts +4 -0
  68. package/lib/types/enforcer/EnforcerOptions.d.ts +3 -0
  69. package/lib/types/index.d.ts +1 -0
  70. package/lib/types/phase/flow/EndEnforcerFlow.d.ts +3 -1
  71. package/lib/types/phase/flow/PostEnforceFlow.d.ts +1 -1
  72. package/lib/types/phase/impl/IdentifyTelemetryRequestPhase.d.ts +9 -0
  73. package/lib/types/phase/impl/ModifyOutgoingResponsePhase.d.ts +5 -1
  74. package/lib/types/phase/impl/{TelemetryPhase.d.ts → SendTelemetryActivityPhase.d.ts} +1 -1
  75. package/lib/types/phase/impl/index.d.ts +2 -1
  76. package/lib/types/risk_api/client/PostRiskApiClientBase.d.ts +1 -1
  77. package/lib/types/risk_api/risk_response/CommonRiskResponsePayload.d.ts +1 -0
  78. package/lib/types/risk_api/risk_response/IRiskResponse.d.ts +1 -0
  79. package/lib/types/risk_api/risk_response/RiskResponseBase.d.ts +1 -0
  80. package/lib/types/risk_api/risk_response/serialize/RiskResponseJson.d.ts +1 -0
  81. package/lib/types/risk_api/risk_response/serialize/SerializedRiskResponse.d.ts +1 -0
  82. package/lib/types/snippet_injection/CustomSnippetFunction.d.ts +2 -0
  83. package/lib/types/snippet_injection/index.d.ts +4 -0
  84. package/lib/types/snippet_injection/snippet_injector/ISnippetInjector.d.ts +5 -0
  85. package/lib/types/snippet_injection/snippet_injector/index.d.ts +1 -0
  86. package/lib/types/snippet_injection/snippet_retriever/DefaultSnippetRetriever.d.ts +10 -0
  87. package/lib/types/snippet_injection/snippet_retriever/ISnippetRetriever.d.ts +5 -0
  88. package/lib/types/snippet_injection/snippet_retriever/index.d.ts +2 -0
  89. package/lib/types/snippet_injection/utils.d.ts +1 -0
  90. package/lib/types/utils/constants.d.ts +1 -1
  91. package/lib/types/utils/request_id_generator/SimpleRequestIdGenerator.d.ts +6 -0
  92. package/lib/types/utils/request_id_generator/index.d.ts +1 -0
  93. package/package.json +1 -1
@@ -75,9 +75,10 @@ export function redactCookieSecret(secret) {
75
75
  return '***'.concat(secret.substring(secret.length - 3, secret.length));
76
76
  }
77
77
  export const addConfigDataToDetails = (details, config) => {
78
- if (config.remoteConfigVersion) {
79
- details.remote_config_version = config.remoteConfigVersion;
80
- }
78
+ transferExistingProperties(config, details, {
79
+ remoteConfigId: 'remote_config_id',
80
+ remoteConfigVersion: 'remote_config_version',
81
+ });
81
82
  };
82
83
  export const addCustomParametersToDetails = (details, customParameters) => {
83
84
  if (customParameters) {
@@ -363,6 +363,12 @@ export class ConfigurationBase {
363
363
  get tokenVersion() {
364
364
  return this.configParams.px_token_version;
365
365
  }
366
+ get snippetInjectionEnabled() {
367
+ return this.configParams.px_snippet_injection_enabled;
368
+ }
369
+ get createCustomSnippet() {
370
+ return this.configParams.px_create_custom_snippet;
371
+ }
366
372
  get enableBlockedUrlOnCaptchaBlockPage() {
367
373
  return true;
368
374
  }
@@ -127,6 +127,8 @@ export const defaultConfigurationParams = () => ({
127
127
  px_remote_config_retry_interval_ms: 1000,
128
128
  px_url_decode_reserved_characters: false,
129
129
  px_secured_pxhd_enabled: false,
130
+ px_snippet_injection_enabled: false,
131
+ px_create_custom_snippet: null,
130
132
  px_custom_is_sensitive_request: null,
131
133
  px_custom_is_monitored_request: null,
132
134
  px_custom_is_enforced_request: null,
@@ -34,6 +34,7 @@ export class DefaultContext {
34
34
  enforcerStartTime;
35
35
  logger;
36
36
  usedCookieSecret;
37
+ shouldSendTelemetry;
37
38
  config;
38
39
  urlUtils;
39
40
  constructor(config, request, options) {
@@ -62,6 +63,7 @@ export class DefaultContext {
62
63
  if (!this.isMobile) {
63
64
  this.setCookiesOnContext();
64
65
  }
66
+ this.shouldSendTelemetry = false;
65
67
  }
66
68
  get isMobile() {
67
69
  return this.tokenOrigin === TokenOrigin.HEADER;
@@ -175,6 +177,7 @@ export class DefaultContext {
175
177
  enforcerStartTime: this.enforcerStartTime,
176
178
  blockAction: this.blockAction,
177
179
  pxdeVerified: this.pxdeVerified,
180
+ shouldSendTelemetry: this.shouldSendTelemetry,
178
181
  logger: this.shouldSendLogs
179
182
  ? {
180
183
  logs: this.logger.getLogs(),
@@ -27,6 +27,7 @@ export class SerializedContext {
27
27
  vidSource;
28
28
  tokenOrigin;
29
29
  uuid;
30
+ shouldSendTelemetry;
30
31
  constructor(config, contextJson, request, urlUtils) {
31
32
  this.action = contextJson.action;
32
33
  this.reasons = contextJson.reasons;
@@ -53,6 +54,7 @@ export class SerializedContext {
53
54
  this.graphqlData = contextJson.graphqlData;
54
55
  this.enforcerStartTime = contextJson.enforcerStartTime;
55
56
  this.blockAction = contextJson.blockAction;
57
+ this.shouldSendTelemetry = contextJson.shouldSendTelemetry;
56
58
  }
57
59
  createRequestData({ requestData }, request, urlUtils) {
58
60
  return {
@@ -9,6 +9,7 @@ import { HttpActivityClient, HttpBatchedActivityClient } from '../activities';
9
9
  import { HttpLogServiceClient } from '../logger';
10
10
  import { AccountDefender, BotDefender, CredentialIntelligence, HypeSaleChallenge, ProductName, } from '../products';
11
11
  import { EnforcerError, isValidTokenVersion } from '../utils';
12
+ import { DefaultSnippetRetriever } from '../snippet_injection';
12
13
  export const createEnforcerInitializationBlock = (config, options) => {
13
14
  const tokenVersion = config.tokenVersion;
14
15
  if (!isValidTokenVersion(tokenVersion)) {
@@ -50,6 +51,8 @@ export const createEnforcerInitializationBlock = (config, options) => {
50
51
  hmacUtils,
51
52
  })
52
53
  : null);
54
+ const snippetInjector = options.snippetInjector ?? null;
55
+ const snippetRetriever = options.snippetRetriever || (snippetInjector ? new DefaultSnippetRetriever(config) : null);
53
56
  const allOptions = {
54
57
  httpClient,
55
58
  base64Utils,
@@ -69,6 +72,8 @@ export const createEnforcerInitializationBlock = (config, options) => {
69
72
  remoteConfigStorageClient,
70
73
  remoteConfigServiceClient,
71
74
  remoteConfigUpdater,
75
+ snippetInjector,
76
+ snippetRetriever,
72
77
  };
73
78
  const products = createEnforcerProducts(config, options.products, base64Utils, hashUtils, urlUtils, ipRangeChecker);
74
79
  return { products, ...allOptions };
package/lib/esm/index.js CHANGED
@@ -7,6 +7,7 @@ export * from './context';
7
7
  export * from './custom_parameters';
8
8
  export * from './sensitive_request';
9
9
  export * from './monitored_request';
10
+ export * from './snippet_injection';
10
11
  export * from './cors';
11
12
  export * from './enforcer';
12
13
  export * from './filter';
@@ -1,8 +1,8 @@
1
1
  import { CompositePhase, SendLogsPhase } from '../impl';
2
- import { ClearLogsPhase } from '../impl/ClearLogsPhase';
2
+ import { ClearLogsPhase, SendTelemetryActivityPhase } from '../impl';
3
3
  export class EndEnforcerFlow extends CompositePhase {
4
- constructor(config, { logServiceClient }) {
5
- const phases = [];
4
+ constructor(config, { logServiceClient, telemetry, }) {
5
+ const phases = [new SendTelemetryActivityPhase(telemetry)];
6
6
  if (logServiceClient) {
7
7
  phases.push(new SendLogsPhase(config, logServiceClient));
8
8
  }
@@ -1,6 +1,6 @@
1
1
  import { ProductName } from '../../products';
2
2
  import { isNullOrUndefined } from '../../utils';
3
- import { CompositePhase, FilterPhase, FirstPartyPhase, PreflightPhase, TelemetryPhase, UpdateRemoteConfigPhase, } from '../impl';
3
+ import { CompositePhase, FilterPhase, FirstPartyPhase, PreflightPhase, UpdateRemoteConfigPhase, IdentifyTelemetryRequestPhase, } from '../impl';
4
4
  export class FilterFlow extends CompositePhase {
5
5
  constructor(config, { httpClient, products, cors, telemetry, remoteConfigUpdater, }) {
6
6
  const phases = [];
@@ -13,7 +13,7 @@ export class FilterFlow extends CompositePhase {
13
13
  phases.push(new FilterPhase(filterProducts));
14
14
  }
15
15
  phases.push(new PreflightPhase(config, cors));
16
- phases.push(new TelemetryPhase(telemetry));
16
+ phases.push(new IdentifyTelemetryRequestPhase(telemetry));
17
17
  if (remoteConfigUpdater) {
18
18
  const updateRemoteConfigPhase = new UpdateRemoteConfigPhase(config, remoteConfigUpdater);
19
19
  phases.push(updateRemoteConfigPhase);
@@ -1,9 +1,9 @@
1
1
  import { CompositePhase, EnrichContextFromResponsePhase, ModifyOutgoingResponsePhase, SendAsyncActivitiesOnResponsePhase, } from '../impl';
2
2
  export class PostEnforceFlow extends CompositePhase {
3
- constructor(config, { products, activityClient, }) {
3
+ constructor(config, { products, activityClient, snippetRetriever, snippetInjector, }) {
4
4
  super([
5
5
  new EnrichContextFromResponsePhase(config, products),
6
- new ModifyOutgoingResponsePhase(config, Object.values(products)),
6
+ new ModifyOutgoingResponsePhase(config, Object.values(products), snippetRetriever, snippetInjector),
7
7
  new SendAsyncActivitiesOnResponsePhase(activityClient),
8
8
  ]);
9
9
  }
@@ -0,0 +1,13 @@
1
+ export class IdentifyTelemetryRequestPhase {
2
+ telemetry;
3
+ constructor(telemetry) {
4
+ this.telemetry = telemetry;
5
+ }
6
+ async execute(context) {
7
+ if (await this.telemetry.isValidTelemetryRequest(context)) {
8
+ context.shouldSendTelemetry = true;
9
+ return { done: true };
10
+ }
11
+ return { done: false };
12
+ }
13
+ }
@@ -1,16 +1,32 @@
1
1
  import { PXHDSource, PXHDUtils } from '../../pxhd';
2
+ import { CONTENT_TYPE_HEADER_NAME, ContentType } from '../../http';
2
3
  export class ModifyOutgoingResponsePhase {
3
4
  config;
4
5
  products;
5
- constructor(config, products) {
6
+ snippetRetriever;
7
+ snippetInjector;
8
+ constructor(config, products, snippetRetriever, snippetInjector) {
6
9
  this.config = config;
7
10
  this.products = products;
11
+ this.snippetRetriever = snippetRetriever;
12
+ this.snippetInjector = snippetInjector;
8
13
  }
9
14
  async execute(context) {
10
15
  await Promise.all(this.products.map((product) => product?.modifyOutgoingResponse(context)));
11
16
  if (context.pxhd?.source === PXHDSource.RISK && context.response) {
12
17
  PXHDUtils.addPxhdToOutgoingResponse(this.config, context, context.response);
13
18
  }
19
+ await this.handleSnippetInjection(context);
14
20
  return { done: false };
15
21
  }
22
+ async handleSnippetInjection(context) {
23
+ if (this.snippetRetriever &&
24
+ this.snippetInjector &&
25
+ this.config.snippetInjectionEnabled &&
26
+ !!context.response?.headers?.get(CONTENT_TYPE_HEADER_NAME)?.includes(ContentType.TEXT_HTML)) {
27
+ context.logger.debug('snippet injection enabled');
28
+ const snippet = await this.snippetRetriever.retrieveSnippet(context);
29
+ context.response = await this.snippetInjector.injectSnippet(context.response, snippet);
30
+ }
31
+ }
16
32
  }
@@ -41,6 +41,7 @@ export class RiskApiPhase extends DecideActionPhase {
41
41
  uuid: 'uuid',
42
42
  action: 'blockAction',
43
43
  dataEnrichment: 'pxde',
44
+ telemetryRequested: 'shouldSendTelemetry',
44
45
  });
45
46
  if (riskResponse.dataEnrichment) {
46
47
  context.pxdeVerified = true;
@@ -1,12 +1,11 @@
1
- export class TelemetryPhase {
1
+ export class SendTelemetryActivityPhase {
2
2
  telemetry;
3
3
  constructor(telemetry) {
4
4
  this.telemetry = telemetry;
5
5
  }
6
6
  async execute(context) {
7
- if (await this.telemetry.isValidTelemetryRequest(context)) {
8
- await this.sendTelemetry(context);
9
- return { done: true };
7
+ if (context.shouldSendTelemetry) {
8
+ await this.telemetry.sendTelemetry(context);
10
9
  }
11
10
  return { done: false };
12
11
  }
@@ -1,7 +1,6 @@
1
1
  export * from './FirstPartyPhase';
2
2
  export * from './FilterPhase';
3
3
  export * from './PreflightPhase';
4
- export * from './TelemetryPhase';
5
4
  export * from './EnrichContextFromRequestPhase';
6
5
  export * from './ParseTokenPhase';
7
6
  export * from './DecideActionPhase';
@@ -17,3 +16,5 @@ export * from './CompositePhase';
17
16
  export * from './SendLogsPhase';
18
17
  export * from './/UpdateRemoteConfigPhase';
19
18
  export * from './ClearLogsPhase';
19
+ export * from './IdentifyTelemetryRequestPhase';
20
+ export * from './SendTelemetryActivityPhase';
@@ -23,12 +23,12 @@ export class PostRiskApiClientBase {
23
23
  const riskActivity = this.createRiskActivity(context);
24
24
  response = await this.sendRiskActivity(riskActivity, riskApiData, context);
25
25
  if (response?.status !== 200) {
26
- return await this.handleS2SError(riskApiData, response);
26
+ return await this.handleS2SError(riskApiData, context, response);
27
27
  }
28
28
  riskResponse = this.createRiskResponse(response);
29
29
  riskApiData.riskResponse = riskResponse;
30
30
  if (!(await riskResponse.validate())) {
31
- return await this.handleS2SError(riskApiData, response);
31
+ return await this.handleS2SError(riskApiData, context, response);
32
32
  }
33
33
  context.logger.debug(`received risk response, score: ${riskResponse.score}, rtt: ${riskApiData.riskRtt}`);
34
34
  return riskApiData;
@@ -38,7 +38,7 @@ export class PostRiskApiClientBase {
38
38
  return this.handleS2STimeout(riskApiData);
39
39
  }
40
40
  context.logger.error(`caught error in risk api: ${err} - ${JSON.stringify(context.requestData.url)}`);
41
- return await this.handleS2SError(riskApiData, response, err);
41
+ return await this.handleS2SError(riskApiData, context, response, err);
42
42
  }
43
43
  }
44
44
  /**
@@ -105,7 +105,7 @@ export class PostRiskApiClientBase {
105
105
  riskApiData.riskApiCallResult = RiskApiCallResult.TIMEOUT;
106
106
  return riskApiData;
107
107
  }
108
- async handleS2SError(riskApiData, response, error) {
108
+ async handleS2SError(riskApiData, context, response, error) {
109
109
  riskApiData.riskApiCallResult = RiskApiCallResult.ERROR;
110
110
  riskApiData.errorReason = S2SErrorReason.UNKNOWN_ERROR;
111
111
  if (response) {
@@ -131,6 +131,7 @@ export class PostRiskApiClientBase {
131
131
  const existingMessage = riskApiData.errorMessage;
132
132
  riskApiData.errorMessage = existingMessage ? `${existingMessage}, ${errorMessage}` : errorMessage;
133
133
  }
134
+ context.logger.debug(`S2S error message: ${riskApiData.errorMessage}`);
134
135
  return riskApiData;
135
136
  }
136
137
  }
@@ -38,6 +38,9 @@ export class RiskResponseBase {
38
38
  get additionalRiskInfo() {
39
39
  return this.riskResponse?.additional_risk_info;
40
40
  }
41
+ get telemetryRequested() {
42
+ return this.riskResponse?.telemetry_requested;
43
+ }
41
44
  get drc() {
42
45
  return this.riskResponse?.drc;
43
46
  }
@@ -52,6 +55,7 @@ export class RiskResponseBase {
52
55
  score: this.score,
53
56
  drc: this.drc,
54
57
  additionalRiskInfo: this.additionalRiskInfo,
58
+ telemetryRequested: this.telemetryRequested,
55
59
  uuid: this.uuid,
56
60
  };
57
61
  }
@@ -1,6 +1,7 @@
1
1
  export class SerializedRiskResponse {
2
2
  action;
3
3
  additionalRiskInfo;
4
+ telemetryRequested;
4
5
  dataEnrichment;
5
6
  drc;
6
7
  message;
@@ -12,6 +13,7 @@ export class SerializedRiskResponse {
12
13
  constructor(riskResponseJson) {
13
14
  this.action = riskResponseJson.action;
14
15
  this.additionalRiskInfo = riskResponseJson.additionalRiskInfo;
16
+ this.telemetryRequested = riskResponseJson.telemetryRequested;
15
17
  this.dataEnrichment = riskResponseJson.dataEnrichment;
16
18
  this.drc = riskResponseJson.drc;
17
19
  this.message = riskResponseJson.message;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ export * from './snippet_injector';
2
+ export * from './snippet_retriever';
3
+ export * from './utils';
4
+ export * from './CustomSnippetFunction';
@@ -0,0 +1 @@
1
+ export * from './ISnippetInjector';
@@ -0,0 +1,36 @@
1
+ import { FirstPartySuffix, getMostCustomizedFirstPartyPath } from '../../products';
2
+ import { createDefaultSnippet } from '../utils';
3
+ export class DefaultSnippetRetriever {
4
+ config;
5
+ constructor(config) {
6
+ this.config = config;
7
+ }
8
+ async retrieveSnippet(context) {
9
+ return (await this.retrieveCustomSnippet(context)) || this.retrieveDefaultSnippet(context);
10
+ }
11
+ async retrieveCustomSnippet(context) {
12
+ if (!this.config.createCustomSnippet) {
13
+ return null;
14
+ }
15
+ try {
16
+ const snippet = await this.config.createCustomSnippet(this.config.getActiveConfig(), context.requestData.request.getUnderlyingRequest(), context.response.getUnderlyingResponse());
17
+ if (!snippet || typeof snippet !== 'string') {
18
+ context.logger.debug(`invalid custom snippet of type ${typeof snippet}`);
19
+ return null;
20
+ }
21
+ context.logger.debug('retrieving custom snippet');
22
+ return snippet;
23
+ }
24
+ catch (e) {
25
+ context.logger.debug(`failed retrieving custom snippet: ${e}`);
26
+ }
27
+ return null;
28
+ }
29
+ retrieveDefaultSnippet(context) {
30
+ const sensorSrc = this.config.firstPartyEnabled
31
+ ? getMostCustomizedFirstPartyPath(this.config, FirstPartySuffix.SENSOR)
32
+ : `${this.config.backendClientUrl}/${this.config.appId}/main.min.js`;
33
+ context.logger.debug(`retrieving default snippet with src ${sensorSrc}`);
34
+ return createDefaultSnippet(this.config.appId, sensorSrc);
35
+ }
36
+ }
@@ -0,0 +1,2 @@
1
+ export * from './ISnippetRetriever';
2
+ export * from './DefaultSnippetRetriever';
@@ -0,0 +1,9 @@
1
+ export const createDefaultSnippet = (appId, sensorSrc) => `<script type="text/javascript">
2
+ (function(){
3
+ window._pxAppId = '${appId}';
4
+ var p = document.getElementsByTagName('script')[0], s = document.createElement('script');
5
+ s.async = 1;
6
+ s.src = '${sensorSrc}';
7
+ p.parentNode.insertBefore(s,p);
8
+ }());
9
+ </script>`;
@@ -11,4 +11,4 @@ export const PUSH_DATA_FEATURE_HEADER_NAME = 'x-px-feature';
11
11
  export const EMAIL_ADDRESS_REGEX = /^[a-zA-Z0-9_+&*-]+(?:\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,7}$/;
12
12
  export const URL_REGEX = /^(https?:)\/\/(([^@\s:\/]+):?([^@\s\/]*)@)?(([^:\/?#]*)(?:\:([0-9]+))?)(\/?[^?#]*)(\?[^#]*|)(#.*|)$/;
13
13
  export const REGEX_STRUCTURE = /^\/(.+?)\/([gimsuyvd]*)$/;
14
- export const CORE_MODULE_VERSION = 'JS Core 0.23.3';
14
+ export const CORE_MODULE_VERSION = 'JS Core 0.24.1';
@@ -0,0 +1,9 @@
1
+ export class SimpleRequestIdGenerator {
2
+ requestId;
3
+ constructor(requestId) {
4
+ this.requestId = requestId;
5
+ }
6
+ generateRequestId() {
7
+ return this.requestId;
8
+ }
9
+ }
@@ -1 +1,2 @@
1
1
  export * from './IRequestIdGenerator';
2
+ export * from './SimpleRequestIdGenerator';
@@ -39,6 +39,7 @@ export type CommonActivityDetails = {
39
39
  ci_version?: CredentialIntelligenceVersion;
40
40
  sso_step?: SsoStep;
41
41
  remote_config_version?: number;
42
+ remote_config_id?: string;
42
43
  enforcer_start_time: number;
43
44
  raw_url?: string;
44
45
  used_cookie_secret?: string;
@@ -9,6 +9,7 @@ import { CredentialEndpointConfiguration, CredentialIntelligenceVersion, CustomL
9
9
  import { CustomRequestFunction } from './CustomRequestFunction';
10
10
  import { ExtractGraphQLKeywordsFunction } from '../graphql';
11
11
  import { TokenVersion } from '../risk_token';
12
+ import { CustomSnippetFunction } from '../snippet_injection';
12
13
  export declare abstract class ConfigurationBase<Req, Res, Added, Removed extends string> implements IConfiguration<Req, Res, Added, Removed> {
13
14
  protected configParams: RequiredAllConfigurationParams<Req, Res, Added, Removed>;
14
15
  protected readonly staticConfigParams: StaticConfigurationParams<Req, Res, Added, Removed>;
@@ -115,5 +116,7 @@ export declare abstract class ConfigurationBase<Req, Res, Added, Removed extends
115
116
  get urlDecodeReservedCharacters(): boolean;
116
117
  get securedPxhdEnabled(): boolean;
117
118
  get tokenVersion(): `${TokenVersion}`;
119
+ get snippetInjectionEnabled(): boolean;
120
+ get createCustomSnippet(): CustomSnippetFunction<Req, Res, Added, Removed> | null;
118
121
  get enableBlockedUrlOnCaptchaBlockPage(): boolean;
119
122
  }
@@ -4,7 +4,7 @@ import { CoreConfigurationParams, StaticConfigurationParams, RemoteConfiguration
4
4
  import { IBase64Utils } from '../utils';
5
5
  export declare abstract class ConfigurationBuilderBase<Req, Res, Added, Removed, ParamsType extends CoreConfigurationParams<Req, Res, Added, Removed>, ConfigurationType extends IConfiguration<Req, Res, Added, Removed>> {
6
6
  protected readonly base64Utils: IBase64Utils;
7
- protected abstract createRemoteConfigStorageClient(staticConfig: ConfigurationType): Promise<IRemoteConfigStorageClient<Req, Res, Added, Removed>>;
7
+ protected abstract createRemoteConfigStorageClient(staticConfig: ConfigurationType): Promise<IRemoteConfigStorageClient<Req, Res, Added, Removed> | null>;
8
8
  protected abstract createConfiguration(params: ParamsType | StaticConfigurationParams<Req, Res, Added, Removed>): ConfigurationType;
9
9
  protected constructor(base64Utils: IBase64Utils);
10
10
  build(params: StaticConfigurationParams<Req, Res, Added, Removed>): Promise<ConfigurationType>;
@@ -8,6 +8,7 @@ import { CredentialEndpointConfiguration, CredentialIntelligenceVersion, CustomL
8
8
  import { CustomRequestFunction } from './CustomRequestFunction';
9
9
  import { ExtractGraphQLKeywordsFunction } from '../graphql';
10
10
  import { TokenVersion } from '../risk_token';
11
+ import { CustomSnippetFunction } from '../snippet_injection';
11
12
  export interface IConfiguration<Req, Res, Added, Removed> {
12
13
  /**
13
14
  * The application ID.
@@ -370,6 +371,14 @@ export interface IConfiguration<Req, Res, Added, Removed> {
370
371
  * Whether to add the Secure attribute when setting the PXHD cookie.
371
372
  */
372
373
  readonly securedPxhdEnabled: boolean;
374
+ /**
375
+ * Whether snippet injection on response bodies is enabled.
376
+ */
377
+ readonly snippetInjectionEnabled: boolean;
378
+ /**
379
+ * A custom function that returns a string representing the snippet to be injected.
380
+ */
381
+ readonly createCustomSnippet: CustomSnippetFunction<Req, Res, Added, Removed> | null;
373
382
  /**
374
383
  * Whether to include the request url in captcha block page
375
384
  */
@@ -7,6 +7,7 @@ import { CustomParametersFunction } from '../../custom_parameters';
7
7
  import { CustomBlockResponseHeadersHandler, CustomPreflightHandler } from '../../cors';
8
8
  import { CustomRequestFunction } from '../CustomRequestFunction';
9
9
  import { TokenVersion } from '../../risk_token';
10
+ import { CustomSnippetFunction } from '../../snippet_injection';
10
11
  export type StaticConfigurationParamsOnly = {
11
12
  px_app_id: string;
12
13
  px_cookie_secret: string | string[];
@@ -34,7 +35,11 @@ export type TokenV3ConfigurationParamsOnly = {
34
35
  px_risk_cookie_min_iterations?: number;
35
36
  px_risk_cookie_max_iterations?: number;
36
37
  };
37
- export type CommonConfigurationParams<Req, Res, Added, Removed> = TokenV3ConfigurationParamsOnly & BatchedActivitiesConfigParamsOnly & {
38
+ export type SnippetInjectionParamsOnly<Req, Res, Added, Removed> = {
39
+ px_snippet_injection_enabled?: boolean;
40
+ px_create_custom_snippet?: CustomSnippetFunction<Req, Res, Added, Removed> | null;
41
+ };
42
+ export type CommonConfigurationParams<Req, Res, Added, Removed> = TokenV3ConfigurationParamsOnly & BatchedActivitiesConfigParamsOnly & SnippetInjectionParamsOnly<Req, Res, Added, Removed> & {
38
43
  px_s2s_timeout?: number;
39
44
  px_backend_url?: string;
40
45
  px_user_agent_max_length?: number;
@@ -41,5 +41,6 @@ export type ContextJson<Req = unknown, Res = unknown> = {
41
41
  logger?: {
42
42
  logs: LogRecord[];
43
43
  };
44
+ shouldSendTelemetry: boolean;
44
45
  };
45
46
  export {};
@@ -44,6 +44,7 @@ export declare class DefaultContext<Req, Res, Added, Removed> implements IContex
44
44
  enforcerStartTime?: number;
45
45
  logger: ILogger;
46
46
  usedCookieSecret?: string;
47
+ shouldSendTelemetry: boolean;
47
48
  protected readonly config: IConfiguration<Req, Res, Added, Removed>;
48
49
  protected readonly urlUtils: IUrlUtils;
49
50
  constructor(config: IConfiguration<Req, Res, Added, Removed>, request: IIncomingRequest<Req>, options: DefaultContextOptions);
@@ -38,6 +38,7 @@ export declare class SerializedContext<Req, Res, Added, Removed> implements ICon
38
38
  vidSource?: VidSource;
39
39
  tokenOrigin: TokenOrigin;
40
40
  uuid?: string;
41
+ shouldSendTelemetry: boolean;
41
42
  constructor(config: IConfiguration<Req, Res, Added, Removed>, contextJson: ContextJson<Req, Res>, request: IIncomingRequest<Req>, urlUtils: IUrlUtils);
42
43
  protected createRequestData({ requestData }: ContextJson<Req, Res>, request: IIncomingRequest<Req>, urlUtils: IUrlUtils): RequestData<Req>;
43
44
  protected createTokenData({ tokenData }: ContextJson<Req, Res>, config: IConfiguration<Req, Res, Added, Removed>): TokenData<Req, Res>;
@@ -129,4 +129,8 @@ export interface IContext<Req, Res> {
129
129
  * The time the Enforcer process started
130
130
  */
131
131
  readonly enforcerStartTime?: number;
132
+ /**
133
+ * Whether to send telemetry activity for the current request.
134
+ */
135
+ shouldSendTelemetry: boolean;
132
136
  }
@@ -10,6 +10,7 @@ import { Products } from '../products';
10
10
  import { IGraphQLParser } from '../graphql';
11
11
  import { ILogServiceClient } from '../logger';
12
12
  import { IRemoteConfigStorageClient, IRemoteConfigServiceClient, IRemoteConfigUpdater } from '../config';
13
+ import { ISnippetInjector, ISnippetRetriever } from '../snippet_injection';
13
14
  export type EnforcerOptions<Req, Res, Added, Removed> = {
14
15
  httpClient: IHttpClient;
15
16
  base64Utils: IBase64Utils;
@@ -30,4 +31,6 @@ export type EnforcerOptions<Req, Res, Added, Removed> = {
30
31
  remoteConfigUpdater?: IRemoteConfigUpdater<Req, Res> | null;
31
32
  remoteConfigStorageClient?: IRemoteConfigStorageClient<Req, Res, Added, Removed> | null;
32
33
  remoteConfigServiceClient?: IRemoteConfigServiceClient<Req, Res, Added, Removed> | null;
34
+ snippetRetriever?: ISnippetRetriever<Req, Res> | null;
35
+ snippetInjector?: ISnippetInjector<Res> | null;
33
36
  };
@@ -7,6 +7,7 @@ export * from './context';
7
7
  export * from './custom_parameters';
8
8
  export * from './sensitive_request';
9
9
  export * from './monitored_request';
10
+ export * from './snippet_injection';
10
11
  export * from './cors';
11
12
  export * from './enforcer';
12
13
  export * from './filter';
@@ -1,6 +1,8 @@
1
1
  import { IConfiguration } from '../../config';
2
2
  import { CompositePhase } from '../impl';
3
3
  import { EnforcerOptions } from '../../enforcer';
4
+ type RequiredEndEnforcerFlowOptions = 'logServiceClient' | 'telemetry';
4
5
  export declare class EndEnforcerFlow<Req, Res, Added, Removed> extends CompositePhase<Req, Res> {
5
- constructor(config: IConfiguration<Req, Res, Added, Removed>, { logServiceClient }: Pick<Required<EnforcerOptions<Req, Res, Added, Removed>>, 'logServiceClient'>);
6
+ constructor(config: IConfiguration<Req, Res, Added, Removed>, { logServiceClient, telemetry, }: Pick<Required<EnforcerOptions<Req, Res, Added, Removed>>, RequiredEndEnforcerFlowOptions>);
6
7
  }
8
+ export {};
@@ -2,5 +2,5 @@ import { IConfiguration } from '../../config';
2
2
  import { EnforcerOptions } from '../../enforcer';
3
3
  import { CompositePhase } from '../impl';
4
4
  export declare class PostEnforceFlow<Req, Res, Added, Removed> extends CompositePhase<Req, Res> {
5
- constructor(config: IConfiguration<Req, Res, Added, Removed>, { products, activityClient, }: Pick<Required<EnforcerOptions<Req, Res, Added, Removed>>, 'products' | 'activityClient'>);
5
+ constructor(config: IConfiguration<Req, Res, Added, Removed>, { products, activityClient, snippetRetriever, snippetInjector, }: Pick<Required<EnforcerOptions<Req, Res, Added, Removed>>, 'products' | 'activityClient' | 'snippetRetriever' | 'snippetInjector'>);
6
6
  }
@@ -0,0 +1,9 @@
1
+ import { IPhase } from '../IPhase';
2
+ import { PhaseResult } from '../PhaseResult';
3
+ import { IContext } from '../../context';
4
+ import { ITelemetry } from '../../telemetry';
5
+ export declare class IdentifyTelemetryRequestPhase<Req, Res> implements IPhase<Req, Res> {
6
+ protected readonly telemetry: ITelemetry<Req, Res>;
7
+ constructor(telemetry: ITelemetry<Req, Res>);
8
+ execute(context: IContext<Req, Res>): Promise<PhaseResult>;
9
+ }
@@ -3,9 +3,13 @@ import { IProduct } from '../../products';
3
3
  import { IPhase } from '../IPhase';
4
4
  import { PhaseResult } from '../PhaseResult';
5
5
  import { IConfiguration } from '../../config';
6
+ import { ISnippetInjector, ISnippetRetriever } from '../../snippet_injection';
6
7
  export declare class ModifyOutgoingResponsePhase<Req, Res, Added, Removed> implements IPhase<Req, Res> {
7
8
  protected readonly config: IConfiguration<Req, Res, Added, Removed>;
8
9
  protected readonly products: IProduct<any, Req, Res>[];
9
- constructor(config: IConfiguration<Req, Res, Added, Removed>, products: IProduct<any, Req, Res>[]);
10
+ protected readonly snippetRetriever?: ISnippetRetriever<Req, Res> | null;
11
+ protected readonly snippetInjector?: ISnippetInjector<Res> | null;
12
+ constructor(config: IConfiguration<Req, Res, Added, Removed>, products: IProduct<any, Req, Res>[], snippetRetriever?: ISnippetRetriever<Req, Res> | null, snippetInjector?: ISnippetInjector<Res> | null);
10
13
  execute(context: IContext<Req, Res>): Promise<PhaseResult>;
14
+ protected handleSnippetInjection(context: IContext<Req, Res>): Promise<void>;
11
15
  }
@@ -2,7 +2,7 @@ import { IPhase } from '../IPhase';
2
2
  import { PhaseResult } from '../PhaseResult';
3
3
  import { IContext } from '../../context';
4
4
  import { ITelemetry } from '../../telemetry';
5
- export declare class TelemetryPhase<Req, Res> implements IPhase<Req, Res> {
5
+ export declare class SendTelemetryActivityPhase<Req, Res> implements IPhase<Req, Res> {
6
6
  protected readonly telemetry: ITelemetry<Req, Res>;
7
7
  constructor(telemetry: ITelemetry<Req, Res>);
8
8
  execute(context: IContext<Req, Res>): Promise<PhaseResult>;