PerimeterX JavaScript Core
Overview
The purpose of this repository is to provide the components and utilities needed to build a JavaScript/TypeScript enforcer
on any platform/environment. This repository is not a complete running enforcer; rather, it is a tool kit with all the
building blocks that an enforcer comprises, separated into small, self-contained chunks.
A single repository for core enforcer components allows for:
- Consistent functionality across all JavaScript enforcers.
- Faster delivery of new features and bug fixes.
Design Principles
This library was designed with an interface-first mindset. In other words, all provided classes are implementations of defined
interfaces. Whenever possible, you should use the provided, default implementation for a particular feature to ensure consistent
behavior across enforcers.
However, if the default component cannot be made to function in the framework you are working on, extend it or create a
new class that implements the relevant interface, and it will work with all the other components in the library.
Another important note is that components such as the risk API client, token parser, products, and other such classes are not
permitted to modify the context. Rather than make alterations to the context directly, they return data that is then used by phases
to adjust the context as needed. This helps separate specific product logic (e.g., Bot Defender, Account Defender, etc.) from
the logic and actions of the enforcer as a whole.
Naming Conventions
- Interfaces always start with the letter
I
(e.g., IFirstParty
) - Abstract classes always end with the word
Base
(e.g., TokenParserBase
) - Complete, standard implementations start with the word
Default
(e.g., DefaultTelemetry
) - Utility functions relevant to a particular feature live in a
Utils
namespace named for the feature (e.g., PXHDUtils
)
Library Components
In using this library, most features will work out-of-the-box. However, there are certain classes you will need to modify
depending on the specific platform you're developing for.
Most development you will need to complete will most likely be in the following areas.
Enforcer
While every enforcer will require a different implementation, the API and sequence of events in an enforcer should be consistent
across different SDKs. To maintain consistency, the following interface and abstract class have been included.
IEnforcer
- Represents the enforcer itself. The parameters and return values are any
due to the varying nature of each
platform. The specific enforcer implementation you create should have a concrete signature.
EnforcerBase
- An abstract base class that provides the basic enforcer flow with the default building blocks. Select abstract
functions must be implemented (such as constructing the context, etc.). All key functional building blocks are interfaces
that can be swapped out, and modifications to the flow can be made by overriding the protected class methods.
Configuration
Every enforcer configuration is different. This includes how the configurations are stored (a JSON file, in-memory object,
or KV namespace), the configuration values themselves, and logging functionality. Utilize these components to simplify
your enforcer's configuration setup.
IConfiguration
- Represents the enforcer configuration and is required to construct most other components
in this library.
ConfigurationBase
- An implementation of the above interface that validates provided configurations and merges
them with default ones.
DefaultConfigurations
- A static object complete with all default enforcer configurations.
Context
Different platforms expose varying levels of information about the HTTP request and server environment. Additionally,
the cryptographic capabilities on each platform may vary, allowing the support of either V2 or V3 of the Risk Cookie.
While you will need to create a custom implementation for the request context, you can use these tools to help.
IContext
- Represents the request context.
DefaultContext
- An implementation of the above interface that takes care of basic context setup.
HTTP
Every framework has a different API for creating and sending HTTP requests and responses. This library reduces this functionality
into several interfaces:
IHeaders
- Represents the HTTP request/response headers.
IIncomingRequest
- Represents an HTTP request from the user.
IOutgoingResponse
- Represents an HTTP response which is returned to the user.
IOutgoingRequest
- Represents an HTTP request that can be executed (sent to other origin)
IIncomingResponse
- Represents an HTTP response that received from sending IOutgoingRequest
IHttpClient
- Represents a client that can send an IIncomingRequest
and return an IOutgoingResponse
.
When developing your JavaScript/TypeScript enforcer with this library, you will need to wrap the native HTTP request/response
objects such that they conform to these interfaces. Of course, you are free to extend these interfaces as needed.
You will also need to implement an HTTP client. You can do this however you see fit: use the platform's built-in API
or use an external library if needed. This library includes implementations using phin
(PhinHttpClient
and PhinIncomingResponse
)
for Node.js-based enforcers.
Crypto
Since cryptographic functionality varies from platform to platform, this library's crypto utils takes the form of interfaces
as well.
IBase64Utils
- Represents a Base64 encoding and decoding utility that other components may require. The following implementations
are provided:
AtobBase64Utils
, which relies on native atob
and btoa
functions.BufferBase64Utils
, which relies on the native NodeJS Buffer.from()
function.JSBase64Base64Utils
, which uses the js-base64
dependency.
IHmacUtils
- Represents an HMAC-generating utility that other components may require. The following implementations are
provided:
CryptoHmacUtils
, which relies on the native NodeJS crypto
package.CryptoJSHmacUtils
, which uses the crypto-js
dependency.
ICipherUtils
- Represents an encryption and decryption utility that is required for RiskTokenV3. The following implementations
are provided:
CryptoCipherUtils
, which relies on the native NodeJS crypto
package.SubtleCryptoCipherUtils
, which relies on an object implementing the SubtleCrypto
interface. By default, it is assumed that the global crypto.subtle
implements this interface.
IHashUtils
- Represents a hashing utility that is required by Credential Intelligence. The following implementations are provided:
CryptoHashUtils
, which relies on the native NodeJS crypto
package.CryptoJSHashUtils
, which uses the crypto-js
dependency.SubtleCryptoHashUtils
, which relies on an object implementing the SubtleCrypto
interface. By default, it is assumed that the global crypto.subtle
implements this interface.
Other Dependencies
All dependencies in this library have been encapsulated into specific implementations with a defined interface and therefore
can be swapped with other implementations. These classes are:
Library | Implementation | Used For |
---|
crypto-js | CryptoJSHmacUtils | PXDE, telemetry, token V2/V3, etc. |
js-base64 | JSBase64Base64Utils | PXDE, telemetry, token V2, etc. |
uuid | UuidRequestIdGenerator | DefaultContext |
ip-range-check | DefaultIpRangeChecker | DefaultBotDefenderFilter |
phin | PhinHttpClient , PhinIncomingResponse | First party, Risk API, Activities |
For Contributors
Installing Dependencies
To install dependencies, run:
npm install
Building
To build the module as a library, run:
npm run build
Testing
To run unit tests, run:
npm run test
To see unit test coverage, run:
npm run coverage