Repository navigation
Expand file tree
/
Copy pathdeep.js
More file actions
104 lines (91 loc) · 2.94 KB
/
Copy pathdeep.js
File metadata and controls
104 lines (91 loc) · 2.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import { restoreText } from "./restore.js"
import { redactText } from "./engine.js"
function isPlainObject(value) {
if (!value || typeof value !== "object") return false
if (Array.isArray(value)) return false
const proto = Object.getPrototypeOf(value)
return proto === Object.prototype || proto === null
}
/**
* Deep-walk a tool argument object and restore every placeholder string in place.
* - Only walks Array / PlainObject
* - Uses a WeakSet to avoid infinite recursion on cycles
* @returns {number} number of restored strings
* @param {unknown} value
* @param {{ prefix: string, lookup(ph: string): string | undefined }} session
*/
export function restoreDeep(value, session) {
const seen = new WeakSet()
let changed = 0
const walk = (node) => {
if (!node || typeof node !== "object") return
if (seen.has(node)) return
seen.add(node)
if (Array.isArray(node)) {
for (let i = 0; i < node.length; i++) {
const v = node[i]
if (typeof v === "string") {
const after = restoreText(v, session)
if (after !== v) changed++
node[i] = after
}
if (v && typeof v === "object") walk(v)
}
return
}
if (!isPlainObject(node)) return
for (const key of Object.keys(node)) {
const v = node[key]
if (typeof v === "string") {
const after = restoreText(v, session)
if (after !== v) changed++
node[key] = after
}
if (v && typeof v === "object") walk(v)
}
}
walk(value)
return changed
}
/**
* Deep-walk an object and replace every sensitive string with a placeholder in place.
* - Only walks Array / PlainObject
* - Uses a WeakSet to avoid infinite recursion on cycles
* @returns {number} number of redacted strings
* @param {unknown} value
* @param {{ keywords: Array<{value:string,category:string}>, regex: Array<{pattern:string,flags:string,category:string}>, exclude: Set<string> }} patterns
* @param {{ getOrCreatePlaceholder(original: string, category: string): string }} session
*/
export function redactDeep(value, patterns, session) {
const seen = new WeakSet()
let changed = 0
const walk = (node) => {
if (!node || typeof node !== "object") return
if (seen.has(node)) return
seen.add(node)
if (Array.isArray(node)) {
for (let i = 0; i < node.length; i++) {
const v = node[i]
if (typeof v === "string") {
const after = redactText(v, patterns, session).text
if (after !== v) changed++
node[i] = after
}
if (v && typeof v === "object") walk(v)
}
return
}
if (!isPlainObject(node)) return
for (const key of Object.keys(node)) {
const v = node[key]
if (typeof v === "string") {
const after = redactText(v, patterns, session).text
if (after !== v) changed++
node[key] = after
}
if (v && typeof v === "object") walk(v)
}
}
walk(value)
return changed
}