n8n-nodes-redactor 5.2.0 → 6.1.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/dist/nodes/Redactor/Redactor.node.js +24 -5
- package/dist/nodes/Redactor/classification.js +9 -6
- package/dist/nodes/Redactor/engine.js +264 -97
- package/dist/nodes/Redactor/injection.js +69 -86
- package/dist/nodes/Redactor/names.js +46 -16
- package/dist/nodes/Redactor/patterns.js +231 -27
- package/dist/nodes/Redactor/presidio.js +5 -1
- package/dist/nodes/Redactor/profiles.js +1 -1
- package/dist/nodes/Redactor/regexSafety.js +19 -2
- package/dist/nodes/Redactor/ropa.js +19 -3
- package/dist/nodes/Redactor/runtime.js +9 -0
- package/dist/nodes/Redactor/types.js +7 -0
- package/dist/nodes/Redactor/vault.js +33 -11
- package/package.json +1 -1
|
@@ -43,6 +43,16 @@ const RECOMMENDED_PATTERNS = [
|
|
|
43
43
|
'vin',
|
|
44
44
|
'uuid', 'imei', 'iccid',
|
|
45
45
|
];
|
|
46
|
+
function versionRopaSummary(ropa, byLevel, typeVersion) {
|
|
47
|
+
const summary = ropa.processingSummary;
|
|
48
|
+
if (typeVersion >= 2) {
|
|
49
|
+
summary.hitsBySensitivity = { ...byLevel };
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
summary.hitsByPiiCategory = summary.hitsByCategory;
|
|
53
|
+
summary.hitsByCategory = { ...byLevel };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
46
56
|
class Redactor {
|
|
47
57
|
constructor() {
|
|
48
58
|
this.description = {
|
|
@@ -50,7 +60,8 @@ class Redactor {
|
|
|
50
60
|
name: 'redactor',
|
|
51
61
|
icon: { light: 'file:redactor.svg', dark: 'file:redactor.dark.svg' },
|
|
52
62
|
group: ['transform'],
|
|
53
|
-
version: 1,
|
|
63
|
+
version: [1, 2],
|
|
64
|
+
defaultVersion: 2,
|
|
54
65
|
subtitle: '={{$parameter["operation"]}}',
|
|
55
66
|
usableAsTool: true,
|
|
56
67
|
description: 'Redact and remove sensitive data, PII, personal information, emails, phone numbers, addresses, IBAN, credit cards, names before sending to LLM. Anonymize, mask, sanitize, scrub customer data. Restore original values after. Helps GDPR, HIPAA and CCPA workflows. Data privacy, data protection, data masking, data anonymization.',
|
|
@@ -787,13 +798,15 @@ class Redactor {
|
|
|
787
798
|
fieldRules: [],
|
|
788
799
|
fieldMode: 'all',
|
|
789
800
|
skipSemantic: operation === 'verify',
|
|
801
|
+
skipNames: operation === 'verify' && this.getNode().typeVersion < 2,
|
|
790
802
|
scanNumbers: true,
|
|
791
803
|
};
|
|
792
804
|
const allHits = [];
|
|
793
805
|
for (let i = 0; i < items.length; i++) {
|
|
794
806
|
const hits = [];
|
|
795
807
|
(0, engine_1.redactValue)(items[i].json, ctx, tempVault, tempSessionId, hits, i);
|
|
796
|
-
|
|
808
|
+
for (const hit of hits)
|
|
809
|
+
allHits.push(hit);
|
|
797
810
|
}
|
|
798
811
|
tempVault.deleteSession(tempSessionId);
|
|
799
812
|
if (operation === 'detect') {
|
|
@@ -867,12 +880,14 @@ class Redactor {
|
|
|
867
880
|
dedup: true,
|
|
868
881
|
fieldRules: [],
|
|
869
882
|
fieldMode: 'all',
|
|
883
|
+
scanNumbers: true,
|
|
870
884
|
};
|
|
871
885
|
const allHits = [];
|
|
872
886
|
for (let i = 0; i < items.length; i++) {
|
|
873
887
|
const hits = [];
|
|
874
888
|
(0, engine_1.redactValue)(items[i].json, ctx, tempVault, tempSessionId, hits, i);
|
|
875
|
-
|
|
889
|
+
for (const hit of hits)
|
|
890
|
+
allHits.push(hit);
|
|
876
891
|
}
|
|
877
892
|
tempVault.deleteSession(tempSessionId);
|
|
878
893
|
const classification = (0, classification_1.classifyData)(allHits);
|
|
@@ -1018,7 +1033,8 @@ class Redactor {
|
|
|
1018
1033
|
{ name: 'Deny list', entries: denyList },
|
|
1019
1034
|
]);
|
|
1020
1035
|
try {
|
|
1021
|
-
vault.getOrCreateSession(sessionId, sessionTtl * 60 * 1000);
|
|
1036
|
+
const session = vault.getOrCreateSession(sessionId, sessionTtl * 60 * 1000);
|
|
1037
|
+
sessionTtl = session.ttl / 60000;
|
|
1022
1038
|
}
|
|
1023
1039
|
catch (error) {
|
|
1024
1040
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), error.message);
|
|
@@ -1041,6 +1057,7 @@ class Redactor {
|
|
|
1041
1057
|
keyOrigins: new WeakMap(),
|
|
1042
1058
|
inputText: (0, engine_1.collectInputText)(items.map((it) => it.json)),
|
|
1043
1059
|
lockedSpans: new WeakMap(),
|
|
1060
|
+
keyLocks: new WeakMap(),
|
|
1044
1061
|
priorTokens: (0, engine_1.priorTokenMatcher)((_e = vault.getSession(sessionId)) === null || _e === void 0 ? void 0 : _e.entries),
|
|
1045
1062
|
};
|
|
1046
1063
|
const allHits = [];
|
|
@@ -1057,7 +1074,8 @@ class Redactor {
|
|
|
1057
1074
|
}
|
|
1058
1075
|
}
|
|
1059
1076
|
returnData.push({ json: redacted, pairedItem: { item: i } });
|
|
1060
|
-
|
|
1077
|
+
for (const hit of hits)
|
|
1078
|
+
allHits.push(hit);
|
|
1061
1079
|
}
|
|
1062
1080
|
vault.save(sessionId);
|
|
1063
1081
|
let injectionResult = null;
|
|
@@ -1093,6 +1111,7 @@ class Redactor {
|
|
|
1093
1111
|
const d = this.getNodeParameter('ropa', 0, {});
|
|
1094
1112
|
const list = (v) => String(v !== null && v !== void 0 ? v : '').split(',').map((x) => x.trim()).filter(Boolean);
|
|
1095
1113
|
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), []);
|
|
1114
|
+
versionRopaSummary(report.ropa, (0, classification_1.classifyData)(allHits).hitCountByLevel, this.getNode().typeVersion);
|
|
1096
1115
|
}
|
|
1097
1116
|
return [returnData, [{ json: report, pairedItem: allPaired }]];
|
|
1098
1117
|
}
|
|
@@ -22,6 +22,7 @@ const LEVEL_NUMERIC = {
|
|
|
22
22
|
RESTRICTED: 3,
|
|
23
23
|
};
|
|
24
24
|
function classifyData(hits) {
|
|
25
|
+
var _a;
|
|
25
26
|
if (hits.length === 0) {
|
|
26
27
|
return {
|
|
27
28
|
level: 'PUBLIC',
|
|
@@ -40,12 +41,14 @@ function classifyData(hits) {
|
|
|
40
41
|
CONFIDENTIAL: 0,
|
|
41
42
|
RESTRICTED: 0,
|
|
42
43
|
};
|
|
43
|
-
const categoryCounts =
|
|
44
|
+
const categoryCounts = new Map();
|
|
44
45
|
let totalConfidence = 0;
|
|
45
46
|
for (const hit of hits) {
|
|
46
|
-
const catLevel = CATEGORY_SENSITIVITY
|
|
47
|
+
const catLevel = Object.prototype.hasOwnProperty.call(CATEGORY_SENSITIVITY, hit.category)
|
|
48
|
+
? CATEGORY_SENSITIVITY[hit.category]
|
|
49
|
+
: 'INTERNAL';
|
|
47
50
|
hitCountByLevel[catLevel]++;
|
|
48
|
-
categoryCounts
|
|
51
|
+
categoryCounts.set(hit.category, ((_a = categoryCounts.get(hit.category)) !== null && _a !== void 0 ? _a : 0) + 1);
|
|
49
52
|
totalConfidence += hit.confidence || 0.65;
|
|
50
53
|
}
|
|
51
54
|
let level = 'PUBLIC';
|
|
@@ -64,10 +67,10 @@ function classifyData(hits) {
|
|
|
64
67
|
level = 'CONFIDENTIAL';
|
|
65
68
|
escalated = true;
|
|
66
69
|
}
|
|
67
|
-
const
|
|
68
|
-
|
|
70
|
+
const ranked = [...categoryCounts.entries()].sort((a, b) => b[1] - a[1]);
|
|
71
|
+
const topCategory = ranked[0][0];
|
|
69
72
|
const reasonParts = [];
|
|
70
|
-
for (const [cat, count] of
|
|
73
|
+
for (const [cat, count] of ranked) {
|
|
71
74
|
reasonParts.push(`${count} ${cat}`);
|
|
72
75
|
}
|
|
73
76
|
let reason = `Contains: ${reasonParts.join(', ')}.`;
|