@arcjet/analyze 1.0.0-alpha.9 → 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -29
- package/index.d.ts +24 -22
- package/index.js +88 -81
- package/package.json +12 -14
- package/index.ts +0 -165
- package/wasm/arcjet.wasm.d.ts +0 -31
- package/wasm/arcjet.wasm.js +0 -38
- package/wasm/arcjet_analyze_js_req.d.ts +0 -70
- package/wasm/arcjet_analyze_js_req.js +0 -508
- package/wasm/arcjet_analyze_js_req_bg.wasm +0 -0
- package/wasm/arcjet_analyze_js_req_bg.wasm.d.ts +0 -10
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<a href="https://arcjet.com" target="_arcjet-home">
|
|
2
2
|
<picture>
|
|
3
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://arcjet.com/arcjet-
|
|
4
|
-
<img src="https://arcjet.com/arcjet-
|
|
3
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://arcjet.com/logo/arcjet-dark-lockup-voyage-horizontal.svg">
|
|
4
|
+
<img src="https://arcjet.com/logo/arcjet-light-lockup-voyage-horizontal.svg" alt="Arcjet Logo" height="128" width="auto">
|
|
5
5
|
</picture>
|
|
6
6
|
</a>
|
|
7
7
|
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
</p>
|
|
18
18
|
|
|
19
19
|
[Arcjet][arcjet] helps developers protect their apps in just a few lines of
|
|
20
|
-
code. Implement rate limiting, bot protection, email verification
|
|
20
|
+
code. Implement rate limiting, bot protection, email verification, and defense
|
|
21
21
|
against common attacks.
|
|
22
22
|
|
|
23
23
|
This is the [Arcjet][arcjet] local analysis engine.
|
|
@@ -42,37 +42,17 @@ console.log("is email valid?", valid);
|
|
|
42
42
|
|
|
43
43
|
## Implementation
|
|
44
44
|
|
|
45
|
-
This package
|
|
46
|
-
|
|
45
|
+
This package uses the Wasm bindings provided by `@arcjet/analyze-wasm` to
|
|
46
|
+
call various functions that are exported by our wasm bindings.
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
Uint8Array, which would take up ~3x the space of the Wasm file. See
|
|
53
|
-
[Better Binary Batter: Mixing Base64 and Uint8Array][wasm-base64-blog] for more
|
|
54
|
-
details.
|
|
55
|
-
|
|
56
|
-
It is then decoded into an ArrayBuffer to be used directly via WebAssembly's
|
|
57
|
-
`compile()` function in our entry point file.
|
|
58
|
-
|
|
59
|
-
This is all done to avoid trying to read or bundle the Wasm asset in various
|
|
60
|
-
ways based on the platform or bundler a user is targeting. One example being
|
|
61
|
-
that Next.js requires special `asyncWebAssembly` webpack config to load our
|
|
62
|
-
Wasm file if we don't do this.
|
|
63
|
-
|
|
64
|
-
In the future, we hope to do away with this workaround when all bundlers
|
|
65
|
-
properly support consistent asset bundling techniques.
|
|
66
|
-
|
|
67
|
-
## API
|
|
68
|
-
|
|
69
|
-
In progress.
|
|
48
|
+
We chose to put this logic in a separate package because we need to change the
|
|
49
|
+
import structure for each runtime that we support in the wasm bindings. Moving
|
|
50
|
+
this to a separate package allows us not to have to duplicate code while providing
|
|
51
|
+
a combined higher-level api for calling our core functionality in Wasm.
|
|
70
52
|
|
|
71
53
|
## License
|
|
72
54
|
|
|
73
55
|
Licensed under the [Apache License, Version 2.0][apache-license].
|
|
74
56
|
|
|
75
57
|
[arcjet]: https://arcjet.com
|
|
76
|
-
[mdn-data-url]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs
|
|
77
|
-
[wasm-base64-blog]: https://blobfolio.com/2019/better-binary-batter-mixing-base64-and-uint8array/
|
|
78
58
|
[apache-license]: http://www.apache.org/licenses/LICENSE-2.0
|
package/index.d.ts
CHANGED
|
@@ -1,27 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import type { BotConfig, BotResult, DetectedSensitiveInfoEntity, DetectSensitiveInfoFunction, EmailValidationConfig, EmailValidationResult, SensitiveInfoEntities, SensitiveInfoEntity, SensitiveInfoResult } from "@arcjet/analyze-wasm";
|
|
2
|
+
import type { ArcjetLogger } from "@arcjet/protocol";
|
|
3
|
+
interface AnalyzeContext {
|
|
4
|
+
log: ArcjetLogger;
|
|
5
|
+
characteristics: string[];
|
|
6
|
+
}
|
|
7
|
+
type AnalyzeRequest = {
|
|
8
|
+
ip?: string;
|
|
9
|
+
method?: string;
|
|
10
|
+
protocol?: string;
|
|
11
|
+
host?: string;
|
|
12
|
+
path?: string;
|
|
13
|
+
headers?: Record<string, string>;
|
|
14
|
+
cookies?: string;
|
|
15
|
+
query?: string;
|
|
16
|
+
extra?: Record<string, string>;
|
|
17
|
+
};
|
|
18
|
+
export { type EmailValidationConfig, type BotConfig, type SensitiveInfoEntity, type DetectedSensitiveInfoEntity, };
|
|
3
19
|
/**
|
|
4
20
|
* Generate a fingerprint for the client. This is used to identify the client
|
|
5
21
|
* across multiple requests.
|
|
6
|
-
* @param
|
|
22
|
+
* @param context - The Arcjet Analyze context.
|
|
23
|
+
* @param request - The request to fingerprint.
|
|
7
24
|
* @returns A SHA-256 string fingerprint.
|
|
8
25
|
*/
|
|
9
|
-
export declare function generateFingerprint(
|
|
10
|
-
export declare function isValidEmail(candidate: string, options
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
*
|
|
14
|
-
* @property `bot_type` - What type of bot this is. This will be one of `BotType`.
|
|
15
|
-
* @property `bot_score` - A score ranging from 0 to 99 representing the degree of
|
|
16
|
-
* certainty. The higher the number within the type category, the greater the
|
|
17
|
-
* degree of certainty. E.g. `BotType.Automated` with a score of 1 means we are
|
|
18
|
-
* sure the request was made by an automated bot. `BotType.LikelyNotABot` with a
|
|
19
|
-
* score of 30 means we don't think this request was a bot, but it's lowest
|
|
20
|
-
* confidence level. `BotType.LikelyNotABot` with a score of 99 means we are
|
|
21
|
-
* almost certain this request was not a bot.
|
|
22
|
-
*/
|
|
23
|
-
export interface BotResult {
|
|
24
|
-
bot_type: number;
|
|
25
|
-
bot_score: number;
|
|
26
|
-
}
|
|
27
|
-
export declare function detectBot(headers: string, patterns_add: string, patterns_remove: string): Promise<BotResult>;
|
|
26
|
+
export declare function generateFingerprint(context: AnalyzeContext, request: AnalyzeRequest): Promise<string>;
|
|
27
|
+
export declare function isValidEmail(context: AnalyzeContext, candidate: string, options: EmailValidationConfig): Promise<EmailValidationResult>;
|
|
28
|
+
export declare function detectBot(context: AnalyzeContext, request: AnalyzeRequest, options: BotConfig): Promise<BotResult>;
|
|
29
|
+
export declare function detectSensitiveInfo(context: AnalyzeContext, candidate: string, entities: SensitiveInfoEntities, contextWindowSize: number, detect?: DetectSensitiveInfoFunction): Promise<SensitiveInfoResult>;
|
package/index.js
CHANGED
|
@@ -1,109 +1,116 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { initializeWasm } from '@arcjet/analyze-wasm';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
if (state === "uninitialized") {
|
|
18
|
-
try {
|
|
19
|
-
let wasmModule;
|
|
20
|
-
// We use `NEXT_RUNTIME` env var to DCE the Node/Browser code in the `else` block
|
|
21
|
-
// possible values: "edge" | "nodejs" | undefined
|
|
22
|
-
if (process.env["NEXT_RUNTIME"] === "edge") {
|
|
23
|
-
const mod = await import(
|
|
24
|
-
// @ts-expect-error
|
|
25
|
-
'./wasm/arcjet_analyze_js_req_bg.wasm?module');
|
|
26
|
-
wasmModule = mod.default;
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
const { wasm } = await import('./wasm/arcjet.wasm.js');
|
|
30
|
-
wasmModule = await WebAssembly.compile(await wasm());
|
|
31
|
-
}
|
|
32
|
-
await initWasm(wasmModule);
|
|
33
|
-
state = "initialized";
|
|
34
|
-
}
|
|
35
|
-
catch (err) {
|
|
36
|
-
state = "errored";
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
3
|
+
const FREE_EMAIL_PROVIDERS = [
|
|
4
|
+
"gmail.com",
|
|
5
|
+
"yahoo.com",
|
|
6
|
+
"hotmail.com",
|
|
7
|
+
"aol.com",
|
|
8
|
+
"hotmail.co.uk",
|
|
9
|
+
];
|
|
10
|
+
function noOpDetect() {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
function createCoreImports(detect) {
|
|
14
|
+
if (typeof detect !== "function") {
|
|
15
|
+
detect = noOpDetect;
|
|
39
16
|
}
|
|
40
17
|
return {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
18
|
+
"arcjet:js-req/email-validator-overrides": {
|
|
19
|
+
isFreeEmail(domain) {
|
|
20
|
+
if (FREE_EMAIL_PROVIDERS.includes(domain)) {
|
|
21
|
+
return "yes";
|
|
22
|
+
}
|
|
23
|
+
return "unknown";
|
|
24
|
+
},
|
|
25
|
+
isDisposableEmail() {
|
|
26
|
+
return "unknown";
|
|
27
|
+
},
|
|
28
|
+
hasMxRecords() {
|
|
29
|
+
return "unknown";
|
|
30
|
+
},
|
|
31
|
+
hasGravatar() {
|
|
32
|
+
return "unknown";
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
"arcjet:js-req/sensitive-information-identifier": {
|
|
36
|
+
detect,
|
|
37
|
+
},
|
|
38
|
+
"arcjet:js-req/verify-bot": {
|
|
39
|
+
verify() {
|
|
40
|
+
return "unverifiable";
|
|
41
|
+
},
|
|
42
|
+
},
|
|
44
43
|
};
|
|
45
44
|
}
|
|
46
45
|
/**
|
|
47
46
|
* Generate a fingerprint for the client. This is used to identify the client
|
|
48
47
|
* across multiple requests.
|
|
49
|
-
* @param
|
|
48
|
+
* @param context - The Arcjet Analyze context.
|
|
49
|
+
* @param request - The request to fingerprint.
|
|
50
50
|
* @returns A SHA-256 string fingerprint.
|
|
51
51
|
*/
|
|
52
|
-
async function generateFingerprint(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (process.env["NEXT_RUNTIME"] === "edge") {
|
|
59
|
-
const analyze = await init();
|
|
60
|
-
// We HAVE to have the WasmAPI in Edge
|
|
61
|
-
const fingerprint = analyze.fingerprint(ip);
|
|
62
|
-
return fingerprint;
|
|
52
|
+
async function generateFingerprint(context, request) {
|
|
53
|
+
const { log } = context;
|
|
54
|
+
const coreImports = createCoreImports();
|
|
55
|
+
const analyze = await initializeWasm(coreImports);
|
|
56
|
+
if (typeof analyze !== "undefined") {
|
|
57
|
+
return analyze.generateFingerprint(JSON.stringify(request), context.characteristics);
|
|
63
58
|
}
|
|
64
59
|
else {
|
|
65
|
-
|
|
66
|
-
if (typeof analyze !== "undefined") {
|
|
67
|
-
const fingerprint = analyze.fingerprint(ip);
|
|
68
|
-
return fingerprint;
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
// Conditional import because it's not available in some runtimes, we know
|
|
72
|
-
// it is when running on Vercel serverless functions.
|
|
73
|
-
// TODO(#180): Avoid nodejs-specific import
|
|
74
|
-
const createHash = await import('crypto');
|
|
75
|
-
// Fingerprint v1 is just the IP address
|
|
76
|
-
const fingerprintRaw = `fp_1_${ip}`;
|
|
77
|
-
const fingerprint = createHash
|
|
78
|
-
.createHash("sha256")
|
|
79
|
-
.update(fingerprintRaw)
|
|
80
|
-
.digest("hex");
|
|
81
|
-
return fingerprint;
|
|
82
|
-
}
|
|
60
|
+
log.debug("WebAssembly is not supported in this runtime");
|
|
83
61
|
}
|
|
62
|
+
return "";
|
|
84
63
|
}
|
|
85
|
-
async function isValidEmail(candidate, options) {
|
|
86
|
-
const
|
|
64
|
+
async function isValidEmail(context, candidate, options) {
|
|
65
|
+
const { log } = context;
|
|
66
|
+
const coreImports = createCoreImports();
|
|
67
|
+
const analyze = await initializeWasm(coreImports);
|
|
87
68
|
if (typeof analyze !== "undefined") {
|
|
88
69
|
return analyze.isValidEmail(candidate, options);
|
|
89
70
|
}
|
|
90
71
|
else {
|
|
91
|
-
|
|
92
|
-
|
|
72
|
+
log.debug("WebAssembly is not supported in this runtime");
|
|
73
|
+
// Skip the local evaluation of the rule if WASM is not available
|
|
74
|
+
return {
|
|
75
|
+
validity: "valid",
|
|
76
|
+
blocked: [],
|
|
77
|
+
};
|
|
93
78
|
}
|
|
94
79
|
}
|
|
95
|
-
async function detectBot(
|
|
96
|
-
const
|
|
80
|
+
async function detectBot(context, request, options) {
|
|
81
|
+
const { log } = context;
|
|
82
|
+
const coreImports = createCoreImports();
|
|
83
|
+
const analyze = await initializeWasm(coreImports);
|
|
97
84
|
if (typeof analyze !== "undefined") {
|
|
98
|
-
return analyze.detectBot(
|
|
85
|
+
return analyze.detectBot(JSON.stringify(request), options);
|
|
99
86
|
}
|
|
100
87
|
else {
|
|
101
|
-
|
|
88
|
+
log.debug("WebAssembly is not supported in this runtime");
|
|
89
|
+
// Skip the local evaluation of the rule if Wasm is not available
|
|
102
90
|
return {
|
|
103
|
-
|
|
104
|
-
|
|
91
|
+
allowed: [],
|
|
92
|
+
denied: [],
|
|
93
|
+
spoofed: false,
|
|
94
|
+
verified: false,
|
|
105
95
|
};
|
|
106
96
|
}
|
|
107
97
|
}
|
|
98
|
+
async function detectSensitiveInfo(context, candidate, entities, contextWindowSize, detect) {
|
|
99
|
+
const { log } = context;
|
|
100
|
+
const coreImports = createCoreImports(detect);
|
|
101
|
+
const analyze = await initializeWasm(coreImports);
|
|
102
|
+
if (typeof analyze !== "undefined") {
|
|
103
|
+
const skipCustomDetect = typeof detect !== "function";
|
|
104
|
+
return analyze.detectSensitiveInfo(candidate, {
|
|
105
|
+
entities,
|
|
106
|
+
contextWindowSize,
|
|
107
|
+
skipCustomDetect,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
log.debug("WebAssembly is not supported in this runtime");
|
|
112
|
+
throw new Error("SENSITIVE_INFO rule failed to run because Wasm is not supported in this environment.");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
108
115
|
|
|
109
|
-
export { detectBot, generateFingerprint, isValidEmail };
|
|
116
|
+
export { detectBot, detectSensitiveInfo, generateFingerprint, isValidEmail };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcjet/analyze",
|
|
3
|
-
"version": "1.0.0-
|
|
3
|
+
"version": "1.0.0-beta.1",
|
|
4
4
|
"description": "Arcjet local analysis engine",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://arcjet.com",
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
"files": [
|
|
28
28
|
"LICENSE",
|
|
29
29
|
"README.md",
|
|
30
|
+
"_virtual/",
|
|
30
31
|
"wasm/",
|
|
31
32
|
"*.js",
|
|
32
33
|
"*.d.ts",
|
|
33
|
-
"*.ts",
|
|
34
34
|
"!*.config.js"
|
|
35
35
|
],
|
|
36
36
|
"scripts": {
|
|
@@ -38,23 +38,21 @@
|
|
|
38
38
|
"build": "rollup --config rollup.config.js",
|
|
39
39
|
"lint": "eslint .",
|
|
40
40
|
"pretest": "npm run build",
|
|
41
|
-
"test": "
|
|
41
|
+
"test": "node --test --experimental-test-coverage"
|
|
42
42
|
},
|
|
43
|
-
"sideEffects": [
|
|
44
|
-
"./wasm/arcjet_analyze_js_req_bg.js"
|
|
45
|
-
],
|
|
46
43
|
"dependencies": {
|
|
47
|
-
"@arcjet/
|
|
44
|
+
"@arcjet/analyze-wasm": "1.0.0-beta.1",
|
|
45
|
+
"@arcjet/protocol": "1.0.0-beta.1"
|
|
48
46
|
},
|
|
49
47
|
"devDependencies": {
|
|
50
|
-
"@arcjet/eslint-config": "1.0.0-
|
|
51
|
-
"@arcjet/rollup-config": "1.0.0-
|
|
52
|
-
"@arcjet/tsconfig": "1.0.0-
|
|
53
|
-
"@
|
|
54
|
-
"@rollup/wasm-node": "4.
|
|
48
|
+
"@arcjet/eslint-config": "1.0.0-beta.1",
|
|
49
|
+
"@arcjet/rollup-config": "1.0.0-beta.1",
|
|
50
|
+
"@arcjet/tsconfig": "1.0.0-beta.1",
|
|
51
|
+
"@bytecodealliance/jco": "1.5.0",
|
|
52
|
+
"@rollup/wasm-node": "4.30.1",
|
|
55
53
|
"@types/node": "18.18.0",
|
|
56
|
-
"
|
|
57
|
-
"typescript": "5.
|
|
54
|
+
"expect": "29.7.0",
|
|
55
|
+
"typescript": "5.7.3"
|
|
58
56
|
},
|
|
59
57
|
"publishConfig": {
|
|
60
58
|
"access": "public",
|
package/index.ts
DELETED
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import initWasm, {
|
|
2
|
-
detect_bot,
|
|
3
|
-
generate_fingerprint,
|
|
4
|
-
is_valid_email,
|
|
5
|
-
type EmailValidationConfig,
|
|
6
|
-
} from "./wasm/arcjet_analyze_js_req.js";
|
|
7
|
-
|
|
8
|
-
export { type EmailValidationConfig };
|
|
9
|
-
|
|
10
|
-
type WasmAPI = {
|
|
11
|
-
/**
|
|
12
|
-
* The WASM detect_bot function. Initialized by calling `init()`. Defined at a
|
|
13
|
-
* class level to avoid having to load the WASM module multiple times.
|
|
14
|
-
*/
|
|
15
|
-
detectBot: typeof detect_bot;
|
|
16
|
-
/**
|
|
17
|
-
* The WASM fingerprint function. Initialized by calling `init()`. Defined at
|
|
18
|
-
* a class level to avoid having to load the WASM module multiple times.
|
|
19
|
-
*/
|
|
20
|
-
fingerprint: typeof generate_fingerprint;
|
|
21
|
-
/**
|
|
22
|
-
* The WASM email validation function. Initialized by calling `init()`. Defined at
|
|
23
|
-
* a class level to avoid having to load the WASM module multiple times.
|
|
24
|
-
*/
|
|
25
|
-
isValidEmail: typeof is_valid_email;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
type WasmState = "initialized" | "uninitialized" | "unsupported" | "errored";
|
|
29
|
-
|
|
30
|
-
let state: WasmState = "uninitialized";
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Initialize the WASM module. This can be explicitly called after creating
|
|
34
|
-
* the client, but it will be called automatically if it has not been called
|
|
35
|
-
* when the first request is made. This uses a factory-style pattern because
|
|
36
|
-
* the call must be async and the constructor cannot be async.
|
|
37
|
-
*/
|
|
38
|
-
async function init(): Promise<WasmAPI | undefined> {
|
|
39
|
-
if (state === "errored" || state === "unsupported") return;
|
|
40
|
-
|
|
41
|
-
if (typeof WebAssembly === "undefined") {
|
|
42
|
-
state = "unsupported";
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (state === "uninitialized") {
|
|
47
|
-
try {
|
|
48
|
-
let wasmModule: WebAssembly.Module;
|
|
49
|
-
// We use `NEXT_RUNTIME` env var to DCE the Node/Browser code in the `else` block
|
|
50
|
-
// possible values: "edge" | "nodejs" | undefined
|
|
51
|
-
if (process.env["NEXT_RUNTIME"] === "edge") {
|
|
52
|
-
const mod = await import(
|
|
53
|
-
// @ts-expect-error
|
|
54
|
-
"./wasm/arcjet_analyze_js_req_bg.wasm?module"
|
|
55
|
-
);
|
|
56
|
-
wasmModule = mod.default;
|
|
57
|
-
} else {
|
|
58
|
-
const { wasm } = await import("./wasm/arcjet.wasm.js");
|
|
59
|
-
wasmModule = await WebAssembly.compile(await wasm());
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
await initWasm(wasmModule);
|
|
63
|
-
state = "initialized";
|
|
64
|
-
} catch (err) {
|
|
65
|
-
state = "errored";
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return {
|
|
71
|
-
detectBot: detect_bot,
|
|
72
|
-
fingerprint: generate_fingerprint,
|
|
73
|
-
isValidEmail: is_valid_email,
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Generate a fingerprint for the client. This is used to identify the client
|
|
79
|
-
* across multiple requests.
|
|
80
|
-
* @param ip - The IP address of the client.
|
|
81
|
-
* @returns A SHA-256 string fingerprint.
|
|
82
|
-
*/
|
|
83
|
-
export async function generateFingerprint(ip: string): Promise<string> {
|
|
84
|
-
if (ip == "") {
|
|
85
|
-
return "";
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// We use `NEXT_RUNTIME` env var to DCE the JS fallback code in the `else` block
|
|
89
|
-
// possible values: "edge" | "nodejs" | undefined
|
|
90
|
-
if (process.env["NEXT_RUNTIME"] === "edge") {
|
|
91
|
-
const analyze = await init();
|
|
92
|
-
// We HAVE to have the WasmAPI in Edge
|
|
93
|
-
const fingerprint = analyze!.fingerprint(ip);
|
|
94
|
-
return fingerprint;
|
|
95
|
-
} else {
|
|
96
|
-
const analyze = await init();
|
|
97
|
-
if (typeof analyze !== "undefined") {
|
|
98
|
-
const fingerprint = analyze.fingerprint(ip);
|
|
99
|
-
return fingerprint;
|
|
100
|
-
} else {
|
|
101
|
-
// Conditional import because it's not available in some runtimes, we know
|
|
102
|
-
// it is when running on Vercel serverless functions.
|
|
103
|
-
// TODO(#180): Avoid nodejs-specific import
|
|
104
|
-
const createHash = await import("crypto");
|
|
105
|
-
|
|
106
|
-
// Fingerprint v1 is just the IP address
|
|
107
|
-
const fingerprintRaw = `fp_1_${ip}`;
|
|
108
|
-
|
|
109
|
-
const fingerprint = createHash
|
|
110
|
-
.createHash("sha256")
|
|
111
|
-
.update(fingerprintRaw)
|
|
112
|
-
.digest("hex");
|
|
113
|
-
return fingerprint;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export async function isValidEmail(
|
|
119
|
-
candidate: string,
|
|
120
|
-
options?: EmailValidationConfig,
|
|
121
|
-
) {
|
|
122
|
-
const analyze = await init();
|
|
123
|
-
|
|
124
|
-
if (typeof analyze !== "undefined") {
|
|
125
|
-
return analyze.isValidEmail(candidate, options);
|
|
126
|
-
} else {
|
|
127
|
-
// TODO: Fallback to JS if we don't have WASM?
|
|
128
|
-
return true;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Represents the result of the bot detection.
|
|
134
|
-
*
|
|
135
|
-
* @property `bot_type` - What type of bot this is. This will be one of `BotType`.
|
|
136
|
-
* @property `bot_score` - A score ranging from 0 to 99 representing the degree of
|
|
137
|
-
* certainty. The higher the number within the type category, the greater the
|
|
138
|
-
* degree of certainty. E.g. `BotType.Automated` with a score of 1 means we are
|
|
139
|
-
* sure the request was made by an automated bot. `BotType.LikelyNotABot` with a
|
|
140
|
-
* score of 30 means we don't think this request was a bot, but it's lowest
|
|
141
|
-
* confidence level. `BotType.LikelyNotABot` with a score of 99 means we are
|
|
142
|
-
* almost certain this request was not a bot.
|
|
143
|
-
*/
|
|
144
|
-
export interface BotResult {
|
|
145
|
-
bot_type: number;
|
|
146
|
-
bot_score: number;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export async function detectBot(
|
|
150
|
-
headers: string,
|
|
151
|
-
patterns_add: string,
|
|
152
|
-
patterns_remove: string,
|
|
153
|
-
): Promise<BotResult> {
|
|
154
|
-
const analyze = await init();
|
|
155
|
-
|
|
156
|
-
if (typeof analyze !== "undefined") {
|
|
157
|
-
return analyze.detectBot(headers, patterns_add, patterns_remove);
|
|
158
|
-
} else {
|
|
159
|
-
// TODO: Fallback to JS if we don't have WASM?
|
|
160
|
-
return {
|
|
161
|
-
bot_type: 1, // NOT_ANALYZED
|
|
162
|
-
bot_score: 0,
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
}
|
package/wasm/arcjet.wasm.d.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
// @generated by wasm2module - DO NOT EDIT
|
|
2
|
-
/* tslint:disable */
|
|
3
|
-
/* eslint-disable */
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* This file contains the Arcjet Wasm binary inlined as a base64
|
|
7
|
-
* [Data URL](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs)
|
|
8
|
-
* with the application/wasm MIME type.
|
|
9
|
-
*
|
|
10
|
-
* This was chosen to save on storage space over inlining the file directly as
|
|
11
|
-
* a Uint8Array, which would take up ~3x the space of the Wasm file. See
|
|
12
|
-
* https://blobfolio.com/2019/better-binary-batter-mixing-base64-and-uint8array/
|
|
13
|
-
* for more details.
|
|
14
|
-
*
|
|
15
|
-
* It is then decoded into an ArrayBuffer to be used directly via WebAssembly's
|
|
16
|
-
* `compile()` function in our entry point file.
|
|
17
|
-
*
|
|
18
|
-
* This is all done to avoid trying to read or bundle the Wasm asset in various
|
|
19
|
-
* ways based on the platform or bundler a user is targeting. One example being
|
|
20
|
-
* that Next.js requires special `asyncWebAssembly` webpack config to load our
|
|
21
|
-
* Wasm file if we don't do this.
|
|
22
|
-
*
|
|
23
|
-
* In the future, we hope to do away with this workaround when all bundlers
|
|
24
|
-
* properly support consistent asset bundling techniques.
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Returns an ArrayBuffer for the Arcjet Wasm binary, decoded from a base64 Data
|
|
29
|
-
* URL.
|
|
30
|
-
*/
|
|
31
|
-
export function wasm(): Promise<ArrayBuffer>;
|