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.
@@ -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
- allHits.push(...hits);
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
- allHits.push(...hits);
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
- allHits.push(...hits);
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[hit.category] || 'INTERNAL';
47
+ const catLevel = Object.prototype.hasOwnProperty.call(CATEGORY_SENSITIVITY, hit.category)
48
+ ? CATEGORY_SENSITIVITY[hit.category]
49
+ : 'INTERNAL';
47
50
  hitCountByLevel[catLevel]++;
48
- categoryCounts[hit.category] = (categoryCounts[hit.category] || 0) + 1;
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 topCategory = Object.entries(categoryCounts)
68
- .sort((a, b) => b[1] - a[1])[0][0];
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 Object.entries(categoryCounts).sort((a, b) => b[1] - a[1])) {
73
+ for (const [cat, count] of ranked) {
71
74
  reasonParts.push(`${count} ${cat}`);
72
75
  }
73
76
  let reason = `Contains: ${reasonParts.join(', ')}.`;