Skip to content

Commit

Permalink
feat: support allow and deny config of validateEmail rule
Browse files Browse the repository at this point in the history
  • Loading branch information
e-moran committed Dec 20, 2024
1 parent 02e4435 commit 1d9dc0e
Show file tree
Hide file tree
Showing 12 changed files with 236 additions and 70 deletions.
Binary file modified analyze-wasm/wasm/arcjet_analyze_js_req.component.core.wasm
Binary file not shown.
18 changes: 16 additions & 2 deletions analyze-wasm/wasm/arcjet_analyze_js_req.component.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,24 @@ export interface EmailValidationResult {
validity: EmailValidity,
blocked: Array<string>,
}
export interface EmailValidationConfig {
export interface AllowEmailValidationConfig {
requireTopLevelDomain: boolean,
allowDomainLiteral: boolean,
blockedEmails: Array<string>,
allow: Array<string>,
}
export interface DenyEmailValidationConfig {
requireTopLevelDomain: boolean,
allowDomainLiteral: boolean,
deny: Array<string>,
}
export type EmailValidationConfig = EmailValidationConfigAllowEmailValidationConfig | EmailValidationConfigDenyEmailValidationConfig;
export interface EmailValidationConfigAllowEmailValidationConfig {
tag: 'allow-email-validation-config',
val: AllowEmailValidationConfig,
}
export interface EmailValidationConfigDenyEmailValidationConfig {
tag: 'deny-email-validation-config',
val: DenyEmailValidationConfig,
}
export type SensitiveInfoEntities = SensitiveInfoEntitiesAllow | SensitiveInfoEntitiesDeny;
export interface SensitiveInfoEntitiesAllow {
Expand Down
106 changes: 73 additions & 33 deletions analyze-wasm/wasm/arcjet_analyze_js_req.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -514,69 +514,109 @@ function instantiate(getCoreModule, imports, instantiateCore = WebAssembly.insta
function isValidEmail(arg0, arg1) {
var ptr0 = utf8Encode(arg0, realloc0, memory0);
var len0 = utf8EncodedLen;
var {requireTopLevelDomain: v1_0, allowDomainLiteral: v1_1, blockedEmails: v1_2 } = arg1;
var vec3 = v1_2;
var len3 = vec3.length;
var result3 = realloc0(0, 0, 4, len3 * 8);
for (let i = 0; i < vec3.length; i++) {
const e = vec3[i];
const base = result3 + i * 8;var ptr2 = utf8Encode(e, realloc0, memory0);
var len2 = utf8EncodedLen;
dataView(memory0).setInt32(base + 4, len2, true);
dataView(memory0).setInt32(base + 0, ptr2, true);
var variant7 = arg1;
let variant7_0;
let variant7_1;
let variant7_2;
let variant7_3;
let variant7_4;
switch (variant7.tag) {
case 'allow-email-validation-config': {
const e = variant7.val;
var {requireTopLevelDomain: v1_0, allowDomainLiteral: v1_1, allow: v1_2 } = e;
var vec3 = v1_2;
var len3 = vec3.length;
var result3 = realloc0(0, 0, 4, len3 * 8);
for (let i = 0; i < vec3.length; i++) {
const e = vec3[i];
const base = result3 + i * 8;var ptr2 = utf8Encode(e, realloc0, memory0);
var len2 = utf8EncodedLen;
dataView(memory0).setInt32(base + 4, len2, true);
dataView(memory0).setInt32(base + 0, ptr2, true);
}
variant7_0 = 0;
variant7_1 = v1_0 ? 1 : 0;
variant7_2 = v1_1 ? 1 : 0;
variant7_3 = result3;
variant7_4 = len3;
break;
}
case 'deny-email-validation-config': {
const e = variant7.val;
var {requireTopLevelDomain: v4_0, allowDomainLiteral: v4_1, deny: v4_2 } = e;
var vec6 = v4_2;
var len6 = vec6.length;
var result6 = realloc0(0, 0, 4, len6 * 8);
for (let i = 0; i < vec6.length; i++) {
const e = vec6[i];
const base = result6 + i * 8;var ptr5 = utf8Encode(e, realloc0, memory0);
var len5 = utf8EncodedLen;
dataView(memory0).setInt32(base + 4, len5, true);
dataView(memory0).setInt32(base + 0, ptr5, true);
}
variant7_0 = 1;
variant7_1 = v4_0 ? 1 : 0;
variant7_2 = v4_1 ? 1 : 0;
variant7_3 = result6;
variant7_4 = len6;
break;
}
default: {
throw new TypeError(`invalid variant tag value \`${JSON.stringify(variant7.tag)}\` (received \`${variant7}\`) specified for \`EmailValidationConfig\``);
}
}
const ret = exports1['is-valid-email'](ptr0, len0, v1_0 ? 1 : 0, v1_1 ? 1 : 0, result3, len3);
let variant8;
const ret = exports1['is-valid-email'](ptr0, len0, variant7_0, variant7_1, variant7_2, variant7_3, variant7_4);
let variant12;
switch (dataView(memory0).getUint8(ret + 0, true)) {
case 0: {
let enum4;
let enum8;
switch (dataView(memory0).getUint8(ret + 4, true)) {
case 0: {
enum4 = 'valid';
enum8 = 'valid';
break;
}
case 1: {
enum4 = 'invalid';
enum8 = 'invalid';
break;
}
default: {
throw new TypeError('invalid discriminant specified for EmailValidity');
}
}
var len6 = dataView(memory0).getInt32(ret + 12, true);
var base6 = dataView(memory0).getInt32(ret + 8, true);
var result6 = [];
for (let i = 0; i < len6; i++) {
const base = base6 + i * 8;
var ptr5 = dataView(memory0).getInt32(base + 0, true);
var len5 = dataView(memory0).getInt32(base + 4, true);
var result5 = utf8Decoder.decode(new Uint8Array(memory0.buffer, ptr5, len5));
result6.push(result5);
var len10 = dataView(memory0).getInt32(ret + 12, true);
var base10 = dataView(memory0).getInt32(ret + 8, true);
var result10 = [];
for (let i = 0; i < len10; i++) {
const base = base10 + i * 8;
var ptr9 = dataView(memory0).getInt32(base + 0, true);
var len9 = dataView(memory0).getInt32(base + 4, true);
var result9 = utf8Decoder.decode(new Uint8Array(memory0.buffer, ptr9, len9));
result10.push(result9);
}
variant8= {
variant12= {
tag: 'ok',
val: {
validity: enum4,
blocked: result6,
validity: enum8,
blocked: result10,
}
};
break;
}
case 1: {
var ptr7 = dataView(memory0).getInt32(ret + 4, true);
var len7 = dataView(memory0).getInt32(ret + 8, true);
var result7 = utf8Decoder.decode(new Uint8Array(memory0.buffer, ptr7, len7));
variant8= {
var ptr11 = dataView(memory0).getInt32(ret + 4, true);
var len11 = dataView(memory0).getInt32(ret + 8, true);
var result11 = utf8Decoder.decode(new Uint8Array(memory0.buffer, ptr11, len11));
variant12= {
tag: 'err',
val: result7
val: result11
};
break;
}
default: {
throw new TypeError('invalid variant discriminant for expected');
}
}
const retVal = variant8;
const retVal = variant12;
postReturn3(ret);
if (typeof retVal === 'object' && retVal.tag === 'err') {
throw new ComponentError(retVal.val);
Expand Down
Binary file modified analyze-wasm/wasm/arcjet_analyze_js_req.component.wasm
Binary file not shown.
10 changes: 2 additions & 8 deletions analyze/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,20 +113,14 @@ export async function generateFingerprint(
export async function isValidEmail(
context: AnalyzeContext,
candidate: string,
options?: EmailValidationConfig,
options: EmailValidationConfig,
): Promise<EmailValidationResult> {
const { log } = context;
const coreImports = createCoreImports();
const analyze = await initializeWasm(coreImports);
const optionsOrDefault = {
requireTopLevelDomain: true,
allowDomainLiteral: false,
blockedEmails: [],
...options,
};

if (typeof analyze !== "undefined") {
return analyze.isValidEmail(candidate, optionsOrDefault);
return analyze.isValidEmail(candidate, options);
} else {
log.debug("WebAssembly is not supported in this runtime");
// Skip the local evaluation of the rule if WASM is not available
Expand Down
73 changes: 64 additions & 9 deletions arcjet/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import type {
DetectedSensitiveInfoEntity,
SensitiveInfoEntity,
BotConfig,
EmailValidationConfig,
} from "@arcjet/analyze";
import * as duration from "@arcjet/duration";
import ArcjetHeaders from "@arcjet/headers";
Expand Down Expand Up @@ -521,13 +522,24 @@ type BotOptionsDeny = {

export type BotOptions = BotOptionsAllow | BotOptionsDeny;

export type EmailOptions = {
type EmailOptionsAllow = {
mode?: ArcjetMode;
block?: ArcjetEmailType[];
allow: ArcjetEmailType[];
deny?: never;
requireTopLevelDomain?: boolean;
allowDomainLiteral?: boolean;
};

type EmailOptionsDeny = {
mode?: ArcjetMode;
allow?: never;
deny: ArcjetEmailType[];
requireTopLevelDomain?: boolean;
allowDomainLiteral?: boolean;
};

export type EmailOptions = EmailOptionsAllow | EmailOptionsDeny;

type DetectSensitiveInfoEntities<T> = (
tokens: string[],
) => Array<ArcjetSensitiveInfoType | T | undefined>;
Expand Down Expand Up @@ -946,22 +958,65 @@ export function validateEmail(
validateEmailOptions(options);

const mode = options.mode === "LIVE" ? "LIVE" : "DRY_RUN";
const block = options.block ?? [];
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 allow = options.allow ?? [];
const deny = options.deny ?? [];
const requireTopLevelDomain = options.requireTopLevelDomain ?? true;
const allowDomainLiteral = options.allowDomainLiteral ?? false;

const emailOpts = {
requireTopLevelDomain,
allowDomainLiteral,
blockedEmails: block,
let config: EmailValidationConfig = {
tag: "allow-email-validation-config",
val: {
requireTopLevelDomain,
allowDomainLiteral,
allow: [],
},
};

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 [
<ArcjetEmailRule<{ email: string }>>{
type: "EMAIL",
priority: Priority.EmailValidation,
mode,
block,
allow,
deny,
requireTopLevelDomain,
allowDomainLiteral,

Expand All @@ -979,7 +1034,7 @@ export function validateEmail(
context: ArcjetContext,
{ email }: ArcjetRequestDetails & { email: string },
): Promise<ArcjetRuleResult> {
const result = await analyze.isValidEmail(context, email, emailOpts);
const result = await analyze.isValidEmail(context, email, config);
if (result.validity === "valid") {
return new ArcjetRuleResult({
ttl: 0,
Expand Down
Loading

0 comments on commit 1d9dc0e

Please sign in to comment.