Skip to content

Commit

Permalink
Merge pull request #279 from PerimeterX/release/v3.10.0
Browse files Browse the repository at this point in the history
Release/v3.10.0 ->master
  • Loading branch information
chen-zimmer-px authored Mar 28, 2023
2 parents 1f84b95 + 1e70847 commit 60200c6
Show file tree
Hide file tree
Showing 10 changed files with 54 additions and 15 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [3.10.0] - 2023-03-28

### Added
- Support for handling graphQL requests with empty query field
- Support custom is sensitive request via function

## [3.9.0] - 2023-01-29

### Added
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
[PerimeterX](http://www.perimeterx.com) Shared base for NodeJS enforcers
=============================================================

> Latest stable version: [v3.9.0](https://www.npmjs.com/package/perimeterx-node-core)
> Latest stable version: [v3.10.0](https://www.npmjs.com/package/perimeterx-node-core)
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.

Expand Down
6 changes: 5 additions & 1 deletion lib/pxconfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ class PxConfig {
['JWT_HEADER_NAME', 'px_jwt_header_name'],
['JWT_HEADER_USER_ID_FIELD_NAME', 'px_jwt_header_user_id_field_name'],
['JWT_HEADER_ADDITIONAL_FIELD_NAMES', 'px_jwt_header_additional_field_names'],
['CUSTOM_IS_SENSITIVE_REQUEST', 'px_custom_is_sensitive_request']
];

configKeyMapping.forEach(([targetKey, sourceKey]) => {
Expand Down Expand Up @@ -176,7 +177,8 @@ class PxConfig {
userInput === 'px_login_successful_custom_callback' ||
userInput === 'px_modify_context' ||
userInput === 'px_cors_create_custom_block_response_headers' ||
userInput === 'px_cors_custom_preflight_handler'
userInput === 'px_cors_custom_preflight_handler' ||
userInput === 'px_custom_is_sensitive_request'
) {
if (typeof params[userInput] === 'function') {
return params[userInput];
Expand Down Expand Up @@ -359,6 +361,7 @@ function pxDefaultConfig() {
JWT_HEADER_NAME: '',
JWT_HEADER_USER_ID_FIELD_NAME: '',
JWT_HEADER_ADDITIONAL_FIELD_NAMES: [],
CUSTOM_IS_SENSITIVE_REQUEST: ''
};
}

Expand Down Expand Up @@ -431,6 +434,7 @@ const allowedConfigKeys = [
'px_jwt_header_name',
'px_jwt_header_user_id_field_name',
'px_jwt_header_additional_field_names',
'px_custom_is_sensitive_request'
];

module.exports = PxConfig;
21 changes: 20 additions & 1 deletion lib/pxcontext.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class PxContext {
this.originalRequest = req.originalRequest || req;
this.httpVersion = req.httpVersion || '';
this.httpMethod = req.method || '';
this.sensitiveRoute = this.isSpecialRoute(config.SENSITIVE_ROUTES, this.uri);
this.sensitiveRequest = () => this.isSensitiveRequest(req, config);
this.enforcedRoute = this.isSpecialRoute(config.ENFORCED_ROUTES, this.uri);
this.whitelistRoute = this.isSpecialRoute(config.WHITELIST_ROUTES, this.uri);
this.monitoredRoute = !this.enforcedRoute && this.isSpecialRoute(config.MONITORED_ROUTES, this.uri);
Expand Down Expand Up @@ -85,6 +85,25 @@ class PxContext {
}
}

isSensitiveRequest(request, config) {
return this.isSpecialRoute(config.SENSITIVE_ROUTES, this.uri) ||
this.isCustomSensitiveRequest(request, config);
}

isCustomSensitiveRequest(request, config) {
const customIsSensitiveRequest = config.CUSTOM_IS_SENSITIVE_REQUEST;
try {
if (customIsSensitiveRequest && customIsSensitiveRequest(request)) {
config.logger.debug('Custom sensitive request matched');
return true;
}
} catch (err) {
config.logger.debug(`Caught exception on custom sensitive request function: ${err}`);
}

return false;
}

getGraphqlDataFromBody(body) {
let jsonBody = null;
if (typeof body === 'string') {
Expand Down
2 changes: 1 addition & 1 deletion lib/pxcookie.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ function evalCookie(ctx, config) {
return ScoreEvaluateAction.COOKIE_INVALID;
}

if (ctx.sensitiveRoute) {
if (ctx.sensitiveRoute()) {
config.logger.debug(`Sensitive route match, sending Risk API. path: ${ctx.uri}`);
ctx.s2sCallReason = 'sensitive_route';
return ScoreEvaluateAction.SENSITIVE_ROUTE;
Expand Down
13 changes: 7 additions & 6 deletions lib/pxutil.js
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,10 @@ function isGraphql(req, config) {
// query: string (not null)
// output: Record [ OperationName -> OperationType ]
function parseGraphqlBody(query) {
if (!query) {
return null;
}

const pattern = /\s*(query|mutation|subscription)\s+(\w+)/gm;
let match;
const ret = {};
Expand Down Expand Up @@ -322,25 +326,22 @@ function isSensitiveGraphqlOperation(graphqlData, config) {
// graphqlBodyObject: {query: string?, operationName: string?, variables: any[]?}
// output: GraphqlData?
function getGraphqlData(graphqlBodyObject) {
if (!graphqlBodyObject || !graphqlBodyObject.query) {
if (!graphqlBodyObject) {
return null;
}

const parsedData = parseGraphqlBody(graphqlBodyObject.query);
if (!parsedData) {
return null;
}

const selectedOperationName =
graphqlBodyObject['operationName'] || (Object.keys(parsedData).length === 1 && Object.keys(parsedData)[0]);

if (!selectedOperationName || !parsedData[selectedOperationName]) {
if (!selectedOperationName || (parsedData && !parsedData[selectedOperationName])) {
return null;
}

const variables = extractVariables(graphqlBodyObject.variables);

return new GraphqlData(parsedData[selectedOperationName], selectedOperationName, variables);
return new GraphqlData(parsedData && parsedData[selectedOperationName], selectedOperationName, variables);
}

// input: object representing variables
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "perimeterx-node-core",
"version": "3.9.0",
"version": "3.10.0",
"description": "PerimeterX NodeJS shared core for various applications to monitor and block traffic according to PerimeterX risk score",
"main": "index.js",
"scripts": {
Expand Down
9 changes: 9 additions & 0 deletions test/graphql.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ describe('Graphql Testing', () => {
graphqlData.type.should.be.exactly('query');
});

it('should extract operation name if !query', () => {
const gqlObj = {
operationName: 'q1',
};

const graphqlData = pxutil.getGraphqlData(gqlObj);
graphqlData.name.should.be.exactly('q1');
});

it('extract with many queries', () => {
const gqlObj = {
query: 'query q1 { \n abc \n }\nmutation q2 {\n def\n }',
Expand Down
4 changes: 2 additions & 2 deletions test/pxenforcer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -847,7 +847,7 @@ describe('PX Enforcer - pxenforcer.js', () => {
return callback ? callback(null, data) : '';
});

const modifyCtx = sinon.stub().callsFake((ctx) => ctx.sensitiveRoute = true);
const modifyCtx = sinon.stub().callsFake((ctx) => ctx.sensitiveRequest = true);
const curParams = {
...params,
px_modify_context: modifyCtx,
Expand All @@ -857,7 +857,7 @@ describe('PX Enforcer - pxenforcer.js', () => {
enforcer = new pxenforcer(curParams, pxClient);
enforcer.enforce(req, null, () => {
(modifyCtx.calledOnce).should.equal(true);
(req.locals.pxCtx.sensitiveRoute).should.equal(true);
(req.locals.pxCtx.sensitiveRequest).should.equal(true);
done();
});
});
Expand Down

0 comments on commit 60200c6

Please sign in to comment.