@arcjet/redact 1.0.0-beta.9 → 1.1.0-rc
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 +37 -12
- package/index.d.ts +64 -8
- package/index.js +64 -40
- package/package.json +11 -13
package/README.md
CHANGED
|
@@ -26,34 +26,59 @@ redaction library.
|
|
|
26
26
|
- [npm package (`@arcjet/redact`)](https://www.npmjs.com/package/@arcjet/redact)
|
|
27
27
|
- [GitHub source code (`redact/` in `arcjet/arcjet-js`)](https://github.com/arcjet/arcjet-js/tree/main/redact)
|
|
28
28
|
|
|
29
|
-
##
|
|
29
|
+
## What is this?
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
This package provides functionality to redact sensitive info.
|
|
32
|
+
The work is done in WebAssembly but is called here from JavaScript.
|
|
33
|
+
|
|
34
|
+
The WebAssembly files are in [`@arcjet/redact-wasm`][github-arcjet-redact-wasm].
|
|
35
|
+
For more info on why we maintain our WebAssembly and JavaScript projects like
|
|
36
|
+
this,
|
|
37
|
+
see [“What is this?” in the readme of
|
|
38
|
+
`@arcjet/analyze-wasm`][github-arcjet-analyze-wasm-what].
|
|
39
|
+
|
|
40
|
+
## When should I use this?
|
|
41
|
+
|
|
42
|
+
You can use this package to redact sensitive information locally.
|
|
43
|
+
You can redact email addresses, credit card numbers, and more.
|
|
44
|
+
It is also possible to reverse the process: to un-redact what was found.
|
|
45
|
+
|
|
46
|
+
## Install
|
|
47
|
+
|
|
48
|
+
This package is ESM only.
|
|
49
|
+
Install with npm in Node.js:
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
npm install @arcjet/redact
|
|
33
53
|
```
|
|
34
54
|
|
|
35
55
|
## Reference
|
|
36
56
|
|
|
37
57
|
The full reference documentation can be found in the [Arcjet docs][redact-ref].
|
|
38
58
|
|
|
39
|
-
##
|
|
59
|
+
## Use
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { redact } from "@arcjet/redact";
|
|
63
|
+
|
|
64
|
+
const value = "Hi, my name is John and my email adress is john@example.com";
|
|
40
65
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const [redacted, unredact] = await redact(text, {
|
|
44
|
-
redact: ["email", "phone-number"],
|
|
66
|
+
const [redacted, unredact] = await redact(value, {
|
|
67
|
+
entities: ["email", "phone-number"],
|
|
45
68
|
});
|
|
46
69
|
console.log(redacted);
|
|
47
|
-
// Hi, my name is John and my email
|
|
70
|
+
// => "Hi, my name is John and my email adress is <Redacted email #0>"
|
|
48
71
|
|
|
49
72
|
const unredacted = unredact("Your email address is <Redacted email #0>");
|
|
50
|
-
console.log(unredacted); // Your email address is john@example.com
|
|
73
|
+
console.log(unredacted); // "Your email address is john@example.com"
|
|
51
74
|
```
|
|
52
75
|
|
|
53
76
|
## License
|
|
54
77
|
|
|
55
|
-
|
|
78
|
+
[Apache License, Version 2.0][apache-license] © [Arcjet Labs, Inc.][arcjet]
|
|
56
79
|
|
|
80
|
+
[apache-license]: http://www.apache.org/licenses/LICENSE-2.0
|
|
57
81
|
[arcjet]: https://arcjet.com
|
|
58
82
|
[redact-ref]: https://docs.arcjet.com/redact/reference
|
|
59
|
-
[
|
|
83
|
+
[github-arcjet-analyze-wasm-what]: https://github.com/arcjet/arcjet-js/tree/main/analyze-wasm#what-is-this
|
|
84
|
+
[github-arcjet-redact-wasm]: https://github.com/arcjet/arcjet-js/tree/main/redact-wasm
|
package/index.d.ts
CHANGED
|
@@ -1,12 +1,68 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import type { SensitiveInfoEntity } from "@arcjet/redact-wasm";
|
|
2
|
+
/**
|
|
3
|
+
* Types of standard sensitive information that can be detected.
|
|
4
|
+
*/
|
|
5
|
+
export type ArcjetSensitiveInfoType = Exclude<SensitiveInfoEntity["tag"], "custom">;
|
|
6
|
+
/**
|
|
7
|
+
* Options for the `redact` function.
|
|
8
|
+
*
|
|
9
|
+
* @template DetectedEntities
|
|
10
|
+
* Custom entity names that are returned from `detect` and optionally listed in `entities`.
|
|
11
|
+
* @template ListedEntities
|
|
12
|
+
* Entity names that can be listed in the `entities` option.
|
|
13
|
+
*/
|
|
14
|
+
export type RedactOptions<DetectedEntities extends string | undefined = undefined, ListedEntities extends ArcjetSensitiveInfoType | Exclude<DetectedEntities, undefined> = ArcjetSensitiveInfoType | Exclude<DetectedEntities, undefined>> = {
|
|
15
|
+
/**
|
|
16
|
+
* Entities to redact.
|
|
17
|
+
*/
|
|
18
|
+
entities?: ReadonlyArray<ListedEntities | Exclude<DetectedEntities, undefined>>;
|
|
19
|
+
/**
|
|
20
|
+
* Size of tokens to consider.
|
|
21
|
+
*/
|
|
6
22
|
contextWindowSize?: number;
|
|
7
|
-
|
|
8
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Custom detection function to identify sensitive information.
|
|
25
|
+
*
|
|
26
|
+
* @template DetectedEntities
|
|
27
|
+
* Custom entity names that are returned from `detect` and optionally listed in `entities`.
|
|
28
|
+
* @template ListedEntities
|
|
29
|
+
* Entity names that can be listed in the `entities` option.
|
|
30
|
+
* @param tokens
|
|
31
|
+
* Tokens.
|
|
32
|
+
* @returns
|
|
33
|
+
* List of entities (or undefined).
|
|
34
|
+
*/
|
|
35
|
+
detect?: (tokens: string[]) => ReadonlyArray<DetectedEntities>;
|
|
36
|
+
/**
|
|
37
|
+
* Custom replace function to redact sensitive information.
|
|
38
|
+
*
|
|
39
|
+
* @template DetectedEntities
|
|
40
|
+
* Custom entity names that are returned from `detect` and optionally listed in `entities`.
|
|
41
|
+
* @template ListedEntities
|
|
42
|
+
* Entity names that can be listed in the `entities` option.
|
|
43
|
+
* @param entity
|
|
44
|
+
* Entity to redact.
|
|
45
|
+
* @param plaintext
|
|
46
|
+
* The plaintext string to redact.
|
|
47
|
+
* @returns
|
|
48
|
+
* Redacted string or nothing.
|
|
49
|
+
*/
|
|
50
|
+
replace?: (entity: ListedEntities, plaintext: string) => string | undefined;
|
|
9
51
|
};
|
|
10
52
|
type Unredact = (input: string) => string;
|
|
11
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Redact sensitive info.
|
|
55
|
+
*
|
|
56
|
+
* @template DetectedEntities
|
|
57
|
+
* Custom entity names that are returned from `detect` and optionally listed in `entities`.
|
|
58
|
+
* @template ListedEntities
|
|
59
|
+
* Entity names that can be listed in the `entities` option.
|
|
60
|
+
* @param candidate
|
|
61
|
+
* Value to redact.
|
|
62
|
+
* @param options
|
|
63
|
+
* Configuration.
|
|
64
|
+
* @returns
|
|
65
|
+
* Promise to a tuple with the redacted string and a function to unredact it.
|
|
66
|
+
*/
|
|
67
|
+
export declare function redact<const DetectedEntities extends string | undefined = undefined, const ListedEntities extends ArcjetSensitiveInfoType | Exclude<DetectedEntities, undefined> = ArcjetSensitiveInfoType | Exclude<DetectedEntities, undefined>>(candidate: string, options?: RedactOptions<DetectedEntities, ListedEntities>): Promise<[string, Unredact]>;
|
|
12
68
|
export {};
|
package/index.js
CHANGED
|
@@ -1,38 +1,43 @@
|
|
|
1
1
|
import { initializeWasm } from '@arcjet/redact-wasm';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Turn an entity name into a `SensitiveInfoEntity`.
|
|
5
|
+
*
|
|
6
|
+
* See also `protocolSensitiveInfoEntitiesToAnalyze` in `arcjet/index.ts`.
|
|
7
|
+
*
|
|
8
|
+
* @param entity
|
|
9
|
+
* Entity name.
|
|
10
|
+
* @returns
|
|
11
|
+
* Entity object.
|
|
12
|
+
*/
|
|
3
13
|
function userEntitiesToWasm(entity) {
|
|
4
14
|
if (typeof entity !== "string") {
|
|
5
15
|
throw new Error("redaction entities must be strings");
|
|
6
16
|
}
|
|
7
|
-
if (entity === "
|
|
8
|
-
|
|
17
|
+
if (entity === "credit-card-number" ||
|
|
18
|
+
entity === "email" ||
|
|
19
|
+
entity === "ip-address" ||
|
|
20
|
+
entity === "phone-number") {
|
|
21
|
+
return { tag: entity };
|
|
9
22
|
}
|
|
10
|
-
|
|
11
|
-
return { tag: "phone-number" };
|
|
12
|
-
}
|
|
13
|
-
if (entity === "ip-address") {
|
|
14
|
-
return { tag: "ip-address" };
|
|
15
|
-
}
|
|
16
|
-
if (entity === "credit-card-number") {
|
|
17
|
-
return { tag: "credit-card-number" };
|
|
18
|
-
}
|
|
19
|
-
return {
|
|
20
|
-
tag: "custom",
|
|
21
|
-
val: entity,
|
|
22
|
-
};
|
|
23
|
+
return { tag: "custom", val: entity };
|
|
23
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Turn a `SensitiveInfoEntity` object into its name.
|
|
27
|
+
*
|
|
28
|
+
* See also `analyzeSensitiveInfoEntitiesToString` in `arcjet/index.ts`.
|
|
29
|
+
*
|
|
30
|
+
* @param entity
|
|
31
|
+
* Entity object.
|
|
32
|
+
* @returns
|
|
33
|
+
* Entity name.
|
|
34
|
+
*/
|
|
24
35
|
function wasmEntitiesToString(entity) {
|
|
25
|
-
if (entity.tag === "
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return
|
|
30
|
-
}
|
|
31
|
-
if (entity.tag === "credit-card-number") {
|
|
32
|
-
return "credit-card-number";
|
|
33
|
-
}
|
|
34
|
-
if (entity.tag === "phone-number") {
|
|
35
|
-
return "phone-number";
|
|
36
|
+
if (entity.tag === "credit-card-number" ||
|
|
37
|
+
entity.tag === "email" ||
|
|
38
|
+
entity.tag === "ip-address" ||
|
|
39
|
+
entity.tag === "phone-number") {
|
|
40
|
+
return entity.tag;
|
|
36
41
|
}
|
|
37
42
|
return entity.val;
|
|
38
43
|
}
|
|
@@ -50,26 +55,31 @@ function noOpReplace(_input, _plaintext) {
|
|
|
50
55
|
}
|
|
51
56
|
function getWasmOptions(options) {
|
|
52
57
|
if (typeof options === "object" && options !== null) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
throw new Error("no entities configured for redaction");
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
58
|
+
const entities = options.entities;
|
|
59
|
+
if (entities !== undefined) {
|
|
60
|
+
if (!Array.isArray(entities)) {
|
|
60
61
|
throw new Error("entities must be an array");
|
|
61
62
|
}
|
|
63
|
+
if (entities.length < 1) {
|
|
64
|
+
throw new Error("no entities configured for redaction");
|
|
65
|
+
}
|
|
62
66
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
// `entities` is an optional field but not allowed to be `undefined`.
|
|
68
|
+
return entities
|
|
69
|
+
? {
|
|
70
|
+
entities: entities.map(userEntitiesToWasm),
|
|
71
|
+
contextWindowSize: options.contextWindowSize || 1,
|
|
72
|
+
skipCustomDetect: typeof options.detect !== "function",
|
|
73
|
+
skipCustomRedact: typeof options.replace !== "function",
|
|
74
|
+
}
|
|
75
|
+
: {
|
|
76
|
+
contextWindowSize: options.contextWindowSize || 1,
|
|
77
|
+
skipCustomDetect: typeof options.detect !== "function",
|
|
78
|
+
skipCustomRedact: typeof options.replace !== "function",
|
|
79
|
+
};
|
|
69
80
|
}
|
|
70
81
|
else {
|
|
71
82
|
return {
|
|
72
|
-
entities: undefined,
|
|
73
83
|
contextWindowSize: 1,
|
|
74
84
|
skipCustomDetect: true,
|
|
75
85
|
skipCustomRedact: true,
|
|
@@ -109,6 +119,20 @@ async function callRedactWasm(candidate, options) {
|
|
|
109
119
|
throw new Error("redact failed to run because Wasm is not supported in this environment");
|
|
110
120
|
}
|
|
111
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* Redact sensitive info.
|
|
124
|
+
*
|
|
125
|
+
* @template DetectedEntities
|
|
126
|
+
* Custom entity names that are returned from `detect` and optionally listed in `entities`.
|
|
127
|
+
* @template ListedEntities
|
|
128
|
+
* Entity names that can be listed in the `entities` option.
|
|
129
|
+
* @param candidate
|
|
130
|
+
* Value to redact.
|
|
131
|
+
* @param options
|
|
132
|
+
* Configuration.
|
|
133
|
+
* @returns
|
|
134
|
+
* Promise to a tuple with the redacted string and a function to unredact it.
|
|
135
|
+
*/
|
|
112
136
|
async function redact(candidate, options) {
|
|
113
137
|
const redactions = await callRedactWasm(candidate, options);
|
|
114
138
|
// Need to apply the redactions in reverse order so that the offsets aren't changed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcjet/redact",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0-rc",
|
|
4
4
|
"description": "Arcjet sensitive information redaction library",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"arcjet",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"url": "https://arcjet.com"
|
|
26
26
|
},
|
|
27
27
|
"engines": {
|
|
28
|
-
"node": ">=
|
|
28
|
+
"node": ">=20"
|
|
29
29
|
},
|
|
30
30
|
"type": "module",
|
|
31
31
|
"main": "./index.js",
|
|
@@ -37,22 +37,20 @@
|
|
|
37
37
|
"scripts": {
|
|
38
38
|
"build": "rollup --config rollup.config.js",
|
|
39
39
|
"lint": "eslint .",
|
|
40
|
-
"
|
|
41
|
-
"test-
|
|
42
|
-
"test-coverage": "node --experimental-test-coverage --test",
|
|
40
|
+
"test-api": "node --test -- test/*.test.js",
|
|
41
|
+
"test-coverage": "node --experimental-test-coverage --test -- test/*.test.js",
|
|
43
42
|
"test": "npm run build && npm run lint && npm run test-coverage"
|
|
44
43
|
},
|
|
45
44
|
"dependencies": {
|
|
46
|
-
"@arcjet/redact-wasm": "1.
|
|
45
|
+
"@arcjet/redact-wasm": "1.1.0-rc"
|
|
47
46
|
},
|
|
48
47
|
"devDependencies": {
|
|
49
|
-
"@arcjet/eslint-config": "1.
|
|
50
|
-
"@arcjet/rollup-config": "1.
|
|
51
|
-
"@
|
|
52
|
-
"@
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"typescript": "5.8.3"
|
|
48
|
+
"@arcjet/eslint-config": "1.1.0-rc",
|
|
49
|
+
"@arcjet/rollup-config": "1.1.0-rc",
|
|
50
|
+
"@rollup/wasm-node": "4.57.0",
|
|
51
|
+
"@types/node": "25.0.10",
|
|
52
|
+
"eslint": "9.39.2",
|
|
53
|
+
"typescript": "5.9.3"
|
|
56
54
|
},
|
|
57
55
|
"publishConfig": {
|
|
58
56
|
"access": "public",
|