@arcjet/analyze 1.0.0-beta.9 → 1.0.0
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 +48 -24
- package/index.d.ts +75 -9
- package/index.js +93 -14
- package/package.json +12 -13
package/README.md
CHANGED
|
@@ -25,43 +25,67 @@ This is the [Arcjet][arcjet] local analysis engine.
|
|
|
25
25
|
- [npm package (`@arcjet/analyze`)](https://www.npmjs.com/package/@arcjet/analyze)
|
|
26
26
|
- [GitHub source code (`analyze/` in `arcjet/arcjet-js`)](https://github.com/arcjet/arcjet-js/tree/main/analyze)
|
|
27
27
|
|
|
28
|
-
##
|
|
28
|
+
## What is this?
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
This package provides functionality to analyze requests.
|
|
31
|
+
The work is done in WebAssembly but is called here from JavaScript.
|
|
32
|
+
The functionality is wrapped up into rules in our core package
|
|
33
|
+
([`arcjet`][github-arcjet-arcjet]),
|
|
34
|
+
in turn exposed from our [SDKs][github-arcjet-sdks] (such as `@arcjet/next`).
|
|
33
35
|
|
|
34
|
-
|
|
36
|
+
The WebAssembly files are in
|
|
37
|
+
[`@arcjet/analyze-wasm`][github-arcjet-analyze-wasm].
|
|
38
|
+
They are separate because we need to change the import structure for each
|
|
39
|
+
runtime that we support in the bindings.
|
|
40
|
+
Separate packages lets us not duplicate code while providing a combined
|
|
41
|
+
higher-level API for calling our core functionality.
|
|
35
42
|
|
|
36
|
-
|
|
37
|
-
TODO(@wooorm-arcjet): I think this example is out of date?
|
|
38
|
-
Either remove it if we don’t want people to use this.
|
|
39
|
-
Or, change the API to allow simpler use?
|
|
40
|
-
-->
|
|
43
|
+
## When should I use this?
|
|
41
44
|
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
This is an internal Arcjet package not designed for public use.
|
|
46
|
+
See our [_Get started_ guide][arcjet-get-started] for how to use Arcjet in your
|
|
47
|
+
application.
|
|
48
|
+
|
|
49
|
+
## Install
|
|
44
50
|
|
|
45
|
-
|
|
46
|
-
|
|
51
|
+
This package is ESM only.
|
|
52
|
+
Install with npm in Node.js:
|
|
47
53
|
|
|
48
|
-
|
|
49
|
-
|
|
54
|
+
```sh
|
|
55
|
+
npm install @arcjet/analyze
|
|
50
56
|
```
|
|
51
57
|
|
|
52
|
-
##
|
|
58
|
+
## Use
|
|
53
59
|
|
|
54
|
-
|
|
55
|
-
|
|
60
|
+
```js
|
|
61
|
+
import { generateFingerprint, isValidEmail } from "@arcjet/analyze";
|
|
56
62
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
const fingerprint = await generateFingerprint(
|
|
64
|
+
{ characteristics: [] },
|
|
65
|
+
{ ip: "127.0.0.1" },
|
|
66
|
+
);
|
|
67
|
+
console.log(fingerprint);
|
|
68
|
+
// => "fp::2::0d219da6100b99f95cf639b77e088c6df3c096aa5fd61dec5287c5cf94d5e545"
|
|
69
|
+
|
|
70
|
+
const result = await isValidEmail({}, "hello@example.com", {
|
|
71
|
+
tag: "allow-email-validation-config",
|
|
72
|
+
val: {
|
|
73
|
+
allowDomainLiteral: false,
|
|
74
|
+
allow: [],
|
|
75
|
+
requireTopLevelDomain: true,
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
console.log(result);
|
|
79
|
+
// => { blocked: [], validity: "valid" }
|
|
80
|
+
```
|
|
61
81
|
|
|
62
82
|
## License
|
|
63
83
|
|
|
64
|
-
|
|
84
|
+
[Apache License, Version 2.0][apache-license] © [Arcjet Labs, Inc.][arcjet]
|
|
65
85
|
|
|
66
86
|
[arcjet]: https://arcjet.com
|
|
87
|
+
[arcjet-get-started]: https://docs.arcjet.com/get-started
|
|
67
88
|
[apache-license]: http://www.apache.org/licenses/LICENSE-2.0
|
|
89
|
+
[github-arcjet-analyze-wasm]: https://github.com/arcjet/arcjet-js/tree/main/analyze-wasm
|
|
90
|
+
[github-arcjet-arcjet]: https://github.com/arcjet/arcjet-js/tree/main/arcjet
|
|
91
|
+
[github-arcjet-sdks]: https://github.com/arcjet/arcjet-js#sdks
|
package/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BotConfig, BotResult, DetectedSensitiveInfoEntity, DetectSensitiveInfoFunction, EmailValidationConfig, EmailValidationResult, SensitiveInfoEntities, SensitiveInfoEntity, SensitiveInfoResult } from "@arcjet/analyze-wasm";
|
|
1
|
+
import type { BotConfig, BotResult, DetectedSensitiveInfoEntity, DetectSensitiveInfoFunction, EmailValidationConfig, EmailValidationResult, FilterResult, SensitiveInfoEntities, SensitiveInfoEntity, SensitiveInfoResult } from "@arcjet/analyze-wasm";
|
|
2
2
|
import type { ArcjetLogger } from "@arcjet/protocol";
|
|
3
3
|
interface AnalyzeContext {
|
|
4
4
|
log: ArcjetLogger;
|
|
@@ -15,15 +15,81 @@ type AnalyzeRequest = {
|
|
|
15
15
|
query?: string;
|
|
16
16
|
extra?: Record<string, string>;
|
|
17
17
|
};
|
|
18
|
-
export { type EmailValidationConfig, type BotConfig, type SensitiveInfoEntity, type DetectedSensitiveInfoEntity, };
|
|
18
|
+
export { type EmailValidationConfig, type BotConfig, type FilterResult, type SensitiveInfoEntity, type DetectedSensitiveInfoEntity, };
|
|
19
19
|
/**
|
|
20
|
-
* Generate a fingerprint
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
20
|
+
* Generate a fingerprint.
|
|
21
|
+
*
|
|
22
|
+
* Fingerprints can be used to identify the client across multiple requests.
|
|
23
|
+
*
|
|
24
|
+
* This considers different things on the `request` based on the passed
|
|
25
|
+
* `context.characteristics`.
|
|
26
|
+
*
|
|
27
|
+
* See [*Fingerprints* on
|
|
28
|
+
* `docs.arcjet.com`](https://docs.arcjet.com/fingerprints/) for more info.
|
|
29
|
+
*
|
|
30
|
+
* @param context
|
|
31
|
+
* Context.
|
|
32
|
+
* @param request
|
|
33
|
+
* Request.
|
|
34
|
+
* @returns
|
|
35
|
+
* Promise for a SHA-256 fingerprint.
|
|
25
36
|
*/
|
|
26
37
|
export declare function generateFingerprint(context: AnalyzeContext, request: AnalyzeRequest): Promise<string>;
|
|
27
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Check whether an email is valid.
|
|
40
|
+
*
|
|
41
|
+
* @param context
|
|
42
|
+
* Context.
|
|
43
|
+
* @param value
|
|
44
|
+
* Value.
|
|
45
|
+
* @param options
|
|
46
|
+
* Configuration.
|
|
47
|
+
* @returns
|
|
48
|
+
* Promise for a result.
|
|
49
|
+
*/
|
|
50
|
+
export declare function isValidEmail(context: AnalyzeContext, value: string, options: EmailValidationConfig): Promise<EmailValidationResult>;
|
|
51
|
+
/**
|
|
52
|
+
* Detect whether a request is by a bot.
|
|
53
|
+
*
|
|
54
|
+
* @param context
|
|
55
|
+
* Context.
|
|
56
|
+
* @param request
|
|
57
|
+
* Request.
|
|
58
|
+
* @param options
|
|
59
|
+
* Configuration.
|
|
60
|
+
* @returns
|
|
61
|
+
* Promise for a result.
|
|
62
|
+
*/
|
|
28
63
|
export declare function detectBot(context: AnalyzeContext, request: AnalyzeRequest, options: BotConfig): Promise<BotResult>;
|
|
29
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Detect sensitive info in a value.
|
|
66
|
+
*
|
|
67
|
+
* @param context
|
|
68
|
+
* Context.
|
|
69
|
+
* @param value
|
|
70
|
+
* Value.
|
|
71
|
+
* @param entities
|
|
72
|
+
* Strategy to use for detecting sensitive info;
|
|
73
|
+
* either by denying everything and allowing certain tags or by allowing
|
|
74
|
+
* everything and denying certain tags.
|
|
75
|
+
* @param contextWindowSize
|
|
76
|
+
* Number of tokens to pass to `detect`.
|
|
77
|
+
* @param detect
|
|
78
|
+
* Function to detect sensitive info (optional).
|
|
79
|
+
* @returns
|
|
80
|
+
* Promise for a result.
|
|
81
|
+
*/
|
|
82
|
+
export declare function detectSensitiveInfo(context: AnalyzeContext, value: string, entities: SensitiveInfoEntities, contextWindowSize: number, detect?: DetectSensitiveInfoFunction): Promise<SensitiveInfoResult>;
|
|
83
|
+
/**
|
|
84
|
+
* Check if a filter matches a request.
|
|
85
|
+
*
|
|
86
|
+
* @param context
|
|
87
|
+
* Arcjet context.
|
|
88
|
+
* @param request
|
|
89
|
+
* Request.
|
|
90
|
+
* @param expressions
|
|
91
|
+
* Filter expressions.
|
|
92
|
+
* @returns
|
|
93
|
+
* Promise to whether the filter matches the request.
|
|
94
|
+
*/
|
|
95
|
+
export declare function matchFilters(context: AnalyzeContext, request: AnalyzeRequest, expressions: ReadonlyArray<string>, allowIfMatch: boolean): Promise<FilterResult>;
|
package/index.js
CHANGED
|
@@ -38,6 +38,11 @@ function createCoreImports(detect) {
|
|
|
38
38
|
return "unknown";
|
|
39
39
|
},
|
|
40
40
|
},
|
|
41
|
+
"arcjet:js-req/filter-overrides": {
|
|
42
|
+
ipLookup() {
|
|
43
|
+
return undefined;
|
|
44
|
+
},
|
|
45
|
+
},
|
|
41
46
|
// TODO(@wooorm-arcjet): figure out a test case for this with the default `detect`.
|
|
42
47
|
"arcjet:js-req/sensitive-information-identifier": {
|
|
43
48
|
detect,
|
|
@@ -50,13 +55,23 @@ function createCoreImports(detect) {
|
|
|
50
55
|
},
|
|
51
56
|
};
|
|
52
57
|
}
|
|
53
|
-
// TODO(@wooorm-arcjet): document what is used to fingerprint.
|
|
54
58
|
/**
|
|
55
|
-
* Generate a fingerprint
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
59
|
+
* Generate a fingerprint.
|
|
60
|
+
*
|
|
61
|
+
* Fingerprints can be used to identify the client across multiple requests.
|
|
62
|
+
*
|
|
63
|
+
* This considers different things on the `request` based on the passed
|
|
64
|
+
* `context.characteristics`.
|
|
65
|
+
*
|
|
66
|
+
* See [*Fingerprints* on
|
|
67
|
+
* `docs.arcjet.com`](https://docs.arcjet.com/fingerprints/) for more info.
|
|
68
|
+
*
|
|
69
|
+
* @param context
|
|
70
|
+
* Context.
|
|
71
|
+
* @param request
|
|
72
|
+
* Request.
|
|
73
|
+
* @returns
|
|
74
|
+
* Promise for a SHA-256 fingerprint.
|
|
60
75
|
*/
|
|
61
76
|
async function generateFingerprint(context, request) {
|
|
62
77
|
const { log } = context;
|
|
@@ -70,20 +85,42 @@ async function generateFingerprint(context, request) {
|
|
|
70
85
|
log.debug("WebAssembly is not supported in this runtime");
|
|
71
86
|
return "";
|
|
72
87
|
}
|
|
73
|
-
|
|
74
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Check whether an email is valid.
|
|
90
|
+
*
|
|
91
|
+
* @param context
|
|
92
|
+
* Context.
|
|
93
|
+
* @param value
|
|
94
|
+
* Value.
|
|
95
|
+
* @param options
|
|
96
|
+
* Configuration.
|
|
97
|
+
* @returns
|
|
98
|
+
* Promise for a result.
|
|
99
|
+
*/
|
|
100
|
+
async function isValidEmail(context, value, options) {
|
|
75
101
|
const { log } = context;
|
|
76
102
|
const coreImports = createCoreImports();
|
|
77
103
|
const analyze = await initializeWasm(coreImports);
|
|
78
104
|
if (typeof analyze !== "undefined") {
|
|
79
|
-
return analyze.isValidEmail(
|
|
105
|
+
return analyze.isValidEmail(value, options);
|
|
80
106
|
// Ignore the `else` branch as we test in places that have WebAssembly.
|
|
81
107
|
/* node:coverage ignore next 4 */
|
|
82
108
|
}
|
|
83
109
|
log.debug("WebAssembly is not supported in this runtime");
|
|
84
110
|
return { blocked: [], validity: "valid" };
|
|
85
111
|
}
|
|
86
|
-
|
|
112
|
+
/**
|
|
113
|
+
* Detect whether a request is by a bot.
|
|
114
|
+
*
|
|
115
|
+
* @param context
|
|
116
|
+
* Context.
|
|
117
|
+
* @param request
|
|
118
|
+
* Request.
|
|
119
|
+
* @param options
|
|
120
|
+
* Configuration.
|
|
121
|
+
* @returns
|
|
122
|
+
* Promise for a result.
|
|
123
|
+
*/
|
|
87
124
|
async function detectBot(context, request, options) {
|
|
88
125
|
const { log } = context;
|
|
89
126
|
const coreImports = createCoreImports();
|
|
@@ -96,14 +133,31 @@ async function detectBot(context, request, options) {
|
|
|
96
133
|
log.debug("WebAssembly is not supported in this runtime");
|
|
97
134
|
return { allowed: [], denied: [], spoofed: false, verified: false };
|
|
98
135
|
}
|
|
99
|
-
|
|
100
|
-
|
|
136
|
+
/**
|
|
137
|
+
* Detect sensitive info in a value.
|
|
138
|
+
*
|
|
139
|
+
* @param context
|
|
140
|
+
* Context.
|
|
141
|
+
* @param value
|
|
142
|
+
* Value.
|
|
143
|
+
* @param entities
|
|
144
|
+
* Strategy to use for detecting sensitive info;
|
|
145
|
+
* either by denying everything and allowing certain tags or by allowing
|
|
146
|
+
* everything and denying certain tags.
|
|
147
|
+
* @param contextWindowSize
|
|
148
|
+
* Number of tokens to pass to `detect`.
|
|
149
|
+
* @param detect
|
|
150
|
+
* Function to detect sensitive info (optional).
|
|
151
|
+
* @returns
|
|
152
|
+
* Promise for a result.
|
|
153
|
+
*/
|
|
154
|
+
async function detectSensitiveInfo(context, value, entities, contextWindowSize, detect) {
|
|
101
155
|
const { log } = context;
|
|
102
156
|
const coreImports = createCoreImports(detect);
|
|
103
157
|
const analyze = await initializeWasm(coreImports);
|
|
104
158
|
if (typeof analyze !== "undefined") {
|
|
105
159
|
const skipCustomDetect = typeof detect !== "function";
|
|
106
|
-
return analyze.detectSensitiveInfo(
|
|
160
|
+
return analyze.detectSensitiveInfo(value, {
|
|
107
161
|
entities,
|
|
108
162
|
contextWindowSize,
|
|
109
163
|
skipCustomDetect,
|
|
@@ -114,5 +168,30 @@ async function detectSensitiveInfo(context, candidate, entities, contextWindowSi
|
|
|
114
168
|
log.debug("WebAssembly is not supported in this runtime");
|
|
115
169
|
throw new Error("SENSITIVE_INFO rule failed to run because Wasm is not supported in this environment.");
|
|
116
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Check if a filter matches a request.
|
|
173
|
+
*
|
|
174
|
+
* @param context
|
|
175
|
+
* Arcjet context.
|
|
176
|
+
* @param request
|
|
177
|
+
* Request.
|
|
178
|
+
* @param expressions
|
|
179
|
+
* Filter expressions.
|
|
180
|
+
* @returns
|
|
181
|
+
* Promise to whether the filter matches the request.
|
|
182
|
+
*/
|
|
183
|
+
async function matchFilters(context, request, expressions, allowIfMatch) {
|
|
184
|
+
const coreImports = createCoreImports();
|
|
185
|
+
const analyze = await initializeWasm(coreImports);
|
|
186
|
+
if (typeof analyze !== "undefined") {
|
|
187
|
+
return analyze.matchFilters(JSON.stringify(request),
|
|
188
|
+
// @ts-expect-error: WebAssembly does not support readonly values.
|
|
189
|
+
expressions, allowIfMatch);
|
|
190
|
+
// Ignore the `else` branch as we test in places that have WebAssembly.
|
|
191
|
+
/* node:coverage ignore next 4 */
|
|
192
|
+
}
|
|
193
|
+
context.log.debug("WebAssembly is not supported in this runtime");
|
|
194
|
+
throw new Error("FILTER rule failed to run because Wasm is not supported in this environment.");
|
|
195
|
+
}
|
|
117
196
|
|
|
118
|
-
export { detectBot, detectSensitiveInfo, generateFingerprint, isValidEmail };
|
|
197
|
+
export { detectBot, detectSensitiveInfo, generateFingerprint, isValidEmail, matchFilters };
|
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",
|
|
4
4
|
"description": "Arcjet local analysis engine",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"analyze",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"url": "https://arcjet.com"
|
|
28
28
|
},
|
|
29
29
|
"engines": {
|
|
30
|
-
"node": ">=
|
|
30
|
+
"node": ">=20"
|
|
31
31
|
},
|
|
32
32
|
"type": "module",
|
|
33
33
|
"main": "./index.js",
|
|
@@ -40,23 +40,22 @@
|
|
|
40
40
|
"build": "rollup --config rollup.config.js",
|
|
41
41
|
"lint": "eslint .",
|
|
42
42
|
"prepublishOnly": "npm run build",
|
|
43
|
-
"test-api": "node --test",
|
|
44
|
-
"test-coverage": "node --experimental-test-coverage --test",
|
|
43
|
+
"test-api": "node --test -- test/*.test.js",
|
|
44
|
+
"test-coverage": "node --experimental-test-coverage --test -- test/*.test.js",
|
|
45
45
|
"test": "npm run build && npm run lint && npm run test-coverage"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@arcjet/analyze-wasm": "1.0.0
|
|
49
|
-
"@arcjet/protocol": "1.0.0
|
|
48
|
+
"@arcjet/analyze-wasm": "1.0.0",
|
|
49
|
+
"@arcjet/protocol": "1.0.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@arcjet/eslint-config": "1.0.0
|
|
53
|
-
"@arcjet/rollup-config": "1.0.0
|
|
54
|
-
"@arcjet/tsconfig": "1.0.0-beta.9",
|
|
52
|
+
"@arcjet/eslint-config": "1.0.0",
|
|
53
|
+
"@arcjet/rollup-config": "1.0.0",
|
|
55
54
|
"@bytecodealliance/jco": "1.5.0",
|
|
56
|
-
"@rollup/wasm-node": "4.
|
|
57
|
-
"@types/node": "
|
|
58
|
-
"eslint": "9.
|
|
59
|
-
"typescript": "5.
|
|
55
|
+
"@rollup/wasm-node": "4.55.1",
|
|
56
|
+
"@types/node": "25.0.8",
|
|
57
|
+
"eslint": "9.39.2",
|
|
58
|
+
"typescript": "5.9.3"
|
|
60
59
|
},
|
|
61
60
|
"publishConfig": {
|
|
62
61
|
"access": "public",
|