Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support allow and deny config of validateEmail rule #2661

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading