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

fix!: Return ERROR decision when fingerprint cannot be generated #1990

Merged
merged 1 commit into from
Oct 22, 2024
Merged
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
8 changes: 0 additions & 8 deletions analyze/edge-light.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,6 @@ async function init(
}

const coreImports: ImportObject = {
"arcjet:js-req/logger": {
debug(msg) {
log.debug(msg);
},
error(msg) {
log.error(msg);
},
},
"arcjet:js-req/email-validator-overrides": {
isFreeEmail(domain) {
if (FREE_EMAIL_PROVIDERS.includes(domain)) {
Expand Down
8 changes: 0 additions & 8 deletions analyze/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,6 @@ async function init(
}

const coreImports: ImportObject = {
"arcjet:js-req/logger": {
debug(msg) {
log.debug(msg);
},
error(msg) {
log.error(msg);
},
},
"arcjet:js-req/email-validator-overrides": {
isFreeEmail(domain) {
if (FREE_EMAIL_PROVIDERS.includes(domain)) {
Expand Down
Binary file modified analyze/wasm/arcjet_analyze_js_req.component.core.wasm
Binary file not shown.
Binary file modified analyze/wasm/arcjet_analyze_js_req.component.core2.wasm
Binary file not shown.
Binary file modified analyze/wasm/arcjet_analyze_js_req.component.core3.wasm
Binary file not shown.
3 changes: 1 addition & 2 deletions analyze/wasm/arcjet_analyze_js_req.component.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,15 @@ export interface BotResult {
denied: Array<BotEntity>,
}
import { ArcjetJsReqEmailValidatorOverrides } from './interfaces/arcjet-js-req-email-validator-overrides.js';
import { ArcjetJsReqLogger } from './interfaces/arcjet-js-req-logger.js';
import { ArcjetJsReqSensitiveInformationIdentifier } from './interfaces/arcjet-js-req-sensitive-information-identifier.js';
export interface ImportObject {
'arcjet:js-req/email-validator-overrides': typeof ArcjetJsReqEmailValidatorOverrides,
'arcjet:js-req/logger': typeof ArcjetJsReqLogger,
'arcjet:js-req/sensitive-information-identifier': typeof ArcjetJsReqSensitiveInformationIdentifier,
}
export interface Root {
detectBot(request: string, options: BotConfig): BotResult,
generateFingerprint(request: string, characteristics: Array<string>): string,
validateCharacteristics(request: string, characteristics: Array<string>): void,
isValidEmail(candidate: string, options: EmailValidationConfig): EmailValidationResult,
detectSensitiveInfo(content: string, options: SensitiveInfoConfig): SensitiveInfoResult,
}
Expand Down
132 changes: 92 additions & 40 deletions analyze/wasm/arcjet_analyze_js_req.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ function instantiate(getCoreModule, imports, instantiateCore = WebAssembly.insta
const module2 = getCoreModule('arcjet_analyze_js_req.component.core3.wasm');

const { hasGravatar, hasMxRecords, isDisposableEmail, isFreeEmail } = imports['arcjet:js-req/email-validator-overrides'];
const { debug, error } = imports['arcjet:js-req/logger'];
const { detect } = imports['arcjet:js-req/sensitive-information-identifier'];
let gen = (function* init () {
let exports0;
Expand All @@ -48,20 +47,6 @@ function instantiate(getCoreModule, imports, instantiateCore = WebAssembly.insta
let realloc0;

function trampoline0(arg0, arg1) {
var ptr0 = arg0;
var len0 = arg1;
var result0 = utf8Decoder.decode(new Uint8Array(memory0.buffer, ptr0, len0));
debug(result0);
}

function trampoline1(arg0, arg1) {
var ptr0 = arg0;
var len0 = arg1;
var result0 = utf8Decoder.decode(new Uint8Array(memory0.buffer, ptr0, len0));
error(result0);
}

function trampoline2(arg0, arg1) {
var ptr0 = arg0;
var len0 = arg1;
var result0 = utf8Decoder.decode(new Uint8Array(memory0.buffer, ptr0, len0));
Expand Down Expand Up @@ -92,7 +77,7 @@ function instantiate(getCoreModule, imports, instantiateCore = WebAssembly.insta
return enum1;
}

function trampoline3(arg0, arg1) {
function trampoline1(arg0, arg1) {
var ptr0 = arg0;
var len0 = arg1;
var result0 = utf8Decoder.decode(new Uint8Array(memory0.buffer, ptr0, len0));
Expand Down Expand Up @@ -123,7 +108,7 @@ function instantiate(getCoreModule, imports, instantiateCore = WebAssembly.insta
return enum1;
}

function trampoline4(arg0, arg1) {
function trampoline2(arg0, arg1) {
var ptr0 = arg0;
var len0 = arg1;
var result0 = utf8Decoder.decode(new Uint8Array(memory0.buffer, ptr0, len0));
Expand Down Expand Up @@ -154,7 +139,7 @@ function instantiate(getCoreModule, imports, instantiateCore = WebAssembly.insta
return enum1;
}

function trampoline5(arg0, arg1) {
function trampoline3(arg0, arg1) {
var ptr0 = arg0;
var len0 = arg1;
var result0 = utf8Decoder.decode(new Uint8Array(memory0.buffer, ptr0, len0));
Expand Down Expand Up @@ -185,7 +170,7 @@ function instantiate(getCoreModule, imports, instantiateCore = WebAssembly.insta
return enum1;
}

function trampoline6(arg0, arg1, arg2) {
function trampoline4(arg0, arg1, arg2) {
var len1 = arg1;
var base1 = arg0;
var result1 = [];
Expand Down Expand Up @@ -248,21 +233,18 @@ function instantiate(getCoreModule, imports, instantiateCore = WebAssembly.insta
let postReturn1;
let postReturn2;
let postReturn3;
let postReturn4;
Promise.all([module0, module1, module2]).catch(() => {});
({ exports: exports0 } = yield instantiateCore(yield module1));
({ exports: exports1 } = yield instantiateCore(yield module0, {
'arcjet:js-req/email-validator-overrides': {
'has-gravatar': exports0['5'],
'has-mx-records': exports0['4'],
'is-disposable-email': exports0['3'],
'is-free-email': exports0['2'],
},
'arcjet:js-req/logger': {
debug: exports0['0'],
error: exports0['1'],
'has-gravatar': exports0['3'],
'has-mx-records': exports0['2'],
'is-disposable-email': exports0['1'],
'is-free-email': exports0['0'],
},
'arcjet:js-req/sensitive-information-identifier': {
detect: exports0['6'],
detect: exports0['4'],
},
}));
memory0 = exports1.memory;
Expand All @@ -275,14 +257,13 @@ function instantiate(getCoreModule, imports, instantiateCore = WebAssembly.insta
'2': trampoline2,
'3': trampoline3,
'4': trampoline4,
'5': trampoline5,
'6': trampoline6,
},
}));
postReturn0 = exports1['cabi_post_detect-bot'];
postReturn1 = exports1['cabi_post_generate-fingerprint'];
postReturn2 = exports1['cabi_post_is-valid-email'];
postReturn3 = exports1['cabi_post_detect-sensitive-info'];
postReturn2 = exports1['cabi_post_validate-characteristics'];
postReturn3 = exports1['cabi_post_is-valid-email'];
postReturn4 = exports1['cabi_post_detect-sensitive-info'];

function detectBot(arg0, arg1) {
var ptr0 = utf8Encode(arg0, realloc0, memory0);
Expand Down Expand Up @@ -404,12 +385,83 @@ function instantiate(getCoreModule, imports, instantiateCore = WebAssembly.insta
dataView(memory0).setInt32(base + 0, ptr1, true);
}
const ret = exports1['generate-fingerprint'](ptr0, len0, result2, len2);
var ptr3 = dataView(memory0).getInt32(ret + 0, true);
var len3 = dataView(memory0).getInt32(ret + 4, true);
var result3 = utf8Decoder.decode(new Uint8Array(memory0.buffer, ptr3, len3));
const retVal = result3;
let variant5;
switch (dataView(memory0).getUint8(ret + 0, true)) {
case 0: {
var ptr3 = dataView(memory0).getInt32(ret + 4, true);
var len3 = dataView(memory0).getInt32(ret + 8, true);
var result3 = utf8Decoder.decode(new Uint8Array(memory0.buffer, ptr3, len3));
variant5= {
tag: 'ok',
val: result3
};
break;
}
case 1: {
var ptr4 = dataView(memory0).getInt32(ret + 4, true);
var len4 = dataView(memory0).getInt32(ret + 8, true);
var result4 = utf8Decoder.decode(new Uint8Array(memory0.buffer, ptr4, len4));
variant5= {
tag: 'err',
val: result4
};
break;
}
default: {
throw new TypeError('invalid variant discriminant for expected');
}
}
const retVal = variant5;
postReturn1(ret);
return retVal;
if (typeof retVal === 'object' && retVal.tag === 'err') {
throw new ComponentError(retVal.val);
}
return retVal.val;
}

function validateCharacteristics(arg0, arg1) {
var ptr0 = utf8Encode(arg0, realloc0, memory0);
var len0 = utf8EncodedLen;
var vec2 = arg1;
var len2 = vec2.length;
var result2 = realloc0(0, 0, 4, len2 * 8);
for (let i = 0; i < vec2.length; i++) {
const e = vec2[i];
const base = result2 + i * 8;var ptr1 = utf8Encode(e, realloc0, memory0);
var len1 = utf8EncodedLen;
dataView(memory0).setInt32(base + 4, len1, true);
dataView(memory0).setInt32(base + 0, ptr1, true);
}
const ret = exports1['validate-characteristics'](ptr0, len0, result2, len2);
let variant4;
switch (dataView(memory0).getUint8(ret + 0, true)) {
case 0: {
variant4= {
tag: 'ok',
val: undefined
};
break;
}
case 1: {
var ptr3 = dataView(memory0).getInt32(ret + 4, true);
var len3 = dataView(memory0).getInt32(ret + 8, true);
var result3 = utf8Decoder.decode(new Uint8Array(memory0.buffer, ptr3, len3));
variant4= {
tag: 'err',
val: result3
};
break;
}
default: {
throw new TypeError('invalid variant discriminant for expected');
}
}
const retVal = variant4;
postReturn2(ret);
if (typeof retVal === 'object' && retVal.tag === 'err') {
throw new ComponentError(retVal.val);
}
return retVal.val;
}

function isValidEmail(arg0, arg1) {
Expand Down Expand Up @@ -478,7 +530,7 @@ function instantiate(getCoreModule, imports, instantiateCore = WebAssembly.insta
}
}
const retVal = variant8;
postReturn2(ret);
postReturn3(ret);
if (typeof retVal === 'object' && retVal.tag === 'err') {
throw new ComponentError(retVal.val);
}
Expand Down Expand Up @@ -704,11 +756,11 @@ function instantiate(getCoreModule, imports, instantiateCore = WebAssembly.insta
allowed: result12,
denied: result15,
};
postReturn3(ret);
postReturn4(ret);
return retVal;
}

return { detectBot, detectSensitiveInfo, generateFingerprint, isValidEmail, };
return { detectBot, detectSensitiveInfo, generateFingerprint, isValidEmail, validateCharacteristics, };
})();
let promise, resolve, reject;
function runNext (value) {
Expand Down
Binary file modified analyze/wasm/arcjet_analyze_js_req.component.wasm
Binary file not shown.
8 changes: 0 additions & 8 deletions analyze/workerd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,6 @@ async function init(
}

const coreImports: ImportObject = {
"arcjet:js-req/logger": {
debug(msg) {
log.debug(msg);
},
error(msg) {
log.error(msg);
},
},
"arcjet:js-req/email-validator-overrides": {
isFreeEmail(domain) {
if (FREE_EMAIL_PROVIDERS.includes(domain)) {
Expand Down
36 changes: 29 additions & 7 deletions arcjet/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1246,13 +1246,35 @@ export default function arcjet<
...ctx,
};

log.time?.("fingerprint");
const fingerprint = await analyze.generateFingerprint(
baseContext,
toAnalyzeRequest(details),
);
log.debug("fingerprint (%s): %s", rt, fingerprint);
log.timeEnd?.("fingerprint");
let fingerprint = "";
try {
log.time?.("fingerprint");
fingerprint = await analyze.generateFingerprint(
baseContext,
toAnalyzeRequest(details),
);
log.debug("fingerprint (%s): %s", rt, fingerprint);
} catch (error) {
log.error(
{ error },
"Failed to build fingerprint. Please verify your Characteristics.",
);

const decision = new ArcjetErrorDecision({
ttl: 0,
reason: new ArcjetErrorReason(
`Failed to build fingerprint - ${errorMessage(error)}`,
),
// No results because we couldn't create a fingerprint
results: [],
});

// TODO: Consider sending this to Report when we have an infallible fingerprint

return decision;
} finally {
log.timeEnd?.("fingerprint");
}

const context: ArcjetContext = Object.freeze({
...baseContext,
Expand Down
2 changes: 1 addition & 1 deletion arcjet/test/index.edge.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ describe("Arcjet: Env = Edge runtime", () => {
abc: 123,
requested: 1,
email: "",
ip: "",
ip: "100.100.100.100",
method: "",
protocol: "",
host: "",
Expand Down
12 changes: 7 additions & 5 deletions arcjet/test/index.node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2623,7 +2623,7 @@ describe("SDK", () => {
expect(denied.protect).toHaveBeenCalledTimes(1);
});

test("works with an empty request object", async () => {
test("returns an ERROR decision if fingerprint cannot be generated", async () => {
const client = {
decide: jest.fn(async () => {
return new ArcjetAllowDecision({
Expand All @@ -2649,10 +2649,10 @@ describe("SDK", () => {
};

const decision = await aj.protect(context, request);
expect(decision.conclusion).toEqual("ALLOW");
expect(decision.conclusion).toEqual("ERROR");
});

test("does not crash with no request object", async () => {
test("returns an ERROR decision with no request object", async () => {
const client = {
decide: jest.fn(async () => {
return new ArcjetAllowDecision({
Expand All @@ -2673,7 +2673,7 @@ describe("SDK", () => {

// @ts-expect-error
const decision = await aj.protect();
expect(decision.conclusion).toEqual("ALLOW");
expect(decision.conclusion).toEqual("ERROR");
});

test("returns an ERROR decision when more than 10 rules are generated", async () => {
Expand All @@ -2688,7 +2688,9 @@ describe("SDK", () => {
report: jest.fn(),
};

const request = {};
const request = {
ip: "100.100.100.100",
};

const rules: ArcjetRule[][] = [];
// We only iterate 4 times because `testRuleMultiple` generates 3 rules
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified redact-wasm/wasm/arcjet_analyze_bindings_redact.component.wasm
Binary file not shown.