n8n-nodes-redactor 7.0.0 → 7.0.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
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="https://raw.githubusercontent.com/mjmirza/n8n-nodes-redactor/
|
|
2
|
+
<img src="https://raw.githubusercontent.com/mjmirza/n8n-nodes-redactor/main/.github/assets/logo.png" alt="Redactor" width="96" />
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<h1 align="center">n8n-nodes-redactor</h1>
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
<p align="center">
|
|
13
13
|
<a href="https://www.npmjs.com/package/n8n-nodes-redactor"><img src="https://img.shields.io/npm/v/n8n-nodes-redactor?label=npm" alt="npm version" /></a>
|
|
14
14
|
<a href="https://www.npmjs.com/package/n8n-nodes-redactor"><img src="https://img.shields.io/npm/dm/n8n-nodes-redactor?label=downloads" alt="npm downloads" /></a>
|
|
15
|
-
<a href="https://github.com/mjmirza/n8n-nodes-redactor/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/mjmirza/n8n-nodes-redactor/ci.yml?branch=
|
|
15
|
+
<a href="https://github.com/mjmirza/n8n-nodes-redactor/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/mjmirza/n8n-nodes-redactor/ci.yml?branch=main&label=CI" alt="CI status" /></a>
|
|
16
16
|
<a href="https://www.npmjs.com/package/n8n-nodes-redactor#provenance"><img src="https://img.shields.io/badge/npm-provenance%20signed-brightgreen" alt="npm provenance" /></a>
|
|
17
17
|
<a href="#license"><img src="https://img.shields.io/npm/l/n8n-nodes-redactor" alt="MIT License" /></a>
|
|
18
18
|
<img src="https://img.shields.io/badge/PII%20patterns-218-blue" alt="218 PII patterns" />
|
|
@@ -54,7 +54,7 @@ After Restore, your workflow gets the real values back.
|
|
|
54
54
|
| Self-hosted n8n 1.x and 2.x | Supported |
|
|
55
55
|
| n8n verified community nodes | Eligible from 4.0.0. Passes n8n's verification scanner with no findings |
|
|
56
56
|
|
|
57
|
-
Version 4.0.0 uses no local disk. The vault lives in workflow storage and audit records go to the Report output. Upgrading from 1.x? Read [docs/MIGRATION.md](https://github.com/mjmirza/n8n-nodes-redactor/blob/
|
|
57
|
+
Version 4.0.0 uses no local disk. The vault lives in workflow storage and audit records go to the Report output. Upgrading from 1.x? Read [docs/MIGRATION.md](https://github.com/mjmirza/n8n-nodes-redactor/blob/main/docs/MIGRATION.md). The 1.x line stays installable with `npm install n8n-nodes-redactor@v1-legacy`.
|
|
58
58
|
|
|
59
59
|
## Operations
|
|
60
60
|
|
|
@@ -121,8 +121,8 @@ Set Verify Output on the Verify node to Items, Stop on FAIL. Verify then passes
|
|
|
121
121
|
| Audit report | A second output with hits by category and pattern |
|
|
122
122
|
| Audit log | Turn on Audit Log to get an `audit` record (counts, labels, timing, never values) on the Report output. Send it to any database, sheet or SIEM node |
|
|
123
123
|
| ROPA record | Turn on Include Audit Report, then Include GDPR Art.30 ROPA Record, and fill in the controller details |
|
|
124
|
-
| Presidio | Optional Microsoft Presidio container for names and organisations in free text. See [docs/PRESIDIO_SETUP.md](https://github.com/mjmirza/n8n-nodes-redactor/blob/
|
|
125
|
-
| Per-tenant profiles | Turn on Use Tenant Profile and paste JSON. Validated on every run. Fields are listed in [docs/MIGRATION.md](https://github.com/mjmirza/n8n-nodes-redactor/blob/
|
|
124
|
+
| Presidio | Optional Microsoft Presidio container for names and organisations in free text. See [docs/PRESIDIO_SETUP.md](https://github.com/mjmirza/n8n-nodes-redactor/blob/main/docs/PRESIDIO_SETUP.md). When Presidio cannot scan a text the run stops, unless If Presidio Fails is set to continue with patterns only |
|
|
125
|
+
| Per-tenant profiles | Turn on Use Tenant Profile and paste JSON. Validated on every run. Fields are listed in [docs/MIGRATION.md](https://github.com/mjmirza/n8n-nodes-redactor/blob/main/docs/MIGRATION.md#tenant-profile-fields) |
|
|
126
126
|
|
|
127
127
|
This node reduces what reaches a model. It is not legal advice and does not on its own make a workflow GDPR or HIPAA compliant.
|
|
128
128
|
|
|
@@ -864,7 +864,7 @@ class Redactor {
|
|
|
864
864
|
};
|
|
865
865
|
}
|
|
866
866
|
async execute() {
|
|
867
|
-
var _a, _b, _c, _d, _e, _f, _g, _h
|
|
867
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
868
868
|
const items = this.getInputData();
|
|
869
869
|
const allPaired = items.map((_, i) => ({ item: i }));
|
|
870
870
|
const operation = (0, runtime_1.runSetting)(this, 'operation');
|
|
@@ -1012,10 +1012,7 @@ class Redactor {
|
|
|
1012
1012
|
}
|
|
1013
1013
|
let sessionId = '';
|
|
1014
1014
|
if (operation === 'redact' || operation === 'restore') {
|
|
1015
|
-
sessionId =
|
|
1016
|
-
if (!sessionId) {
|
|
1017
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Session ID is required');
|
|
1018
|
-
}
|
|
1015
|
+
sessionId = (0, runtime_1.readSessionId)(this, 'sessionId');
|
|
1019
1016
|
}
|
|
1020
1017
|
if (operation === 'redact') {
|
|
1021
1018
|
let mode = (0, runtime_1.runSetting)(this, 'redactionMode', 'token');
|
|
@@ -1030,9 +1027,9 @@ class Redactor {
|
|
|
1030
1027
|
let sessionTtl = (0, runtime_1.runSetting)(this, 'sessionTtl', 60);
|
|
1031
1028
|
const includeReport = (0, runtime_1.runSetting)(this, 'includeReport', false);
|
|
1032
1029
|
const customPatternsRaw = (0, runtime_1.runSetting)(this, 'customPatterns', {});
|
|
1033
|
-
let customPatterns = (
|
|
1030
|
+
let customPatterns = (_a = customPatternsRaw.patterns) !== null && _a !== void 0 ? _a : [];
|
|
1034
1031
|
const fieldRulesRaw = (0, runtime_1.runSetting)(this, 'fieldRules', {});
|
|
1035
|
-
let fieldRules = ((
|
|
1032
|
+
let fieldRules = ((_b = fieldRulesRaw.rules) !== null && _b !== void 0 ? _b : []).map((r) => ({
|
|
1036
1033
|
field: r.field,
|
|
1037
1034
|
mode: fieldMode === 'allowlist' ? 'include' : 'exclude',
|
|
1038
1035
|
}));
|
|
@@ -1067,12 +1064,12 @@ class Redactor {
|
|
|
1067
1064
|
}
|
|
1068
1065
|
}
|
|
1069
1066
|
const allowListRaw = (0, runtime_1.runSetting)(this, 'allowList', {});
|
|
1070
|
-
let allowList = ((
|
|
1067
|
+
let allowList = ((_c = allowListRaw.entries) !== null && _c !== void 0 ? _c : []).map((e) => ({
|
|
1071
1068
|
value: e.value,
|
|
1072
1069
|
type: (e.type || 'contains'),
|
|
1073
1070
|
}));
|
|
1074
1071
|
const denyListRaw = (0, runtime_1.runSetting)(this, 'denyList', {});
|
|
1075
|
-
let denyList = ((
|
|
1072
|
+
let denyList = ((_d = denyListRaw.entries) !== null && _d !== void 0 ? _d : []).map((e) => ({
|
|
1076
1073
|
value: e.value,
|
|
1077
1074
|
type: (e.type || 'exact'),
|
|
1078
1075
|
}));
|
|
@@ -1159,8 +1156,8 @@ class Redactor {
|
|
|
1159
1156
|
inputIndex: {},
|
|
1160
1157
|
lockedSpans: new WeakMap(),
|
|
1161
1158
|
keyLocks: new WeakMap(),
|
|
1162
|
-
priorTokens: (0, engine_1.priorTokenMatcher)((_f = vault.getSession(sessionId)) === null || _f === void 0 ? void 0 : _f.entries),
|
|
1163
1159
|
};
|
|
1160
|
+
ctx.priorTokens = (0, engine_1.priorTokensInInput)(ctx, (_e = vault.getSession(sessionId)) === null || _e === void 0 ? void 0 : _e.entries);
|
|
1164
1161
|
const allHits = [];
|
|
1165
1162
|
const returnData = [];
|
|
1166
1163
|
for (let i = 0; i < items.length; i++) {
|
|
@@ -1229,7 +1226,7 @@ class Redactor {
|
|
|
1229
1226
|
if (includeReport && (0, runtime_1.runSetting)(this, 'includeRopa', false)) {
|
|
1230
1227
|
const d = (0, runtime_1.runSetting)(this, 'ropa', {});
|
|
1231
1228
|
const list = (v) => String(v !== null && v !== void 0 ? v : '').split(',').map((x) => x.trim()).filter(Boolean);
|
|
1232
|
-
report.ropa = (0, ropa_1.generateRopaRecord)(allHits, (0, classification_1.classifyData)(allHits), items.length, enabledPatterns.length, mode, sessionTtl, !!vaultPassphrase, auditLog, { name: String((
|
|
1229
|
+
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), []);
|
|
1233
1230
|
versionRopaSummary(report.ropa, (0, classification_1.classifyData)(allHits).hitCountByLevel, this.getNode().typeVersion);
|
|
1234
1231
|
}
|
|
1235
1232
|
return [returnData, [{ json: report, pairedItem: allPaired }]];
|
|
@@ -1264,10 +1261,7 @@ class Redactor {
|
|
|
1264
1261
|
if (operation === 'purge') {
|
|
1265
1262
|
const purgeScope = (0, runtime_1.runSetting)(this, 'purgeScope', 'all');
|
|
1266
1263
|
if (purgeScope === 'specific') {
|
|
1267
|
-
const purgeSessionId = (0, runtime_1.
|
|
1268
|
-
if (!purgeSessionId) {
|
|
1269
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Session ID is required for purging a specific session.');
|
|
1270
|
-
}
|
|
1264
|
+
const purgeSessionId = (0, runtime_1.readSessionId)(this, 'purgeSessionId');
|
|
1271
1265
|
const purged = vault.purgeSession(purgeSessionId);
|
|
1272
1266
|
return [[{
|
|
1273
1267
|
json: {
|
|
@@ -36,7 +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.
|
|
39
|
+
exports.priorTokensInInput = priorTokensInInput;
|
|
40
40
|
exports.ensureRestorable = ensureRestorable;
|
|
41
41
|
exports.restoreValue = restoreValue;
|
|
42
42
|
exports.restoreFromSession = restoreFromSession;
|
|
@@ -1095,8 +1095,29 @@ function lockChild(ctx, container, key, compute) {
|
|
|
1095
1095
|
ctx.lastLocked = undefined;
|
|
1096
1096
|
return out;
|
|
1097
1097
|
}
|
|
1098
|
-
function
|
|
1099
|
-
const
|
|
1098
|
+
function priorTokensPresent(input, entries) {
|
|
1099
|
+
const shape = tokenShape(entries);
|
|
1100
|
+
if (shape.byFirst.size === 0)
|
|
1101
|
+
return [];
|
|
1102
|
+
const found = new Set();
|
|
1103
|
+
for (let at = 0; at < input.length; at++) {
|
|
1104
|
+
const lengths = shape.byFirst.get(input.charCodeAt(at));
|
|
1105
|
+
if (!lengths)
|
|
1106
|
+
continue;
|
|
1107
|
+
for (const length of lengths) {
|
|
1108
|
+
if (at + length > input.length)
|
|
1109
|
+
break;
|
|
1110
|
+
const token = input.slice(at, at + length);
|
|
1111
|
+
if (Object.prototype.hasOwnProperty.call(entries, token))
|
|
1112
|
+
found.add(token);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
return [...found].sort((a, b) => b.length - a.length);
|
|
1116
|
+
}
|
|
1117
|
+
function priorTokensInInput(ctx, entries) {
|
|
1118
|
+
if (!entries || !ctx.inputText)
|
|
1119
|
+
return undefined;
|
|
1120
|
+
const tokens = priorTokensPresent(ctx.inputText, entries);
|
|
1100
1121
|
return tokens.length > 0 ? new RegExp(tokens.map(escapeRe).join('|'), 'g') : undefined;
|
|
1101
1122
|
}
|
|
1102
1123
|
function literalStage(pass) {
|
|
@@ -484,17 +484,22 @@ function scanLdapDns(text) {
|
|
|
484
484
|
return spans;
|
|
485
485
|
}
|
|
486
486
|
const isLineBreak = (ch) => ch === '\n' || ch === '\r';
|
|
487
|
-
|
|
488
|
-
|
|
487
|
+
const CONN_SPACE = String.raw `(?:[^\S\r\n]|\u0085)`;
|
|
488
|
+
const connSpace = new RegExp(CONN_SPACE);
|
|
489
|
+
const CONN_HOST_KEY = 'Server|Data Source|Host';
|
|
490
|
+
const CONN_PASSWORD_KEY = 'Password|Pwd';
|
|
491
|
+
const CONN_SENSITIVE_KEY = `${CONN_PASSWORD_KEY}|User ID|Uid|User|Access Token|Token|Client Secret|Secret|API Key`;
|
|
492
|
+
const CONN_KEY = `(?:${CONN_HOST_KEY}|${CONN_SENSITIVE_KEY})`;
|
|
493
|
+
const connHostKey = new RegExp(`^(?:${CONN_HOST_KEY})$`, 'i');
|
|
494
|
+
const connPasswordKey = new RegExp(`^(?:${CONN_PASSWORD_KEY})$`, 'i');
|
|
495
|
+
function connValueStart(text, from) {
|
|
489
496
|
let at = from;
|
|
490
|
-
while (at <
|
|
491
|
-
at++;
|
|
492
|
-
if (/["'{]/.test((_a = text[at]) !== null && _a !== void 0 ? _a : ''))
|
|
493
|
-
at = quotedValueEnd(text, at, limit);
|
|
494
|
-
while (at < limit && text[at] !== ';')
|
|
497
|
+
while (at < text.length && connSpace.test(text[at]))
|
|
495
498
|
at++;
|
|
496
499
|
return at;
|
|
497
500
|
}
|
|
501
|
+
const connQuoteAt = (text, at, braceEnd) => { var _a; return /["']/.test((_a = text[at]) !== null && _a !== void 0 ? _a : '') || (text[at] === '{' && at < braceEnd); };
|
|
502
|
+
const connHasValue = (text, at) => at < text.length && text[at] !== ';';
|
|
498
503
|
function quotedValueEnd(text, at, limit) {
|
|
499
504
|
const opener = text[at++];
|
|
500
505
|
const quote = opener === '{' ? '}' : opener;
|
|
@@ -507,60 +512,88 @@ function quotedValueEnd(text, at, limit) {
|
|
|
507
512
|
}
|
|
508
513
|
return at;
|
|
509
514
|
}
|
|
510
|
-
function
|
|
511
|
-
let at =
|
|
512
|
-
while (at
|
|
513
|
-
at
|
|
514
|
-
|
|
515
|
+
function lastBraceClose(text, from, limit) {
|
|
516
|
+
let at = limit;
|
|
517
|
+
while (at > from) {
|
|
518
|
+
if (text[--at] !== '}')
|
|
519
|
+
continue;
|
|
520
|
+
const end = at + 1;
|
|
521
|
+
while (at > from && text[at - 1] === '}')
|
|
522
|
+
at--;
|
|
523
|
+
if ((end - at) % 2 === 1)
|
|
524
|
+
return end;
|
|
525
|
+
}
|
|
526
|
+
return -1;
|
|
515
527
|
}
|
|
516
|
-
function
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
return { end, password: lastPasswordSegment(text, lineStart, end), semi: -2 };
|
|
528
|
+
function recordConnKey(chain, key) {
|
|
529
|
+
if (connPasswordKey.test(key))
|
|
530
|
+
chain.password = true;
|
|
531
|
+
if (!connHostKey.test(key))
|
|
532
|
+
return true;
|
|
533
|
+
chain.host = true;
|
|
534
|
+
return false;
|
|
524
535
|
}
|
|
525
|
-
function
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
536
|
+
function connChain(text, from, limit, braceEnd) {
|
|
537
|
+
const chain = { start: from, end: from, host: false, password: false };
|
|
538
|
+
const line = text.slice(from, limit);
|
|
539
|
+
const elements = new RegExp(`(?<![A-Za-z])(${CONN_KEY})${CONN_SPACE}*=|[=;]`, 'gi');
|
|
540
|
+
let sensitive = false;
|
|
541
|
+
let match;
|
|
542
|
+
while ((match = elements.exec(line)) !== null) {
|
|
543
|
+
if (match[0] === ';') {
|
|
544
|
+
if (sensitive)
|
|
545
|
+
chain.end = from + match.index;
|
|
546
|
+
sensitive = false;
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
const at = connValueStart(line, elements.lastIndex);
|
|
550
|
+
if (match[1] && connHasValue(line, at))
|
|
551
|
+
sensitive = recordConnKey(chain, match[1]) || sensitive;
|
|
552
|
+
if (connQuoteAt(line, at, braceEnd - from))
|
|
553
|
+
elements.lastIndex = quotedValueEnd(line, at, line.length);
|
|
530
554
|
}
|
|
531
|
-
|
|
555
|
+
if (sensitive)
|
|
556
|
+
chain.end = limit;
|
|
557
|
+
return chain;
|
|
532
558
|
}
|
|
533
|
-
function
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
const
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
559
|
+
function scanConnGrammar(text, odbc) {
|
|
560
|
+
const spans = [];
|
|
561
|
+
const starts = new RegExp(`(?<![A-Za-z])${CONN_KEY}${CONN_SPACE}*=`, 'gi');
|
|
562
|
+
let match;
|
|
563
|
+
while ((match = starts.exec(text)) !== null) {
|
|
564
|
+
const start = match.index;
|
|
565
|
+
let end = starts.lastIndex;
|
|
566
|
+
while (end < text.length && !isLineBreak(text[end]))
|
|
567
|
+
end++;
|
|
568
|
+
const braceEnd = odbc ? lastBraceClose(text, start, end) : -1;
|
|
569
|
+
const chain = connChain(text, start, end, braceEnd);
|
|
570
|
+
if (chain.host && chain.password)
|
|
571
|
+
spans.push({ start: chain.start, end: chain.end });
|
|
572
|
+
starts.lastIndex = end;
|
|
543
573
|
}
|
|
544
|
-
return
|
|
574
|
+
return spans;
|
|
545
575
|
}
|
|
546
|
-
function
|
|
576
|
+
function mergeConnSpans(ado, odbc) {
|
|
547
577
|
const spans = [];
|
|
548
|
-
|
|
549
|
-
let
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
const
|
|
557
|
-
if (
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
578
|
+
let a = 0;
|
|
579
|
+
let b = 0;
|
|
580
|
+
while (a < ado.length || b < odbc.length) {
|
|
581
|
+
let next;
|
|
582
|
+
if (b >= odbc.length || (a < ado.length && ado[a].start <= odbc[b].start))
|
|
583
|
+
next = ado[a++];
|
|
584
|
+
else
|
|
585
|
+
next = odbc[b++];
|
|
586
|
+
const previous = spans[spans.length - 1];
|
|
587
|
+
if (previous && next.start < previous.end)
|
|
588
|
+
previous.end = Math.max(previous.end, next.end);
|
|
589
|
+
else
|
|
590
|
+
spans.push({ ...next });
|
|
561
591
|
}
|
|
562
592
|
return spans;
|
|
563
593
|
}
|
|
594
|
+
function scanConnStrings(text) {
|
|
595
|
+
return mergeConnSpans(scanConnGrammar(text, false), scanConnGrammar(text, true));
|
|
596
|
+
}
|
|
564
597
|
const CTX_NAME_WORD = String.raw `\p{L}[\p{L}\p{M}]*(?:['’-]\p{L}[\p{L}\p{M}]*)*`;
|
|
565
598
|
const FULL_NAME_LABELS = String.raw `full\s*name|first\s*name|last\s*name|surname|family\s*name|given\s*name|Vorname|Nachname|Familienname|nom\s*de\s*famille|prenom|nombre|apellido`;
|
|
566
599
|
const TITLED_WORD = String.raw `(?:\p{Lu}{2,}(?:['’-]\p{Lu}+)*|\p{Lu}[\p{Ll}\p{M}]*(?:\p{Lu}[\p{Ll}\p{M}]+)*(?:['’-]\p{Lu}?[\p{Ll}\p{M}]+(?:\p{Lu}[\p{Ll}\p{M}]+)*)*)`;
|
|
@@ -1320,8 +1353,8 @@ exports.PII_PATTERNS = [
|
|
|
1320
1353
|
name: 'dbConnectionString',
|
|
1321
1354
|
label: 'DB_CONN_STRING',
|
|
1322
1355
|
category: 'enterprise',
|
|
1323
|
-
regex: /(?:jdbc|mongodb(?:\+srv)?|postgres(?:ql)?|mysql|redis(?:s)?|mssql|mariadb):\/\/[^\s"
|
|
1324
|
-
description: 'Database connection strings (JDBC, MongoDB, PostgreSQL, MySQL, Redis)',
|
|
1356
|
+
regex: /(?:jdbc|mongodb(?:\+srv)?|postgres(?:ql)?|mysql|redis(?:s)?|amqps?|mssql|mariadb):\/\/[^\s"]+/gi,
|
|
1357
|
+
description: 'Database and broker connection strings (JDBC, MongoDB, PostgreSQL, MySQL, Redis, AMQP)',
|
|
1325
1358
|
},
|
|
1326
1359
|
{
|
|
1327
1360
|
name: 'connStringKV',
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.selectedPatterns = selectedPatterns;
|
|
4
4
|
exports.runSetting = runSetting;
|
|
5
|
+
exports.readSessionId = readSessionId;
|
|
5
6
|
exports.resolvePassphrase = resolvePassphrase;
|
|
6
7
|
exports.workflowNamespace = workflowNamespace;
|
|
7
8
|
exports.assertUserRegexes = assertUserRegexes;
|
|
@@ -53,6 +54,13 @@ function runSetting(fx, name, fallback) {
|
|
|
53
54
|
}
|
|
54
55
|
return first;
|
|
55
56
|
}
|
|
57
|
+
function readSessionId(fns, name) {
|
|
58
|
+
var _a;
|
|
59
|
+
const sessionId = String((_a = runSetting(fns, name, '')) !== null && _a !== void 0 ? _a : '');
|
|
60
|
+
if (!sessionId)
|
|
61
|
+
throw new n8n_workflow_1.NodeOperationError(fns.getNode(), 'Session ID is required');
|
|
62
|
+
return sessionId;
|
|
63
|
+
}
|
|
56
64
|
async function resolvePassphrase(fns) {
|
|
57
65
|
var _a;
|
|
58
66
|
if ((_a = fns.getNode().credentials) === null || _a === void 0 ? void 0 : _a[VAULT_CREDENTIAL]) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n8n-nodes-redactor",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.1",
|
|
4
4
|
"description": "Redact, mask and anonymize PII before it reaches an LLM, then restore it. 218 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",
|
|
@@ -94,6 +94,7 @@
|
|
|
94
94
|
"@typescript-eslint/parser": "^8.72.1",
|
|
95
95
|
"eslint": "^9.29.0",
|
|
96
96
|
"eslint-plugin-n8n-nodes-base": "^2.0.0",
|
|
97
|
+
"fast-check": "4.10.2",
|
|
97
98
|
"jest": "^29.7.0",
|
|
98
99
|
"n8n-workflow": "^2.45.4",
|
|
99
100
|
"prettier": "^3.6.2",
|