Comparing version 1.0.0-alpha.21 to 1.0.0-alpha.22
@@ -1,3 +0,4 @@ | ||
import { ArcjetBotType, ArcjetEmailType, ArcjetMode, ArcjetDecision, ArcjetRule, ArcjetLogger } from "@arcjet/protocol"; | ||
import { Client } from "@arcjet/protocol/client.js"; | ||
import type { ArcjetRule, ArcjetLogger } from "@arcjet/protocol"; | ||
import { ArcjetBotType, ArcjetEmailType, ArcjetMode, ArcjetSensitiveInfoType, ArcjetDecision } from "@arcjet/protocol"; | ||
import type { Client } from "@arcjet/protocol/client.js"; | ||
export * from "@arcjet/protocol"; | ||
@@ -97,2 +98,18 @@ type Simplify<T> = { | ||
}; | ||
type DetectSensitiveInfoEntities<T> = (tokens: string[]) => Array<ArcjetSensitiveInfoType | T | undefined>; | ||
type SensitiveInfoOptionsAllow<Detect extends DetectSensitiveInfoEntities<CustomEntities>, CustomEntities extends string> = { | ||
allow: Array<ArcjetSensitiveInfoType | Exclude<ReturnType<Detect>[number], undefined>>; | ||
deny?: never; | ||
contextWindowSize?: number; | ||
mode?: ArcjetMode; | ||
detect?: Detect; | ||
}; | ||
type SensitiveInfoOptionsDeny<Detect extends DetectSensitiveInfoEntities<CustomEntities>, CustomEntities extends string> = { | ||
allow?: never; | ||
deny: Array<ArcjetSensitiveInfoType | Exclude<ReturnType<Detect>[number], undefined>>; | ||
contextWindowSize?: number; | ||
mode?: ArcjetMode; | ||
detect?: Detect; | ||
}; | ||
export type SensitiveInfoOptions<Detect extends DetectSensitiveInfoEntities<CustomEntities>, CustomEntities extends string> = SensitiveInfoOptionsAllow<Detect, CustomEntities> | SensitiveInfoOptionsDeny<Detect, CustomEntities>; | ||
type PlainObject = { | ||
@@ -113,3 +130,6 @@ [key: string]: unknown; | ||
*/ | ||
export type ArcjetAdapterContext = Record<string, unknown>; | ||
export type ArcjetAdapterContext = { | ||
[key: string]: unknown; | ||
getBody(): Promise<string | undefined>; | ||
}; | ||
/** | ||
@@ -143,2 +163,3 @@ * @property {string} ip - The IP address of the client. | ||
export declare function slidingWindow<const Characteristics extends readonly string[] = []>(options?: SlidingWindowRateLimitOptions<Characteristics>, ...additionalOptions: SlidingWindowRateLimitOptions<Characteristics>[]): Primitive<Simplify<CharacteristicProps<Characteristics>>>; | ||
export declare function sensitiveInfo<const Detect extends DetectSensitiveInfoEntities<CustomEntities>, const CustomEntities extends string>(options: SensitiveInfoOptions<Detect, CustomEntities>, ...additionalOptions: SensitiveInfoOptions<Detect, CustomEntities>[]): Primitive<{}>; | ||
export declare function validateEmail(options?: EmailOptions, ...additionalOptions: EmailOptions[]): Primitive<{ | ||
@@ -145,0 +166,0 @@ email: string; |
135
index.js
@@ -1,2 +0,2 @@ | ||
import { ArcjetRuleResult, ArcjetEmailReason, ArcjetBotType, ArcjetErrorReason, ArcjetBotReason, ArcjetErrorDecision, ArcjetReason, ArcjetDenyDecision } from '@arcjet/protocol'; | ||
import { ArcjetRuleResult, ArcjetErrorReason, ArcjetSensitiveInfoReason, ArcjetEmailReason, ArcjetBotType, ArcjetBotReason, ArcjetErrorDecision, ArcjetReason, ArcjetDenyDecision } from '@arcjet/protocol'; | ||
export * from '@arcjet/protocol'; | ||
@@ -102,6 +102,7 @@ import { ArcjetBotTypeToProtocol, isRateLimitRule } from '@arcjet/protocol/convert.js'; | ||
const Priority = { | ||
Shield: 1, | ||
RateLimit: 2, | ||
BotDetection: 3, | ||
EmailValidation: 4, | ||
SensitiveInfo: 1, | ||
Shield: 2, | ||
RateLimit: 3, | ||
BotDetection: 4, | ||
EmailValidation: 5, | ||
}; | ||
@@ -194,2 +195,124 @@ function isLocalRule(rule) { | ||
} | ||
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, | ||
}; | ||
} | ||
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; | ||
} | ||
function convertAnalyzeDetectedSensitiveInfoEntity(detectedEntities) { | ||
return detectedEntities.map((detectedEntity) => { | ||
return { | ||
...detectedEntity, | ||
identifiedType: analyzeSensitiveInfoEntitiesToString(detectedEntity.identifiedType), | ||
}; | ||
}); | ||
} | ||
function sensitiveInfo(options, ...additionalOptions) { | ||
const rules = []; | ||
// Always create at least one SENSITIVE_INFO rule | ||
for (const opt of [options, ...additionalOptions]) { | ||
const mode = opt.mode === "LIVE" ? "LIVE" : "DRY_RUN"; | ||
if (typeof opt.allow !== "undefined" && typeof opt.deny !== "undefined") { | ||
throw new Error("Both allow and deny cannot be provided to sensitiveInfo"); | ||
} | ||
rules.push({ | ||
type: "SENSITIVE_INFO", | ||
priority: Priority.SensitiveInfo, | ||
mode, | ||
allow: opt.allow || [], | ||
deny: opt.deny || [], | ||
validate(context, details) { }, | ||
async protect(context, details) { | ||
const body = await context.getBody(); | ||
if (typeof body === "undefined") { | ||
return new ArcjetRuleResult({ | ||
ttl: 0, | ||
state: "NOT_RUN", | ||
conclusion: "ERROR", | ||
reason: new ArcjetErrorReason("Couldn't read the body of the request to perform sensitive info identification."), | ||
}); | ||
} | ||
let convertedDetect = undefined; | ||
if (typeof opt.detect !== "undefined") { | ||
const detect = opt.detect; | ||
convertedDetect = (tokens) => { | ||
return detect(tokens) | ||
.filter((e) => typeof e !== "undefined") | ||
.map(protocolSensitiveInfoEntitiesToAnalyze); | ||
}; | ||
} | ||
let entitiesTag = "allow"; | ||
let entitiesVal = []; | ||
if (Array.isArray(opt.allow)) { | ||
entitiesTag = "allow"; | ||
entitiesVal = opt.allow | ||
.filter((e) => typeof e !== "undefined") | ||
.map(protocolSensitiveInfoEntitiesToAnalyze); | ||
} | ||
if (Array.isArray(opt.deny)) { | ||
entitiesTag = "deny"; | ||
entitiesVal = opt.deny | ||
.filter((e) => typeof e !== "undefined") | ||
.map(protocolSensitiveInfoEntitiesToAnalyze); | ||
} | ||
const entities = { | ||
tag: entitiesTag, | ||
val: entitiesVal, | ||
}; | ||
const result = await analyze.detectSensitiveInfo(context, body, entities, options.contextWindowSize || 1, convertedDetect); | ||
const reason = new ArcjetSensitiveInfoReason({ | ||
denied: convertAnalyzeDetectedSensitiveInfoEntity(result.denied), | ||
allowed: convertAnalyzeDetectedSensitiveInfoEntity(result.allowed), | ||
}); | ||
if (result.denied.length === 0) { | ||
return new ArcjetRuleResult({ | ||
ttl: 0, | ||
state: "RUN", | ||
conclusion: "ALLOW", | ||
reason, | ||
}); | ||
} | ||
else { | ||
return new ArcjetRuleResult({ | ||
ttl: 0, | ||
state: "RUN", | ||
conclusion: "DENY", | ||
reason, | ||
}); | ||
} | ||
}, | ||
}); | ||
} | ||
return rules; | ||
} | ||
function validateEmail(options, ...additionalOptions) { | ||
@@ -636,2 +759,2 @@ const rules = []; | ||
export { arcjet as default, detectBot, fixedWindow, protectSignup, shield, slidingWindow, tokenBucket, validateEmail }; | ||
export { arcjet as default, detectBot, fixedWindow, protectSignup, sensitiveInfo, shield, slidingWindow, tokenBucket, validateEmail }; |
265
index.ts
@@ -0,7 +1,20 @@ | ||
import type { | ||
ArcjetContext, | ||
ArcjetEmailRule, | ||
ArcjetBotRule, | ||
ArcjetRule, | ||
ArcjetLocalRule, | ||
ArcjetRequestDetails, | ||
ArcjetTokenBucketRateLimitRule, | ||
ArcjetFixedWindowRateLimitRule, | ||
ArcjetSlidingWindowRateLimitRule, | ||
ArcjetShieldRule, | ||
ArcjetLogger, | ||
ArcjetSensitiveInfoRule, | ||
ArcjetIdentifiedEntity, | ||
} from "@arcjet/protocol"; | ||
import { | ||
ArcjetContext, | ||
ArcjetBotReason, | ||
ArcjetBotType, | ||
ArcjetEmailReason, | ||
ArcjetEmailRule, | ||
ArcjetEmailType, | ||
@@ -12,15 +25,7 @@ ArcjetErrorReason, | ||
ArcjetRuleResult, | ||
ArcjetSensitiveInfoType, | ||
ArcjetSensitiveInfoReason, | ||
ArcjetDecision, | ||
ArcjetDenyDecision, | ||
ArcjetErrorDecision, | ||
ArcjetBotRule, | ||
ArcjetRule, | ||
ArcjetLocalRule, | ||
ArcjetRequestDetails, | ||
ArcjetTokenBucketRateLimitRule, | ||
ArcjetFixedWindowRateLimitRule, | ||
ArcjetSlidingWindowRateLimitRule, | ||
ArcjetShieldRule, | ||
ArcjetLogger, | ||
ArcjetRateLimitRule, | ||
} from "@arcjet/protocol"; | ||
@@ -31,4 +36,8 @@ import { | ||
} from "@arcjet/protocol/convert.js"; | ||
import { Client } from "@arcjet/protocol/client.js"; | ||
import type { Client } from "@arcjet/protocol/client.js"; | ||
import * as analyze from "@arcjet/analyze"; | ||
import type { | ||
DetectedSensitiveInfoEntity, | ||
SensitiveInfoEntity, | ||
} from "@arcjet/analyze"; | ||
import * as duration from "@arcjet/duration"; | ||
@@ -314,7 +323,45 @@ import ArcjetHeaders from "@arcjet/headers"; | ||
type DetectSensitiveInfoEntities<T> = ( | ||
tokens: string[], | ||
) => Array<ArcjetSensitiveInfoType | T | undefined>; | ||
type SensitiveInfoOptionsAllow< | ||
Detect extends DetectSensitiveInfoEntities<CustomEntities>, | ||
CustomEntities extends string, | ||
> = { | ||
allow: Array< | ||
ArcjetSensitiveInfoType | Exclude<ReturnType<Detect>[number], undefined> | ||
>; | ||
deny?: never; | ||
contextWindowSize?: number; | ||
mode?: ArcjetMode; | ||
detect?: Detect; | ||
}; | ||
type SensitiveInfoOptionsDeny< | ||
Detect extends DetectSensitiveInfoEntities<CustomEntities>, | ||
CustomEntities extends string, | ||
> = { | ||
allow?: never; | ||
deny: Array< | ||
ArcjetSensitiveInfoType | Exclude<ReturnType<Detect>[number], undefined> | ||
>; | ||
contextWindowSize?: number; | ||
mode?: ArcjetMode; | ||
detect?: Detect; | ||
}; | ||
export type SensitiveInfoOptions< | ||
Detect extends DetectSensitiveInfoEntities<CustomEntities>, | ||
CustomEntities extends string, | ||
> = | ||
| SensitiveInfoOptionsAllow<Detect, CustomEntities> | ||
| SensitiveInfoOptionsDeny<Detect, CustomEntities>; | ||
const Priority = { | ||
Shield: 1, | ||
RateLimit: 2, | ||
BotDetection: 3, | ||
EmailValidation: 4, | ||
SensitiveInfo: 1, | ||
Shield: 2, | ||
RateLimit: 3, | ||
BotDetection: 4, | ||
EmailValidation: 5, | ||
}; | ||
@@ -371,3 +418,6 @@ | ||
*/ | ||
export type ArcjetAdapterContext = Record<string, unknown>; | ||
export type ArcjetAdapterContext = { | ||
[key: string]: unknown; | ||
getBody(): Promise<string | undefined>; | ||
}; | ||
@@ -530,2 +580,181 @@ /** | ||
function protocolSensitiveInfoEntitiesToAnalyze<Custom extends string>( | ||
entity: ArcjetSensitiveInfoType | Custom, | ||
) { | ||
if (typeof entity !== "string") { | ||
throw new Error("invalid entity type"); | ||
} | ||
if (entity === "EMAIL") { | ||
return { tag: "email" as const }; | ||
} | ||
if (entity === "PHONE_NUMBER") { | ||
return { tag: "phone-number" as const }; | ||
} | ||
if (entity === "IP_ADDRESS") { | ||
return { tag: "ip-address" as const }; | ||
} | ||
if (entity === "CREDIT_CARD_NUMBER") { | ||
return { tag: "credit-card-number" as const }; | ||
} | ||
return { | ||
tag: "custom" as const, | ||
val: entity, | ||
}; | ||
} | ||
function analyzeSensitiveInfoEntitiesToString( | ||
entity: SensitiveInfoEntity, | ||
): string { | ||
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; | ||
} | ||
function convertAnalyzeDetectedSensitiveInfoEntity( | ||
detectedEntities: DetectedSensitiveInfoEntity[], | ||
): ArcjetIdentifiedEntity[] { | ||
return detectedEntities.map((detectedEntity) => { | ||
return { | ||
...detectedEntity, | ||
identifiedType: analyzeSensitiveInfoEntitiesToString( | ||
detectedEntity.identifiedType, | ||
), | ||
}; | ||
}); | ||
} | ||
export function sensitiveInfo< | ||
const Detect extends DetectSensitiveInfoEntities<CustomEntities>, | ||
const CustomEntities extends string, | ||
>( | ||
options: SensitiveInfoOptions<Detect, CustomEntities>, | ||
...additionalOptions: SensitiveInfoOptions<Detect, CustomEntities>[] | ||
): Primitive<{}> { | ||
const rules: ArcjetSensitiveInfoRule<{}>[] = []; | ||
// Always create at least one SENSITIVE_INFO rule | ||
for (const opt of [options, ...additionalOptions]) { | ||
const mode = opt.mode === "LIVE" ? "LIVE" : "DRY_RUN"; | ||
if (typeof opt.allow !== "undefined" && typeof opt.deny !== "undefined") { | ||
throw new Error( | ||
"Both allow and deny cannot be provided to sensitiveInfo", | ||
); | ||
} | ||
rules.push({ | ||
type: "SENSITIVE_INFO", | ||
priority: Priority.SensitiveInfo, | ||
mode, | ||
allow: opt.allow || [], | ||
deny: opt.deny || [], | ||
validate( | ||
context: ArcjetContext, | ||
details: ArcjetRequestDetails, | ||
): asserts details is ArcjetRequestDetails {}, | ||
async protect( | ||
context: ArcjetContext, | ||
details: ArcjetRequestDetails, | ||
): Promise<ArcjetRuleResult> { | ||
const body = await context.getBody(); | ||
if (typeof body === "undefined") { | ||
return new ArcjetRuleResult({ | ||
ttl: 0, | ||
state: "NOT_RUN", | ||
conclusion: "ERROR", | ||
reason: new ArcjetErrorReason( | ||
"Couldn't read the body of the request to perform sensitive info identification.", | ||
), | ||
}); | ||
} | ||
let convertedDetect = undefined; | ||
if (typeof opt.detect !== "undefined") { | ||
const detect = opt.detect; | ||
convertedDetect = (tokens: string[]) => { | ||
return detect(tokens) | ||
.filter((e) => typeof e !== "undefined") | ||
.map(protocolSensitiveInfoEntitiesToAnalyze); | ||
}; | ||
} | ||
let entitiesTag: "allow" | "deny" = "allow"; | ||
let entitiesVal: Array< | ||
ReturnType<typeof protocolSensitiveInfoEntitiesToAnalyze> | ||
> = []; | ||
if (Array.isArray(opt.allow)) { | ||
entitiesTag = "allow"; | ||
entitiesVal = opt.allow | ||
.filter((e) => typeof e !== "undefined") | ||
.map(protocolSensitiveInfoEntitiesToAnalyze); | ||
} | ||
if (Array.isArray(opt.deny)) { | ||
entitiesTag = "deny"; | ||
entitiesVal = opt.deny | ||
.filter((e) => typeof e !== "undefined") | ||
.map(protocolSensitiveInfoEntitiesToAnalyze); | ||
} | ||
const entities = { | ||
tag: entitiesTag, | ||
val: entitiesVal, | ||
}; | ||
const result = await analyze.detectSensitiveInfo( | ||
context, | ||
body, | ||
entities, | ||
options.contextWindowSize || 1, | ||
convertedDetect, | ||
); | ||
const reason = new ArcjetSensitiveInfoReason({ | ||
denied: convertAnalyzeDetectedSensitiveInfoEntity(result.denied), | ||
allowed: convertAnalyzeDetectedSensitiveInfoEntity(result.allowed), | ||
}); | ||
if (result.denied.length === 0) { | ||
return new ArcjetRuleResult({ | ||
ttl: 0, | ||
state: "RUN", | ||
conclusion: "ALLOW", | ||
reason, | ||
}); | ||
} else { | ||
return new ArcjetRuleResult({ | ||
ttl: 0, | ||
state: "RUN", | ||
conclusion: "DENY", | ||
reason, | ||
}); | ||
} | ||
}, | ||
}); | ||
} | ||
return rules; | ||
} | ||
export function validateEmail( | ||
@@ -532,0 +761,0 @@ options?: EmailOptions, |
{ | ||
"name": "arcjet", | ||
"version": "1.0.0-alpha.21", | ||
"version": "1.0.0-alpha.22", | ||
"description": "Arcjet TypeScript and JavaScript SDK core", | ||
@@ -43,15 +43,15 @@ "license": "Apache-2.0", | ||
"dependencies": { | ||
"@arcjet/analyze": "1.0.0-alpha.21", | ||
"@arcjet/duration": "1.0.0-alpha.21", | ||
"@arcjet/headers": "1.0.0-alpha.21", | ||
"@arcjet/protocol": "1.0.0-alpha.21", | ||
"@arcjet/runtime": "1.0.0-alpha.21" | ||
"@arcjet/analyze": "1.0.0-alpha.22", | ||
"@arcjet/duration": "1.0.0-alpha.22", | ||
"@arcjet/headers": "1.0.0-alpha.22", | ||
"@arcjet/protocol": "1.0.0-alpha.22", | ||
"@arcjet/runtime": "1.0.0-alpha.22" | ||
}, | ||
"devDependencies": { | ||
"@arcjet/eslint-config": "1.0.0-alpha.21", | ||
"@arcjet/rollup-config": "1.0.0-alpha.21", | ||
"@arcjet/tsconfig": "1.0.0-alpha.21", | ||
"@edge-runtime/jest-environment": "3.0.1", | ||
"@arcjet/eslint-config": "1.0.0-alpha.22", | ||
"@arcjet/rollup-config": "1.0.0-alpha.22", | ||
"@arcjet/tsconfig": "1.0.0-alpha.22", | ||
"@edge-runtime/jest-environment": "3.0.2", | ||
"@jest/globals": "29.7.0", | ||
"@rollup/wasm-node": "4.20.0", | ||
"@rollup/wasm-node": "4.21.0", | ||
"@types/node": "18.18.0", | ||
@@ -58,0 +58,0 @@ "jest": "29.7.0", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
96038
2258
+ Added@arcjet/analyze@1.0.0-alpha.22(transitive)
+ Added@arcjet/duration@1.0.0-alpha.22(transitive)
+ Added@arcjet/headers@1.0.0-alpha.22(transitive)
+ Added@arcjet/protocol@1.0.0-alpha.22(transitive)
+ Added@arcjet/runtime@1.0.0-alpha.22(transitive)
- Removed@arcjet/analyze@1.0.0-alpha.21(transitive)
- Removed@arcjet/duration@1.0.0-alpha.21(transitive)
- Removed@arcjet/headers@1.0.0-alpha.21(transitive)
- Removed@arcjet/protocol@1.0.0-alpha.21(transitive)
- Removed@arcjet/runtime@1.0.0-alpha.21(transitive)