Skip to content
This repository was archived by the owner on Oct 29, 2025. It is now read-only.

Commit 6552837

Browse files
authored
Merge pull request #165 from PerimeterX/dev
Dev to master
2 parents 63de221 + 4bf82e6 commit 6552837

File tree

6 files changed

+91
-4
lines changed

6 files changed

+91
-4
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/)
66
and this project adheres to [Semantic Versioning](http://semver.org/).
77

8+
## [2.13.0] - 2021-06-08
9+
10+
### Added
11+
12+
- CSP Support.
13+
814
## [2.12.1] - 2021-05-25
915

1016
### Fixed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
[PerimeterX](http://www.perimeterx.com) Shared base for NodeJS enforcers
77
=============================================================
88

9-
> Latest stable version: [v2.12.1](https://www.npmjs.com/package/perimeterx-node-core)
9+
> Latest stable version: [v2.13.0](https://www.npmjs.com/package/perimeterx-node-core)
1010
1111
This is a shared base implementation for PerimeterX Express enforcer and future NodeJS enforcers. For a fully functioning implementation example, see the [Node-Express enforcer](https://github.com/PerimeterX/perimeterx-node-express/) implementation.
1212

index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,5 @@
2626
module.exports = {
2727
PxEnforcer: require('./lib/pxenforcer'),
2828
PxClient: require('./lib/pxclient'),
29+
PxCdEnforcer: require('./lib/pxcdenforcer'),
2930
};

lib/pxcdenforcer.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
const { v4: uuidv4 } = require('uuid');
2+
const CSP_DATA = {
3+
CSP: 'csp',
4+
CSPRO: 'cspro',
5+
CSPRO_EXPOSURE: 'cspro_exposure',
6+
CSPRO_2: 'cspro_p2',
7+
CSPRO_2_EXPOSURE: 'cspro_p2_exposure',
8+
SESSION_ID_QUERY_PARAM: 'p',
9+
VID_QUERY_PARAM: 'v',
10+
REPORT_URI_STRING_LENGTH: 11
11+
};
12+
const CD_COOCKIE = '__pxvid';
13+
14+
function CdEnforce (cspData, req, res) {
15+
const cdVid = getCdCookie(req);
16+
handleCSP(res, cspData, cdVid);
17+
18+
}
19+
20+
function handleCSP(response, cspData, vid) {
21+
const rand = Math.floor(Math.random() * 100) + 1; //random number between 1-100 include
22+
try {
23+
let csp = cspData[CSP_DATA.CSP];
24+
const sessionId = uuidv4();
25+
if (csp) {
26+
csp = updateCspReportUri(csp, sessionId, vid);
27+
response.setHeader('Content-Security-Policy', csp);
28+
}
29+
30+
let cspReportOnly = getCSPROHeader(cspData, CSP_DATA.CSPRO_EXPOSURE, CSP_DATA.CSPRO, rand, sessionId, vid);
31+
if (cspReportOnly) {
32+
response.setHeader('Content-Security-Policy-Report-Only', cspReportOnly);
33+
} else {
34+
cspReportOnly = getCSPROHeader(cspData, CSP_DATA.CSPRO_2_EXPOSURE, CSP_DATA.CSPRO_2, rand, sessionId, vid);
35+
if (cspReportOnly) {
36+
response.setHeader('Content-Security-Policy-Report-Only', cspReportOnly);
37+
}
38+
}
39+
} catch (e) {
40+
// CSP couldnt be read, continue without it
41+
console.log(`Exception Caught in HandleCSP. error: ${e}`);
42+
}
43+
}
44+
45+
function getCSPROHeader(cspData, exposureKey, policyKey, rand, sessionId, vid) {
46+
const percentage = parseInt(cspData[exposureKey]);
47+
if (rand <= percentage) {
48+
let cspReportOnly = cspData[policyKey];
49+
if (cspReportOnly) {
50+
cspReportOnly = updateCspReportUri(cspReportOnly, sessionId, vid);
51+
return cspReportOnly;
52+
}
53+
}
54+
}
55+
56+
function updateCspReportUri(policy, sessionId, vid) {
57+
const matches = policy.match(/report-uri ([^ ;]+)/);
58+
if (matches && matches.length > 1) {
59+
const reportUrl = new URL(matches[1]);
60+
reportUrl.searchParams.append(CSP_DATA.SESSION_ID_QUERY_PARAM, sessionId);
61+
if (vid) {
62+
reportUrl.searchParams.append(CSP_DATA.VID_QUERY_PARAM, vid);
63+
}
64+
const result = reportUrl.toString();
65+
const index = matches.index + matches[0].length;
66+
policy = policy.slice(0, matches.index + CSP_DATA.REPORT_URI_STRING_LENGTH) + result + policy.slice(index);
67+
}
68+
69+
return policy;
70+
}
71+
72+
function getCdCookie(req) {
73+
const cookies = req.cookies;
74+
if (cookies && cookies[CD_COOCKIE]) {
75+
return cookies[CD_COOCKIE];
76+
}
77+
}
78+
79+
module.exports = CdEnforce;

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "perimeterx-node-core",
3-
"version": "2.12.1",
3+
"version": "2.13.0",
44
"description": "PerimeterX NodeJS shared core for various applications to monitor and block traffic according to PerimeterX risk score",
55
"main": "index.js",
66
"scripts": {
@@ -24,7 +24,8 @@
2424
"https-proxy-agent": "^5.0.0",
2525
"ip-range-check": "^0.2.0",
2626
"mu2": "^0.5.21",
27-
"raw-body": "^2.3.2"
27+
"raw-body": "^2.3.2",
28+
"uuid": "^8.3.2"
2829
},
2930
"devDependencies": {
3031
"eslint": "^6.8.0",

0 commit comments

Comments
 (0)