+1243
| import type { ArcjetEmailRule, ArcjetBotRule, ArcjetFilterRule, ArcjetPromptInjectionDetectionRule, ArcjetRule, ArcjetMode, ArcjetTokenBucketRateLimitRule, ArcjetFixedWindowRateLimitRule, ArcjetSlidingWindowRateLimitRule, ArcjetShieldRule, ArcjetLogger, ArcjetSensitiveInfoRule, ArcjetWellKnownBot, ArcjetBotCategory, ArcjetEmailType, ArcjetSensitiveInfoType } from "@arcjet/protocol"; | ||
| import { ArcjetDecision } from "@arcjet/protocol"; | ||
| import type { Client } from "@arcjet/protocol/client.js"; | ||
| export * from "@arcjet/protocol"; | ||
| type Simplify<T> = { | ||
| [KeyType in keyof T]: T[KeyType]; | ||
| } & {}; | ||
| type UnionToIntersection<Union> = (Union extends unknown ? (distributedUnion: Union) => void : never) extends (mergedIntersection: infer Intersection) => void ? // The `& Union` is to allow indexing by the resulting type | ||
| Intersection & Union : never; | ||
| /** | ||
| * Configuration for the token bucket rate limit rule. | ||
| * | ||
| * @template Characteristics | ||
| * Characteristics to track a user by. | ||
| */ | ||
| export type TokenBucketRateLimitOptions<Characteristics extends readonly string[]> = { | ||
| /** | ||
| * Block mode of the rule (default: `"DRY_RUN"`). | ||
| * | ||
| * `"DRY_RUN"` will allow all requests while still providing access to the | ||
| * rule results. | ||
| * `"LIVE"` will block requests when the rate limit is exceeded. | ||
| */ | ||
| mode?: ArcjetMode; | ||
| /** | ||
| * Characteristics to track a user by (default: global characteristics or `["ip.src"]`). | ||
| */ | ||
| characteristics?: Characteristics; | ||
| /** | ||
| * Tokens to add to the bucket at each interval (required). | ||
| * | ||
| * For example, if you set `interval` to `60` and the `refillRate` to `10`, | ||
| * the bucket will refill `10` tokens every `60` seconds. | ||
| */ | ||
| refillRate: number; | ||
| /** | ||
| * Time interval for the refill rate (required). | ||
| * | ||
| * This can be a string like `"1m"` for one minute, | ||
| * `"1h45m"` for 1 hour and 45 minutes, | ||
| * or a number like `120` for 120 seconds. | ||
| * | ||
| * Valid string time units are `s` (seconds), | ||
| * `m` (minutes), | ||
| * `h` (hours), and | ||
| * `d` (days). | ||
| */ | ||
| interval: string | number; | ||
| /** | ||
| * Max tokens the bucket can hold (required). | ||
| * | ||
| * The bucket starts at this full capacity, and after being used, will refill | ||
| * until it reaches full capacity. | ||
| */ | ||
| capacity: number; | ||
| }; | ||
| /** | ||
| * Configuration for the fixed window rate limit rule. | ||
| * | ||
| * @template Characteristics | ||
| * Characteristics to track a user by. | ||
| */ | ||
| export type FixedWindowRateLimitOptions<Characteristics extends readonly string[]> = { | ||
| /** | ||
| * Block mode of the rule (default: `"DRY_RUN"`). | ||
| * | ||
| * `"DRY_RUN"` will allow all requests while still providing access to the | ||
| * rule results. | ||
| * `"LIVE"` will block requests when the rate limit is exceeded. | ||
| */ | ||
| mode?: ArcjetMode; | ||
| /** | ||
| * Characteristics to track a user by (default: global characteristics or `["ip.src"]`). | ||
| */ | ||
| characteristics?: Characteristics; | ||
| /** | ||
| * Fixed time window (required). | ||
| * | ||
| * This can be a string like `"1m"` for one minute, | ||
| * `"1h45m"` for 1 hour and 45 minutes, | ||
| * or a number like `120` for 120 seconds. | ||
| * | ||
| * Valid string time units are `s` (seconds), | ||
| * `m` (minutes), | ||
| * `h` (hours), and | ||
| * `d` (days). | ||
| */ | ||
| window: string | number; | ||
| /** | ||
| * Max requests allowed in the fixed time window (required). | ||
| */ | ||
| max: number; | ||
| }; | ||
| /** | ||
| * Configuration for the sliding window rate limit rule. | ||
| * | ||
| * @template Characteristics | ||
| * Characteristics to track a user by. | ||
| */ | ||
| export type SlidingWindowRateLimitOptions<Characteristics extends readonly string[]> = { | ||
| /** | ||
| * Block mode of the rule (default: `"DRY_RUN"`). | ||
| * | ||
| * `"DRY_RUN"` will allow all requests while still providing access to the | ||
| * rule results. | ||
| * `"LIVE"` will block requests when the rate limit is exceeded. | ||
| */ | ||
| mode?: ArcjetMode; | ||
| /** | ||
| * Characteristics to track a user by (default: global characteristics or `["ip.src"]`). | ||
| */ | ||
| characteristics?: Characteristics; | ||
| /** | ||
| * Time interval for the rate limit (required). | ||
| * | ||
| * This can be a string like `"1m"` for one minute, | ||
| * `"1h45m"` for 1 hour and 45 minutes, | ||
| * or a number like `120` for 120 seconds. | ||
| * | ||
| * Valid string time units are `s` (seconds), | ||
| * `m` (minutes), | ||
| * `h` (hours), and | ||
| * `d` (days). | ||
| */ | ||
| interval: string | number; | ||
| /** | ||
| * Max requests allowed in the sliding time window (required). | ||
| */ | ||
| max: number; | ||
| }; | ||
| /** | ||
| * Configuration for the bot detection rule to allow certain bots and deny | ||
| * others. | ||
| */ | ||
| export type BotOptionsAllow = { | ||
| /** | ||
| * Block mode of the rule (default: `"DRY_RUN"`). | ||
| * | ||
| * `"DRY_RUN"` will allow all requests while still providing access to the | ||
| * rule results. | ||
| * `"LIVE"` will block requests when the rate limit is exceeded. | ||
| */ | ||
| mode?: ArcjetMode; | ||
| /** | ||
| * List of bots to allow (required). | ||
| * | ||
| * Only the bots in this list will be allowed and any other detected bot will | ||
| * be denied. | ||
| * All bots will be denied if empty. | ||
| * You must provide either `allow` or `deny`, not both. | ||
| * | ||
| * You can use an individual bot name such as `"CURL"` to allow the default | ||
| * user-agent of the `curl` tool. | ||
| * You can also use bot categories such as `"CATEGORY:SEARCH_ENGINE"` to allow | ||
| * all search engine bots. | ||
| * See [*Identifying bots* on | ||
| * `docs.arcjet.com`](https://docs.arcjet.com/bot-protection/identifying-bots) | ||
| * for a list of bots and categories. | ||
| */ | ||
| allow: Array<ArcjetWellKnownBot | ArcjetBotCategory>; | ||
| /** | ||
| * List of bots to deny, | ||
| * cannot be combined with `allow`. | ||
| */ | ||
| deny?: never; | ||
| }; | ||
| /** | ||
| * Configuration for the bot detection rule to deny certain bots and allow | ||
| * others. | ||
| */ | ||
| export type BotOptionsDeny = { | ||
| /** | ||
| * Block mode of the rule (default: `"DRY_RUN"`). | ||
| * | ||
| * `"DRY_RUN"` will allow all requests while still providing access to the | ||
| * rule results. | ||
| * `"LIVE"` will block requests when the rate limit is exceeded. | ||
| */ | ||
| mode?: ArcjetMode; | ||
| /** | ||
| * List of bots to allow, | ||
| * cannot be combined with `allow`. | ||
| */ | ||
| allow?: never; | ||
| /** | ||
| * List of bots to deny (required). | ||
| * | ||
| * Only the bots in this list will be denied and any other detected bot will | ||
| * be allowed. | ||
| * All bots will be allowed if empty, which is equivalent to not using | ||
| * `detectBot()`. | ||
| * You must provide either `allow` or `deny`, not both. | ||
| * | ||
| * You can use an individual bot name such as `"CURL"` to deny the default | ||
| * user-agent of the `curl` tool. | ||
| * You can also use bot categories such as `"CATEGORY:SEARCH_ENGINE"` to deny | ||
| * all search engine bots. | ||
| * See [*Identifying bots* on | ||
| * `docs.arcjet.com`](https://docs.arcjet.com/bot-protection/identifying-bots) | ||
| * for a list of bots and categories. | ||
| */ | ||
| deny: Array<ArcjetWellKnownBot | ArcjetBotCategory>; | ||
| }; | ||
| /** | ||
| * Configuration for the bot detection rule. | ||
| */ | ||
| export type BotOptions = BotOptionsAllow | BotOptionsDeny; | ||
| /** | ||
| * Configuration for the email validation rule to allow certain email addresses | ||
| * and deny others. | ||
| */ | ||
| export type EmailOptionsAllow = { | ||
| /** | ||
| * Block mode of the rule (default: `"DRY_RUN"`). | ||
| * | ||
| * `"DRY_RUN"` will allow all requests while still providing access to the | ||
| * rule results. | ||
| * `"LIVE"` will block requests when the rate limit is exceeded. | ||
| */ | ||
| mode?: ArcjetMode; | ||
| /** | ||
| * List of email address types to allow (required). | ||
| * | ||
| * Only email addresses of the email types in this list will be allowed and | ||
| * any other address will be denied. | ||
| * All addresses will be denied if empty. | ||
| * You must provide either `allow` or `deny`, not both. | ||
| * | ||
| * You can use the following email types: | ||
| * | ||
| * - `"DISPOSABLE"` (disposable email addresses) | ||
| * - `"FREE"` (free email addresses) | ||
| * - `"NO_GRAVATAR"` (email addresses with no Gravatar) | ||
| * - `"NO_MX_RECORDS"` (email addresses with no MX records) | ||
| * - `"INVALID"` (invalid email addresses) | ||
| */ | ||
| allow: ArcjetEmailType[]; | ||
| /** | ||
| * List of email address types to deny, | ||
| * cannot be combined with `allow`. | ||
| */ | ||
| deny?: never; | ||
| /** | ||
| * Whether to see email addresses that contain a single domain segment as | ||
| * invalid (default: `true`). | ||
| * | ||
| * For example, `foo@bar` is seen as valid when `false` and invalid when | ||
| * `true`. | ||
| */ | ||
| requireTopLevelDomain?: boolean; | ||
| /** | ||
| * Whether to see email addresses that contain a domain literal as valid | ||
| * (default: `false`). | ||
| * | ||
| * For example, `foo@[192.168.1.1]` is valid when `true` and invalid when | ||
| * `false`. | ||
| */ | ||
| allowDomainLiteral?: boolean; | ||
| }; | ||
| /** | ||
| * Configuration for the email validation rule to deny certain email addresses | ||
| * and allow others. | ||
| */ | ||
| export type EmailOptionsDeny = { | ||
| /** | ||
| * Block mode of the rule (default: `"DRY_RUN"`). | ||
| * | ||
| * `"DRY_RUN"` will allow all requests while still providing access to the | ||
| * rule results. | ||
| * `"LIVE"` will block requests when the rate limit is exceeded. | ||
| */ | ||
| mode?: ArcjetMode; | ||
| /** | ||
| * List of email address types to allow, | ||
| * cannot be combined with `deny`. | ||
| */ | ||
| allow?: never; | ||
| /** | ||
| * List of email address types to deny (required). | ||
| * | ||
| * Email addresses of the email types in this list will be denied and | ||
| * any other address will be allowed. | ||
| * All addresses will be allowed if empty. | ||
| * You must provide either `allow` or `deny`, not both. | ||
| * | ||
| * You can use the following email types: | ||
| * | ||
| * - `"DISPOSABLE"` (disposable email addresses) | ||
| * - `"FREE"` (free email addresses) | ||
| * - `"NO_GRAVATAR"` (email addresses with no Gravatar) | ||
| * - `"NO_MX_RECORDS"` (email addresses with no MX records) | ||
| * - `"INVALID"` (invalid email addresses) | ||
| */ | ||
| deny: ArcjetEmailType[]; | ||
| /** | ||
| * Whether to see email addresses that contain a single domain segment as | ||
| * invalid (default: `true`). | ||
| * | ||
| * For example, `foo@bar` is seen as valid when `false` and invalid when | ||
| * `true`. | ||
| */ | ||
| requireTopLevelDomain?: boolean; | ||
| /** | ||
| * Whether to see email addresses that contain a domain literal as valid | ||
| * (default: `false`). | ||
| * | ||
| * For example, `foo@[192.168.1.1]` is valid when `true` and invalid when | ||
| * `false`. | ||
| */ | ||
| allowDomainLiteral?: boolean; | ||
| }; | ||
| /** | ||
| * Configuration for the email validation rule. | ||
| */ | ||
| export type EmailOptions = EmailOptionsAllow | EmailOptionsDeny; | ||
| /** | ||
| * Configuration for the sensitive info detection rule to allow certain | ||
| * sensitive info types and deny others. | ||
| * | ||
| * @template DetectedEntities | ||
| * Custom entity names that are returned from `detect` and optionally listed in `allow`. | ||
| * @template ListedEntities | ||
| * Entity names that can be listed in the `allow` field. | ||
| */ | ||
| export type SensitiveInfoOptionsAllow<DetectedEntities extends string | undefined = undefined, ListedEntities extends ArcjetSensitiveInfoType | Exclude<DetectedEntities, undefined> = ArcjetSensitiveInfoType | Exclude<DetectedEntities, undefined>> = { | ||
| /** | ||
| * List of sensitive info types to allow (required). | ||
| * | ||
| * Only the types in this list will be allowed and any other detected types | ||
| * will be denied. | ||
| * All sensitive info (including that detected by `detect`) will be denied if | ||
| * empty. | ||
| * You must provide either `allow` or `deny`, not both. | ||
| * | ||
| * You can use the following sensitive info types that can be detected | ||
| * natively: | ||
| * | ||
| * - `"CREDIT_CARD_NUMBER"` | ||
| * - `"EMAIL"` | ||
| * - `"IP_ADDRESS"` | ||
| * - `"PHONE_NUMBER"` | ||
| * | ||
| * You can also use labels of custom info detected by `detect`. | ||
| */ | ||
| allow: Array<ListedEntities | Exclude<DetectedEntities, undefined>>; | ||
| /** | ||
| * List of sensitive info types to deny, | ||
| * cannot be combined with `allow`. | ||
| */ | ||
| deny?: never; | ||
| /** | ||
| * Tokens to consider (default: `1`). | ||
| * | ||
| * A list of tokens of this size will be passed to the custom detect | ||
| * function. | ||
| */ | ||
| contextWindowSize?: number; | ||
| /** | ||
| * Block mode of the rule (default: `"DRY_RUN"`). | ||
| * | ||
| * `"DRY_RUN"` will allow all requests while still providing access to the | ||
| * rule results. | ||
| * `"LIVE"` will block requests when the rate limit is exceeded. | ||
| */ | ||
| mode?: ArcjetMode; | ||
| /** | ||
| * Custom detection function to identify sensitive information. | ||
| * | ||
| * @param tokens | ||
| * Tokens. | ||
| * @returns | ||
| * List of entities (or undefined). | ||
| */ | ||
| detect?: (tokens: string[]) => ReadonlyArray<DetectedEntities>; | ||
| }; | ||
| /** | ||
| * Configuration for the sensitive info detection rule to deny certain | ||
| * sensitive info types and allow others. | ||
| * | ||
| * @template DetectedEntities | ||
| * Custom entity names that are returned from `detect` and optionally listed | ||
| * in `deny`. | ||
| * @template ListedEntities | ||
| * Entity names that can be listed in the `deny` field. | ||
| */ | ||
| export type SensitiveInfoOptionsDeny<DetectedEntities extends string | undefined = undefined, ListedEntities extends ArcjetSensitiveInfoType | Exclude<DetectedEntities, undefined> = ArcjetSensitiveInfoType | Exclude<DetectedEntities, undefined>> = { | ||
| /** | ||
| * List of sensitive info types to allow, | ||
| * cannot be combined with `deny`. | ||
| */ | ||
| allow?: never; | ||
| /** | ||
| * List of sensitive info types to deny (required). | ||
| * | ||
| * Only the types in this list will be denied and any other detected types | ||
| * will be allowed. | ||
| * All sensitive info (including that detected by `detect`) will be allowed if | ||
| * empty. | ||
| * You must provide either `allow` or `deny`, not both. | ||
| * | ||
| * You can use the following sensitive info types that can be detected | ||
| * natively: | ||
| * | ||
| * - `"CREDIT_CARD_NUMBER"` | ||
| * - `"EMAIL"` | ||
| * - `"IP_ADDRESS"` | ||
| * - `"PHONE_NUMBER"` | ||
| * | ||
| * You can also use labels of custom info detected by `detect`. | ||
| */ | ||
| deny: Array<ListedEntities | Exclude<DetectedEntities, undefined>>; | ||
| /** | ||
| * Tokens to consider (default: `1`). | ||
| * | ||
| * A list of tokens of this size will be passed to the custom detect | ||
| * function. | ||
| */ | ||
| contextWindowSize?: number; | ||
| /** | ||
| * Block mode of the rule (default: `"DRY_RUN"`). | ||
| * | ||
| * `"DRY_RUN"` will allow all requests while still providing access to the | ||
| * rule results. | ||
| * `"LIVE"` will block requests when the rate limit is exceeded. | ||
| */ | ||
| mode?: ArcjetMode; | ||
| /** | ||
| * Custom detection function to identify sensitive information. | ||
| * | ||
| * @param tokens | ||
| * Tokens. | ||
| * @returns | ||
| * List of entities (or undefined). | ||
| */ | ||
| detect?: (tokens: string[]) => ReadonlyArray<DetectedEntities>; | ||
| }; | ||
| /** | ||
| * Configuration for the sensitive info detection rule. | ||
| * | ||
| * @template DetectedEntities | ||
| * Custom entity names that are returned from `detect` and optionally listed | ||
| * in `allow` or `deny`. | ||
| * @template ListedEntities | ||
| * Entity names that can be listed in the `allow` or `deny` field. | ||
| */ | ||
| export type SensitiveInfoOptions<DetectedEntities extends string | undefined = undefined, ListedEntities extends ArcjetSensitiveInfoType | Exclude<DetectedEntities, undefined> = ArcjetSensitiveInfoType | Exclude<DetectedEntities, undefined>> = SensitiveInfoOptionsAllow<DetectedEntities, ListedEntities> | SensitiveInfoOptionsDeny<DetectedEntities, ListedEntities>; | ||
| /** | ||
| * Configuration to allow if a filter matches and deny otherwise. | ||
| */ | ||
| export type FilterOptionsAllow = { | ||
| /** | ||
| * Expressions. | ||
| */ | ||
| allow: ReadonlyArray<string>; | ||
| /** | ||
| * Expressions, | ||
| * must not be set if `allow` is set. | ||
| */ | ||
| deny?: never; | ||
| /** | ||
| * Mode. | ||
| */ | ||
| mode?: ArcjetMode | undefined; | ||
| }; | ||
| /** | ||
| * Configuration to deny if a filter matches and allow otherwise. | ||
| */ | ||
| export type FilterOptionsDeny = { | ||
| /** | ||
| * Expressions, | ||
| * must not be set if `deny` is set. | ||
| */ | ||
| allow?: never; | ||
| /** | ||
| * Expressions. | ||
| */ | ||
| deny: ReadonlyArray<string>; | ||
| /** | ||
| * Mode. | ||
| */ | ||
| mode?: ArcjetMode | undefined; | ||
| }; | ||
| /** | ||
| * Configuration for `filter` rule. | ||
| */ | ||
| export type FilterOptions = FilterOptionsAllow | FilterOptionsDeny; | ||
| type PlainObject = { | ||
| [key: string]: unknown; | ||
| }; | ||
| /** | ||
| * Arcjet provides a set of key primitives which can be used to build security | ||
| * functionality. | ||
| * Each primitive can be used independently or combined as part of a | ||
| * pre-configured product. | ||
| * | ||
| * @template Props | ||
| * Configuration. | ||
| */ | ||
| export type Primitive<Props extends PlainObject = {}> = [ArcjetRule<Props>]; | ||
| /** | ||
| * Pre-configured product consisting of combined primitives. | ||
| * | ||
| * @template Props | ||
| * Configuration. | ||
| */ | ||
| export type Product<Props extends PlainObject = {}> = ArcjetRule<Props>[]; | ||
| type ExcludeBuiltinCharacteristic<T> = T extends "ip.src" | "http.host" | "http.method" | "http.request.uri.path" | `http.request.headers["${string}"]` | `http.request.cookie["${string}"]` | `http.request.uri.args["${string}"]` ? never : [ | ||
| string | ||
| ] extends [T] ? never : T extends string ? T : never; | ||
| /** | ||
| * Props for characteristics. | ||
| * | ||
| * Utility type to generate a record of props for each characteristic. | ||
| * It excludes the known characteristics which Arcjet provides. | ||
| * | ||
| * @template Characteristics | ||
| * List of characteristics. | ||
| */ | ||
| export type CharacteristicProps<Characteristics extends readonly string[]> = Characteristics extends [] ? {} : { | ||
| [K in ExcludeBuiltinCharacteristic<Characteristics[number]>]: boolean | number | string; | ||
| }; | ||
| type PropsForRule<R> = R extends ArcjetRule<infer Props> ? Props : {}; | ||
| /** | ||
| * Props for rules. | ||
| * | ||
| * Utility type to generate a record of props for all rules. | ||
| * | ||
| * @template Rules | ||
| * Matrix or list of rules. | ||
| */ | ||
| export type ExtraProps<Rules> = Rules extends [] ? {} : Rules extends ArcjetRule[][] ? UnionToIntersection<PropsForRule<Rules[number][number]>> : Rules extends ArcjetRule[] ? UnionToIntersection<PropsForRule<Rules[number]>> : never; | ||
| /** | ||
| * Additional context provided by adapters. | ||
| */ | ||
| export type ArcjetAdapterContext = { | ||
| /** | ||
| * Allow arbitrary indexing. | ||
| * | ||
| * Adapters could include the Arcjet API Key if it were only available in a | ||
| * runtime handler or IP details provided by a platform. | ||
| */ | ||
| [key: string]: unknown; | ||
| /** | ||
| * Read the request body (required). | ||
| */ | ||
| getBody(): Promise<string>; | ||
| /** | ||
| * Wait for a promise to resolve before continuing (optional). | ||
| * | ||
| * @param promise | ||
| * The promise to wait for. | ||
| * @returns | ||
| * Nothing. | ||
| */ | ||
| waitUntil?: ((promise: Promise<unknown>) => void) | undefined; | ||
| }; | ||
| /** | ||
| * Arcjet request. | ||
| * | ||
| * @template Props | ||
| * Extra data that might be useful for Arcjet. | ||
| * For example, requested tokens are specified as the `requested` property. | ||
| */ | ||
| export type ArcjetRequest<Props extends PlainObject> = Simplify<{ | ||
| /** | ||
| * Additional properties. | ||
| * | ||
| * For example, an email address related to the request is commonly passed | ||
| * as `email` (`string`). | ||
| */ | ||
| [key: string]: unknown; | ||
| /** | ||
| * IP address of the client. | ||
| */ | ||
| ip: string; | ||
| /** | ||
| * HTTP method of the request. | ||
| */ | ||
| method: string; | ||
| /** | ||
| * Protocol of the request. | ||
| */ | ||
| protocol: string; | ||
| /** | ||
| * Host of the request. | ||
| */ | ||
| host: string; | ||
| /** | ||
| * Path of the request. | ||
| */ | ||
| path: string; | ||
| /** | ||
| * Headers of the request. | ||
| */ | ||
| headers: Headers | Record<string, string | string[] | undefined>; | ||
| /** | ||
| * Semicolon-separated cookies for a request. | ||
| */ | ||
| cookies: string; | ||
| /** | ||
| * Query string for a request. | ||
| * Commonly referred to as a "querystring". | ||
| * Starts with `?` when not empty. | ||
| */ | ||
| query: string; | ||
| } & Props>; | ||
| /** | ||
| * Arcjet token bucket rate limiting rule. | ||
| * | ||
| * Applying this rule sets a token bucket rate limit. | ||
| * | ||
| * This algorithm is based on a bucket filled with a specific number of tokens. | ||
| * Each request withdraws some amount of tokens from the bucket and the bucket | ||
| * is refilled at a fixed rate. | ||
| * Once the bucket is empty, the client is blocked until the bucket refills. | ||
| * | ||
| * This algorithm is useful when you want to allow clients to make a burst of | ||
| * requests and then still be able to make requests at a slower rate. | ||
| * | ||
| * @template Characteristics | ||
| * Characteristics to track a user by. | ||
| * @param options | ||
| * Configuration for the token bucket rate limiting rule (required). | ||
| * @returns | ||
| * Token bucket rule to provide to the SDK in the `rules` field. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * tokenBucket({ | ||
| * mode: "LIVE", | ||
| * refillRate: 10, | ||
| * interval: "60s", | ||
| * capacity: 100, | ||
| * }); | ||
| * ``` | ||
| * @example | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [ | ||
| * tokenBucket({ | ||
| * mode: "LIVE", | ||
| * refillRate: 10, | ||
| * interval: "60s", | ||
| * capacity: 100, | ||
| * }), | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @link https://docs.arcjet.com/rate-limiting | ||
| * @link https://docs.arcjet.com/rate-limiting/algorithms#token-bucket | ||
| * @link https://docs.arcjet.com/rate-limiting/reference | ||
| */ | ||
| export declare function tokenBucket<const Characteristics extends readonly string[] = []>(options: TokenBucketRateLimitOptions<Characteristics>): [ | ||
| ArcjetTokenBucketRateLimitRule<Simplify<UnionToIntersection<{ | ||
| requested: number; | ||
| } | CharacteristicProps<Characteristics>>>> | ||
| ]; | ||
| /** | ||
| * Arcjet fixed window rate limiting rule. | ||
| * | ||
| * Applying this rule sets a fixed window rate limit which tracks the number of | ||
| * requests made by a client over a fixed time window. | ||
| * | ||
| * This is the simplest algorithm. | ||
| * It tracks the number of requests made by a client over a fixed time window | ||
| * such as 60 seconds. | ||
| * If the client exceeds the limit, they are blocked until the window expires. | ||
| * | ||
| * This algorithm is useful when you want to apply a simple fixed limit in a | ||
| * fixed time window. | ||
| * For example, a simple limit on the total number of requests a client can make. | ||
| * However, it can be susceptible to the stampede problem where a client makes | ||
| * a burst of requests at the start of a window and then is blocked for the rest | ||
| * of the window. | ||
| * The sliding window algorithm can be used to avoid this. | ||
| * | ||
| * @template Characteristics | ||
| * Characteristics to track a user by. | ||
| * @param options | ||
| * Configuration for the fixed window rate limiting rule (required). | ||
| * @returns | ||
| * Fixed window rule to provide to the SDK in the `rules` field. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * fixedWindow({ mode: "LIVE", window: "60s", max: 100 }); | ||
| * ``` | ||
| * @example | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [ | ||
| * fixedWindow({ | ||
| * mode: "LIVE", | ||
| * window: "60s", | ||
| * max: 100, | ||
| * }) | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @link https://docs.arcjet.com/rate-limiting | ||
| * @link https://docs.arcjet.com/rate-limiting/algorithms#fixed-window | ||
| * @link https://docs.arcjet.com/rate-limiting/reference | ||
| */ | ||
| export declare function fixedWindow<const Characteristics extends readonly string[] = []>(options: FixedWindowRateLimitOptions<Characteristics>): [ArcjetFixedWindowRateLimitRule<CharacteristicProps<Characteristics>>]; | ||
| /** | ||
| * Arcjet sliding window rate limiting rule. | ||
| * | ||
| * Applying this rule sets a sliding window rate limit which tracks the number | ||
| * of requests made by a client over a sliding window so that the window moves | ||
| * with time. | ||
| * | ||
| * This algorithm is useful to avoid the stampede problem of the fixed window. | ||
| * It provides smoother rate limiting over time and can prevent a client from | ||
| * making a burst of requests at the start of a window and then being blocked | ||
| * for the rest of the window. | ||
| * | ||
| * @template Characteristics | ||
| * Characteristics to track a user by. | ||
| * @param options | ||
| * Configuration for the sliding window rate limiting rule (required). | ||
| * @returns | ||
| * Token bucket rule to provide to the SDK in the `rules` field. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * slidingWindow({ mode: "LIVE", interval: "60s", max: 100 }); | ||
| * ``` | ||
| * @example | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [ | ||
| * slidingWindow({ | ||
| * mode: "LIVE", | ||
| * interval: "60s", | ||
| * max: 100, | ||
| * }) | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @link https://docs.arcjet.com/rate-limiting | ||
| * @link https://docs.arcjet.com/rate-limiting/algorithms#sliding-window | ||
| * @link https://docs.arcjet.com/rate-limiting/reference | ||
| */ | ||
| export declare function slidingWindow<const Characteristics extends readonly string[] = []>(options: SlidingWindowRateLimitOptions<Characteristics>): [ArcjetSlidingWindowRateLimitRule<CharacteristicProps<Characteristics>>]; | ||
| /** | ||
| * Arcjet sensitive information detection rule. | ||
| * | ||
| * Applying this rule protects against clients sending you sensitive information | ||
| * such as personally identifiable information (PII) that you do not wish to | ||
| * handle. | ||
| * The rule runs entirely locally so no data ever leaves your environment. | ||
| * | ||
| * This rule includes built-in detections for email addresses, credit/debit card | ||
| * numbers, IP addresses, and phone numbers. | ||
| * You can also provide a custom detection function to identify additional | ||
| * sensitive information. | ||
| * | ||
| * You should pass the value to check to `protect`: | ||
| * | ||
| * ```js | ||
| * const decision = await arcjet.protect(request, { sensitiveInfoValue: "..." }); | ||
| * ``` | ||
| * | ||
| * The entire request body is checked if no value is provided. | ||
| * This behavior is deprecated: prefer passing the value explicitly. | ||
| * | ||
| * @template DetectedEntities | ||
| * Custom entity names that are returned from `detect` and optionally listed | ||
| * in `allow` or `deny`. | ||
| * @template ListedEntities | ||
| * Entity names that can be listed in the `allow` or `deny` field. | ||
| * @param options | ||
| * Configuration for the sensitive information detection rule (required). | ||
| * @returns | ||
| * Sensitive information rule to provide to the SDK in the `rules` field. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * sensitiveInfo({ mode: "LIVE", deny: ["EMAIL"] }); | ||
| * ``` | ||
| * @example | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [ | ||
| * sensitiveInfo({ | ||
| * mode: "LIVE", | ||
| * deny: ["EMAIL"], | ||
| * }) | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| * @example | ||
| * Custom detection function: | ||
| * | ||
| * ```ts | ||
| * function detectDash(tokens: string[]): Array<"CONTAINS_DASH" | undefined> { | ||
| * return tokens.map((token) => { | ||
| * if (token.includes("-")) { | ||
| * return "CONTAINS_DASH"; | ||
| * } | ||
| * }); | ||
| * } | ||
| * | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [ | ||
| * sensitiveInfo({ | ||
| * mode: "LIVE", | ||
| * deny: ["EMAIL", "CONTAINS_DASH"], | ||
| * detect: detectDash, | ||
| * contextWindowSize: 2, | ||
| * }) | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @link https://docs.arcjet.com/sensitive-info | ||
| * @link https://docs.arcjet.com/sensitive-info/reference | ||
| */ | ||
| export declare function sensitiveInfo<DetectedEntities extends string | undefined = undefined, ListedEntities extends ArcjetSensitiveInfoType | Exclude<DetectedEntities, undefined> = ArcjetSensitiveInfoType | Exclude<DetectedEntities, undefined>>(options: SensitiveInfoOptions<DetectedEntities, ListedEntities>): [ | ||
| ArcjetSensitiveInfoRule<{ | ||
| sensitiveInfoValue?: string | null | undefined; | ||
| }> | ||
| ]; | ||
| /** | ||
| * Arcjet email validation rule. | ||
| * | ||
| * Applying this rule allows you to validate and verify an email address. | ||
| * | ||
| * The first step of the analysis is to validate the email address syntax. | ||
| * This runs locally within the SDK and validates the email address is in the | ||
| * correct format. | ||
| * If the email syntax is valid, the SDK will pass the email address to the | ||
| * Arcjet cloud API to verify the email address. | ||
| * This performs several checks, depending on the rule configuration. | ||
| * | ||
| * @param options | ||
| * Configuration for the email validation rule (required). | ||
| * @returns | ||
| * Email rule to provide to the SDK in the `rules` field. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * validateEmail({ mode: "LIVE", deny: ["DISPOSABLE", "INVALID"] }); | ||
| * ``` | ||
| * @example | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [ | ||
| * validateEmail({ | ||
| * mode: "LIVE", | ||
| * deny: ["DISPOSABLE", "INVALID"] | ||
| * }) | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @link https://docs.arcjet.com/email-validation | ||
| * @link https://docs.arcjet.com/email-validation/reference | ||
| */ | ||
| export declare function validateEmail(options: EmailOptions): [ArcjetEmailRule<{ | ||
| email: string; | ||
| }>]; | ||
| /** | ||
| * Arcjet bot detection rule. | ||
| * | ||
| * Applying this rule allows you to manage traffic by automated clients and | ||
| * bots. | ||
| * | ||
| * Bots can be good (such as search engine crawlers or monitoring agents) or bad | ||
| * (such as scrapers or automated scripts). | ||
| * Arcjet allows you to configure which bots you want to allow or deny by | ||
| * specific bot names such as curl, as well as by category such as search | ||
| * engine bots. | ||
| * | ||
| * Bots are detected based on various signals such as the user agent, IP | ||
| * address, DNS records, and more. | ||
| * | ||
| * @param options | ||
| * Configuration for the bot rule (required). | ||
| * @returns | ||
| * Bot rule to provide to the SDK in the `rules` field. | ||
| * | ||
| * @example | ||
| * Allow search engine bots and curl, deny all other bots: | ||
| * | ||
| * ```ts | ||
| * detectBot({ mode: "LIVE", allow: ["CATEGORY:SEARCH_ENGINE", "CURL"] }); | ||
| * ``` | ||
| * @example | ||
| * Allow search engine bots and curl, deny all other bots: | ||
| * | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [ | ||
| * detectBot({ | ||
| * mode: "LIVE", | ||
| * allow: ["CATEGORY:SEARCH_ENGINE", "CURL"] | ||
| * }) | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| * @example | ||
| * Deny AI crawlers, allow all other bots: | ||
| * | ||
| * ```ts | ||
| * detectBot({ mode: "LIVE", deny: ["CATEGORY:AI"] }); | ||
| * ``` | ||
| * @example | ||
| * Deny AI crawlers, allows all other bots: | ||
| * | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [ | ||
| * detectBot({ | ||
| * mode: "LIVE", | ||
| * deny: ["CATEGORY:AI"] | ||
| * }) | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @link https://docs.arcjet.com/bot-protection | ||
| * @link https://docs.arcjet.com/bot-protection/identifying-bots | ||
| * @link https://docs.arcjet.com/bot-protection/reference | ||
| */ | ||
| export declare function detectBot(options: BotOptions): [ArcjetBotRule<{}>]; | ||
| /** | ||
| * Configuration for the Shield WAF rule. | ||
| */ | ||
| export type ShieldOptions = { | ||
| /** | ||
| * Block mode of the rule (default: `"DRY_RUN"`). | ||
| * | ||
| * `"DRY_RUN"` will allow all requests while still providing access to the | ||
| * rule results. | ||
| * `"LIVE"` will block requests when the rate limit is exceeded. | ||
| */ | ||
| mode?: ArcjetMode; | ||
| }; | ||
| /** | ||
| * Configuration for the prompt injection detection rule. | ||
| */ | ||
| export type DetectPromptInjectionOptions = { | ||
| /** | ||
| * Block mode of the rule (default: `"DRY_RUN"`). | ||
| * | ||
| * `"DRY_RUN"` will allow all requests while still providing access to the | ||
| * rule results. | ||
| * `"LIVE"` will block requests when prompt injection is detected. | ||
| */ | ||
| mode?: ArcjetMode; | ||
| /** | ||
| * The score threshold above which a request is considered a prompt injection | ||
| * attempt (default: `0.5`) e.g. anything over `0.5` is malicious. | ||
| * | ||
| * Must be in the range (0.0, 1.0) exclusive. | ||
| * | ||
| * @deprecated | ||
| * This option is no longer respected by the server and will be removed in | ||
| * a future release. | ||
| */ | ||
| threshold?: number; | ||
| }; | ||
| /** | ||
| * Arcjet Shield WAF rule. | ||
| * | ||
| * Applying this rule protects your application against common attacks, | ||
| * including the OWASP Top 10. | ||
| * | ||
| * The Arcjet Shield WAF analyzes every request to your application to detect | ||
| * suspicious activity. | ||
| * Once a certain suspicion threshold is reached, | ||
| * subsequent requests from that client are blocked for a period of time. | ||
| * | ||
| * @param options | ||
| * Configuration for the Shield rule. | ||
| * @returns | ||
| * Shield rule to provide to the SDK in the `rules` field. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * shield({ mode: "LIVE" }); | ||
| * ``` | ||
| * @example | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [shield({ mode: "LIVE" })], | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @link https://docs.arcjet.com/shield | ||
| * @link https://docs.arcjet.com/shield/reference | ||
| */ | ||
| export declare function shield(options: ShieldOptions): [ArcjetShieldRule<{}>]; | ||
| /** | ||
| * Arcjet prompt injection detection rule. | ||
| * | ||
| * Analyzes LLM prompts to detect prompt injection attempts. | ||
| * | ||
| * The Arcjet prompt injection detection rule analyzes prompts sent to LLM | ||
| * applications to detect jailbreak and injection attempts. | ||
| * The analysis is performed through Arcjet's Cloud API. | ||
| * | ||
| * @param options | ||
| * Configuration for the prompt injection detection rule. | ||
| * @returns | ||
| * Prompt injection detection rule to provide to the SDK in the `rules` field. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * detectPromptInjection({ mode: "LIVE" }); | ||
| * ``` | ||
| * @example | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [detectPromptInjection({ mode: "LIVE" })], | ||
| * }); | ||
| * ``` | ||
| */ | ||
| export declare function detectPromptInjection(options?: DetectPromptInjectionOptions): [ArcjetPromptInjectionDetectionRule]; | ||
| /** | ||
| * Arcjet prompt injection detection rule. | ||
| * | ||
| * @deprecated | ||
| * Use `detectPromptInjection` instead. | ||
| */ | ||
| export declare const experimental_detectPromptInjection: typeof detectPromptInjection; | ||
| /** | ||
| * Configuration for signup form protection rule. | ||
| * | ||
| * @template Characteristics | ||
| * Characteristics to track a user by. | ||
| */ | ||
| export type ProtectSignupOptions<Characteristics extends readonly string[]> = { | ||
| /** | ||
| * Configuration for rate limit rule (required). | ||
| */ | ||
| rateLimit: SlidingWindowRateLimitOptions<Characteristics>; | ||
| /** | ||
| * Configuration for bot protection rule (required). | ||
| */ | ||
| bots: BotOptions; | ||
| /** | ||
| * Configuration for email validation rule (required). | ||
| */ | ||
| email: EmailOptions; | ||
| }; | ||
| /** | ||
| * Arcjet signup form protection rule. | ||
| * | ||
| * Applying this rule combines rate limiting, bot protection, and email | ||
| * validation to protect your signup forms from abuse. | ||
| * Using this rule will configure the following: | ||
| * | ||
| * - Rate limiting - signup forms are a common target for bots. Arcjet’s rate | ||
| * limiting helps to prevent bots and other automated or malicious clients | ||
| * from submitting your signup form too many times in a short period of time. | ||
| * - Bot protection - signup forms are usually exclusively used by humans, which | ||
| * means that any automated submissions to the form are likely to be | ||
| * fraudulent. | ||
| * - Email validation - email addresses should be validated to ensure the signup | ||
| * is coming from a legitimate user with a real email address that can | ||
| * actually receive messages. | ||
| * | ||
| * @template Characteristics | ||
| * Characteristics to track a user by. | ||
| * @param options | ||
| * Configuration for the signup form protection rule. | ||
| * @returns | ||
| * Signup form protection rule to provide to the SDK in the `rules` field. | ||
| * | ||
| * @example | ||
| * Our recommended configuration for most signup forms is: | ||
| * | ||
| * - Block email addresses with invalid syntax, that are from disposable email providers, | ||
| * or do not have valid MX records configured. | ||
| * - Block all bots. | ||
| * - Apply a rate limit of 5 submissions per 10 minutes from a single IP | ||
| * address. | ||
| * | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [ | ||
| * protectSignup({ | ||
| * email: { | ||
| * mode: "LIVE", | ||
| * deny: ["DISPOSABLE", "INVALID", "NO_MX_RECORDS"], | ||
| * }, | ||
| * bots: { | ||
| * mode: "LIVE", | ||
| * allow: [], // block all detected bots | ||
| * }, | ||
| * rateLimit: { | ||
| * mode: "LIVE", | ||
| * interval: "10m", | ||
| * max: 5, | ||
| * }, | ||
| * }), | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @link https://docs.arcjet.com/signup-protection | ||
| * @link https://docs.arcjet.com/signup-protection/reference | ||
| */ | ||
| export declare function protectSignup<const Characteristics extends string[] = []>(options: ProtectSignupOptions<Characteristics>): [ | ||
| rateLimit: ArcjetSlidingWindowRateLimitRule<CharacteristicProps<Characteristics>>, | ||
| bot: ArcjetBotRule<{}>, | ||
| email: ArcjetEmailRule<{ | ||
| email: string; | ||
| }> | ||
| ]; | ||
| /** | ||
| * Arcjet filter rule. | ||
| * | ||
| * Applying this rule lets you block requests using Wireshark-like display | ||
| * filter expressions over HTTP headers, IP addresses, and other request | ||
| * fields. | ||
| * You can quickly enforce rules like allow/deny by country, network, or | ||
| * `user-agent` pattern. | ||
| * | ||
| * See the [reference guide](https://docs.arcjet.com/filters/reference) for | ||
| * more info on the expression language fields, functions, and values. | ||
| * | ||
| * @param options | ||
| * Configuration (required). | ||
| * @returns | ||
| * Filter rule. | ||
| * | ||
| * @example | ||
| * In this example, the expression matches non-VPN GET requests from the US. | ||
| * Requests matching the expression are allowed, all others are denied. | ||
| * | ||
| * ```ts | ||
| * filter({ | ||
| * allow: [ | ||
| * 'http.request.method eq "GET" and ip.src.country eq "US" and not ip.src.vpn', | ||
| * ], | ||
| * mode: "LIVE", | ||
| * }) | ||
| * ``` | ||
| * | ||
| * @link https://docs.arcjet.com/filters | ||
| * @link https://docs.arcjet.com/filters/reference | ||
| */ | ||
| export declare function filter(options: FilterOptions): [ArcjetFilterRule]; | ||
| /** | ||
| * Configuration for Arcjet. | ||
| * | ||
| * @template Rules | ||
| * List of rules. | ||
| * @template Characteristics | ||
| * Characteristics to track a user by. | ||
| */ | ||
| export interface ArcjetOptions<Rules extends [...(Primitive | Product)[]], Characteristics extends readonly string[]> { | ||
| /** | ||
| * API key to identify the site in Arcjet (required). | ||
| */ | ||
| key: string; | ||
| /** | ||
| * Rules to apply when protecting a request (required). | ||
| */ | ||
| rules: readonly [...Rules]; | ||
| /** | ||
| * Characteristics to track a user by (default: `["ip.src"]`). | ||
| * Can also be passed to rules. | ||
| */ | ||
| characteristics?: Characteristics; | ||
| /** | ||
| * Client used to make requests to the Arcjet API (optional). | ||
| * | ||
| * This should not be passed by end users but is configured by integrations | ||
| * (such as `@arcjet/next`) or for testing purposes. | ||
| */ | ||
| client?: Client; | ||
| /** | ||
| * Log interface to emit useful information from the SDK (optional). | ||
| */ | ||
| log?: ArcjetLogger; | ||
| } | ||
| /** | ||
| * Arcjet instance. | ||
| * | ||
| * Primarily has a `protect()` method to make a decision about how a request | ||
| * should be handled. | ||
| */ | ||
| export interface Arcjet<Props extends PlainObject> { | ||
| /** | ||
| * Make a decision about how to handle a request. | ||
| * | ||
| * This will analyze the request locally where possible and otherwise call | ||
| * the Arcjet decision API. | ||
| * | ||
| * @param ctx | ||
| * Additional context for this function call. | ||
| * @param request | ||
| * Details about the {@linkcode ArcjetRequest} that Arcjet needs to make a | ||
| * decision. | ||
| * @returns | ||
| * Promise that resolves to an {@linkcode ArcjetDecision} indicating | ||
| * Arcjet’s decision about the request. | ||
| */ | ||
| protect(ctx: ArcjetAdapterContext, request: ArcjetRequest<Props>): Promise<ArcjetDecision>; | ||
| /** | ||
| * Augment the client with another rule. | ||
| * | ||
| * Useful for varying rules based on criteria in your handler such as | ||
| * different rate limit for logged in users. | ||
| * | ||
| * @param rule | ||
| * Rule to add to Arcjet. | ||
| * @returns | ||
| * Arcjet instance augmented with the given rule. | ||
| */ | ||
| withRule<Rule extends ArcjetRule>(rule: Array<Rule>): Arcjet<Props & (Rule extends ArcjetRule<infer P> ? P : {})>; | ||
| } | ||
| /** | ||
| * Create a new Arcjet instance. | ||
| * | ||
| * @template Rules | ||
| * List of rules. | ||
| * @template Characteristics | ||
| * Characteristics to track a user by. | ||
| * @param options | ||
| * Configuration. | ||
| * @returns | ||
| * Arcjet instance. | ||
| */ | ||
| export default function arcjet<const Rules extends [...(Primitive | Product)[]] = [], const Characteristics extends readonly string[] = []>(options: ArcjetOptions<Rules, Characteristics>): Arcjet<ExtraProps<Rules> & CharacteristicProps<Characteristics>>; |
Sorry, the diff of this file is too big to display
+35
-38
| { | ||
| "name": "arcjet", | ||
| "version": "1.7.0-rc.1", | ||
| "version": "1.7.0", | ||
| "description": "Arcjet runtime security SDK — bot protection, rate limiting, prompt injection detection, PII blocking, and WAF for JavaScript and TypeScript apps", | ||
@@ -22,3 +22,9 @@ "keywords": [ | ||
| ], | ||
| "license": "Apache-2.0", | ||
| "homepage": "https://arcjet.com", | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "git+https://github.com/arcjet/arcjet-js.git", | ||
| "directory": "arcjet" | ||
| }, | ||
| "bugs": { | ||
@@ -28,3 +34,2 @@ "url": "https://github.com/arcjet/arcjet-js/issues", | ||
| }, | ||
| "license": "Apache-2.0", | ||
| "author": { | ||
@@ -35,55 +40,47 @@ "name": "Arcjet", | ||
| }, | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "git+https://github.com/arcjet/arcjet-js.git", | ||
| "directory": "arcjet" | ||
| "engines": { | ||
| "node": ">=22.21.0 <23 || >=24.5.0" | ||
| }, | ||
| "type": "module", | ||
| "main": "./index.js", | ||
| "types": "./index.d.ts", | ||
| "files": [ | ||
| "dist" | ||
| "index.d.ts", | ||
| "index.js" | ||
| ], | ||
| "type": "module", | ||
| "main": "./dist/index.js", | ||
| "types": "./dist/index.d.ts", | ||
| "exports": { | ||
| ".": { | ||
| "types": "./dist/index.d.ts", | ||
| "default": "./dist/index.js" | ||
| }, | ||
| "./package.json": "./package.json" | ||
| }, | ||
| "publishConfig": { | ||
| "access": "public", | ||
| "tag": "latest" | ||
| }, | ||
| "scripts": { | ||
| "build": "tsdown", | ||
| "typecheck": "tsgo --noEmit", | ||
| "test-api": "node --test -- test/*.test.ts", | ||
| "build": "rollup --config rollup.config.js", | ||
| "lint": "eslint .", | ||
| "test-api": "node --test -- test/*.test.js", | ||
| "test-runtime-cloudflare": "npm run build && node --test test/runtime/cloudflare/cloudflare.test.ts", | ||
| "test-coverage": "node --experimental-test-coverage --test --test-coverage-branches=95 --test-coverage-exclude \"../{analyze-wasm,analyze,arcjet,cache,duration,env,headers,ip,logger,protocol,runtime,sprintf,stable-hash,transport}/**/*.{js,ts}\" --test-coverage-exclude \"test/**/*.{js,ts}\" --test-coverage-functions=100 --test-coverage-lines=95 -- test/*.test.ts", | ||
| "test": "npm run build && npm run test-coverage" | ||
| "test-coverage": "node --experimental-test-coverage --test --test-coverage-branches=95 --test-coverage-exclude \"../{analyze-wasm,analyze,arcjet,cache,duration,env,headers,ip,logger,protocol,runtime,sprintf,stable-hash,transport}/**/*.{js,ts}\" --test-coverage-exclude \"test/**/*.{js,ts}\" --test-coverage-functions=100 --test-coverage-lines=95 -- test/*.test.js", | ||
| "test": "npm run build && npm run lint && npm run test-coverage" | ||
| }, | ||
| "dependencies": { | ||
| "@arcjet/analyze": "1.7.0-rc.1", | ||
| "@arcjet/cache": "1.7.0-rc.1", | ||
| "@arcjet/duration": "1.7.0-rc.1", | ||
| "@arcjet/headers": "1.7.0-rc.1", | ||
| "@arcjet/protocol": "1.7.0-rc.1", | ||
| "@arcjet/runtime": "1.7.0-rc.1", | ||
| "@arcjet/stable-hash": "1.7.0-rc.1" | ||
| "@arcjet/analyze": "1.7.0", | ||
| "@arcjet/cache": "1.7.0", | ||
| "@arcjet/duration": "1.7.0", | ||
| "@arcjet/headers": "1.7.0", | ||
| "@arcjet/protocol": "1.7.0", | ||
| "@arcjet/runtime": "1.7.0", | ||
| "@arcjet/stable-hash": "1.7.0" | ||
| }, | ||
| "devDependencies": { | ||
| "@arcjet/transport": "1.7.0-rc.1", | ||
| "@arcjet/eslint-config": "1.7.0", | ||
| "@arcjet/rollup-config": "1.7.0", | ||
| "@arcjet/transport": "1.7.0", | ||
| "@bufbuild/protobuf": "2.12.0", | ||
| "@connectrpc/connect": "2.1.2", | ||
| "@connectrpc/connect-node": "2.1.2", | ||
| "@rollup/wasm-node": "4.62.2", | ||
| "@types/node": "22.19.21", | ||
| "eslint": "9.39.4", | ||
| "miniflare": "4.20260617.1", | ||
| "rolldown": "1.1.2", | ||
| "tsdown": "0.22.3", | ||
| "typescript": "6.0.3" | ||
| "typescript": "5.9.3" | ||
| }, | ||
| "engines": { | ||
| "node": ">=22.21.0 <23 || >=24.5.0" | ||
| "publishConfig": { | ||
| "access": "public", | ||
| "tag": "latest" | ||
| } | ||
| } |
-1231
| import { ArcjetBotCategory, ArcjetBotRule, ArcjetDecision, ArcjetEmailRule, ArcjetEmailType, ArcjetFilterRule, ArcjetFixedWindowRateLimitRule, ArcjetLogger, ArcjetMode, ArcjetPromptInjectionDetectionRule, ArcjetRule, ArcjetSensitiveInfoRule, ArcjetSensitiveInfoType, ArcjetShieldRule, ArcjetSlidingWindowRateLimitRule, ArcjetTokenBucketRateLimitRule, ArcjetWellKnownBot } from "@arcjet/protocol"; | ||
| import { Client } from "@arcjet/protocol/client.js"; | ||
| export * from "@arcjet/protocol"; | ||
| //#region src/index.d.ts | ||
| type Simplify<T> = { [KeyType in keyof T]: T[KeyType] } & {}; | ||
| type UnionToIntersection<Union> = (Union extends unknown ? (distributedUnion: Union) => void : never) extends ((mergedIntersection: infer Intersection) => void) ? Intersection & Union : never; | ||
| /** | ||
| * Configuration for the token bucket rate limit rule. | ||
| * | ||
| * @template Characteristics | ||
| * Characteristics to track a user by. | ||
| */ | ||
| type TokenBucketRateLimitOptions<Characteristics extends readonly string[]> = { | ||
| /** | ||
| * Block mode of the rule (default: `"DRY_RUN"`). | ||
| * | ||
| * `"DRY_RUN"` will allow all requests while still providing access to the | ||
| * rule results. | ||
| * `"LIVE"` will block requests when the rate limit is exceeded. | ||
| */ | ||
| mode?: ArcjetMode; | ||
| /** | ||
| * Characteristics to track a user by (default: global characteristics or `["ip.src"]`). | ||
| */ | ||
| characteristics?: Characteristics; | ||
| /** | ||
| * Tokens to add to the bucket at each interval (required). | ||
| * | ||
| * For example, if you set `interval` to `60` and the `refillRate` to `10`, | ||
| * the bucket will refill `10` tokens every `60` seconds. | ||
| */ | ||
| refillRate: number; | ||
| /** | ||
| * Time interval for the refill rate (required). | ||
| * | ||
| * This can be a string like `"1m"` for one minute, | ||
| * `"1h45m"` for 1 hour and 45 minutes, | ||
| * or a number like `120` for 120 seconds. | ||
| * | ||
| * Valid string time units are `s` (seconds), | ||
| * `m` (minutes), | ||
| * `h` (hours), and | ||
| * `d` (days). | ||
| */ | ||
| interval: string | number; | ||
| /** | ||
| * Max tokens the bucket can hold (required). | ||
| * | ||
| * The bucket starts at this full capacity, and after being used, will refill | ||
| * until it reaches full capacity. | ||
| */ | ||
| capacity: number; | ||
| }; | ||
| /** | ||
| * Configuration for the fixed window rate limit rule. | ||
| * | ||
| * @template Characteristics | ||
| * Characteristics to track a user by. | ||
| */ | ||
| type FixedWindowRateLimitOptions<Characteristics extends readonly string[]> = { | ||
| /** | ||
| * Block mode of the rule (default: `"DRY_RUN"`). | ||
| * | ||
| * `"DRY_RUN"` will allow all requests while still providing access to the | ||
| * rule results. | ||
| * `"LIVE"` will block requests when the rate limit is exceeded. | ||
| */ | ||
| mode?: ArcjetMode; | ||
| /** | ||
| * Characteristics to track a user by (default: global characteristics or `["ip.src"]`). | ||
| */ | ||
| characteristics?: Characteristics; | ||
| /** | ||
| * Fixed time window (required). | ||
| * | ||
| * This can be a string like `"1m"` for one minute, | ||
| * `"1h45m"` for 1 hour and 45 minutes, | ||
| * or a number like `120` for 120 seconds. | ||
| * | ||
| * Valid string time units are `s` (seconds), | ||
| * `m` (minutes), | ||
| * `h` (hours), and | ||
| * `d` (days). | ||
| */ | ||
| window: string | number; | ||
| /** | ||
| * Max requests allowed in the fixed time window (required). | ||
| */ | ||
| max: number; | ||
| }; | ||
| /** | ||
| * Configuration for the sliding window rate limit rule. | ||
| * | ||
| * @template Characteristics | ||
| * Characteristics to track a user by. | ||
| */ | ||
| type SlidingWindowRateLimitOptions<Characteristics extends readonly string[]> = { | ||
| /** | ||
| * Block mode of the rule (default: `"DRY_RUN"`). | ||
| * | ||
| * `"DRY_RUN"` will allow all requests while still providing access to the | ||
| * rule results. | ||
| * `"LIVE"` will block requests when the rate limit is exceeded. | ||
| */ | ||
| mode?: ArcjetMode; | ||
| /** | ||
| * Characteristics to track a user by (default: global characteristics or `["ip.src"]`). | ||
| */ | ||
| characteristics?: Characteristics; | ||
| /** | ||
| * Time interval for the rate limit (required). | ||
| * | ||
| * This can be a string like `"1m"` for one minute, | ||
| * `"1h45m"` for 1 hour and 45 minutes, | ||
| * or a number like `120` for 120 seconds. | ||
| * | ||
| * Valid string time units are `s` (seconds), | ||
| * `m` (minutes), | ||
| * `h` (hours), and | ||
| * `d` (days). | ||
| */ | ||
| interval: string | number; | ||
| /** | ||
| * Max requests allowed in the sliding time window (required). | ||
| */ | ||
| max: number; | ||
| }; | ||
| /** | ||
| * Configuration for the bot detection rule to allow certain bots and deny | ||
| * others. | ||
| */ | ||
| type BotOptionsAllow = { | ||
| /** | ||
| * Block mode of the rule (default: `"DRY_RUN"`). | ||
| * | ||
| * `"DRY_RUN"` will allow all requests while still providing access to the | ||
| * rule results. | ||
| * `"LIVE"` will block requests when the rate limit is exceeded. | ||
| */ | ||
| mode?: ArcjetMode; | ||
| /** | ||
| * List of bots to allow (required). | ||
| * | ||
| * Only the bots in this list will be allowed and any other detected bot will | ||
| * be denied. | ||
| * All bots will be denied if empty. | ||
| * You must provide either `allow` or `deny`, not both. | ||
| * | ||
| * You can use an individual bot name such as `"CURL"` to allow the default | ||
| * user-agent of the `curl` tool. | ||
| * You can also use bot categories such as `"CATEGORY:SEARCH_ENGINE"` to allow | ||
| * all search engine bots. | ||
| * See [*Identifying bots* on | ||
| * `docs.arcjet.com`](https://docs.arcjet.com/bot-protection/identifying-bots) | ||
| * for a list of bots and categories. | ||
| */ | ||
| allow: Array<ArcjetWellKnownBot | ArcjetBotCategory>; | ||
| /** | ||
| * List of bots to deny, | ||
| * cannot be combined with `allow`. | ||
| */ | ||
| deny?: never; | ||
| }; | ||
| /** | ||
| * Configuration for the bot detection rule to deny certain bots and allow | ||
| * others. | ||
| */ | ||
| type BotOptionsDeny = { | ||
| /** | ||
| * Block mode of the rule (default: `"DRY_RUN"`). | ||
| * | ||
| * `"DRY_RUN"` will allow all requests while still providing access to the | ||
| * rule results. | ||
| * `"LIVE"` will block requests when the rate limit is exceeded. | ||
| */ | ||
| mode?: ArcjetMode; | ||
| /** | ||
| * List of bots to allow, | ||
| * cannot be combined with `allow`. | ||
| */ | ||
| allow?: never; | ||
| /** | ||
| * List of bots to deny (required). | ||
| * | ||
| * Only the bots in this list will be denied and any other detected bot will | ||
| * be allowed. | ||
| * All bots will be allowed if empty, which is equivalent to not using | ||
| * `detectBot()`. | ||
| * You must provide either `allow` or `deny`, not both. | ||
| * | ||
| * You can use an individual bot name such as `"CURL"` to deny the default | ||
| * user-agent of the `curl` tool. | ||
| * You can also use bot categories such as `"CATEGORY:SEARCH_ENGINE"` to deny | ||
| * all search engine bots. | ||
| * See [*Identifying bots* on | ||
| * `docs.arcjet.com`](https://docs.arcjet.com/bot-protection/identifying-bots) | ||
| * for a list of bots and categories. | ||
| */ | ||
| deny: Array<ArcjetWellKnownBot | ArcjetBotCategory>; | ||
| }; | ||
| /** | ||
| * Configuration for the bot detection rule. | ||
| */ | ||
| type BotOptions = BotOptionsAllow | BotOptionsDeny; | ||
| /** | ||
| * Configuration for the email validation rule to allow certain email addresses | ||
| * and deny others. | ||
| */ | ||
| type EmailOptionsAllow = { | ||
| /** | ||
| * Block mode of the rule (default: `"DRY_RUN"`). | ||
| * | ||
| * `"DRY_RUN"` will allow all requests while still providing access to the | ||
| * rule results. | ||
| * `"LIVE"` will block requests when the rate limit is exceeded. | ||
| */ | ||
| mode?: ArcjetMode; | ||
| /** | ||
| * List of email address types to allow (required). | ||
| * | ||
| * Only email addresses of the email types in this list will be allowed and | ||
| * any other address will be denied. | ||
| * All addresses will be denied if empty. | ||
| * You must provide either `allow` or `deny`, not both. | ||
| * | ||
| * You can use the following email types: | ||
| * | ||
| * - `"DISPOSABLE"` (disposable email addresses) | ||
| * - `"FREE"` (free email addresses) | ||
| * - `"NO_GRAVATAR"` (email addresses with no Gravatar) | ||
| * - `"NO_MX_RECORDS"` (email addresses with no MX records) | ||
| * - `"INVALID"` (invalid email addresses) | ||
| */ | ||
| allow: ArcjetEmailType[]; | ||
| /** | ||
| * List of email address types to deny, | ||
| * cannot be combined with `allow`. | ||
| */ | ||
| deny?: never; | ||
| /** | ||
| * Whether to see email addresses that contain a single domain segment as | ||
| * invalid (default: `true`). | ||
| * | ||
| * For example, `foo@bar` is seen as valid when `false` and invalid when | ||
| * `true`. | ||
| */ | ||
| requireTopLevelDomain?: boolean; | ||
| /** | ||
| * Whether to see email addresses that contain a domain literal as valid | ||
| * (default: `false`). | ||
| * | ||
| * For example, `foo@[192.168.1.1]` is valid when `true` and invalid when | ||
| * `false`. | ||
| */ | ||
| allowDomainLiteral?: boolean; | ||
| }; | ||
| /** | ||
| * Configuration for the email validation rule to deny certain email addresses | ||
| * and allow others. | ||
| */ | ||
| type EmailOptionsDeny = { | ||
| /** | ||
| * Block mode of the rule (default: `"DRY_RUN"`). | ||
| * | ||
| * `"DRY_RUN"` will allow all requests while still providing access to the | ||
| * rule results. | ||
| * `"LIVE"` will block requests when the rate limit is exceeded. | ||
| */ | ||
| mode?: ArcjetMode; | ||
| /** | ||
| * List of email address types to allow, | ||
| * cannot be combined with `deny`. | ||
| */ | ||
| allow?: never; | ||
| /** | ||
| * List of email address types to deny (required). | ||
| * | ||
| * Email addresses of the email types in this list will be denied and | ||
| * any other address will be allowed. | ||
| * All addresses will be allowed if empty. | ||
| * You must provide either `allow` or `deny`, not both. | ||
| * | ||
| * You can use the following email types: | ||
| * | ||
| * - `"DISPOSABLE"` (disposable email addresses) | ||
| * - `"FREE"` (free email addresses) | ||
| * - `"NO_GRAVATAR"` (email addresses with no Gravatar) | ||
| * - `"NO_MX_RECORDS"` (email addresses with no MX records) | ||
| * - `"INVALID"` (invalid email addresses) | ||
| */ | ||
| deny: ArcjetEmailType[]; | ||
| /** | ||
| * Whether to see email addresses that contain a single domain segment as | ||
| * invalid (default: `true`). | ||
| * | ||
| * For example, `foo@bar` is seen as valid when `false` and invalid when | ||
| * `true`. | ||
| */ | ||
| requireTopLevelDomain?: boolean; | ||
| /** | ||
| * Whether to see email addresses that contain a domain literal as valid | ||
| * (default: `false`). | ||
| * | ||
| * For example, `foo@[192.168.1.1]` is valid when `true` and invalid when | ||
| * `false`. | ||
| */ | ||
| allowDomainLiteral?: boolean; | ||
| }; | ||
| /** | ||
| * Configuration for the email validation rule. | ||
| */ | ||
| type EmailOptions = EmailOptionsAllow | EmailOptionsDeny; | ||
| /** | ||
| * Configuration for the sensitive info detection rule to allow certain | ||
| * sensitive info types and deny others. | ||
| * | ||
| * @template DetectedEntities | ||
| * Custom entity names that are returned from `detect` and optionally listed in `allow`. | ||
| * @template ListedEntities | ||
| * Entity names that can be listed in the `allow` field. | ||
| */ | ||
| type SensitiveInfoOptionsAllow<DetectedEntities extends string | undefined = undefined, ListedEntities extends ArcjetSensitiveInfoType | Exclude<DetectedEntities, undefined> = ArcjetSensitiveInfoType | Exclude<DetectedEntities, undefined>> = { | ||
| /** | ||
| * List of sensitive info types to allow (required). | ||
| * | ||
| * Only the types in this list will be allowed and any other detected types | ||
| * will be denied. | ||
| * All sensitive info (including that detected by `detect`) will be denied if | ||
| * empty. | ||
| * You must provide either `allow` or `deny`, not both. | ||
| * | ||
| * You can use the following sensitive info types that can be detected | ||
| * natively: | ||
| * | ||
| * - `"CREDIT_CARD_NUMBER"` | ||
| * - `"EMAIL"` | ||
| * - `"IP_ADDRESS"` | ||
| * - `"PHONE_NUMBER"` | ||
| * | ||
| * You can also use labels of custom info detected by `detect`. | ||
| */ | ||
| allow: Array<ListedEntities | Exclude<DetectedEntities, undefined>>; | ||
| /** | ||
| * List of sensitive info types to deny, | ||
| * cannot be combined with `allow`. | ||
| */ | ||
| deny?: never; | ||
| /** | ||
| * Tokens to consider (default: `1`). | ||
| * | ||
| * A list of tokens of this size will be passed to the custom detect | ||
| * function. | ||
| */ | ||
| contextWindowSize?: number; | ||
| /** | ||
| * Block mode of the rule (default: `"DRY_RUN"`). | ||
| * | ||
| * `"DRY_RUN"` will allow all requests while still providing access to the | ||
| * rule results. | ||
| * `"LIVE"` will block requests when the rate limit is exceeded. | ||
| */ | ||
| mode?: ArcjetMode; | ||
| /** | ||
| * Custom detection function to identify sensitive information. | ||
| * | ||
| * @param tokens | ||
| * Tokens. | ||
| * @returns | ||
| * List of entities (or undefined). | ||
| */ | ||
| detect?: (tokens: string[]) => ReadonlyArray<DetectedEntities>; | ||
| }; | ||
| /** | ||
| * Configuration for the sensitive info detection rule to deny certain | ||
| * sensitive info types and allow others. | ||
| * | ||
| * @template DetectedEntities | ||
| * Custom entity names that are returned from `detect` and optionally listed | ||
| * in `deny`. | ||
| * @template ListedEntities | ||
| * Entity names that can be listed in the `deny` field. | ||
| */ | ||
| type SensitiveInfoOptionsDeny<DetectedEntities extends string | undefined = undefined, ListedEntities extends ArcjetSensitiveInfoType | Exclude<DetectedEntities, undefined> = ArcjetSensitiveInfoType | Exclude<DetectedEntities, undefined>> = { | ||
| /** | ||
| * List of sensitive info types to allow, | ||
| * cannot be combined with `deny`. | ||
| */ | ||
| allow?: never; | ||
| /** | ||
| * List of sensitive info types to deny (required). | ||
| * | ||
| * Only the types in this list will be denied and any other detected types | ||
| * will be allowed. | ||
| * All sensitive info (including that detected by `detect`) will be allowed if | ||
| * empty. | ||
| * You must provide either `allow` or `deny`, not both. | ||
| * | ||
| * You can use the following sensitive info types that can be detected | ||
| * natively: | ||
| * | ||
| * - `"CREDIT_CARD_NUMBER"` | ||
| * - `"EMAIL"` | ||
| * - `"IP_ADDRESS"` | ||
| * - `"PHONE_NUMBER"` | ||
| * | ||
| * You can also use labels of custom info detected by `detect`. | ||
| */ | ||
| deny: Array<ListedEntities | Exclude<DetectedEntities, undefined>>; | ||
| /** | ||
| * Tokens to consider (default: `1`). | ||
| * | ||
| * A list of tokens of this size will be passed to the custom detect | ||
| * function. | ||
| */ | ||
| contextWindowSize?: number; | ||
| /** | ||
| * Block mode of the rule (default: `"DRY_RUN"`). | ||
| * | ||
| * `"DRY_RUN"` will allow all requests while still providing access to the | ||
| * rule results. | ||
| * `"LIVE"` will block requests when the rate limit is exceeded. | ||
| */ | ||
| mode?: ArcjetMode; | ||
| /** | ||
| * Custom detection function to identify sensitive information. | ||
| * | ||
| * @param tokens | ||
| * Tokens. | ||
| * @returns | ||
| * List of entities (or undefined). | ||
| */ | ||
| detect?: (tokens: string[]) => ReadonlyArray<DetectedEntities>; | ||
| }; | ||
| /** | ||
| * Configuration for the sensitive info detection rule. | ||
| * | ||
| * @template DetectedEntities | ||
| * Custom entity names that are returned from `detect` and optionally listed | ||
| * in `allow` or `deny`. | ||
| * @template ListedEntities | ||
| * Entity names that can be listed in the `allow` or `deny` field. | ||
| */ | ||
| type SensitiveInfoOptions<DetectedEntities extends string | undefined = undefined, ListedEntities extends ArcjetSensitiveInfoType | Exclude<DetectedEntities, undefined> = ArcjetSensitiveInfoType | Exclude<DetectedEntities, undefined>> = SensitiveInfoOptionsAllow<DetectedEntities, ListedEntities> | SensitiveInfoOptionsDeny<DetectedEntities, ListedEntities>; | ||
| /** | ||
| * Configuration to allow if a filter matches and deny otherwise. | ||
| */ | ||
| type FilterOptionsAllow = { | ||
| /** | ||
| * Expressions. | ||
| */ | ||
| allow: ReadonlyArray<string>; | ||
| /** | ||
| * Expressions, | ||
| * must not be set if `allow` is set. | ||
| */ | ||
| deny?: never; | ||
| /** | ||
| * Mode. | ||
| */ | ||
| mode?: ArcjetMode | undefined; | ||
| }; | ||
| /** | ||
| * Configuration to deny if a filter matches and allow otherwise. | ||
| */ | ||
| type FilterOptionsDeny = { | ||
| /** | ||
| * Expressions, | ||
| * must not be set if `deny` is set. | ||
| */ | ||
| allow?: never; | ||
| /** | ||
| * Expressions. | ||
| */ | ||
| deny: ReadonlyArray<string>; | ||
| /** | ||
| * Mode. | ||
| */ | ||
| mode?: ArcjetMode | undefined; | ||
| }; | ||
| /** | ||
| * Configuration for `filter` rule. | ||
| */ | ||
| type FilterOptions = FilterOptionsAllow | FilterOptionsDeny; | ||
| type PlainObject = { | ||
| [key: string]: unknown; | ||
| }; | ||
| /** | ||
| * Arcjet provides a set of key primitives which can be used to build security | ||
| * functionality. | ||
| * Each primitive can be used independently or combined as part of a | ||
| * pre-configured product. | ||
| * | ||
| * @template Props | ||
| * Configuration. | ||
| */ | ||
| type Primitive<Props extends PlainObject = {}> = [ArcjetRule<Props>]; | ||
| /** | ||
| * Pre-configured product consisting of combined primitives. | ||
| * | ||
| * @template Props | ||
| * Configuration. | ||
| */ | ||
| type Product<Props extends PlainObject = {}> = ArcjetRule<Props>[]; | ||
| type ExcludeBuiltinCharacteristic<T> = T extends "ip.src" | "http.host" | "http.method" | "http.request.uri.path" | `http.request.headers["${string}"]` | `http.request.cookie["${string}"]` | `http.request.uri.args["${string}"]` ? never : [string] extends [T] ? never : T extends string ? T : never; | ||
| /** | ||
| * Props for characteristics. | ||
| * | ||
| * Utility type to generate a record of props for each characteristic. | ||
| * It excludes the known characteristics which Arcjet provides. | ||
| * | ||
| * @template Characteristics | ||
| * List of characteristics. | ||
| */ | ||
| type CharacteristicProps<Characteristics extends readonly string[]> = Characteristics extends [] ? {} : { [K in ExcludeBuiltinCharacteristic<Characteristics[number]>]: boolean | number | string }; | ||
| type PropsForRule<R> = R extends ArcjetRule<infer Props> ? Props : {}; | ||
| /** | ||
| * Props for rules. | ||
| * | ||
| * Utility type to generate a record of props for all rules. | ||
| * | ||
| * @template Rules | ||
| * Matrix or list of rules. | ||
| */ | ||
| type ExtraProps<Rules> = Rules extends [] ? {} : Rules extends ArcjetRule[][] ? UnionToIntersection<PropsForRule<Rules[number][number]>> : Rules extends ArcjetRule[] ? UnionToIntersection<PropsForRule<Rules[number]>> : never; | ||
| /** | ||
| * Additional context provided by adapters. | ||
| */ | ||
| type ArcjetAdapterContext = { | ||
| /** | ||
| * Allow arbitrary indexing. | ||
| * | ||
| * Adapters could include the Arcjet API Key if it were only available in a | ||
| * runtime handler or IP details provided by a platform. | ||
| */ | ||
| [key: string]: unknown; | ||
| /** | ||
| * Read the request body (required). | ||
| */ | ||
| getBody(): Promise<string>; | ||
| /** | ||
| * Wait for a promise to resolve before continuing (optional). | ||
| * | ||
| * @param promise | ||
| * The promise to wait for. | ||
| * @returns | ||
| * Nothing. | ||
| */ | ||
| waitUntil?: ((promise: Promise<unknown>) => void) | undefined; | ||
| }; | ||
| /** | ||
| * Arcjet request. | ||
| * | ||
| * @template Props | ||
| * Extra data that might be useful for Arcjet. | ||
| * For example, requested tokens are specified as the `requested` property. | ||
| */ | ||
| type ArcjetRequest<Props extends PlainObject> = Simplify<{ | ||
| /** | ||
| * Additional properties. | ||
| * | ||
| * For example, an email address related to the request is commonly passed | ||
| * as `email` (`string`). | ||
| */ | ||
| [key: string]: unknown; | ||
| /** | ||
| * IP address of the client. | ||
| */ | ||
| ip: string; | ||
| /** | ||
| * HTTP method of the request. | ||
| */ | ||
| method: string; | ||
| /** | ||
| * Protocol of the request. | ||
| */ | ||
| protocol: string; | ||
| /** | ||
| * Host of the request. | ||
| */ | ||
| host: string; | ||
| /** | ||
| * Path of the request. | ||
| */ | ||
| path: string; | ||
| /** | ||
| * Headers of the request. | ||
| */ | ||
| headers: Headers | Record<string, string | string[] | undefined>; | ||
| /** | ||
| * Semicolon-separated cookies for a request. | ||
| */ | ||
| cookies: string; | ||
| /** | ||
| * Query string for a request. | ||
| * Commonly referred to as a "querystring". | ||
| * Starts with `?` when not empty. | ||
| */ | ||
| query: string; | ||
| } & Props>; | ||
| /** | ||
| * Arcjet token bucket rate limiting rule. | ||
| * | ||
| * Applying this rule sets a token bucket rate limit. | ||
| * | ||
| * This algorithm is based on a bucket filled with a specific number of tokens. | ||
| * Each request withdraws some amount of tokens from the bucket and the bucket | ||
| * is refilled at a fixed rate. | ||
| * Once the bucket is empty, the client is blocked until the bucket refills. | ||
| * | ||
| * This algorithm is useful when you want to allow clients to make a burst of | ||
| * requests and then still be able to make requests at a slower rate. | ||
| * | ||
| * @template Characteristics | ||
| * Characteristics to track a user by. | ||
| * @param options | ||
| * Configuration for the token bucket rate limiting rule (required). | ||
| * @returns | ||
| * Token bucket rule to provide to the SDK in the `rules` field. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * tokenBucket({ | ||
| * mode: "LIVE", | ||
| * refillRate: 10, | ||
| * interval: "60s", | ||
| * capacity: 100, | ||
| * }); | ||
| * ``` | ||
| * @example | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [ | ||
| * tokenBucket({ | ||
| * mode: "LIVE", | ||
| * refillRate: 10, | ||
| * interval: "60s", | ||
| * capacity: 100, | ||
| * }), | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @link https://docs.arcjet.com/rate-limiting | ||
| * @link https://docs.arcjet.com/rate-limiting/algorithms#token-bucket | ||
| * @link https://docs.arcjet.com/rate-limiting/reference | ||
| */ | ||
| declare function tokenBucket<const Characteristics extends readonly string[] = []>(options: TokenBucketRateLimitOptions<Characteristics>): [ArcjetTokenBucketRateLimitRule<Simplify<UnionToIntersection<{ | ||
| requested: number; | ||
| } | CharacteristicProps<Characteristics>>>>]; | ||
| /** | ||
| * Arcjet fixed window rate limiting rule. | ||
| * | ||
| * Applying this rule sets a fixed window rate limit which tracks the number of | ||
| * requests made by a client over a fixed time window. | ||
| * | ||
| * This is the simplest algorithm. | ||
| * It tracks the number of requests made by a client over a fixed time window | ||
| * such as 60 seconds. | ||
| * If the client exceeds the limit, they are blocked until the window expires. | ||
| * | ||
| * This algorithm is useful when you want to apply a simple fixed limit in a | ||
| * fixed time window. | ||
| * For example, a simple limit on the total number of requests a client can make. | ||
| * However, it can be susceptible to the stampede problem where a client makes | ||
| * a burst of requests at the start of a window and then is blocked for the rest | ||
| * of the window. | ||
| * The sliding window algorithm can be used to avoid this. | ||
| * | ||
| * @template Characteristics | ||
| * Characteristics to track a user by. | ||
| * @param options | ||
| * Configuration for the fixed window rate limiting rule (required). | ||
| * @returns | ||
| * Fixed window rule to provide to the SDK in the `rules` field. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * fixedWindow({ mode: "LIVE", window: "60s", max: 100 }); | ||
| * ``` | ||
| * @example | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [ | ||
| * fixedWindow({ | ||
| * mode: "LIVE", | ||
| * window: "60s", | ||
| * max: 100, | ||
| * }) | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @link https://docs.arcjet.com/rate-limiting | ||
| * @link https://docs.arcjet.com/rate-limiting/algorithms#fixed-window | ||
| * @link https://docs.arcjet.com/rate-limiting/reference | ||
| */ | ||
| declare function fixedWindow<const Characteristics extends readonly string[] = []>(options: FixedWindowRateLimitOptions<Characteristics>): [ArcjetFixedWindowRateLimitRule<CharacteristicProps<Characteristics>>]; | ||
| /** | ||
| * Arcjet sliding window rate limiting rule. | ||
| * | ||
| * Applying this rule sets a sliding window rate limit which tracks the number | ||
| * of requests made by a client over a sliding window so that the window moves | ||
| * with time. | ||
| * | ||
| * This algorithm is useful to avoid the stampede problem of the fixed window. | ||
| * It provides smoother rate limiting over time and can prevent a client from | ||
| * making a burst of requests at the start of a window and then being blocked | ||
| * for the rest of the window. | ||
| * | ||
| * @template Characteristics | ||
| * Characteristics to track a user by. | ||
| * @param options | ||
| * Configuration for the sliding window rate limiting rule (required). | ||
| * @returns | ||
| * Token bucket rule to provide to the SDK in the `rules` field. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * slidingWindow({ mode: "LIVE", interval: "60s", max: 100 }); | ||
| * ``` | ||
| * @example | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [ | ||
| * slidingWindow({ | ||
| * mode: "LIVE", | ||
| * interval: "60s", | ||
| * max: 100, | ||
| * }) | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @link https://docs.arcjet.com/rate-limiting | ||
| * @link https://docs.arcjet.com/rate-limiting/algorithms#sliding-window | ||
| * @link https://docs.arcjet.com/rate-limiting/reference | ||
| */ | ||
| declare function slidingWindow<const Characteristics extends readonly string[] = []>(options: SlidingWindowRateLimitOptions<Characteristics>): [ArcjetSlidingWindowRateLimitRule<CharacteristicProps<Characteristics>>]; | ||
| /** | ||
| * Arcjet sensitive information detection rule. | ||
| * | ||
| * Applying this rule protects against clients sending you sensitive information | ||
| * such as personally identifiable information (PII) that you do not wish to | ||
| * handle. | ||
| * The rule runs entirely locally so no data ever leaves your environment. | ||
| * | ||
| * This rule includes built-in detections for email addresses, credit/debit card | ||
| * numbers, IP addresses, and phone numbers. | ||
| * You can also provide a custom detection function to identify additional | ||
| * sensitive information. | ||
| * | ||
| * You should pass the value to check to `protect`: | ||
| * | ||
| * ```js | ||
| * const decision = await arcjet.protect(request, { sensitiveInfoValue: "..." }); | ||
| * ``` | ||
| * | ||
| * The entire request body is checked if no value is provided. | ||
| * This behavior is deprecated: prefer passing the value explicitly. | ||
| * | ||
| * @template DetectedEntities | ||
| * Custom entity names that are returned from `detect` and optionally listed | ||
| * in `allow` or `deny`. | ||
| * @template ListedEntities | ||
| * Entity names that can be listed in the `allow` or `deny` field. | ||
| * @param options | ||
| * Configuration for the sensitive information detection rule (required). | ||
| * @returns | ||
| * Sensitive information rule to provide to the SDK in the `rules` field. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * sensitiveInfo({ mode: "LIVE", deny: ["EMAIL"] }); | ||
| * ``` | ||
| * @example | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [ | ||
| * sensitiveInfo({ | ||
| * mode: "LIVE", | ||
| * deny: ["EMAIL"], | ||
| * }) | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| * @example | ||
| * Custom detection function: | ||
| * | ||
| * ```ts | ||
| * function detectDash(tokens: string[]): Array<"CONTAINS_DASH" | undefined> { | ||
| * return tokens.map((token) => { | ||
| * if (token.includes("-")) { | ||
| * return "CONTAINS_DASH"; | ||
| * } | ||
| * }); | ||
| * } | ||
| * | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [ | ||
| * sensitiveInfo({ | ||
| * mode: "LIVE", | ||
| * deny: ["EMAIL", "CONTAINS_DASH"], | ||
| * detect: detectDash, | ||
| * contextWindowSize: 2, | ||
| * }) | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @link https://docs.arcjet.com/sensitive-info | ||
| * @link https://docs.arcjet.com/sensitive-info/reference | ||
| */ | ||
| declare function sensitiveInfo<DetectedEntities extends string | undefined = undefined, ListedEntities extends ArcjetSensitiveInfoType | Exclude<DetectedEntities, undefined> = ArcjetSensitiveInfoType | Exclude<DetectedEntities, undefined>>(options: SensitiveInfoOptions<DetectedEntities, ListedEntities>): [ArcjetSensitiveInfoRule<{ | ||
| sensitiveInfoValue?: string | null | undefined; | ||
| }>]; | ||
| /** | ||
| * Arcjet email validation rule. | ||
| * | ||
| * Applying this rule allows you to validate and verify an email address. | ||
| * | ||
| * The first step of the analysis is to validate the email address syntax. | ||
| * This runs locally within the SDK and validates the email address is in the | ||
| * correct format. | ||
| * If the email syntax is valid, the SDK will pass the email address to the | ||
| * Arcjet cloud API to verify the email address. | ||
| * This performs several checks, depending on the rule configuration. | ||
| * | ||
| * @param options | ||
| * Configuration for the email validation rule (required). | ||
| * @returns | ||
| * Email rule to provide to the SDK in the `rules` field. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * validateEmail({ mode: "LIVE", deny: ["DISPOSABLE", "INVALID"] }); | ||
| * ``` | ||
| * @example | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [ | ||
| * validateEmail({ | ||
| * mode: "LIVE", | ||
| * deny: ["DISPOSABLE", "INVALID"] | ||
| * }) | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @link https://docs.arcjet.com/email-validation | ||
| * @link https://docs.arcjet.com/email-validation/reference | ||
| */ | ||
| declare function validateEmail(options: EmailOptions): [ArcjetEmailRule<{ | ||
| email: string; | ||
| }>]; | ||
| /** | ||
| * Arcjet bot detection rule. | ||
| * | ||
| * Applying this rule allows you to manage traffic by automated clients and | ||
| * bots. | ||
| * | ||
| * Bots can be good (such as search engine crawlers or monitoring agents) or bad | ||
| * (such as scrapers or automated scripts). | ||
| * Arcjet allows you to configure which bots you want to allow or deny by | ||
| * specific bot names such as curl, as well as by category such as search | ||
| * engine bots. | ||
| * | ||
| * Bots are detected based on various signals such as the user agent, IP | ||
| * address, DNS records, and more. | ||
| * | ||
| * @param options | ||
| * Configuration for the bot rule (required). | ||
| * @returns | ||
| * Bot rule to provide to the SDK in the `rules` field. | ||
| * | ||
| * @example | ||
| * Allow search engine bots and curl, deny all other bots: | ||
| * | ||
| * ```ts | ||
| * detectBot({ mode: "LIVE", allow: ["CATEGORY:SEARCH_ENGINE", "CURL"] }); | ||
| * ``` | ||
| * @example | ||
| * Allow search engine bots and curl, deny all other bots: | ||
| * | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [ | ||
| * detectBot({ | ||
| * mode: "LIVE", | ||
| * allow: ["CATEGORY:SEARCH_ENGINE", "CURL"] | ||
| * }) | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| * @example | ||
| * Deny AI crawlers, allow all other bots: | ||
| * | ||
| * ```ts | ||
| * detectBot({ mode: "LIVE", deny: ["CATEGORY:AI"] }); | ||
| * ``` | ||
| * @example | ||
| * Deny AI crawlers, allows all other bots: | ||
| * | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [ | ||
| * detectBot({ | ||
| * mode: "LIVE", | ||
| * deny: ["CATEGORY:AI"] | ||
| * }) | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @link https://docs.arcjet.com/bot-protection | ||
| * @link https://docs.arcjet.com/bot-protection/identifying-bots | ||
| * @link https://docs.arcjet.com/bot-protection/reference | ||
| */ | ||
| declare function detectBot(options: BotOptions): [ArcjetBotRule<{}>]; | ||
| /** | ||
| * Configuration for the Shield WAF rule. | ||
| */ | ||
| type ShieldOptions = { | ||
| /** | ||
| * Block mode of the rule (default: `"DRY_RUN"`). | ||
| * | ||
| * `"DRY_RUN"` will allow all requests while still providing access to the | ||
| * rule results. | ||
| * `"LIVE"` will block requests when the rate limit is exceeded. | ||
| */ | ||
| mode?: ArcjetMode; | ||
| }; | ||
| /** | ||
| * Configuration for the prompt injection detection rule. | ||
| */ | ||
| type DetectPromptInjectionOptions = { | ||
| /** | ||
| * Block mode of the rule (default: `"DRY_RUN"`). | ||
| * | ||
| * `"DRY_RUN"` will allow all requests while still providing access to the | ||
| * rule results. | ||
| * `"LIVE"` will block requests when prompt injection is detected. | ||
| */ | ||
| mode?: ArcjetMode; | ||
| /** | ||
| * The score threshold above which a request is considered a prompt injection | ||
| * attempt (default: `0.5`) e.g. anything over `0.5` is malicious. | ||
| * | ||
| * Must be in the range (0.0, 1.0) exclusive. | ||
| * | ||
| * @deprecated | ||
| * This option is no longer respected by the server and will be removed in | ||
| * a future release. | ||
| */ | ||
| threshold?: number; | ||
| }; | ||
| /** | ||
| * Arcjet Shield WAF rule. | ||
| * | ||
| * Applying this rule protects your application against common attacks, | ||
| * including the OWASP Top 10. | ||
| * | ||
| * The Arcjet Shield WAF analyzes every request to your application to detect | ||
| * suspicious activity. | ||
| * Once a certain suspicion threshold is reached, | ||
| * subsequent requests from that client are blocked for a period of time. | ||
| * | ||
| * @param options | ||
| * Configuration for the Shield rule. | ||
| * @returns | ||
| * Shield rule to provide to the SDK in the `rules` field. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * shield({ mode: "LIVE" }); | ||
| * ``` | ||
| * @example | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [shield({ mode: "LIVE" })], | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @link https://docs.arcjet.com/shield | ||
| * @link https://docs.arcjet.com/shield/reference | ||
| */ | ||
| declare function shield(options: ShieldOptions): [ArcjetShieldRule<{}>]; | ||
| /** | ||
| * Arcjet prompt injection detection rule. | ||
| * | ||
| * Analyzes LLM prompts to detect prompt injection attempts. | ||
| * | ||
| * The Arcjet prompt injection detection rule analyzes prompts sent to LLM | ||
| * applications to detect jailbreak and injection attempts. | ||
| * The analysis is performed through Arcjet's Cloud API. | ||
| * | ||
| * @param options | ||
| * Configuration for the prompt injection detection rule. | ||
| * @returns | ||
| * Prompt injection detection rule to provide to the SDK in the `rules` field. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * detectPromptInjection({ mode: "LIVE" }); | ||
| * ``` | ||
| * @example | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [detectPromptInjection({ mode: "LIVE" })], | ||
| * }); | ||
| * ``` | ||
| */ | ||
| declare function detectPromptInjection(options?: DetectPromptInjectionOptions): [ArcjetPromptInjectionDetectionRule]; | ||
| /** | ||
| * Arcjet prompt injection detection rule. | ||
| * | ||
| * @deprecated | ||
| * Use `detectPromptInjection` instead. | ||
| */ | ||
| declare const experimental_detectPromptInjection: typeof detectPromptInjection; | ||
| /** | ||
| * Configuration for signup form protection rule. | ||
| * | ||
| * @template Characteristics | ||
| * Characteristics to track a user by. | ||
| */ | ||
| type ProtectSignupOptions<Characteristics extends readonly string[]> = { | ||
| /** | ||
| * Configuration for rate limit rule (required). | ||
| */ | ||
| rateLimit: SlidingWindowRateLimitOptions<Characteristics>; | ||
| /** | ||
| * Configuration for bot protection rule (required). | ||
| */ | ||
| bots: BotOptions; | ||
| /** | ||
| * Configuration for email validation rule (required). | ||
| */ | ||
| email: EmailOptions; | ||
| }; | ||
| /** | ||
| * Arcjet signup form protection rule. | ||
| * | ||
| * Applying this rule combines rate limiting, bot protection, and email | ||
| * validation to protect your signup forms from abuse. | ||
| * Using this rule will configure the following: | ||
| * | ||
| * - Rate limiting - signup forms are a common target for bots. Arcjet’s rate | ||
| * limiting helps to prevent bots and other automated or malicious clients | ||
| * from submitting your signup form too many times in a short period of time. | ||
| * - Bot protection - signup forms are usually exclusively used by humans, which | ||
| * means that any automated submissions to the form are likely to be | ||
| * fraudulent. | ||
| * - Email validation - email addresses should be validated to ensure the signup | ||
| * is coming from a legitimate user with a real email address that can | ||
| * actually receive messages. | ||
| * | ||
| * @template Characteristics | ||
| * Characteristics to track a user by. | ||
| * @param options | ||
| * Configuration for the signup form protection rule. | ||
| * @returns | ||
| * Signup form protection rule to provide to the SDK in the `rules` field. | ||
| * | ||
| * @example | ||
| * Our recommended configuration for most signup forms is: | ||
| * | ||
| * - Block email addresses with invalid syntax, that are from disposable email providers, | ||
| * or do not have valid MX records configured. | ||
| * - Block all bots. | ||
| * - Apply a rate limit of 5 submissions per 10 minutes from a single IP | ||
| * address. | ||
| * | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [ | ||
| * protectSignup({ | ||
| * email: { | ||
| * mode: "LIVE", | ||
| * deny: ["DISPOSABLE", "INVALID", "NO_MX_RECORDS"], | ||
| * }, | ||
| * bots: { | ||
| * mode: "LIVE", | ||
| * allow: [], // block all detected bots | ||
| * }, | ||
| * rateLimit: { | ||
| * mode: "LIVE", | ||
| * interval: "10m", | ||
| * max: 5, | ||
| * }, | ||
| * }), | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @link https://docs.arcjet.com/signup-protection | ||
| * @link https://docs.arcjet.com/signup-protection/reference | ||
| */ | ||
| declare function protectSignup<const Characteristics extends string[] = []>(options: ProtectSignupOptions<Characteristics>): [rateLimit: ArcjetSlidingWindowRateLimitRule<CharacteristicProps<Characteristics>>, bot: ArcjetBotRule<{}>, email: ArcjetEmailRule<{ | ||
| email: string; | ||
| }>]; | ||
| /** | ||
| * Arcjet filter rule. | ||
| * | ||
| * Applying this rule lets you block requests using Wireshark-like display | ||
| * filter expressions over HTTP headers, IP addresses, and other request | ||
| * fields. | ||
| * You can quickly enforce rules like allow/deny by country, network, or | ||
| * `user-agent` pattern. | ||
| * | ||
| * See the [reference guide](https://docs.arcjet.com/filters/reference) for | ||
| * more info on the expression language fields, functions, and values. | ||
| * | ||
| * @param options | ||
| * Configuration (required). | ||
| * @returns | ||
| * Filter rule. | ||
| * | ||
| * @example | ||
| * In this example, the expression matches non-VPN GET requests from the US. | ||
| * Requests matching the expression are allowed, all others are denied. | ||
| * | ||
| * ```ts | ||
| * filter({ | ||
| * allow: [ | ||
| * 'http.request.method eq "GET" and ip.src.country eq "US" and not ip.src.vpn', | ||
| * ], | ||
| * mode: "LIVE", | ||
| * }) | ||
| * ``` | ||
| * | ||
| * @link https://docs.arcjet.com/filters | ||
| * @link https://docs.arcjet.com/filters/reference | ||
| */ | ||
| declare function filter(options: FilterOptions): [ArcjetFilterRule]; | ||
| /** | ||
| * Configuration for Arcjet. | ||
| * | ||
| * @template Rules | ||
| * List of rules. | ||
| * @template Characteristics | ||
| * Characteristics to track a user by. | ||
| */ | ||
| interface ArcjetOptions<Rules extends [...(Primitive | Product)[]], Characteristics extends readonly string[]> { | ||
| /** | ||
| * API key to identify the site in Arcjet (required). | ||
| */ | ||
| key: string; | ||
| /** | ||
| * Rules to apply when protecting a request (required). | ||
| */ | ||
| rules: readonly [...Rules]; | ||
| /** | ||
| * Characteristics to track a user by (default: `["ip.src"]`). | ||
| * Can also be passed to rules. | ||
| */ | ||
| characteristics?: Characteristics; | ||
| /** | ||
| * Client used to make requests to the Arcjet API (optional). | ||
| * | ||
| * This should not be passed by end users but is configured by integrations | ||
| * (such as `@arcjet/next`) or for testing purposes. | ||
| */ | ||
| client?: Client; | ||
| /** | ||
| * Log interface to emit useful information from the SDK (optional). | ||
| */ | ||
| log?: ArcjetLogger; | ||
| } | ||
| /** | ||
| * Arcjet instance. | ||
| * | ||
| * Primarily has a `protect()` method to make a decision about how a request | ||
| * should be handled. | ||
| */ | ||
| interface Arcjet<Props extends PlainObject> { | ||
| /** | ||
| * Make a decision about how to handle a request. | ||
| * | ||
| * This will analyze the request locally where possible and otherwise call | ||
| * the Arcjet decision API. | ||
| * | ||
| * @param ctx | ||
| * Additional context for this function call. | ||
| * @param request | ||
| * Details about the {@linkcode ArcjetRequest} that Arcjet needs to make a | ||
| * decision. | ||
| * @returns | ||
| * Promise that resolves to an {@linkcode ArcjetDecision} indicating | ||
| * Arcjet’s decision about the request. | ||
| */ | ||
| protect(ctx: ArcjetAdapterContext, request: ArcjetRequest<Props>): Promise<ArcjetDecision>; | ||
| /** | ||
| * Augment the client with another rule. | ||
| * | ||
| * Useful for varying rules based on criteria in your handler such as | ||
| * different rate limit for logged in users. | ||
| * | ||
| * @param rule | ||
| * Rule to add to Arcjet. | ||
| * @returns | ||
| * Arcjet instance augmented with the given rule. | ||
| */ | ||
| withRule<Rule extends ArcjetRule>(rule: Array<Rule>): Arcjet<Props & (Rule extends ArcjetRule<infer P> ? P : {})>; | ||
| } | ||
| /** | ||
| * Create a new Arcjet instance. | ||
| * | ||
| * @template Rules | ||
| * List of rules. | ||
| * @template Characteristics | ||
| * Characteristics to track a user by. | ||
| * @param options | ||
| * Configuration. | ||
| * @returns | ||
| * Arcjet instance. | ||
| */ | ||
| declare function arcjet<const Rules extends [...(Primitive | Product)[]] = [], const Characteristics extends readonly string[] = []>(options: ArcjetOptions<Rules, Characteristics>): Arcjet<ExtraProps<Rules> & CharacteristicProps<Characteristics>>; | ||
| //#endregion | ||
| export { Arcjet, ArcjetAdapterContext, ArcjetOptions, ArcjetRequest, BotOptions, BotOptionsAllow, BotOptionsDeny, CharacteristicProps, DetectPromptInjectionOptions, EmailOptions, EmailOptionsAllow, EmailOptionsDeny, ExtraProps, FilterOptions, FilterOptionsAllow, FilterOptionsDeny, FixedWindowRateLimitOptions, Primitive, Product, ProtectSignupOptions, SensitiveInfoOptions, SensitiveInfoOptionsAllow, SensitiveInfoOptionsDeny, ShieldOptions, SlidingWindowRateLimitOptions, TokenBucketRateLimitOptions, arcjet as default, detectBot, detectPromptInjection, experimental_detectPromptInjection, filter, fixedWindow, protectSignup, sensitiveInfo, shield, slidingWindow, tokenBucket, validateEmail }; |
-2121
| import * as analyze from "@arcjet/analyze"; | ||
| import { MemoryCache } from "@arcjet/cache"; | ||
| import * as duration from "@arcjet/duration"; | ||
| import { ArcjetHeaders } from "@arcjet/headers"; | ||
| import { ArcjetBotReason, ArcjetDenyDecision, ArcjetEmailReason, ArcjetErrorDecision, ArcjetErrorReason, ArcjetFilterReason, ArcjetPromptInjectionReason, ArcjetRateLimitReason, ArcjetReason, ArcjetRuleResult, ArcjetSensitiveInfoReason, ArcjetShieldReason } from "@arcjet/protocol"; | ||
| import { runtime } from "@arcjet/runtime"; | ||
| import * as hasher from "@arcjet/stable-hash"; | ||
| export * from "@arcjet/protocol"; | ||
| //#region src/index.ts | ||
| function assert(condition, msg) { | ||
| if (!condition) throw new Error(msg); | ||
| } | ||
| function errorMessage(err) { | ||
| if (err) { | ||
| if (typeof err === "string") return err; | ||
| if (typeof err === "object" && "message" in err && typeof err.message === "string") return err.message; | ||
| } | ||
| return "Unknown problem"; | ||
| } | ||
| /** | ||
| * List of known fields on {@linkcode ArcjetRequest}. | ||
| * | ||
| * For unknown reasons this includes the `body` field. | ||
| * It also includes the semi-known `email` field. | ||
| */ | ||
| const knownFields = [ | ||
| "ip", | ||
| "method", | ||
| "protocol", | ||
| "host", | ||
| "path", | ||
| "headers", | ||
| "body", | ||
| "email", | ||
| "cookies", | ||
| "query", | ||
| "correlationId" | ||
| ]; | ||
| /** | ||
| * Check if a field is an unknown field. | ||
| * | ||
| * This affects whether it is moved into `extra` on the {@linkcode ArcjetRequest}. | ||
| * | ||
| * @param key | ||
| * Field name. | ||
| * @returns | ||
| * Whether the field is unknown. | ||
| */ | ||
| function isUnknownRequestProperty(key) { | ||
| return !knownFields.includes(key); | ||
| } | ||
| /** | ||
| * List of JSON fields on {@linkcode ArcjetRequest}. | ||
| */ | ||
| const jsonFields = ["filterLocal"]; | ||
| /** | ||
| * Check if a field is a known JSON field. | ||
| * | ||
| * This affects whether it is serialized as JSON. | ||
| * | ||
| * @param key | ||
| * Field name. | ||
| * @returns | ||
| * Whether the field is a known JSON field. | ||
| */ | ||
| function isJsonRequestProperty(key) { | ||
| return jsonFields.includes(key); | ||
| } | ||
| /** | ||
| * Check if a value is a known Arcjet email validation type. | ||
| * | ||
| * @param type | ||
| * Value to check.. | ||
| * @returns | ||
| * Whether the value is a known Arcjet email validation type. | ||
| */ | ||
| function isEmailType(type) { | ||
| return type === "FREE" || type === "DISPOSABLE" || type === "NO_MX_RECORDS" || type === "NO_GRAVATAR" || type === "INVALID"; | ||
| } | ||
| var Performance = class { | ||
| log; | ||
| constructor(logger) { | ||
| this.log = logger; | ||
| } | ||
| measure(label) { | ||
| const start = performance.now(); | ||
| return () => { | ||
| const diff = performance.now() - start; | ||
| this.log.debug("LATENCY %s: %sms", label, diff.toFixed(3)); | ||
| }; | ||
| } | ||
| }; | ||
| /** | ||
| * Turn an arbitrary value of an unknown field (one directly on an | ||
| * {@linkcode ArcjetRequest}) into a string. | ||
| * | ||
| * This supports `boolean`, `number`, and `string` values. | ||
| * Other values are serialized as `<unsupported value>`. | ||
| * | ||
| * These extra fields can be used for user-provided characteristics or as | ||
| * fields requested by custom rules. | ||
| * They are moved onto the `extra` and sent into WebAssembly or to the Arcjet | ||
| * Cloud API. | ||
| * | ||
| * @param value | ||
| * Value. | ||
| * @returns | ||
| * Serialized value. | ||
| */ | ||
| function toStringArbitrary(value) { | ||
| if (typeof value === "string") return value; | ||
| if (typeof value === "number") return `${value}`; | ||
| if (typeof value === "boolean") return value ? "true" : "false"; | ||
| return "<unsupported value>"; | ||
| } | ||
| /** | ||
| * Turn a value of an unknown field (one directly on an | ||
| * {@linkcode ArcjetRequest}) into a string, | ||
| * with JSON capabilities. | ||
| * | ||
| * @param value | ||
| * Value. | ||
| * @returns | ||
| * Serialized value. | ||
| */ | ||
| function toStringJson(value) { | ||
| if (typeof value === "string") return value; | ||
| try { | ||
| return JSON.stringify(value); | ||
| } catch {} | ||
| return "<unsupported value>"; | ||
| } | ||
| const SYMBOL_FOR_REQ_CONTEXT = Symbol.for("@vercel/request-context"); | ||
| function lookupWaitUntil() { | ||
| const fromSymbol = globalThis; | ||
| if (typeof fromSymbol[SYMBOL_FOR_REQ_CONTEXT] === "object" && fromSymbol[SYMBOL_FOR_REQ_CONTEXT] !== null && "get" in fromSymbol[SYMBOL_FOR_REQ_CONTEXT] && typeof fromSymbol[SYMBOL_FOR_REQ_CONTEXT].get === "function") { | ||
| const vercelCtx = fromSymbol[SYMBOL_FOR_REQ_CONTEXT].get(); | ||
| if (typeof vercelCtx === "object" && vercelCtx !== null && "waitUntil" in vercelCtx && typeof vercelCtx.waitUntil === "function") return vercelCtx.waitUntil; | ||
| } | ||
| } | ||
| /** | ||
| * Turn an SDK request into a request for `@arcjet/analyze`. | ||
| * | ||
| * In JavaScript more complex types are used. | ||
| * For WebAssembly JSON compatibility is needed. | ||
| * | ||
| * Practically, this turns a request where `headers` is a `Headers` instance, | ||
| * into the same request where `headers` is a plain object. | ||
| * | ||
| * @param request | ||
| * Request. | ||
| * @returns | ||
| * Transformed request. | ||
| */ | ||
| function toAnalyzeRequest(request) { | ||
| const headers = {}; | ||
| if (typeof request.headers !== "undefined") for (const [key, value] of request.headers.entries()) headers[key] = value; | ||
| const { correlationId, ...rest } = request; | ||
| return { | ||
| ...rest, | ||
| headers | ||
| }; | ||
| } | ||
| function extraProps(details) { | ||
| const extra = /* @__PURE__ */ new Map(); | ||
| for (const [key, value] of Object.entries(details)) if (isUnknownRequestProperty(key)) { | ||
| const toString = isJsonRequestProperty(key) ? toStringJson : toStringArbitrary; | ||
| extra.set(key, toString(value)); | ||
| } | ||
| return Object.fromEntries(extra.entries()); | ||
| } | ||
| /** | ||
| * Validations for {@linkcode BotOptions}. | ||
| */ | ||
| const botValidations = [ | ||
| { | ||
| key: "mode", | ||
| required: false, | ||
| validate: validateMode | ||
| }, | ||
| { | ||
| key: "allow", | ||
| required: false, | ||
| validate: validateStringArray | ||
| }, | ||
| { | ||
| key: "deny", | ||
| required: false, | ||
| validate: validateStringArray | ||
| } | ||
| ]; | ||
| /** | ||
| * Validations for {@linkcode SlidingWindowRateLimitOptions}. | ||
| */ | ||
| const slidingWindowValidations = [ | ||
| { | ||
| key: "mode", | ||
| required: false, | ||
| validate: validateMode | ||
| }, | ||
| { | ||
| key: "characteristics", | ||
| validate: validateStringArray, | ||
| required: false | ||
| }, | ||
| { | ||
| key: "max", | ||
| required: true, | ||
| validate: validateNumber | ||
| }, | ||
| { | ||
| key: "interval", | ||
| required: true, | ||
| validate: validateNumberOrString | ||
| } | ||
| ]; | ||
| /** | ||
| * Validations for {@linkcode EmailOptions}. | ||
| */ | ||
| const emailValidations = [ | ||
| { | ||
| key: "mode", | ||
| required: false, | ||
| validate: validateMode | ||
| }, | ||
| { | ||
| key: "allow", | ||
| required: false, | ||
| validate: validateEmailTypes | ||
| }, | ||
| { | ||
| key: "deny", | ||
| required: false, | ||
| validate: validateEmailTypes | ||
| }, | ||
| { | ||
| key: "requireTopLevelDomain", | ||
| required: false, | ||
| validate: validateBoolean | ||
| }, | ||
| { | ||
| key: "allowDomainLiteral", | ||
| required: false, | ||
| validate: validateBoolean | ||
| } | ||
| ]; | ||
| /** | ||
| * Validate an interface: an object of a particular shape. | ||
| * | ||
| * @template Type | ||
| * Type of the value object after validation. | ||
| * @param value | ||
| * Thing to validate. | ||
| * @param configuration | ||
| * Configuration. | ||
| * @returns | ||
| * Nothing. | ||
| */ | ||
| function validateInterface(value, configuration) { | ||
| const { name, validations } = configuration; | ||
| if (value === null || typeof value !== "object") throw new Error(`\`${name}\` options error: expected object`); | ||
| const valueRecord = value; | ||
| for (const { key, validate, required } of validations) { | ||
| if (required && !Object.hasOwn(valueRecord, key)) throw new Error(`\`${name}\` options error: \`${String(key)}\` is required`); | ||
| const value = valueRecord[key]; | ||
| if (typeof value !== "undefined") try { | ||
| validate(String(key), value); | ||
| } catch (err) { | ||
| throw new Error(`\`${name}\` options error: ${errorMessage(err)}`); | ||
| } | ||
| } | ||
| } | ||
| /** | ||
| * Validate an array. | ||
| * | ||
| * @param path | ||
| * Path to value. | ||
| * @param value | ||
| * Value to validate. | ||
| * @returns | ||
| * Nothing. | ||
| * @throws | ||
| * When not an array. | ||
| */ | ||
| function validateArray(path, value) { | ||
| if (!Array.isArray(value)) throw new Error(`invalid type for \`${path}\` - expected an array`); | ||
| } | ||
| /** | ||
| * Validate a boolean. | ||
| * | ||
| * @param path | ||
| * Path to value. | ||
| * @param value | ||
| * Value to validate. | ||
| * @returns | ||
| * Nothing. | ||
| * @throws | ||
| * When not a boolean. | ||
| */ | ||
| function validateBoolean(path, value) { | ||
| if (typeof value !== "boolean") throw new Error(`invalid type for \`${path}\` - expected boolean`); | ||
| } | ||
| /** | ||
| * Validate request details. | ||
| * | ||
| * @param value | ||
| * Value to validate. | ||
| * @returns | ||
| * Nothing. | ||
| * @throws | ||
| * When not request details. | ||
| */ | ||
| function validateDetails(value) { | ||
| validateInterface(value, { | ||
| name: "details", | ||
| validations: [...knownFields.map(function(key) { | ||
| return { | ||
| key, | ||
| required: key !== "body" && key !== "email" && key !== "correlationId", | ||
| validate: key === "headers" ? validateHeaders : validateString | ||
| }; | ||
| }), { | ||
| key: "extra", | ||
| required: true, | ||
| validate: validateStringRecord | ||
| }] | ||
| }); | ||
| } | ||
| /** | ||
| * Validate a headers interface. | ||
| * | ||
| * @param path | ||
| * Path to value. | ||
| * @param value | ||
| * Value to validate. | ||
| * @returns | ||
| * Nothing. | ||
| * @throws | ||
| * When not a headers interface. | ||
| */ | ||
| function validateHeaders(path, value) { | ||
| if (value === null || typeof value !== "object") throw new Error(`invalid value for \`${path}\` - expected headers object`); | ||
| const methods = [ | ||
| "has", | ||
| "get", | ||
| "set" | ||
| ]; | ||
| const valueRecord = value; | ||
| for (const method of methods) if (!(method in valueRecord) || typeof valueRecord[method] !== "function") throw new Error(`invalid value for \`${path}\` - expected headers object with method \`${method}\``); | ||
| } | ||
| /** | ||
| * Validate a number or string. | ||
| * | ||
| * @param path | ||
| * Path to value. | ||
| * @param value | ||
| * Value to validate. | ||
| * @returns | ||
| * Nothing. | ||
| * @throws | ||
| * When neither number nor string. | ||
| */ | ||
| function validateNumberOrString(path, value) { | ||
| if (typeof value !== "number" && typeof value !== "string") throw new Error(`invalid type for \`${path}\` - expected one of string, number`); | ||
| } | ||
| /** | ||
| * Validate a number. | ||
| * | ||
| * @param path | ||
| * Path to value. | ||
| * @param value | ||
| * Value to validate. | ||
| * @returns | ||
| * Nothing. | ||
| * @throws | ||
| * When not a number. | ||
| */ | ||
| function validateNumber(path, value) { | ||
| if (typeof value !== "number") throw new Error(`invalid type for \`${path}\` - expected number`); | ||
| } | ||
| /** | ||
| * Validate an array of string. | ||
| * | ||
| * @param path | ||
| * Path to value. | ||
| * @param value | ||
| * Value to validate. | ||
| * @returns | ||
| * Nothing. | ||
| * @throws | ||
| * When not an array of strings. | ||
| */ | ||
| function validateStringArray(path, value) { | ||
| validateArray(path, value); | ||
| for (const [index, subvalue] of value.entries()) validateString(path + "[" + index + "]", subvalue); | ||
| } | ||
| /** | ||
| * Validate a string. | ||
| * | ||
| * @param path | ||
| * Path to value. | ||
| * @param value | ||
| * Value to validate. | ||
| * @returns | ||
| * Nothing. | ||
| * @throws | ||
| * When not a string. | ||
| */ | ||
| function validateStringRecord(path, value) { | ||
| if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error(`invalid value for \`${path}\` - expected plain object`); | ||
| for (const [field, subvalue] of Object.entries(value)) validateString(path + "." + field, subvalue); | ||
| } | ||
| /** | ||
| * Validate a string. | ||
| * | ||
| * @param path | ||
| * Path to value. | ||
| * @param value | ||
| * Value to validate. | ||
| * @returns | ||
| * Nothing. | ||
| * @throws | ||
| * When not a string. | ||
| */ | ||
| function validateString(path, value) { | ||
| if (typeof value !== "string") throw new Error(`invalid type for \`${path}\` - expected string`); | ||
| } | ||
| /** | ||
| * Validate a function. | ||
| * | ||
| * @param path | ||
| * Path to value. | ||
| * @param value | ||
| * Value to validate. | ||
| * @returns | ||
| * Nothing. | ||
| * @throws | ||
| * When not a function. | ||
| */ | ||
| function validateFunction(path, value) { | ||
| if (typeof value !== "function") throw new Error(`invalid type for \`${path}\` - expected function`); | ||
| } | ||
| /** | ||
| * Validate an Arcjet mode. | ||
| * | ||
| * @param path | ||
| * Path to value. | ||
| * @param value | ||
| * Value to validate. | ||
| * @returns | ||
| * Nothing. | ||
| * @throws | ||
| * When not an Arcjet mode. | ||
| */ | ||
| function validateMode(path, value) { | ||
| if (value !== "DRY_RUN" && value !== "LIVE") throw new Error(`invalid value for \`${path}\` - expected one of 'LIVE', 'DRY_RUN'`); | ||
| } | ||
| /** | ||
| * Validate an array of email types. | ||
| * | ||
| * @param path | ||
| * Path to value. | ||
| * @param value | ||
| * Value to validate. | ||
| * @returns | ||
| * Nothing. | ||
| * @throws | ||
| * When not an array of email types. | ||
| */ | ||
| function validateEmailTypes(path, value) { | ||
| validateArray(path, value); | ||
| for (const [index, subvalue] of value.entries()) if (!isEmailType(subvalue)) throw new Error(`invalid value for \`${path}[${index}]\` - expected one of 'DISPOSABLE', 'FREE', 'NO_MX_RECORDS', 'NO_GRAVATAR', 'INVALID'`); | ||
| } | ||
| /** | ||
| * Validate token bucket options. | ||
| * | ||
| * @param value | ||
| * Value to validate. | ||
| * @returns | ||
| * Nothing. | ||
| * @throws | ||
| * When not token bucket options. | ||
| */ | ||
| function validateTokenBucketOptions(value) { | ||
| validateInterface(value, { | ||
| name: "tokenBucket", | ||
| validations: [ | ||
| { | ||
| key: "mode", | ||
| required: false, | ||
| validate: validateMode | ||
| }, | ||
| { | ||
| key: "characteristics", | ||
| validate: validateStringArray, | ||
| required: false | ||
| }, | ||
| { | ||
| key: "refillRate", | ||
| required: true, | ||
| validate: validateNumber | ||
| }, | ||
| { | ||
| key: "interval", | ||
| required: true, | ||
| validate: validateNumberOrString | ||
| }, | ||
| { | ||
| key: "capacity", | ||
| required: true, | ||
| validate: validateNumber | ||
| } | ||
| ] | ||
| }); | ||
| } | ||
| /** | ||
| * Validate fixed window options. | ||
| * | ||
| * @param value | ||
| * Value to validate. | ||
| * @returns | ||
| * Nothing. | ||
| * @throws | ||
| * When not fixed window options. | ||
| */ | ||
| function validateFixedWindowOptions(value) { | ||
| validateInterface(value, { | ||
| name: "fixedWindow", | ||
| validations: [ | ||
| { | ||
| key: "mode", | ||
| required: false, | ||
| validate: validateMode | ||
| }, | ||
| { | ||
| key: "characteristics", | ||
| validate: validateStringArray, | ||
| required: false | ||
| }, | ||
| { | ||
| key: "max", | ||
| required: true, | ||
| validate: validateNumber | ||
| }, | ||
| { | ||
| key: "window", | ||
| required: true, | ||
| validate: validateNumberOrString | ||
| } | ||
| ] | ||
| }); | ||
| } | ||
| /** | ||
| * Validate sliding window options. | ||
| * | ||
| * @param value | ||
| * Value to validate. | ||
| * @returns | ||
| * Nothing. | ||
| * @throws | ||
| * When not sliding window options. | ||
| */ | ||
| function validateSlidingWindowOptions(value) { | ||
| validateInterface(value, { | ||
| name: "slidingWindow", | ||
| validations: slidingWindowValidations | ||
| }); | ||
| } | ||
| /** | ||
| * Validate sensitive info options. | ||
| * | ||
| * @param value | ||
| * Value to validate. | ||
| * @returns | ||
| * Nothing. | ||
| * @throws | ||
| * When not sensitive info options. | ||
| */ | ||
| function validateSensitiveInfoOptions(value) { | ||
| validateInterface(value, { | ||
| name: "sensitiveInfo", | ||
| validations: [ | ||
| { | ||
| key: "mode", | ||
| required: false, | ||
| validate: validateMode | ||
| }, | ||
| { | ||
| key: "allow", | ||
| required: false, | ||
| validate: validateStringArray | ||
| }, | ||
| { | ||
| key: "deny", | ||
| required: false, | ||
| validate: validateStringArray | ||
| }, | ||
| { | ||
| key: "contextWindowSize", | ||
| required: false, | ||
| validate: validateNumber | ||
| }, | ||
| { | ||
| key: "detect", | ||
| required: false, | ||
| validate: validateFunction | ||
| } | ||
| ] | ||
| }); | ||
| } | ||
| /** | ||
| * Validate email options. | ||
| * | ||
| * @param value | ||
| * Value to validate. | ||
| * @returns | ||
| * Nothing. | ||
| * @throws | ||
| * When not email options. | ||
| */ | ||
| function validateEmailOptions(value) { | ||
| validateInterface(value, { | ||
| name: "validateEmail", | ||
| validations: emailValidations | ||
| }); | ||
| } | ||
| /** | ||
| * Validate bot options. | ||
| * | ||
| * @param value | ||
| * Value to validate. | ||
| * @returns | ||
| * Nothing. | ||
| * @throws | ||
| * When not bot options. | ||
| */ | ||
| function validateBotOptions(value) { | ||
| validateInterface(value, { | ||
| name: "detectBot", | ||
| validations: botValidations | ||
| }); | ||
| } | ||
| /** | ||
| * Validate shield options. | ||
| * | ||
| * @param value | ||
| * Value to validate. | ||
| * @returns | ||
| * Nothing. | ||
| * @throws | ||
| * When not shield options. | ||
| */ | ||
| function validateShieldOptions(value) { | ||
| validateInterface(value, { | ||
| name: "shield", | ||
| validations: [{ | ||
| key: "mode", | ||
| required: false, | ||
| validate: validateMode | ||
| }] | ||
| }); | ||
| } | ||
| /** | ||
| * Validate detect prompt injection options. | ||
| * | ||
| * @param value | ||
| * Value to validate. | ||
| * @returns | ||
| * Nothing. | ||
| * @throws | ||
| * When not detect prompt injection options. | ||
| */ | ||
| function validateDetectPromptInjectionOptions(value) { | ||
| validateInterface(value, { | ||
| name: "detectPromptInjection", | ||
| validations: [{ | ||
| key: "mode", | ||
| required: false, | ||
| validate: validateMode | ||
| }, { | ||
| key: "threshold", | ||
| required: false, | ||
| validate: validateNumber | ||
| }] | ||
| }); | ||
| } | ||
| /** | ||
| * Validate protect signup options. | ||
| * | ||
| * @param value | ||
| * Value to validate. | ||
| * @returns | ||
| * Nothing. | ||
| * @throws | ||
| * When not protect signup options. | ||
| */ | ||
| function validateProtectSignupOptions(value) { | ||
| validateInterface(value, { | ||
| name: "protectSignup", | ||
| validations: [ | ||
| { | ||
| key: "rateLimit", | ||
| required: true, | ||
| validate(_path, value) { | ||
| validateInterface(value, { | ||
| name: "rateLimit", | ||
| validations: slidingWindowValidations | ||
| }); | ||
| } | ||
| }, | ||
| { | ||
| key: "bots", | ||
| required: true, | ||
| validate(_path, value) { | ||
| validateInterface(value, { | ||
| name: "bots", | ||
| validations: botValidations | ||
| }); | ||
| } | ||
| }, | ||
| { | ||
| key: "email", | ||
| required: true, | ||
| validate(_path, value) { | ||
| validateInterface(value, { | ||
| name: "email", | ||
| validations: emailValidations | ||
| }); | ||
| } | ||
| } | ||
| ] | ||
| }); | ||
| } | ||
| /** | ||
| * Validate filter options. | ||
| * | ||
| * @param value | ||
| * Value to validate. | ||
| * @returns | ||
| * Nothing. | ||
| * @throws | ||
| * When not filter options. | ||
| */ | ||
| function validateFilterOptions(value) { | ||
| validateInterface(value, { | ||
| name: "filter", | ||
| validations: [ | ||
| { | ||
| key: "allow", | ||
| required: false, | ||
| validate: validateStringArray | ||
| }, | ||
| { | ||
| key: "deny", | ||
| required: false, | ||
| validate: validateStringArray | ||
| }, | ||
| { | ||
| key: "mode", | ||
| required: false, | ||
| validate: validateMode | ||
| } | ||
| ] | ||
| }); | ||
| } | ||
| const Priority = { | ||
| SensitiveInfo: 1, | ||
| Filter: 2, | ||
| Shield: 3, | ||
| RateLimit: 4, | ||
| BotDetection: 5, | ||
| EmailValidation: 6, | ||
| DetectPromptInjection: 7 | ||
| }; | ||
| function isRateLimitRule(rule) { | ||
| return rule.type === "RATE_LIMIT"; | ||
| } | ||
| /** | ||
| * Arcjet token bucket rate limiting rule. | ||
| * | ||
| * Applying this rule sets a token bucket rate limit. | ||
| * | ||
| * This algorithm is based on a bucket filled with a specific number of tokens. | ||
| * Each request withdraws some amount of tokens from the bucket and the bucket | ||
| * is refilled at a fixed rate. | ||
| * Once the bucket is empty, the client is blocked until the bucket refills. | ||
| * | ||
| * This algorithm is useful when you want to allow clients to make a burst of | ||
| * requests and then still be able to make requests at a slower rate. | ||
| * | ||
| * @template Characteristics | ||
| * Characteristics to track a user by. | ||
| * @param options | ||
| * Configuration for the token bucket rate limiting rule (required). | ||
| * @returns | ||
| * Token bucket rule to provide to the SDK in the `rules` field. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * tokenBucket({ | ||
| * mode: "LIVE", | ||
| * refillRate: 10, | ||
| * interval: "60s", | ||
| * capacity: 100, | ||
| * }); | ||
| * ``` | ||
| * @example | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [ | ||
| * tokenBucket({ | ||
| * mode: "LIVE", | ||
| * refillRate: 10, | ||
| * interval: "60s", | ||
| * capacity: 100, | ||
| * }), | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @link https://docs.arcjet.com/rate-limiting | ||
| * @link https://docs.arcjet.com/rate-limiting/algorithms#token-bucket | ||
| * @link https://docs.arcjet.com/rate-limiting/reference | ||
| */ | ||
| function tokenBucket(options) { | ||
| validateTokenBucketOptions(options); | ||
| const type = "RATE_LIMIT"; | ||
| const version = 0; | ||
| const mode = options.mode === "LIVE" ? "LIVE" : "DRY_RUN"; | ||
| const characteristics = Array.isArray(options.characteristics) ? options.characteristics : void 0; | ||
| const refillRate = options.refillRate; | ||
| const interval = duration.parse(options.interval); | ||
| const capacity = options.capacity; | ||
| return [{ | ||
| type, | ||
| version, | ||
| priority: Priority.RateLimit, | ||
| mode, | ||
| characteristics, | ||
| algorithm: "TOKEN_BUCKET", | ||
| refillRate, | ||
| interval, | ||
| capacity, | ||
| validate(_context, details) { | ||
| validateDetails(details); | ||
| assert(typeof details.extra.requested === "string", "TokenBucket requires `requested` to be set."); | ||
| }, | ||
| async protect(context, details) { | ||
| const localCharacteristics = characteristics ?? context.characteristics; | ||
| const ruleId = await hasher.hash(hasher.string("type", type), hasher.uint32("version", version), hasher.string("mode", mode), hasher.string("algorithm", "TOKEN_BUCKET"), hasher.stringSliceOrdered("characteristics", localCharacteristics), hasher.string("match", ""), hasher.uint32("refillRate", refillRate), hasher.uint32("interval", interval), hasher.uint32("capacity", capacity)); | ||
| const analyzeContext = { | ||
| characteristics: localCharacteristics, | ||
| log: context.log | ||
| }; | ||
| const fingerprint = await analyze.generateFingerprint(analyzeContext, toAnalyzeRequest(details)); | ||
| const [cached, ttl] = await context.cache.get(ruleId, fingerprint); | ||
| if (cached && cached.reason.isRateLimit()) return new ArcjetRuleResult({ | ||
| ruleId, | ||
| fingerprint, | ||
| ttl, | ||
| state: "CACHED", | ||
| conclusion: cached.conclusion, | ||
| reason: new ArcjetRateLimitReason({ | ||
| max: cached.reason.max, | ||
| remaining: cached.reason.remaining, | ||
| reset: ttl, | ||
| window: cached.reason.window, | ||
| resetTime: cached.reason.resetTime | ||
| }) | ||
| }); | ||
| return new ArcjetRuleResult({ | ||
| ruleId, | ||
| fingerprint, | ||
| ttl: 0, | ||
| state: "NOT_RUN", | ||
| conclusion: "ALLOW", | ||
| reason: new ArcjetRateLimitReason({ | ||
| max: 0, | ||
| remaining: 0, | ||
| reset: 0, | ||
| window: 0, | ||
| resetTime: /* @__PURE__ */ new Date() | ||
| }) | ||
| }); | ||
| } | ||
| }]; | ||
| } | ||
| /** | ||
| * Arcjet fixed window rate limiting rule. | ||
| * | ||
| * Applying this rule sets a fixed window rate limit which tracks the number of | ||
| * requests made by a client over a fixed time window. | ||
| * | ||
| * This is the simplest algorithm. | ||
| * It tracks the number of requests made by a client over a fixed time window | ||
| * such as 60 seconds. | ||
| * If the client exceeds the limit, they are blocked until the window expires. | ||
| * | ||
| * This algorithm is useful when you want to apply a simple fixed limit in a | ||
| * fixed time window. | ||
| * For example, a simple limit on the total number of requests a client can make. | ||
| * However, it can be susceptible to the stampede problem where a client makes | ||
| * a burst of requests at the start of a window and then is blocked for the rest | ||
| * of the window. | ||
| * The sliding window algorithm can be used to avoid this. | ||
| * | ||
| * @template Characteristics | ||
| * Characteristics to track a user by. | ||
| * @param options | ||
| * Configuration for the fixed window rate limiting rule (required). | ||
| * @returns | ||
| * Fixed window rule to provide to the SDK in the `rules` field. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * fixedWindow({ mode: "LIVE", window: "60s", max: 100 }); | ||
| * ``` | ||
| * @example | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [ | ||
| * fixedWindow({ | ||
| * mode: "LIVE", | ||
| * window: "60s", | ||
| * max: 100, | ||
| * }) | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @link https://docs.arcjet.com/rate-limiting | ||
| * @link https://docs.arcjet.com/rate-limiting/algorithms#fixed-window | ||
| * @link https://docs.arcjet.com/rate-limiting/reference | ||
| */ | ||
| function fixedWindow(options) { | ||
| validateFixedWindowOptions(options); | ||
| const type = "RATE_LIMIT"; | ||
| const version = 0; | ||
| const mode = options.mode === "LIVE" ? "LIVE" : "DRY_RUN"; | ||
| const characteristics = Array.isArray(options.characteristics) ? options.characteristics : void 0; | ||
| const max = options.max; | ||
| const window = duration.parse(options.window); | ||
| return [{ | ||
| type, | ||
| version, | ||
| priority: Priority.RateLimit, | ||
| mode, | ||
| characteristics, | ||
| algorithm: "FIXED_WINDOW", | ||
| max, | ||
| window, | ||
| validate(_context, details) { | ||
| validateDetails(details); | ||
| }, | ||
| async protect(context, details) { | ||
| const localCharacteristics = characteristics ?? context.characteristics; | ||
| const ruleId = await hasher.hash(hasher.string("type", type), hasher.uint32("version", version), hasher.string("mode", mode), hasher.string("algorithm", "FIXED_WINDOW"), hasher.stringSliceOrdered("characteristics", localCharacteristics), hasher.string("match", ""), hasher.uint32("max", max), hasher.uint32("window", window)); | ||
| const analyzeContext = { | ||
| characteristics: localCharacteristics, | ||
| log: context.log | ||
| }; | ||
| const fingerprint = await analyze.generateFingerprint(analyzeContext, toAnalyzeRequest(details)); | ||
| const [cached, ttl] = await context.cache.get(ruleId, fingerprint); | ||
| if (cached && cached.reason.isRateLimit()) return new ArcjetRuleResult({ | ||
| ruleId, | ||
| fingerprint, | ||
| ttl, | ||
| state: "CACHED", | ||
| conclusion: cached.conclusion, | ||
| reason: new ArcjetRateLimitReason({ | ||
| max: cached.reason.max, | ||
| remaining: cached.reason.remaining, | ||
| reset: ttl, | ||
| window: cached.reason.window, | ||
| resetTime: cached.reason.resetTime | ||
| }) | ||
| }); | ||
| return new ArcjetRuleResult({ | ||
| ruleId, | ||
| fingerprint, | ||
| ttl: 0, | ||
| state: "NOT_RUN", | ||
| conclusion: "ALLOW", | ||
| reason: new ArcjetRateLimitReason({ | ||
| max: 0, | ||
| remaining: 0, | ||
| reset: 0, | ||
| window: 0 | ||
| }) | ||
| }); | ||
| } | ||
| }]; | ||
| } | ||
| /** | ||
| * Arcjet sliding window rate limiting rule. | ||
| * | ||
| * Applying this rule sets a sliding window rate limit which tracks the number | ||
| * of requests made by a client over a sliding window so that the window moves | ||
| * with time. | ||
| * | ||
| * This algorithm is useful to avoid the stampede problem of the fixed window. | ||
| * It provides smoother rate limiting over time and can prevent a client from | ||
| * making a burst of requests at the start of a window and then being blocked | ||
| * for the rest of the window. | ||
| * | ||
| * @template Characteristics | ||
| * Characteristics to track a user by. | ||
| * @param options | ||
| * Configuration for the sliding window rate limiting rule (required). | ||
| * @returns | ||
| * Token bucket rule to provide to the SDK in the `rules` field. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * slidingWindow({ mode: "LIVE", interval: "60s", max: 100 }); | ||
| * ``` | ||
| * @example | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [ | ||
| * slidingWindow({ | ||
| * mode: "LIVE", | ||
| * interval: "60s", | ||
| * max: 100, | ||
| * }) | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @link https://docs.arcjet.com/rate-limiting | ||
| * @link https://docs.arcjet.com/rate-limiting/algorithms#sliding-window | ||
| * @link https://docs.arcjet.com/rate-limiting/reference | ||
| */ | ||
| function slidingWindow(options) { | ||
| validateSlidingWindowOptions(options); | ||
| const type = "RATE_LIMIT"; | ||
| const version = 0; | ||
| const mode = options.mode === "LIVE" ? "LIVE" : "DRY_RUN"; | ||
| const characteristics = Array.isArray(options.characteristics) ? options.characteristics : void 0; | ||
| const max = options.max; | ||
| const interval = duration.parse(options.interval); | ||
| return [{ | ||
| type, | ||
| version, | ||
| priority: Priority.RateLimit, | ||
| mode, | ||
| characteristics, | ||
| algorithm: "SLIDING_WINDOW", | ||
| max, | ||
| interval, | ||
| validate(_context, details) { | ||
| validateDetails(details); | ||
| }, | ||
| async protect(context, details) { | ||
| const localCharacteristics = characteristics ?? context.characteristics; | ||
| const ruleId = await hasher.hash(hasher.string("type", type), hasher.uint32("version", version), hasher.string("mode", mode), hasher.string("algorithm", "SLIDING_WINDOW"), hasher.stringSliceOrdered("characteristics", localCharacteristics), hasher.string("match", ""), hasher.uint32("max", max), hasher.uint32("interval", interval)); | ||
| const analyzeContext = { | ||
| characteristics: localCharacteristics, | ||
| log: context.log | ||
| }; | ||
| const fingerprint = await analyze.generateFingerprint(analyzeContext, toAnalyzeRequest(details)); | ||
| const [cached, ttl] = await context.cache.get(ruleId, fingerprint); | ||
| if (cached && cached.reason.isRateLimit()) return new ArcjetRuleResult({ | ||
| ruleId, | ||
| fingerprint, | ||
| ttl, | ||
| state: "CACHED", | ||
| conclusion: cached.conclusion, | ||
| reason: new ArcjetRateLimitReason({ | ||
| max: cached.reason.max, | ||
| remaining: cached.reason.remaining, | ||
| reset: ttl, | ||
| window: cached.reason.window, | ||
| resetTime: cached.reason.resetTime | ||
| }) | ||
| }); | ||
| return new ArcjetRuleResult({ | ||
| ruleId, | ||
| fingerprint, | ||
| ttl: 0, | ||
| state: "NOT_RUN", | ||
| conclusion: "ALLOW", | ||
| reason: new ArcjetRateLimitReason({ | ||
| max: 0, | ||
| remaining: 0, | ||
| reset: 0, | ||
| window: 0 | ||
| }) | ||
| }); | ||
| } | ||
| }]; | ||
| } | ||
| /** | ||
| * Turn a protocol entity name into a `SensitiveInfoEntity`. | ||
| * | ||
| * See also `userEntitiesToWasm` in `redact/index.ts`. | ||
| * | ||
| * Note that the protocol uses `SCREAM_CASE` whereas `redact` | ||
| * uses `kebab-case`. | ||
| * | ||
| * @param entity | ||
| * Entity name. | ||
| * @returns | ||
| * Entity object. | ||
| */ | ||
| function protocolSensitiveInfoEntitiesToAnalyze(entity) { | ||
| if (typeof entity !== "string") throw new Error("invalid entity type"); | ||
| if (entity === "EMAIL") return { tag: "email" }; | ||
| if (entity === "PHONE_NUMBER") return { tag: "phone-number" }; | ||
| if (entity === "IP_ADDRESS") return { tag: "ip-address" }; | ||
| if (entity === "CREDIT_CARD_NUMBER") return { tag: "credit-card-number" }; | ||
| return { | ||
| tag: "custom", | ||
| val: entity | ||
| }; | ||
| } | ||
| /** | ||
| * Turn a `SensitiveInfoEntity` object into a protocol name. | ||
| * | ||
| * See also `wasmEntitiesToString` in `redact/index.ts`. | ||
| * | ||
| * Note that the protocol uses `SCREAM_CASE` whereas `redact` | ||
| * uses `kebab-case`. | ||
| * | ||
| * @param entity | ||
| * Entity object. | ||
| * @returns | ||
| * Entity name. | ||
| */ | ||
| function analyzeSensitiveInfoEntitiesToString(entity) { | ||
| if (entity.tag === "email") return "EMAIL"; | ||
| if (entity.tag === "ip-address") return "IP_ADDRESS"; | ||
| if (entity.tag === "credit-card-number") return "CREDIT_CARD_NUMBER"; | ||
| if (entity.tag === "phone-number") return "PHONE_NUMBER"; | ||
| return entity.val; | ||
| } | ||
| /** | ||
| * Convert spans of detected entities from `@arcjet/analyze` to protocol format. | ||
| * | ||
| * @param detectedEntities | ||
| * Detected entities. | ||
| * @returns | ||
| * Transformed entities. | ||
| */ | ||
| function convertAnalyzeDetectedSensitiveInfoEntity(detectedEntities) { | ||
| return detectedEntities.map(({ start, end, identifiedType }) => { | ||
| return { | ||
| start, | ||
| end, | ||
| identifiedType: analyzeSensitiveInfoEntitiesToString(identifiedType) | ||
| }; | ||
| }); | ||
| } | ||
| /** | ||
| * Arcjet sensitive information detection rule. | ||
| * | ||
| * Applying this rule protects against clients sending you sensitive information | ||
| * such as personally identifiable information (PII) that you do not wish to | ||
| * handle. | ||
| * The rule runs entirely locally so no data ever leaves your environment. | ||
| * | ||
| * This rule includes built-in detections for email addresses, credit/debit card | ||
| * numbers, IP addresses, and phone numbers. | ||
| * You can also provide a custom detection function to identify additional | ||
| * sensitive information. | ||
| * | ||
| * You should pass the value to check to `protect`: | ||
| * | ||
| * ```js | ||
| * const decision = await arcjet.protect(request, { sensitiveInfoValue: "..." }); | ||
| * ``` | ||
| * | ||
| * The entire request body is checked if no value is provided. | ||
| * This behavior is deprecated: prefer passing the value explicitly. | ||
| * | ||
| * @template DetectedEntities | ||
| * Custom entity names that are returned from `detect` and optionally listed | ||
| * in `allow` or `deny`. | ||
| * @template ListedEntities | ||
| * Entity names that can be listed in the `allow` or `deny` field. | ||
| * @param options | ||
| * Configuration for the sensitive information detection rule (required). | ||
| * @returns | ||
| * Sensitive information rule to provide to the SDK in the `rules` field. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * sensitiveInfo({ mode: "LIVE", deny: ["EMAIL"] }); | ||
| * ``` | ||
| * @example | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [ | ||
| * sensitiveInfo({ | ||
| * mode: "LIVE", | ||
| * deny: ["EMAIL"], | ||
| * }) | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| * @example | ||
| * Custom detection function: | ||
| * | ||
| * ```ts | ||
| * function detectDash(tokens: string[]): Array<"CONTAINS_DASH" | undefined> { | ||
| * return tokens.map((token) => { | ||
| * if (token.includes("-")) { | ||
| * return "CONTAINS_DASH"; | ||
| * } | ||
| * }); | ||
| * } | ||
| * | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [ | ||
| * sensitiveInfo({ | ||
| * mode: "LIVE", | ||
| * deny: ["EMAIL", "CONTAINS_DASH"], | ||
| * detect: detectDash, | ||
| * contextWindowSize: 2, | ||
| * }) | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @link https://docs.arcjet.com/sensitive-info | ||
| * @link https://docs.arcjet.com/sensitive-info/reference | ||
| */ | ||
| function sensitiveInfo(options) { | ||
| validateSensitiveInfoOptions(options); | ||
| if (typeof options.allow !== "undefined" && typeof options.deny !== "undefined") throw new Error("`sensitiveInfo` options error: `allow` and `deny` cannot be provided together"); | ||
| if (typeof options.allow === "undefined" && typeof options.deny === "undefined") throw new Error("`sensitiveInfo` options error: either `allow` or `deny` must be specified"); | ||
| const type = "SENSITIVE_INFO"; | ||
| const version = 0; | ||
| const mode = options.mode === "LIVE" ? "LIVE" : "DRY_RUN"; | ||
| const allow = options.allow || []; | ||
| const deny = options.deny || []; | ||
| return [{ | ||
| version, | ||
| priority: Priority.SensitiveInfo, | ||
| type, | ||
| mode, | ||
| allow, | ||
| deny, | ||
| validate(_context, details) { | ||
| validateDetails(details); | ||
| }, | ||
| async protect(context, details) { | ||
| const ruleId = await hasher.hash(hasher.string("type", type), hasher.uint32("version", version), hasher.string("mode", mode), hasher.stringSliceOrdered("allow", allow), hasher.stringSliceOrdered("deny", deny)); | ||
| const { fingerprint } = context; | ||
| let value; | ||
| if ("sensitiveInfoValue" in details.extra) value = details.extra.sensitiveInfoValue; | ||
| else try { | ||
| value = await context.getBody(); | ||
| } catch (error) { | ||
| context.log.error("failed to get request body: %s", errorMessage(error)); | ||
| return new ArcjetRuleResult({ | ||
| ruleId, | ||
| fingerprint, | ||
| ttl: 0, | ||
| state: "NOT_RUN", | ||
| conclusion: "ERROR", | ||
| reason: new ArcjetErrorReason("Cannot read body for sensitive info detection: " + errorMessage(error)) | ||
| }); | ||
| } | ||
| let convertedDetect = void 0; | ||
| if (typeof options.detect !== "undefined") { | ||
| const detect = options.detect; | ||
| convertedDetect = (tokens) => { | ||
| return detect(tokens).filter((e) => typeof e !== "undefined").map(protocolSensitiveInfoEntitiesToAnalyze); | ||
| }; | ||
| } | ||
| let entitiesTag = "allow"; | ||
| let entitiesVal = []; | ||
| if (Array.isArray(options.allow)) { | ||
| entitiesTag = "allow"; | ||
| entitiesVal = options.allow.filter((e) => typeof e !== "undefined").map(protocolSensitiveInfoEntitiesToAnalyze); | ||
| } | ||
| if (Array.isArray(options.deny)) { | ||
| entitiesTag = "deny"; | ||
| entitiesVal = options.deny.filter((e) => typeof e !== "undefined").map(protocolSensitiveInfoEntitiesToAnalyze); | ||
| } | ||
| const entities = { | ||
| tag: entitiesTag, | ||
| val: entitiesVal | ||
| }; | ||
| const result = await analyze.detectSensitiveInfo(context, value, entities, options.contextWindowSize || 1, convertedDetect); | ||
| const state = mode === "LIVE" ? "RUN" : "DRY_RUN"; | ||
| const reason = new ArcjetSensitiveInfoReason({ | ||
| denied: convertAnalyzeDetectedSensitiveInfoEntity(result.denied), | ||
| allowed: convertAnalyzeDetectedSensitiveInfoEntity(result.allowed) | ||
| }); | ||
| if (result.denied.length === 0) return new ArcjetRuleResult({ | ||
| ruleId, | ||
| fingerprint, | ||
| ttl: 0, | ||
| state, | ||
| conclusion: "ALLOW", | ||
| reason | ||
| }); | ||
| else return new ArcjetRuleResult({ | ||
| ruleId, | ||
| fingerprint, | ||
| ttl: 0, | ||
| state, | ||
| conclusion: "DENY", | ||
| reason | ||
| }); | ||
| } | ||
| }]; | ||
| } | ||
| /** | ||
| * Arcjet email validation rule. | ||
| * | ||
| * Applying this rule allows you to validate and verify an email address. | ||
| * | ||
| * The first step of the analysis is to validate the email address syntax. | ||
| * This runs locally within the SDK and validates the email address is in the | ||
| * correct format. | ||
| * If the email syntax is valid, the SDK will pass the email address to the | ||
| * Arcjet cloud API to verify the email address. | ||
| * This performs several checks, depending on the rule configuration. | ||
| * | ||
| * @param options | ||
| * Configuration for the email validation rule (required). | ||
| * @returns | ||
| * Email rule to provide to the SDK in the `rules` field. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * validateEmail({ mode: "LIVE", deny: ["DISPOSABLE", "INVALID"] }); | ||
| * ``` | ||
| * @example | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [ | ||
| * validateEmail({ | ||
| * mode: "LIVE", | ||
| * deny: ["DISPOSABLE", "INVALID"] | ||
| * }) | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @link https://docs.arcjet.com/email-validation | ||
| * @link https://docs.arcjet.com/email-validation/reference | ||
| */ | ||
| function validateEmail(options) { | ||
| validateEmailOptions(options); | ||
| if (typeof options.allow !== "undefined" && typeof options.deny !== "undefined") throw new Error("`validateEmail` options error: `allow` and `deny` cannot be provided together"); | ||
| if (typeof options.allow === "undefined" && typeof options.deny === "undefined") throw new Error("`validateEmail` options error: either `allow` or `deny` must be specified"); | ||
| const type = "EMAIL"; | ||
| const version = 0; | ||
| const mode = options.mode === "LIVE" ? "LIVE" : "DRY_RUN"; | ||
| const allow = options.allow ?? []; | ||
| const deny = options.deny ?? []; | ||
| const requireTopLevelDomain = options.requireTopLevelDomain ?? true; | ||
| const allowDomainLiteral = options.allowDomainLiteral ?? false; | ||
| let config = { | ||
| tag: "deny-email-validation-config", | ||
| val: { | ||
| requireTopLevelDomain, | ||
| allowDomainLiteral, | ||
| deny: [] | ||
| } | ||
| }; | ||
| if (typeof options.allow !== "undefined") config = { | ||
| tag: "allow-email-validation-config", | ||
| val: { | ||
| requireTopLevelDomain, | ||
| allowDomainLiteral, | ||
| allow: options.allow | ||
| } | ||
| }; | ||
| if (typeof options.deny !== "undefined") config = { | ||
| tag: "deny-email-validation-config", | ||
| val: { | ||
| requireTopLevelDomain, | ||
| allowDomainLiteral, | ||
| deny: options.deny | ||
| } | ||
| }; | ||
| return [{ | ||
| version, | ||
| priority: Priority.EmailValidation, | ||
| type, | ||
| mode, | ||
| allow, | ||
| deny, | ||
| requireTopLevelDomain, | ||
| allowDomainLiteral, | ||
| validate(_context, details) { | ||
| validateDetails(details); | ||
| assert(typeof details.email === "string", "ValidateEmail requires `email` to be set."); | ||
| }, | ||
| async protect(context, { email }) { | ||
| const ruleId = await hasher.hash(hasher.string("type", type), hasher.uint32("version", version), hasher.string("mode", mode), hasher.stringSliceOrdered("allow", allow), hasher.stringSliceOrdered("deny", deny), hasher.bool("requireTopLevelDomain", requireTopLevelDomain), hasher.bool("allowDomainLiteral", allowDomainLiteral)); | ||
| const { fingerprint } = context; | ||
| const result = await analyze.isValidEmail(context, email, config); | ||
| const state = mode === "LIVE" ? "RUN" : "DRY_RUN"; | ||
| if (result.validity === "valid") return new ArcjetRuleResult({ | ||
| ruleId, | ||
| fingerprint, | ||
| ttl: 0, | ||
| state, | ||
| conclusion: "ALLOW", | ||
| reason: new ArcjetEmailReason({ emailTypes: [] }) | ||
| }); | ||
| else return new ArcjetRuleResult({ | ||
| ruleId, | ||
| fingerprint, | ||
| ttl: 0, | ||
| state, | ||
| conclusion: "DENY", | ||
| reason: new ArcjetEmailReason({ emailTypes: result.blocked.filter(isEmailType) }) | ||
| }); | ||
| } | ||
| }]; | ||
| } | ||
| /** | ||
| * Arcjet bot detection rule. | ||
| * | ||
| * Applying this rule allows you to manage traffic by automated clients and | ||
| * bots. | ||
| * | ||
| * Bots can be good (such as search engine crawlers or monitoring agents) or bad | ||
| * (such as scrapers or automated scripts). | ||
| * Arcjet allows you to configure which bots you want to allow or deny by | ||
| * specific bot names such as curl, as well as by category such as search | ||
| * engine bots. | ||
| * | ||
| * Bots are detected based on various signals such as the user agent, IP | ||
| * address, DNS records, and more. | ||
| * | ||
| * @param options | ||
| * Configuration for the bot rule (required). | ||
| * @returns | ||
| * Bot rule to provide to the SDK in the `rules` field. | ||
| * | ||
| * @example | ||
| * Allow search engine bots and curl, deny all other bots: | ||
| * | ||
| * ```ts | ||
| * detectBot({ mode: "LIVE", allow: ["CATEGORY:SEARCH_ENGINE", "CURL"] }); | ||
| * ``` | ||
| * @example | ||
| * Allow search engine bots and curl, deny all other bots: | ||
| * | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [ | ||
| * detectBot({ | ||
| * mode: "LIVE", | ||
| * allow: ["CATEGORY:SEARCH_ENGINE", "CURL"] | ||
| * }) | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| * @example | ||
| * Deny AI crawlers, allow all other bots: | ||
| * | ||
| * ```ts | ||
| * detectBot({ mode: "LIVE", deny: ["CATEGORY:AI"] }); | ||
| * ``` | ||
| * @example | ||
| * Deny AI crawlers, allows all other bots: | ||
| * | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [ | ||
| * detectBot({ | ||
| * mode: "LIVE", | ||
| * deny: ["CATEGORY:AI"] | ||
| * }) | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @link https://docs.arcjet.com/bot-protection | ||
| * @link https://docs.arcjet.com/bot-protection/identifying-bots | ||
| * @link https://docs.arcjet.com/bot-protection/reference | ||
| */ | ||
| function detectBot(options) { | ||
| validateBotOptions(options); | ||
| if (typeof options.allow !== "undefined" && typeof options.deny !== "undefined") throw new Error("`detectBot` options error: `allow` and `deny` cannot be provided together"); | ||
| if (typeof options.allow === "undefined" && typeof options.deny === "undefined") throw new Error("`detectBot` options error: either `allow` or `deny` must be specified"); | ||
| const type = "BOT"; | ||
| const version = 0; | ||
| const mode = options.mode === "LIVE" ? "LIVE" : "DRY_RUN"; | ||
| const allow = options.allow ?? []; | ||
| const deny = options.deny ?? []; | ||
| let config = { | ||
| tag: "allowed-bot-config", | ||
| val: { | ||
| entities: [], | ||
| skipCustomDetect: true | ||
| } | ||
| }; | ||
| if (typeof options.allow !== "undefined") config = { | ||
| tag: "allowed-bot-config", | ||
| val: { | ||
| entities: options.allow, | ||
| skipCustomDetect: true | ||
| } | ||
| }; | ||
| if (typeof options.deny !== "undefined") config = { | ||
| tag: "denied-bot-config", | ||
| val: { | ||
| entities: options.deny, | ||
| skipCustomDetect: true | ||
| } | ||
| }; | ||
| return [{ | ||
| version, | ||
| priority: Priority.BotDetection, | ||
| type, | ||
| mode, | ||
| allow, | ||
| deny, | ||
| validate(_context, details) { | ||
| validateDetails(details); | ||
| if (details.headers.get("user-agent") === null) throw new Error("DetectBot requires `user-agent` header to be set."); | ||
| }, | ||
| /** | ||
| * Attempts to call the bot detection on the headers. | ||
| */ | ||
| async protect(context, request) { | ||
| const ruleId = await hasher.hash(hasher.string("type", type), hasher.uint32("version", version), hasher.string("mode", mode), hasher.stringSliceOrdered("allow", allow), hasher.stringSliceOrdered("deny", deny)); | ||
| const { fingerprint } = context; | ||
| const [cached, ttl] = await context.cache.get(ruleId, fingerprint); | ||
| if (cached) return new ArcjetRuleResult({ | ||
| ruleId, | ||
| fingerprint, | ||
| ttl, | ||
| state: "CACHED", | ||
| conclusion: cached.conclusion, | ||
| reason: cached.reason | ||
| }); | ||
| const result = await analyze.detectBot(context, toAnalyzeRequest(request), config); | ||
| const state = mode === "LIVE" ? "RUN" : "DRY_RUN"; | ||
| if (result.denied.length > 0) return new ArcjetRuleResult({ | ||
| ruleId, | ||
| fingerprint, | ||
| ttl: 60, | ||
| state, | ||
| conclusion: "DENY", | ||
| reason: new ArcjetBotReason({ | ||
| allowed: result.allowed, | ||
| denied: result.denied, | ||
| verified: result.verified, | ||
| spoofed: result.spoofed | ||
| }) | ||
| }); | ||
| else return new ArcjetRuleResult({ | ||
| ruleId, | ||
| fingerprint, | ||
| ttl: 0, | ||
| state, | ||
| conclusion: "ALLOW", | ||
| reason: new ArcjetBotReason({ | ||
| allowed: result.allowed, | ||
| denied: result.denied, | ||
| verified: result.verified, | ||
| spoofed: result.spoofed | ||
| }) | ||
| }); | ||
| } | ||
| }]; | ||
| } | ||
| /** | ||
| * Arcjet Shield WAF rule. | ||
| * | ||
| * Applying this rule protects your application against common attacks, | ||
| * including the OWASP Top 10. | ||
| * | ||
| * The Arcjet Shield WAF analyzes every request to your application to detect | ||
| * suspicious activity. | ||
| * Once a certain suspicion threshold is reached, | ||
| * subsequent requests from that client are blocked for a period of time. | ||
| * | ||
| * @param options | ||
| * Configuration for the Shield rule. | ||
| * @returns | ||
| * Shield rule to provide to the SDK in the `rules` field. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * shield({ mode: "LIVE" }); | ||
| * ``` | ||
| * @example | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [shield({ mode: "LIVE" })], | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @link https://docs.arcjet.com/shield | ||
| * @link https://docs.arcjet.com/shield/reference | ||
| */ | ||
| function shield(options) { | ||
| validateShieldOptions(options); | ||
| const type = "SHIELD"; | ||
| const version = 0; | ||
| const mode = options.mode === "LIVE" ? "LIVE" : "DRY_RUN"; | ||
| return [{ | ||
| type, | ||
| version, | ||
| priority: Priority.Shield, | ||
| mode, | ||
| validate(_context, details) { | ||
| validateDetails(details); | ||
| }, | ||
| async protect(context, details) { | ||
| const localCharacteristics = context.characteristics; | ||
| const ruleId = await hasher.hash(hasher.string("type", type), hasher.uint32("version", version), hasher.string("mode", mode), hasher.stringSliceOrdered("characteristics", localCharacteristics)); | ||
| const analyzeContext = { | ||
| characteristics: localCharacteristics, | ||
| log: context.log | ||
| }; | ||
| const fingerprint = await analyze.generateFingerprint(analyzeContext, toAnalyzeRequest(details)); | ||
| const [cached, ttl] = await context.cache.get(ruleId, fingerprint); | ||
| if (cached) return new ArcjetRuleResult({ | ||
| ruleId, | ||
| fingerprint, | ||
| ttl, | ||
| state: "CACHED", | ||
| conclusion: cached.conclusion, | ||
| reason: cached.reason | ||
| }); | ||
| return new ArcjetRuleResult({ | ||
| ruleId, | ||
| fingerprint, | ||
| ttl: 0, | ||
| state: "NOT_RUN", | ||
| conclusion: "ALLOW", | ||
| reason: new ArcjetShieldReason({ shieldTriggered: false }) | ||
| }); | ||
| } | ||
| }]; | ||
| } | ||
| /** | ||
| * Arcjet prompt injection detection rule. | ||
| * | ||
| * Analyzes LLM prompts to detect prompt injection attempts. | ||
| * | ||
| * The Arcjet prompt injection detection rule analyzes prompts sent to LLM | ||
| * applications to detect jailbreak and injection attempts. | ||
| * The analysis is performed through Arcjet's Cloud API. | ||
| * | ||
| * @param options | ||
| * Configuration for the prompt injection detection rule. | ||
| * @returns | ||
| * Prompt injection detection rule to provide to the SDK in the `rules` field. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * detectPromptInjection({ mode: "LIVE" }); | ||
| * ``` | ||
| * @example | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [detectPromptInjection({ mode: "LIVE" })], | ||
| * }); | ||
| * ``` | ||
| */ | ||
| function detectPromptInjection(options = {}) { | ||
| validateDetectPromptInjectionOptions(options); | ||
| const type = "PROMPT_INJECTION_DETECTION"; | ||
| const version = 0; | ||
| const mode = options.mode === "LIVE" ? "LIVE" : "DRY_RUN"; | ||
| const threshold = options.threshold ?? .5; | ||
| if (threshold <= 0 || threshold >= 1) throw new Error("`detectPromptInjection` options error: `threshold` must be between 0.0 and 1.0 (exclusive)"); | ||
| return [{ | ||
| type, | ||
| version, | ||
| threshold, | ||
| priority: Priority.DetectPromptInjection, | ||
| mode, | ||
| validate(_context, details) { | ||
| validateDetails(details); | ||
| if (!("detectPromptInjectionMessage" in details.extra)) throw new Error("detectPromptInjection rule requires `detectPromptInjectionMessage` to be set"); | ||
| const message = details.extra.detectPromptInjectionMessage; | ||
| if (typeof message !== "string") throw new Error("detectPromptInjection rule requires `detectPromptInjectionMessage` to be a string"); | ||
| if (message.length === 0) throw new Error("detectPromptInjection rule requires `detectPromptInjectionMessage` to be non-empty"); | ||
| }, | ||
| async protect(context, _details) { | ||
| const ruleId = await hasher.hash(hasher.string("type", type), hasher.uint32("version", version), hasher.string("mode", mode), hasher.float64("threshold", threshold)); | ||
| const { fingerprint } = context; | ||
| const [cached, ttl] = await context.cache.get(ruleId, fingerprint); | ||
| if (cached) return new ArcjetRuleResult({ | ||
| ruleId, | ||
| fingerprint, | ||
| ttl, | ||
| state: "CACHED", | ||
| conclusion: cached.conclusion, | ||
| reason: cached.reason | ||
| }); | ||
| return new ArcjetRuleResult({ | ||
| ruleId, | ||
| fingerprint, | ||
| ttl: 0, | ||
| state: "NOT_RUN", | ||
| conclusion: "ALLOW", | ||
| reason: new ArcjetPromptInjectionReason({ | ||
| injectionDetected: false, | ||
| score: 0 | ||
| }) | ||
| }); | ||
| } | ||
| }]; | ||
| } | ||
| /** | ||
| * Arcjet prompt injection detection rule. | ||
| * | ||
| * @deprecated | ||
| * Use `detectPromptInjection` instead. | ||
| */ | ||
| const experimental_detectPromptInjection = detectPromptInjection; | ||
| /** | ||
| * Arcjet signup form protection rule. | ||
| * | ||
| * Applying this rule combines rate limiting, bot protection, and email | ||
| * validation to protect your signup forms from abuse. | ||
| * Using this rule will configure the following: | ||
| * | ||
| * - Rate limiting - signup forms are a common target for bots. Arcjet’s rate | ||
| * limiting helps to prevent bots and other automated or malicious clients | ||
| * from submitting your signup form too many times in a short period of time. | ||
| * - Bot protection - signup forms are usually exclusively used by humans, which | ||
| * means that any automated submissions to the form are likely to be | ||
| * fraudulent. | ||
| * - Email validation - email addresses should be validated to ensure the signup | ||
| * is coming from a legitimate user with a real email address that can | ||
| * actually receive messages. | ||
| * | ||
| * @template Characteristics | ||
| * Characteristics to track a user by. | ||
| * @param options | ||
| * Configuration for the signup form protection rule. | ||
| * @returns | ||
| * Signup form protection rule to provide to the SDK in the `rules` field. | ||
| * | ||
| * @example | ||
| * Our recommended configuration for most signup forms is: | ||
| * | ||
| * - Block email addresses with invalid syntax, that are from disposable email providers, | ||
| * or do not have valid MX records configured. | ||
| * - Block all bots. | ||
| * - Apply a rate limit of 5 submissions per 10 minutes from a single IP | ||
| * address. | ||
| * | ||
| * ```ts | ||
| * const aj = arcjet({ | ||
| * key: process.env.ARCJET_KEY, | ||
| * rules: [ | ||
| * protectSignup({ | ||
| * email: { | ||
| * mode: "LIVE", | ||
| * deny: ["DISPOSABLE", "INVALID", "NO_MX_RECORDS"], | ||
| * }, | ||
| * bots: { | ||
| * mode: "LIVE", | ||
| * allow: [], // block all detected bots | ||
| * }, | ||
| * rateLimit: { | ||
| * mode: "LIVE", | ||
| * interval: "10m", | ||
| * max: 5, | ||
| * }, | ||
| * }), | ||
| * ], | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @link https://docs.arcjet.com/signup-protection | ||
| * @link https://docs.arcjet.com/signup-protection/reference | ||
| */ | ||
| function protectSignup(options) { | ||
| validateProtectSignupOptions(options); | ||
| const [rateLimit] = slidingWindow(options.rateLimit); | ||
| const [bot] = detectBot(options.bots); | ||
| const [email] = validateEmail(options.email); | ||
| return [ | ||
| rateLimit, | ||
| bot, | ||
| ]; | ||
| } | ||
| /** | ||
| * Arcjet filter rule. | ||
| * | ||
| * Applying this rule lets you block requests using Wireshark-like display | ||
| * filter expressions over HTTP headers, IP addresses, and other request | ||
| * fields. | ||
| * You can quickly enforce rules like allow/deny by country, network, or | ||
| * `user-agent` pattern. | ||
| * | ||
| * See the [reference guide](https://docs.arcjet.com/filters/reference) for | ||
| * more info on the expression language fields, functions, and values. | ||
| * | ||
| * @param options | ||
| * Configuration (required). | ||
| * @returns | ||
| * Filter rule. | ||
| * | ||
| * @example | ||
| * In this example, the expression matches non-VPN GET requests from the US. | ||
| * Requests matching the expression are allowed, all others are denied. | ||
| * | ||
| * ```ts | ||
| * filter({ | ||
| * allow: [ | ||
| * 'http.request.method eq "GET" and ip.src.country eq "US" and not ip.src.vpn', | ||
| * ], | ||
| * mode: "LIVE", | ||
| * }) | ||
| * ``` | ||
| * | ||
| * @link https://docs.arcjet.com/filters | ||
| * @link https://docs.arcjet.com/filters/reference | ||
| */ | ||
| function filter(options) { | ||
| validateFilterOptions(options); | ||
| const mode = options.mode === "LIVE" ? "LIVE" : "DRY_RUN"; | ||
| const allow = options.allow ?? []; | ||
| const deny = options.deny ?? []; | ||
| if (allow.length > 0 && deny.length > 0) throw new Error("`filter` options error: expressions must be passed in either `allow` or `deny` instead of both"); | ||
| if (allow.length === 0 && deny.length === 0) throw new Error("`filter` options error: one or more expressions must be passed in `allow` or `deny`"); | ||
| const state = mode === "LIVE" ? "RUN" : "DRY_RUN"; | ||
| const type = "FILTER"; | ||
| const version = 0; | ||
| const ruleIdPromise = hasher.hash(hasher.string("type", type), hasher.uint32("version", version), hasher.string("mode", mode), hasher.stringSliceOrdered("allow", allow), hasher.stringSliceOrdered("deny", deny)); | ||
| return [{ | ||
| allow, | ||
| deny, | ||
| mode, | ||
| priority: Priority.Filter, | ||
| async protect(context, request) { | ||
| const ruleId = await ruleIdPromise; | ||
| const [cached, ttl] = await context.cache.get(ruleId, context.fingerprint); | ||
| if (cached) return new ArcjetRuleResult({ | ||
| conclusion: cached.conclusion, | ||
| fingerprint: context.fingerprint, | ||
| reason: cached.reason, | ||
| ruleId, | ||
| state: "CACHED", | ||
| ttl | ||
| }); | ||
| const request_ = toAnalyzeRequest(request); | ||
| const fields = request.extra.filterLocal || "{}"; | ||
| let ruleResult; | ||
| try { | ||
| const result = await analyze.matchFilters(context, request_, fields, allow.length > 0 ? allow : deny, allow.length > 0); | ||
| ruleResult = new ArcjetRuleResult({ | ||
| conclusion: result.allowed ? "ALLOW" : "DENY", | ||
| fingerprint: context.fingerprint, | ||
| reason: new ArcjetFilterReason(result), | ||
| ruleId, | ||
| state, | ||
| ttl: result.allowed ? 0 : 60 | ||
| }); | ||
| } catch (error) { | ||
| ruleResult = new ArcjetRuleResult({ | ||
| conclusion: "ERROR", | ||
| fingerprint: context.fingerprint, | ||
| reason: new ArcjetErrorReason(error), | ||
| ruleId, | ||
| state, | ||
| ttl: 0 | ||
| }); | ||
| } | ||
| return ruleResult; | ||
| }, | ||
| type, | ||
| validate(_context, details) { | ||
| validateDetails(details); | ||
| if (details.extra.filterLocal) validateStringRecord("filterLocal", JSON.parse(details.extra.filterLocal)); | ||
| }, | ||
| version | ||
| }]; | ||
| } | ||
| /** | ||
| * Create a new Arcjet instance. | ||
| * | ||
| * @template Rules | ||
| * List of rules. | ||
| * @template Characteristics | ||
| * Characteristics to track a user by. | ||
| * @param options | ||
| * Configuration. | ||
| * @returns | ||
| * Arcjet instance. | ||
| */ | ||
| function arcjet(options) { | ||
| const { key, rules } = options; | ||
| const rt = runtime(); | ||
| if (typeof options.log === "undefined") throw new Error("Log is required"); | ||
| const log = options.log; | ||
| const perf = new Performance(log); | ||
| if (typeof options.client === "undefined") throw new Error("Client is required"); | ||
| const client = options.client; | ||
| const cache = new MemoryCache(); | ||
| if (!Array.isArray(rules)) throw new Error("Rules are required"); | ||
| async function protect(rules, ctx, request) { | ||
| if (typeof request === "undefined") request = {}; | ||
| const details = Object.freeze({ | ||
| ip: request.ip, | ||
| method: request.method, | ||
| protocol: request.protocol, | ||
| host: request.host, | ||
| path: request.path, | ||
| headers: new ArcjetHeaders(request.headers), | ||
| cookies: request.cookies, | ||
| query: request.query, | ||
| extra: extraProps(request), | ||
| email: typeof request.email === "string" ? request.email : void 0, | ||
| correlationId: typeof request.correlationId === "string" ? request.correlationId : void 0 | ||
| }); | ||
| let remoteDetails = { | ||
| ...details, | ||
| extra: { ...details.extra } | ||
| }; | ||
| for (const field of ["filterLocal", "sensitiveInfoValue"]) if (remoteDetails.extra[field] !== void 0) remoteDetails.extra[field] = "<redacted>"; | ||
| remoteDetails = Object.freeze(remoteDetails); | ||
| const reportExtra = { ...remoteDetails.extra }; | ||
| if (reportExtra.detectPromptInjectionMessage !== void 0) reportExtra.detectPromptInjectionMessage = "<redacted>"; | ||
| const reportDetails = Object.freeze({ | ||
| ...remoteDetails, | ||
| extra: reportExtra | ||
| }); | ||
| const characteristics = options.characteristics ? [...options.characteristics] : []; | ||
| const waitUntil = lookupWaitUntil(); | ||
| const baseContext = { | ||
| key, | ||
| log, | ||
| characteristics, | ||
| waitUntil, | ||
| ...ctx | ||
| }; | ||
| let fingerprint = ""; | ||
| const logFingerprintPerf = perf.measure("fingerprint"); | ||
| try { | ||
| fingerprint = await analyze.generateFingerprint(baseContext, toAnalyzeRequest(details)); | ||
| log.debug("fingerprint (%s): %s", rt, fingerprint); | ||
| } catch (error) { | ||
| log.error({ error: errorMessage(error) }, "Failed to build fingerprint. Please verify your Characteristics."); | ||
| return new ArcjetErrorDecision({ | ||
| ttl: 0, | ||
| reason: new ArcjetErrorReason(`Failed to build fingerprint - ${errorMessage(error)}`), | ||
| results: [] | ||
| }); | ||
| } finally { | ||
| logFingerprintPerf(); | ||
| } | ||
| const context = Object.freeze({ | ||
| ...baseContext, | ||
| cache, | ||
| fingerprint, | ||
| runtime: rt | ||
| }); | ||
| if (rules.length > 10) { | ||
| log.error("Failure running rules. Only 10 rules may be specified."); | ||
| const decision = new ArcjetErrorDecision({ | ||
| ttl: 0, | ||
| reason: new ArcjetErrorReason("Only 10 rules may be specified"), | ||
| results: [] | ||
| }); | ||
| client.report(context, reportDetails, decision, []); | ||
| return decision; | ||
| } | ||
| const results = []; | ||
| for (let idx = 0; idx < rules.length; idx++) { | ||
| results[idx] = new ArcjetRuleResult({ | ||
| ruleId: "", | ||
| fingerprint, | ||
| ttl: 0, | ||
| state: "NOT_RUN", | ||
| conclusion: "ALLOW", | ||
| reason: new ArcjetReason() | ||
| }); | ||
| const candidate_rule = rules[idx]; | ||
| if (isRateLimitRule(candidate_rule)) { | ||
| if (typeof candidate_rule.characteristics === "undefined") { | ||
| candidate_rule.characteristics = characteristics; | ||
| rules[idx] = candidate_rule; | ||
| } | ||
| } | ||
| } | ||
| const logLocalPerf = perf.measure("local"); | ||
| try { | ||
| for (const [idx, rule] of rules.entries()) { | ||
| const localRule = rule; | ||
| const logRulePerf = perf.measure(rule.type); | ||
| try { | ||
| if (typeof localRule.validate !== "function") throw new Error("rule must have a `validate` function"); | ||
| localRule.validate(context, details); | ||
| if (typeof localRule.protect !== "function") throw new Error("rule must have a `protect` function"); | ||
| results[idx] = await localRule.protect(context, details); | ||
| if (typeof results[idx] === "undefined") results[idx] = new ArcjetRuleResult({ | ||
| ruleId: "", | ||
| fingerprint, | ||
| ttl: 0, | ||
| state: "RUN", | ||
| conclusion: "ERROR", | ||
| reason: new ArcjetErrorReason("rule result missing") | ||
| }); | ||
| log.debug({ | ||
| id: results[idx].ruleId, | ||
| rule: rule.type, | ||
| fingerprint, | ||
| path: details.path, | ||
| runtime: rt, | ||
| ttl: results[idx].ttl, | ||
| conclusion: results[idx].conclusion, | ||
| reason: results[idx].reason | ||
| }, "Local rule result:"); | ||
| } catch (err) { | ||
| log.error("Failure running rule: %s due to %s", rule.type, errorMessage(err)); | ||
| results[idx] = new ArcjetRuleResult({ | ||
| ruleId: "", | ||
| fingerprint, | ||
| ttl: 0, | ||
| state: "RUN", | ||
| conclusion: "ERROR", | ||
| reason: new ArcjetErrorReason(err) | ||
| }); | ||
| } finally { | ||
| logRulePerf(); | ||
| } | ||
| const result = results[idx]; | ||
| if (result.isDenied()) { | ||
| if (result.state !== "DRY_RUN") { | ||
| const decision = new ArcjetDenyDecision({ | ||
| ttl: result.ttl, | ||
| reason: result.reason, | ||
| results | ||
| }); | ||
| client.report(context, reportDetails, decision, rules); | ||
| if (result.ttl > 0) { | ||
| log.debug({ | ||
| fingerprint: result.fingerprint, | ||
| conclusion: result.conclusion, | ||
| reason: result.reason | ||
| }, "Caching decision for %d seconds", decision.ttl); | ||
| cache.set(result.ruleId, result.fingerprint, { | ||
| conclusion: result.conclusion, | ||
| reason: result.reason | ||
| }, result.ttl); | ||
| } | ||
| return decision; | ||
| } | ||
| log.warn(`Dry run mode is enabled for "%s" rule. Overriding decision. Decision was: DENY`, rule.type); | ||
| } | ||
| } | ||
| } finally { | ||
| logLocalPerf(); | ||
| } | ||
| const logRemotePerf = perf.measure("remote"); | ||
| try { | ||
| const logDediceApiPerf = perf.measure("decideApi"); | ||
| const decision = await client.decide(context, remoteDetails, rules).finally(() => { | ||
| logDediceApiPerf(); | ||
| }); | ||
| if (decision.isDenied() && decision.ttl > 0) { | ||
| log.debug("decide: Caching block locally for %d seconds", decision.ttl); | ||
| for (const result of decision.results) if (result.conclusion === "DENY") cache.set(result.ruleId, result.fingerprint, { | ||
| conclusion: result.conclusion, | ||
| reason: result.reason | ||
| }, result.ttl); | ||
| } | ||
| return decision; | ||
| } catch (err) { | ||
| log.info("Encountered problem getting remote decision: %s", errorMessage(err)); | ||
| const decision = new ArcjetErrorDecision({ | ||
| ttl: 0, | ||
| reason: new ArcjetErrorReason(err), | ||
| results | ||
| }); | ||
| client.report(context, reportDetails, decision, rules); | ||
| return decision; | ||
| } finally { | ||
| logRemotePerf(); | ||
| } | ||
| } | ||
| function createClient(rules) { | ||
| const sortedRules = [...rules].sort(sortRule); | ||
| return Object.freeze({ | ||
| withRule(rule) { | ||
| return createClient([...rules, ...rule]); | ||
| }, | ||
| protect(ctx, request) { | ||
| return protect(sortedRules, ctx, request); | ||
| } | ||
| }); | ||
| } | ||
| return createClient(rules.flat(1)); | ||
| } | ||
| /** | ||
| * Sort rules by priority. | ||
| * | ||
| * @param a | ||
| * Rule. | ||
| * @param b | ||
| * Other rule. | ||
| * @returns | ||
| * Comparison result. | ||
| */ | ||
| function sortRule(a, b) { | ||
| return a.priority - b.priority; | ||
| } | ||
| //#endregion | ||
| export { arcjet as default, detectBot, detectPromptInjection, experimental_detectPromptInjection, filter, fixedWindow, protectSignup, sensitiveInfo, shield, slidingWindow, tokenBucket, validateEmail }; |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
149688
18.76%3596
7.06%0
-100%12
33.33%1
Infinity%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
Updated
Updated
Updated
Updated
Updated
Updated
Updated