n8n-nodes-redactor 5.1.0 → 5.2.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.
|
@@ -767,7 +767,7 @@ class Redactor {
|
|
|
767
767
|
};
|
|
768
768
|
}
|
|
769
769
|
async execute() {
|
|
770
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
770
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
771
771
|
const items = this.getInputData();
|
|
772
772
|
const allPaired = items.map((_, i) => ({ item: i }));
|
|
773
773
|
const operation = this.getNodeParameter('operation', 0);
|
|
@@ -1041,6 +1041,7 @@ class Redactor {
|
|
|
1041
1041
|
keyOrigins: new WeakMap(),
|
|
1042
1042
|
inputText: (0, engine_1.collectInputText)(items.map((it) => it.json)),
|
|
1043
1043
|
lockedSpans: new WeakMap(),
|
|
1044
|
+
priorTokens: (0, engine_1.priorTokenMatcher)((_e = vault.getSession(sessionId)) === null || _e === void 0 ? void 0 : _e.entries),
|
|
1044
1045
|
};
|
|
1045
1046
|
const allHits = [];
|
|
1046
1047
|
const returnData = [];
|
|
@@ -1091,7 +1092,7 @@ class Redactor {
|
|
|
1091
1092
|
if (includeReport && this.getNodeParameter('includeRopa', 0, false)) {
|
|
1092
1093
|
const d = this.getNodeParameter('ropa', 0, {});
|
|
1093
1094
|
const list = (v) => String(v !== null && v !== void 0 ? v : '').split(',').map((x) => x.trim()).filter(Boolean);
|
|
1094
|
-
report.ropa = (0, ropa_1.generateRopaRecord)(allHits, (0, classification_1.classifyData)(allHits), items.length, enabledPatterns.length, mode, sessionTtl, !!vaultPassphrase, auditLog, { name: String((
|
|
1095
|
+
report.ropa = (0, ropa_1.generateRopaRecord)(allHits, (0, classification_1.classifyData)(allHits), items.length, enabledPatterns.length, mode, sessionTtl, !!vaultPassphrase, auditLog, { name: String((_f = d.controllerName) !== null && _f !== void 0 ? _f : ''), contactEmail: String((_g = d.controllerContact) !== null && _g !== void 0 ? _g : ''), dpo: d.dpo ? String(d.dpo) : undefined }, list(d.purposes), String((_h = d.legalBasis) !== null && _h !== void 0 ? _h : 'legitimate_interest'), list(d.dataSubjects), list(d.recipients), []);
|
|
1095
1096
|
}
|
|
1096
1097
|
return [returnData, [{ json: report, pairedItem: allPaired }]];
|
|
1097
1098
|
}
|
|
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.collectTokenLike = collectTokenLike;
|
|
37
37
|
exports.collectInputText = collectInputText;
|
|
38
38
|
exports.redactValue = redactValue;
|
|
39
|
+
exports.priorTokenMatcher = priorTokenMatcher;
|
|
39
40
|
exports.restoreValue = restoreValue;
|
|
40
41
|
exports.enhanceWithPresidio = enhanceWithPresidio;
|
|
41
42
|
exports.buildReport = buildReport;
|
|
@@ -548,33 +549,56 @@ function assertUserRegexInput(text, what) {
|
|
|
548
549
|
throw new Error(`${what} cannot scan a value of ${text.length} characters, the limit is ${USER_REGEX_MAX_INPUT}. ` +
|
|
549
550
|
'Exclude the field with field rules, or remove the regex.');
|
|
550
551
|
}
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
552
|
+
class SegmentCursor {
|
|
553
|
+
constructor(segs) {
|
|
554
|
+
this.segs = segs;
|
|
555
|
+
this.next = [];
|
|
556
|
+
this.i = 0;
|
|
557
|
+
this.cur = segs[0];
|
|
558
|
+
}
|
|
559
|
+
freeAt(start, end) {
|
|
560
|
+
while (this.cur && (this.cur.locked || this.cur.src + this.cur.text.length <= start))
|
|
561
|
+
this.take();
|
|
562
|
+
const cur = this.cur;
|
|
563
|
+
return cur && cur.src <= start && end <= cur.src + cur.text.length ? cur : undefined;
|
|
564
|
+
}
|
|
565
|
+
lock(start, end, replacement) {
|
|
566
|
+
const cur = this.cur;
|
|
567
|
+
if (start > cur.src)
|
|
568
|
+
this.next.push({ text: cur.text.slice(0, start - cur.src), locked: false, src: cur.src });
|
|
569
|
+
this.next.push({ text: replacement, locked: true, src: -1 });
|
|
570
|
+
const tail = end - cur.src;
|
|
571
|
+
if (tail < cur.text.length)
|
|
572
|
+
this.cur = { text: cur.text.slice(tail), locked: false, src: end };
|
|
573
|
+
else
|
|
574
|
+
this.cur = this.segs[++this.i];
|
|
575
|
+
}
|
|
576
|
+
finish() {
|
|
577
|
+
while (this.cur)
|
|
578
|
+
this.take();
|
|
579
|
+
return this.next;
|
|
580
|
+
}
|
|
581
|
+
take() {
|
|
582
|
+
this.next.push(this.cur);
|
|
583
|
+
this.cur = this.segs[++this.i];
|
|
584
|
+
}
|
|
564
585
|
}
|
|
565
586
|
function replaceFree(pass, re, fn) {
|
|
566
|
-
|
|
567
|
-
const
|
|
568
|
-
|
|
569
|
-
|
|
587
|
+
const global = new RegExp(re.source, re.flags.includes('g') ? re.flags : `${re.flags}g`);
|
|
588
|
+
const cursor = new SegmentCursor(pass.segs);
|
|
589
|
+
let m;
|
|
590
|
+
while ((m = global.exec(pass.source)) !== null) {
|
|
591
|
+
const start = m.index;
|
|
570
592
|
const end = start + m[0].length;
|
|
571
|
-
|
|
572
|
-
|
|
593
|
+
if (m[0] === '' || !cursor.freeAt(start, end)) {
|
|
594
|
+
global.lastIndex = start + 1;
|
|
573
595
|
continue;
|
|
596
|
+
}
|
|
574
597
|
const replacement = fn(m[0], start);
|
|
575
598
|
if (replacement !== null)
|
|
576
|
-
|
|
599
|
+
cursor.lock(start, end, replacement);
|
|
577
600
|
}
|
|
601
|
+
pass.segs = cursor.finish();
|
|
578
602
|
}
|
|
579
603
|
function redactMatch(pass, match, pattern, detail) {
|
|
580
604
|
const replacement = applyRedaction(match, pattern, pass.ctx, pass.vault, pass.sessionId);
|
|
@@ -625,6 +649,10 @@ function builtInStage(pass) {
|
|
|
625
649
|
}
|
|
626
650
|
function sourceNames(pass) {
|
|
627
651
|
const names = new Set((0, names_1.detectNamesInText)(pass.source).map((nm) => nm.name));
|
|
652
|
+
for (const seg of pass.segs)
|
|
653
|
+
if (!seg.locked)
|
|
654
|
+
for (const nm of (0, names_1.detectNamesInText)(seg.text))
|
|
655
|
+
names.add(nm.name);
|
|
628
656
|
return [...names].sort((x, y) => y.length - x.length);
|
|
629
657
|
}
|
|
630
658
|
function nameStage(pass) {
|
|
@@ -686,12 +714,27 @@ function lockChild(ctx, container, key, compute) {
|
|
|
686
714
|
ctx.lastLocked = undefined;
|
|
687
715
|
return out;
|
|
688
716
|
}
|
|
717
|
+
function priorTokenMatcher(entries) {
|
|
718
|
+
const tokens = Object.keys(entries !== null && entries !== void 0 ? entries : {}).filter((t) => t.length > 0).sort((a, b) => b.length - a.length);
|
|
719
|
+
return tokens.length > 0 ? new RegExp(tokens.map(escapeRe).join('|'), 'g') : undefined;
|
|
720
|
+
}
|
|
721
|
+
function literalStage(pass) {
|
|
722
|
+
const { ctx, vault, sessionId } = pass;
|
|
723
|
+
if (!ctx.priorTokens || (ctx.mode !== 'token' && ctx.mode !== 'pseudonymize'))
|
|
724
|
+
return;
|
|
725
|
+
replaceFree(pass, ctx.priorTokens, (match) => {
|
|
726
|
+
const token = freshToken('LITERAL', vault, sessionId, ctx.reservedTokens);
|
|
727
|
+
vault.addEntry(sessionId, { token, original: match, patternLabel: 'LITERAL', category: 'other', createdAt: new Date().toISOString() });
|
|
728
|
+
return token;
|
|
729
|
+
});
|
|
730
|
+
}
|
|
689
731
|
function redactText(text, ctx, vault, sessionId, hits, itemIndex, fieldPath) {
|
|
690
732
|
var _a;
|
|
691
733
|
const pass = {
|
|
692
734
|
source: text, segs: [{ text, locked: false, src: 0 }], threshold: (_a = ctx.confidenceThreshold) !== null && _a !== void 0 ? _a : 0,
|
|
693
735
|
ctx, vault, sessionId, hits, itemIndex, fieldPath,
|
|
694
736
|
};
|
|
737
|
+
literalStage(pass);
|
|
695
738
|
denyListStage(pass);
|
|
696
739
|
builtInStage(pass);
|
|
697
740
|
nameStage(pass);
|
|
@@ -93,6 +93,8 @@ function escapeSet(src, i) {
|
|
|
93
93
|
function classMember(body, j) {
|
|
94
94
|
if (body[j] === '\\' && body[j + 1] === 'b')
|
|
95
95
|
return { set: codeSet(8), next: j + 2, code: 8 };
|
|
96
|
+
if (body[j] === '\\' && body[j + 1] === 'B')
|
|
97
|
+
return { set: codeSet(66), next: j + 2, code: 66 };
|
|
96
98
|
if (body[j] === '\\') {
|
|
97
99
|
const { length, code } = readEscape(body, j);
|
|
98
100
|
return { set: escapeSet(body, j), next: j + length, code };
|
|
@@ -3,9 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.checkRegex = checkRegex;
|
|
4
4
|
const regexCharset_1 = require("./regexCharset");
|
|
5
5
|
const MAX_REGEX_LENGTH = 500;
|
|
6
|
-
const newFrame = (zeroWidth = false) => ({
|
|
6
|
+
const newFrame = (zeroWidth = false, inLook = zeroWidth) => ({
|
|
7
7
|
hasVariableQuantifier: false, hasAlternation: false, run: null, chars: (0, regexCharset_1.emptySet)(), zeroWidth,
|
|
8
|
-
branchConsumes: false, allConsume: true, nullable: false,
|
|
8
|
+
branchConsumes: false, allConsume: true, nullable: false, inLook,
|
|
9
9
|
});
|
|
10
10
|
function endBranch(frame) {
|
|
11
11
|
frame.allConsume && (frame.allConsume = frame.branchConsumes);
|
|
@@ -48,7 +48,7 @@ function skipClass(src, i) {
|
|
|
48
48
|
function groupPrefixLength(src, i) {
|
|
49
49
|
if (src[i + 1] !== '?')
|
|
50
50
|
return 1;
|
|
51
|
-
const named = /^\(\?<[
|
|
51
|
+
const named = /^\(\?<[^=!>][^>]*>/.exec(src.slice(i));
|
|
52
52
|
if (named)
|
|
53
53
|
return named[0].length;
|
|
54
54
|
const look = /^\(\?(?::|=|!|<=|<!)/.exec(src.slice(i));
|
|
@@ -79,7 +79,8 @@ function readToken(src, i, stack) {
|
|
|
79
79
|
if (c === '[')
|
|
80
80
|
return { atomEnd: skipClass(src, i), closed: null };
|
|
81
81
|
if (c === '(') {
|
|
82
|
-
|
|
82
|
+
const look = /^\(\?(?:=|!|<=|<!)/.test(src.slice(i));
|
|
83
|
+
stack.push(newFrame(look, look || stack[stack.length - 1].inLook));
|
|
83
84
|
return { atomEnd: i, closed: null, next: i + groupPrefixLength(src, i) };
|
|
84
85
|
}
|
|
85
86
|
if (c === '|')
|
|
@@ -127,8 +128,11 @@ function consumes(q, chars, closed) {
|
|
|
127
128
|
return false;
|
|
128
129
|
return !(closed === null || closed === void 0 ? void 0 : closed.nullable);
|
|
129
130
|
}
|
|
131
|
+
const IN_LOOK = 'repeats a variable part inside a lookahead or lookbehind, which is not supported';
|
|
130
132
|
function stepProblem(src, i, step, q, top) {
|
|
131
133
|
var _a;
|
|
134
|
+
if (top.inLook && isVariable(q))
|
|
135
|
+
return IN_LOOK;
|
|
132
136
|
const problem = applyQuantifier(q, step.closed, top);
|
|
133
137
|
if (problem || (q === null || q === void 0 ? void 0 : q.max) === 0)
|
|
134
138
|
return problem;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n8n-nodes-redactor",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.2.0",
|
|
4
4
|
"description": "Redact, mask and anonymize PII before it reaches an LLM, then restore it. 216 patterns for emails, phones, addresses, IBAN, cards, names, SSN, passport and tax IDs. Reversible encrypted vault, GDPR Art.30 ROPA record, prompt-injection check. Runs on n8n Cloud and self-hosted n8n 2.x.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"n8n-community-node-package",
|