pompelmi 0.32.1 → 0.34.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.
Files changed (43) hide show
  1. package/README.md +355 -957
  2. package/dist/pompelmi.audit.cjs +130 -0
  3. package/dist/pompelmi.audit.cjs.map +1 -0
  4. package/dist/pompelmi.audit.esm.js +109 -0
  5. package/dist/pompelmi.audit.esm.js.map +1 -0
  6. package/dist/pompelmi.browser.cjs +1455 -0
  7. package/dist/pompelmi.browser.cjs.map +1 -0
  8. package/dist/pompelmi.browser.esm.js +1429 -0
  9. package/dist/pompelmi.browser.esm.js.map +1 -0
  10. package/dist/pompelmi.cjs +1403 -3118
  11. package/dist/pompelmi.cjs.map +1 -1
  12. package/dist/pompelmi.esm.js +1397 -3116
  13. package/dist/pompelmi.esm.js.map +1 -1
  14. package/dist/pompelmi.hooks.cjs +75 -0
  15. package/dist/pompelmi.hooks.cjs.map +1 -0
  16. package/dist/pompelmi.hooks.esm.js +72 -0
  17. package/dist/pompelmi.hooks.esm.js.map +1 -0
  18. package/dist/pompelmi.policy-packs.cjs +239 -0
  19. package/dist/pompelmi.policy-packs.cjs.map +1 -0
  20. package/dist/pompelmi.policy-packs.esm.js +231 -0
  21. package/dist/pompelmi.policy-packs.esm.js.map +1 -0
  22. package/dist/pompelmi.quarantine.cjs +315 -0
  23. package/dist/pompelmi.quarantine.cjs.map +1 -0
  24. package/dist/pompelmi.quarantine.esm.js +291 -0
  25. package/dist/pompelmi.quarantine.esm.js.map +1 -0
  26. package/dist/pompelmi.react.cjs +1486 -0
  27. package/dist/pompelmi.react.cjs.map +1 -0
  28. package/dist/pompelmi.react.esm.js +1459 -0
  29. package/dist/pompelmi.react.esm.js.map +1 -0
  30. package/dist/types/audit.d.ts +84 -0
  31. package/dist/types/browser-index.d.ts +28 -2
  32. package/dist/types/config.d.ts +3 -2
  33. package/dist/types/hooks.d.ts +89 -0
  34. package/dist/types/index.d.ts +17 -9
  35. package/dist/types/policy-packs.d.ts +98 -0
  36. package/dist/types/quarantine/index.d.ts +18 -0
  37. package/dist/types/quarantine/storage.d.ts +77 -0
  38. package/dist/types/quarantine/types.d.ts +78 -0
  39. package/dist/types/quarantine/workflow.d.ts +97 -0
  40. package/dist/types/react-index.d.ts +13 -0
  41. package/dist/types/scanners/common-heuristics.d.ts +2 -2
  42. package/dist/types/types.d.ts +0 -1
  43. package/package.json +55 -4
package/dist/pompelmi.cjs CHANGED
@@ -25,6 +25,81 @@ var crypto__namespace = /*#__PURE__*/_interopNamespaceDefault(crypto);
25
25
  var os__namespace = /*#__PURE__*/_interopNamespaceDefault(os);
26
26
  var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
27
27
 
28
+ function hasAsciiToken(buf, token) {
29
+ // Use latin1 so we can safely search binary
30
+ return buf.indexOf(token, 0, 'latin1') !== -1;
31
+ }
32
+ function startsWith(buf, bytes) {
33
+ if (buf.length < bytes.length)
34
+ return false;
35
+ for (let i = 0; i < bytes.length; i++)
36
+ if (buf[i] !== bytes[i])
37
+ return false;
38
+ return true;
39
+ }
40
+ function isPDF(buf) {
41
+ // %PDF-
42
+ return startsWith(buf, [0x25, 0x50, 0x44, 0x46, 0x2d]);
43
+ }
44
+ function isOleCfb(buf) {
45
+ // D0 CF 11 E0 A1 B1 1A E1
46
+ const sig = [0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1];
47
+ return startsWith(buf, sig);
48
+ }
49
+ function isZipLike$1(buf) {
50
+ // PK\x03\x04
51
+ return startsWith(buf, [0x50, 0x4b, 0x03, 0x04]);
52
+ }
53
+ function isPeExecutable(buf) {
54
+ // "MZ"
55
+ return startsWith(buf, [0x4d, 0x5a]);
56
+ }
57
+ /** OOXML macro hint via filename token in ZIP container */
58
+ function hasOoxmlMacros(buf) {
59
+ if (!isZipLike$1(buf))
60
+ return false;
61
+ return hasAsciiToken(buf, 'vbaProject.bin');
62
+ }
63
+ /** PDF risky features (/JavaScript, /OpenAction, /AA, /Launch) */
64
+ function pdfRiskTokens(buf) {
65
+ const tokens = ['/JavaScript', '/OpenAction', '/AA', '/Launch'];
66
+ return tokens.filter(t => hasAsciiToken(buf, t));
67
+ }
68
+ const CommonHeuristicsScanner = {
69
+ async scan(input) {
70
+ const buf = Buffer.from(input);
71
+ const matches = [];
72
+ // Office macros (OLE / OOXML)
73
+ if (isOleCfb(buf)) {
74
+ matches.push({ rule: 'office_ole_container', severity: 'suspicious' });
75
+ }
76
+ if (hasOoxmlMacros(buf)) {
77
+ matches.push({ rule: 'office_ooxml_macros', severity: 'suspicious' });
78
+ }
79
+ // PDF risky tokens
80
+ if (isPDF(buf)) {
81
+ const toks = pdfRiskTokens(buf);
82
+ if (toks.length) {
83
+ matches.push({
84
+ rule: 'pdf_risky_actions',
85
+ severity: 'suspicious',
86
+ meta: { tokens: toks }
87
+ });
88
+ }
89
+ }
90
+ // Executable header
91
+ if (isPeExecutable(buf)) {
92
+ matches.push({ rule: 'pe_executable_signature', severity: 'suspicious' });
93
+ }
94
+ // EICAR test file
95
+ const EICAR_NEEDLE = "X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!";
96
+ if (hasAsciiToken(buf, EICAR_NEEDLE)) {
97
+ matches.push({ rule: 'eicar_test_file', severity: 'high', meta: { note: 'EICAR standard antivirus test file detected' } });
98
+ }
99
+ return matches;
100
+ }
101
+ };
102
+
28
103
  function toScanFn(s) {
29
104
  return (typeof s === "function" ? s : s.scan);
30
105
  }
@@ -135,6 +210,8 @@ function composeScanners(...args) {
135
210
  }
136
211
  function createPresetScanner(preset, opts = {}) {
137
212
  const scanners = [];
213
+ // Always include heuristics (EICAR, PHP webshells, JS obfuscation, PE hints, etc.)
214
+ scanners.push(CommonHeuristicsScanner);
138
215
  // Add decompilation scanners based on preset
139
216
  if (preset === 'decompilation-basic' || preset === 'decompilation-deep' ||
140
217
  preset === 'malware-analysis' || opts.enableDecompilation) {
@@ -182,17 +259,6 @@ function createPresetScanner(preset, opts = {}) {
182
259
  }
183
260
  }
184
261
  }
185
- // Add other scanners for advanced presets
186
- if (preset === 'advanced' || preset === 'malware-analysis') {
187
- // Add heuristics scanner
188
- try {
189
- const { CommonHeuristicsScanner } = require('./scanners/common-heuristics');
190
- scanners.push(new CommonHeuristicsScanner());
191
- }
192
- catch {
193
- // Heuristics not available
194
- }
195
- }
196
262
  if (scanners.length === 0) {
197
263
  // Fallback scanner that returns no matches
198
264
  return async (_input, _ctx) => {
@@ -201,32 +267,6 @@ function createPresetScanner(preset, opts = {}) {
201
267
  }
202
268
  return composeScanners(...scanners);
203
269
  }
204
- // Preset configurations
205
- const PRESET_CONFIGS = {
206
- 'basic': {
207
- timeout: 10000
208
- },
209
- 'advanced': {
210
- timeout: 30000,
211
- enableDecompilation: false
212
- },
213
- 'malware-analysis': {
214
- timeout: 60000,
215
- enableDecompilation: true,
216
- decompilationEngine: 'both',
217
- decompilationDepth: 'deep'
218
- },
219
- 'decompilation-basic': {
220
- timeout: 30000,
221
- enableDecompilation: true,
222
- decompilationDepth: 'basic'
223
- },
224
- 'decompilation-deep': {
225
- timeout: 120000,
226
- enableDecompilation: true,
227
- decompilationDepth: 'deep'
228
- }
229
- };
230
270
 
231
271
  /**
232
272
  * Performance monitoring utilities for pompelmi scans
@@ -804,1900 +844,6 @@ function validateFile(file) {
804
844
  return { valid: true };
805
845
  }
806
846
 
807
- var react = {exports: {}};
808
-
809
- var react_production = {};
810
-
811
- /**
812
- * @license React
813
- * react.production.js
814
- *
815
- * Copyright (c) Meta Platforms, Inc. and affiliates.
816
- *
817
- * This source code is licensed under the MIT license found in the
818
- * LICENSE file in the root directory of this source tree.
819
- */
820
-
821
- var hasRequiredReact_production;
822
-
823
- function requireReact_production () {
824
- if (hasRequiredReact_production) return react_production;
825
- hasRequiredReact_production = 1;
826
- var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element"),
827
- REACT_PORTAL_TYPE = Symbol.for("react.portal"),
828
- REACT_FRAGMENT_TYPE = Symbol.for("react.fragment"),
829
- REACT_STRICT_MODE_TYPE = Symbol.for("react.strict_mode"),
830
- REACT_PROFILER_TYPE = Symbol.for("react.profiler"),
831
- REACT_CONSUMER_TYPE = Symbol.for("react.consumer"),
832
- REACT_CONTEXT_TYPE = Symbol.for("react.context"),
833
- REACT_FORWARD_REF_TYPE = Symbol.for("react.forward_ref"),
834
- REACT_SUSPENSE_TYPE = Symbol.for("react.suspense"),
835
- REACT_MEMO_TYPE = Symbol.for("react.memo"),
836
- REACT_LAZY_TYPE = Symbol.for("react.lazy"),
837
- REACT_ACTIVITY_TYPE = Symbol.for("react.activity"),
838
- MAYBE_ITERATOR_SYMBOL = Symbol.iterator;
839
- function getIteratorFn(maybeIterable) {
840
- if (null === maybeIterable || "object" !== typeof maybeIterable) return null;
841
- maybeIterable =
842
- (MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL]) ||
843
- maybeIterable["@@iterator"];
844
- return "function" === typeof maybeIterable ? maybeIterable : null;
845
- }
846
- var ReactNoopUpdateQueue = {
847
- isMounted: function () {
848
- return false;
849
- },
850
- enqueueForceUpdate: function () {},
851
- enqueueReplaceState: function () {},
852
- enqueueSetState: function () {}
853
- },
854
- assign = Object.assign,
855
- emptyObject = {};
856
- function Component(props, context, updater) {
857
- this.props = props;
858
- this.context = context;
859
- this.refs = emptyObject;
860
- this.updater = updater || ReactNoopUpdateQueue;
861
- }
862
- Component.prototype.isReactComponent = {};
863
- Component.prototype.setState = function (partialState, callback) {
864
- if (
865
- "object" !== typeof partialState &&
866
- "function" !== typeof partialState &&
867
- null != partialState
868
- )
869
- throw Error(
870
- "takes an object of state variables to update or a function which returns an object of state variables."
871
- );
872
- this.updater.enqueueSetState(this, partialState, callback, "setState");
873
- };
874
- Component.prototype.forceUpdate = function (callback) {
875
- this.updater.enqueueForceUpdate(this, callback, "forceUpdate");
876
- };
877
- function ComponentDummy() {}
878
- ComponentDummy.prototype = Component.prototype;
879
- function PureComponent(props, context, updater) {
880
- this.props = props;
881
- this.context = context;
882
- this.refs = emptyObject;
883
- this.updater = updater || ReactNoopUpdateQueue;
884
- }
885
- var pureComponentPrototype = (PureComponent.prototype = new ComponentDummy());
886
- pureComponentPrototype.constructor = PureComponent;
887
- assign(pureComponentPrototype, Component.prototype);
888
- pureComponentPrototype.isPureReactComponent = true;
889
- var isArrayImpl = Array.isArray;
890
- function noop() {}
891
- var ReactSharedInternals = { H: null, A: null, T: null, S: null },
892
- hasOwnProperty = Object.prototype.hasOwnProperty;
893
- function ReactElement(type, key, props) {
894
- var refProp = props.ref;
895
- return {
896
- $$typeof: REACT_ELEMENT_TYPE,
897
- type: type,
898
- key: key,
899
- ref: void 0 !== refProp ? refProp : null,
900
- props: props
901
- };
902
- }
903
- function cloneAndReplaceKey(oldElement, newKey) {
904
- return ReactElement(oldElement.type, newKey, oldElement.props);
905
- }
906
- function isValidElement(object) {
907
- return (
908
- "object" === typeof object &&
909
- null !== object &&
910
- object.$$typeof === REACT_ELEMENT_TYPE
911
- );
912
- }
913
- function escape(key) {
914
- var escaperLookup = { "=": "=0", ":": "=2" };
915
- return (
916
- "$" +
917
- key.replace(/[=:]/g, function (match) {
918
- return escaperLookup[match];
919
- })
920
- );
921
- }
922
- var userProvidedKeyEscapeRegex = /\/+/g;
923
- function getElementKey(element, index) {
924
- return "object" === typeof element && null !== element && null != element.key
925
- ? escape("" + element.key)
926
- : index.toString(36);
927
- }
928
- function resolveThenable(thenable) {
929
- switch (thenable.status) {
930
- case "fulfilled":
931
- return thenable.value;
932
- case "rejected":
933
- throw thenable.reason;
934
- default:
935
- switch (
936
- ("string" === typeof thenable.status
937
- ? thenable.then(noop, noop)
938
- : ((thenable.status = "pending"),
939
- thenable.then(
940
- function (fulfilledValue) {
941
- "pending" === thenable.status &&
942
- ((thenable.status = "fulfilled"),
943
- (thenable.value = fulfilledValue));
944
- },
945
- function (error) {
946
- "pending" === thenable.status &&
947
- ((thenable.status = "rejected"), (thenable.reason = error));
948
- }
949
- )),
950
- thenable.status)
951
- ) {
952
- case "fulfilled":
953
- return thenable.value;
954
- case "rejected":
955
- throw thenable.reason;
956
- }
957
- }
958
- throw thenable;
959
- }
960
- function mapIntoArray(children, array, escapedPrefix, nameSoFar, callback) {
961
- var type = typeof children;
962
- if ("undefined" === type || "boolean" === type) children = null;
963
- var invokeCallback = false;
964
- if (null === children) invokeCallback = true;
965
- else
966
- switch (type) {
967
- case "bigint":
968
- case "string":
969
- case "number":
970
- invokeCallback = true;
971
- break;
972
- case "object":
973
- switch (children.$$typeof) {
974
- case REACT_ELEMENT_TYPE:
975
- case REACT_PORTAL_TYPE:
976
- invokeCallback = true;
977
- break;
978
- case REACT_LAZY_TYPE:
979
- return (
980
- (invokeCallback = children._init),
981
- mapIntoArray(
982
- invokeCallback(children._payload),
983
- array,
984
- escapedPrefix,
985
- nameSoFar,
986
- callback
987
- )
988
- );
989
- }
990
- }
991
- if (invokeCallback)
992
- return (
993
- (callback = callback(children)),
994
- (invokeCallback =
995
- "" === nameSoFar ? "." + getElementKey(children, 0) : nameSoFar),
996
- isArrayImpl(callback)
997
- ? ((escapedPrefix = ""),
998
- null != invokeCallback &&
999
- (escapedPrefix =
1000
- invokeCallback.replace(userProvidedKeyEscapeRegex, "$&/") + "/"),
1001
- mapIntoArray(callback, array, escapedPrefix, "", function (c) {
1002
- return c;
1003
- }))
1004
- : null != callback &&
1005
- (isValidElement(callback) &&
1006
- (callback = cloneAndReplaceKey(
1007
- callback,
1008
- escapedPrefix +
1009
- (null == callback.key ||
1010
- (children && children.key === callback.key)
1011
- ? ""
1012
- : ("" + callback.key).replace(
1013
- userProvidedKeyEscapeRegex,
1014
- "$&/"
1015
- ) + "/") +
1016
- invokeCallback
1017
- )),
1018
- array.push(callback)),
1019
- 1
1020
- );
1021
- invokeCallback = 0;
1022
- var nextNamePrefix = "" === nameSoFar ? "." : nameSoFar + ":";
1023
- if (isArrayImpl(children))
1024
- for (var i = 0; i < children.length; i++)
1025
- (nameSoFar = children[i]),
1026
- (type = nextNamePrefix + getElementKey(nameSoFar, i)),
1027
- (invokeCallback += mapIntoArray(
1028
- nameSoFar,
1029
- array,
1030
- escapedPrefix,
1031
- type,
1032
- callback
1033
- ));
1034
- else if (((i = getIteratorFn(children)), "function" === typeof i))
1035
- for (
1036
- children = i.call(children), i = 0;
1037
- !(nameSoFar = children.next()).done;
1038
-
1039
- )
1040
- (nameSoFar = nameSoFar.value),
1041
- (type = nextNamePrefix + getElementKey(nameSoFar, i++)),
1042
- (invokeCallback += mapIntoArray(
1043
- nameSoFar,
1044
- array,
1045
- escapedPrefix,
1046
- type,
1047
- callback
1048
- ));
1049
- else if ("object" === type) {
1050
- if ("function" === typeof children.then)
1051
- return mapIntoArray(
1052
- resolveThenable(children),
1053
- array,
1054
- escapedPrefix,
1055
- nameSoFar,
1056
- callback
1057
- );
1058
- array = String(children);
1059
- throw Error(
1060
- "Objects are not valid as a React child (found: " +
1061
- ("[object Object]" === array
1062
- ? "object with keys {" + Object.keys(children).join(", ") + "}"
1063
- : array) +
1064
- "). If you meant to render a collection of children, use an array instead."
1065
- );
1066
- }
1067
- return invokeCallback;
1068
- }
1069
- function mapChildren(children, func, context) {
1070
- if (null == children) return children;
1071
- var result = [],
1072
- count = 0;
1073
- mapIntoArray(children, result, "", "", function (child) {
1074
- return func.call(context, child, count++);
1075
- });
1076
- return result;
1077
- }
1078
- function lazyInitializer(payload) {
1079
- if (-1 === payload._status) {
1080
- var ctor = payload._result;
1081
- ctor = ctor();
1082
- ctor.then(
1083
- function (moduleObject) {
1084
- if (0 === payload._status || -1 === payload._status)
1085
- (payload._status = 1), (payload._result = moduleObject);
1086
- },
1087
- function (error) {
1088
- if (0 === payload._status || -1 === payload._status)
1089
- (payload._status = 2), (payload._result = error);
1090
- }
1091
- );
1092
- -1 === payload._status && ((payload._status = 0), (payload._result = ctor));
1093
- }
1094
- if (1 === payload._status) return payload._result.default;
1095
- throw payload._result;
1096
- }
1097
- var reportGlobalError =
1098
- "function" === typeof reportError
1099
- ? reportError
1100
- : function (error) {
1101
- if (
1102
- "object" === typeof window &&
1103
- "function" === typeof window.ErrorEvent
1104
- ) {
1105
- var event = new window.ErrorEvent("error", {
1106
- bubbles: true,
1107
- cancelable: true,
1108
- message:
1109
- "object" === typeof error &&
1110
- null !== error &&
1111
- "string" === typeof error.message
1112
- ? String(error.message)
1113
- : String(error),
1114
- error: error
1115
- });
1116
- if (!window.dispatchEvent(event)) return;
1117
- } else if (
1118
- "object" === typeof process &&
1119
- "function" === typeof process.emit
1120
- ) {
1121
- process.emit("uncaughtException", error);
1122
- return;
1123
- }
1124
- console.error(error);
1125
- },
1126
- Children = {
1127
- map: mapChildren,
1128
- forEach: function (children, forEachFunc, forEachContext) {
1129
- mapChildren(
1130
- children,
1131
- function () {
1132
- forEachFunc.apply(this, arguments);
1133
- },
1134
- forEachContext
1135
- );
1136
- },
1137
- count: function (children) {
1138
- var n = 0;
1139
- mapChildren(children, function () {
1140
- n++;
1141
- });
1142
- return n;
1143
- },
1144
- toArray: function (children) {
1145
- return (
1146
- mapChildren(children, function (child) {
1147
- return child;
1148
- }) || []
1149
- );
1150
- },
1151
- only: function (children) {
1152
- if (!isValidElement(children))
1153
- throw Error(
1154
- "React.Children.only expected to receive a single React element child."
1155
- );
1156
- return children;
1157
- }
1158
- };
1159
- react_production.Activity = REACT_ACTIVITY_TYPE;
1160
- react_production.Children = Children;
1161
- react_production.Component = Component;
1162
- react_production.Fragment = REACT_FRAGMENT_TYPE;
1163
- react_production.Profiler = REACT_PROFILER_TYPE;
1164
- react_production.PureComponent = PureComponent;
1165
- react_production.StrictMode = REACT_STRICT_MODE_TYPE;
1166
- react_production.Suspense = REACT_SUSPENSE_TYPE;
1167
- react_production.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE =
1168
- ReactSharedInternals;
1169
- react_production.__COMPILER_RUNTIME = {
1170
- __proto__: null,
1171
- c: function (size) {
1172
- return ReactSharedInternals.H.useMemoCache(size);
1173
- }
1174
- };
1175
- react_production.cache = function (fn) {
1176
- return function () {
1177
- return fn.apply(null, arguments);
1178
- };
1179
- };
1180
- react_production.cacheSignal = function () {
1181
- return null;
1182
- };
1183
- react_production.cloneElement = function (element, config, children) {
1184
- if (null === element || void 0 === element)
1185
- throw Error(
1186
- "The argument must be a React element, but you passed " + element + "."
1187
- );
1188
- var props = assign({}, element.props),
1189
- key = element.key;
1190
- if (null != config)
1191
- for (propName in (void 0 !== config.key && (key = "" + config.key), config))
1192
- !hasOwnProperty.call(config, propName) ||
1193
- "key" === propName ||
1194
- "__self" === propName ||
1195
- "__source" === propName ||
1196
- ("ref" === propName && void 0 === config.ref) ||
1197
- (props[propName] = config[propName]);
1198
- var propName = arguments.length - 2;
1199
- if (1 === propName) props.children = children;
1200
- else if (1 < propName) {
1201
- for (var childArray = Array(propName), i = 0; i < propName; i++)
1202
- childArray[i] = arguments[i + 2];
1203
- props.children = childArray;
1204
- }
1205
- return ReactElement(element.type, key, props);
1206
- };
1207
- react_production.createContext = function (defaultValue) {
1208
- defaultValue = {
1209
- $$typeof: REACT_CONTEXT_TYPE,
1210
- _currentValue: defaultValue,
1211
- _currentValue2: defaultValue,
1212
- _threadCount: 0,
1213
- Provider: null,
1214
- Consumer: null
1215
- };
1216
- defaultValue.Provider = defaultValue;
1217
- defaultValue.Consumer = {
1218
- $$typeof: REACT_CONSUMER_TYPE,
1219
- _context: defaultValue
1220
- };
1221
- return defaultValue;
1222
- };
1223
- react_production.createElement = function (type, config, children) {
1224
- var propName,
1225
- props = {},
1226
- key = null;
1227
- if (null != config)
1228
- for (propName in (void 0 !== config.key && (key = "" + config.key), config))
1229
- hasOwnProperty.call(config, propName) &&
1230
- "key" !== propName &&
1231
- "__self" !== propName &&
1232
- "__source" !== propName &&
1233
- (props[propName] = config[propName]);
1234
- var childrenLength = arguments.length - 2;
1235
- if (1 === childrenLength) props.children = children;
1236
- else if (1 < childrenLength) {
1237
- for (var childArray = Array(childrenLength), i = 0; i < childrenLength; i++)
1238
- childArray[i] = arguments[i + 2];
1239
- props.children = childArray;
1240
- }
1241
- if (type && type.defaultProps)
1242
- for (propName in ((childrenLength = type.defaultProps), childrenLength))
1243
- void 0 === props[propName] &&
1244
- (props[propName] = childrenLength[propName]);
1245
- return ReactElement(type, key, props);
1246
- };
1247
- react_production.createRef = function () {
1248
- return { current: null };
1249
- };
1250
- react_production.forwardRef = function (render) {
1251
- return { $$typeof: REACT_FORWARD_REF_TYPE, render: render };
1252
- };
1253
- react_production.isValidElement = isValidElement;
1254
- react_production.lazy = function (ctor) {
1255
- return {
1256
- $$typeof: REACT_LAZY_TYPE,
1257
- _payload: { _status: -1, _result: ctor },
1258
- _init: lazyInitializer
1259
- };
1260
- };
1261
- react_production.memo = function (type, compare) {
1262
- return {
1263
- $$typeof: REACT_MEMO_TYPE,
1264
- type: type,
1265
- compare: void 0 === compare ? null : compare
1266
- };
1267
- };
1268
- react_production.startTransition = function (scope) {
1269
- var prevTransition = ReactSharedInternals.T,
1270
- currentTransition = {};
1271
- ReactSharedInternals.T = currentTransition;
1272
- try {
1273
- var returnValue = scope(),
1274
- onStartTransitionFinish = ReactSharedInternals.S;
1275
- null !== onStartTransitionFinish &&
1276
- onStartTransitionFinish(currentTransition, returnValue);
1277
- "object" === typeof returnValue &&
1278
- null !== returnValue &&
1279
- "function" === typeof returnValue.then &&
1280
- returnValue.then(noop, reportGlobalError);
1281
- } catch (error) {
1282
- reportGlobalError(error);
1283
- } finally {
1284
- null !== prevTransition &&
1285
- null !== currentTransition.types &&
1286
- (prevTransition.types = currentTransition.types),
1287
- (ReactSharedInternals.T = prevTransition);
1288
- }
1289
- };
1290
- react_production.unstable_useCacheRefresh = function () {
1291
- return ReactSharedInternals.H.useCacheRefresh();
1292
- };
1293
- react_production.use = function (usable) {
1294
- return ReactSharedInternals.H.use(usable);
1295
- };
1296
- react_production.useActionState = function (action, initialState, permalink) {
1297
- return ReactSharedInternals.H.useActionState(action, initialState, permalink);
1298
- };
1299
- react_production.useCallback = function (callback, deps) {
1300
- return ReactSharedInternals.H.useCallback(callback, deps);
1301
- };
1302
- react_production.useContext = function (Context) {
1303
- return ReactSharedInternals.H.useContext(Context);
1304
- };
1305
- react_production.useDebugValue = function () {};
1306
- react_production.useDeferredValue = function (value, initialValue) {
1307
- return ReactSharedInternals.H.useDeferredValue(value, initialValue);
1308
- };
1309
- react_production.useEffect = function (create, deps) {
1310
- return ReactSharedInternals.H.useEffect(create, deps);
1311
- };
1312
- react_production.useEffectEvent = function (callback) {
1313
- return ReactSharedInternals.H.useEffectEvent(callback);
1314
- };
1315
- react_production.useId = function () {
1316
- return ReactSharedInternals.H.useId();
1317
- };
1318
- react_production.useImperativeHandle = function (ref, create, deps) {
1319
- return ReactSharedInternals.H.useImperativeHandle(ref, create, deps);
1320
- };
1321
- react_production.useInsertionEffect = function (create, deps) {
1322
- return ReactSharedInternals.H.useInsertionEffect(create, deps);
1323
- };
1324
- react_production.useLayoutEffect = function (create, deps) {
1325
- return ReactSharedInternals.H.useLayoutEffect(create, deps);
1326
- };
1327
- react_production.useMemo = function (create, deps) {
1328
- return ReactSharedInternals.H.useMemo(create, deps);
1329
- };
1330
- react_production.useOptimistic = function (passthrough, reducer) {
1331
- return ReactSharedInternals.H.useOptimistic(passthrough, reducer);
1332
- };
1333
- react_production.useReducer = function (reducer, initialArg, init) {
1334
- return ReactSharedInternals.H.useReducer(reducer, initialArg, init);
1335
- };
1336
- react_production.useRef = function (initialValue) {
1337
- return ReactSharedInternals.H.useRef(initialValue);
1338
- };
1339
- react_production.useState = function (initialState) {
1340
- return ReactSharedInternals.H.useState(initialState);
1341
- };
1342
- react_production.useSyncExternalStore = function (
1343
- subscribe,
1344
- getSnapshot,
1345
- getServerSnapshot
1346
- ) {
1347
- return ReactSharedInternals.H.useSyncExternalStore(
1348
- subscribe,
1349
- getSnapshot,
1350
- getServerSnapshot
1351
- );
1352
- };
1353
- react_production.useTransition = function () {
1354
- return ReactSharedInternals.H.useTransition();
1355
- };
1356
- react_production.version = "19.2.0";
1357
- return react_production;
1358
- }
1359
-
1360
- var react_development = {exports: {}};
1361
-
1362
- /**
1363
- * @license React
1364
- * react.development.js
1365
- *
1366
- * Copyright (c) Meta Platforms, Inc. and affiliates.
1367
- *
1368
- * This source code is licensed under the MIT license found in the
1369
- * LICENSE file in the root directory of this source tree.
1370
- */
1371
- react_development.exports;
1372
-
1373
- var hasRequiredReact_development;
1374
-
1375
- function requireReact_development () {
1376
- if (hasRequiredReact_development) return react_development.exports;
1377
- hasRequiredReact_development = 1;
1378
- (function (module, exports) {
1379
- "production" !== process.env.NODE_ENV &&
1380
- (function () {
1381
- function defineDeprecationWarning(methodName, info) {
1382
- Object.defineProperty(Component.prototype, methodName, {
1383
- get: function () {
1384
- console.warn(
1385
- "%s(...) is deprecated in plain JavaScript React classes. %s",
1386
- info[0],
1387
- info[1]
1388
- );
1389
- }
1390
- });
1391
- }
1392
- function getIteratorFn(maybeIterable) {
1393
- if (null === maybeIterable || "object" !== typeof maybeIterable)
1394
- return null;
1395
- maybeIterable =
1396
- (MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL]) ||
1397
- maybeIterable["@@iterator"];
1398
- return "function" === typeof maybeIterable ? maybeIterable : null;
1399
- }
1400
- function warnNoop(publicInstance, callerName) {
1401
- publicInstance =
1402
- ((publicInstance = publicInstance.constructor) &&
1403
- (publicInstance.displayName || publicInstance.name)) ||
1404
- "ReactClass";
1405
- var warningKey = publicInstance + "." + callerName;
1406
- didWarnStateUpdateForUnmountedComponent[warningKey] ||
1407
- (console.error(
1408
- "Can't call %s on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to `this.state` directly or define a `state = {};` class property with the desired state in the %s component.",
1409
- callerName,
1410
- publicInstance
1411
- ),
1412
- (didWarnStateUpdateForUnmountedComponent[warningKey] = true));
1413
- }
1414
- function Component(props, context, updater) {
1415
- this.props = props;
1416
- this.context = context;
1417
- this.refs = emptyObject;
1418
- this.updater = updater || ReactNoopUpdateQueue;
1419
- }
1420
- function ComponentDummy() {}
1421
- function PureComponent(props, context, updater) {
1422
- this.props = props;
1423
- this.context = context;
1424
- this.refs = emptyObject;
1425
- this.updater = updater || ReactNoopUpdateQueue;
1426
- }
1427
- function noop() {}
1428
- function testStringCoercion(value) {
1429
- return "" + value;
1430
- }
1431
- function checkKeyStringCoercion(value) {
1432
- try {
1433
- testStringCoercion(value);
1434
- var JSCompiler_inline_result = !1;
1435
- } catch (e) {
1436
- JSCompiler_inline_result = true;
1437
- }
1438
- if (JSCompiler_inline_result) {
1439
- JSCompiler_inline_result = console;
1440
- var JSCompiler_temp_const = JSCompiler_inline_result.error;
1441
- var JSCompiler_inline_result$jscomp$0 =
1442
- ("function" === typeof Symbol &&
1443
- Symbol.toStringTag &&
1444
- value[Symbol.toStringTag]) ||
1445
- value.constructor.name ||
1446
- "Object";
1447
- JSCompiler_temp_const.call(
1448
- JSCompiler_inline_result,
1449
- "The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",
1450
- JSCompiler_inline_result$jscomp$0
1451
- );
1452
- return testStringCoercion(value);
1453
- }
1454
- }
1455
- function getComponentNameFromType(type) {
1456
- if (null == type) return null;
1457
- if ("function" === typeof type)
1458
- return type.$$typeof === REACT_CLIENT_REFERENCE
1459
- ? null
1460
- : type.displayName || type.name || null;
1461
- if ("string" === typeof type) return type;
1462
- switch (type) {
1463
- case REACT_FRAGMENT_TYPE:
1464
- return "Fragment";
1465
- case REACT_PROFILER_TYPE:
1466
- return "Profiler";
1467
- case REACT_STRICT_MODE_TYPE:
1468
- return "StrictMode";
1469
- case REACT_SUSPENSE_TYPE:
1470
- return "Suspense";
1471
- case REACT_SUSPENSE_LIST_TYPE:
1472
- return "SuspenseList";
1473
- case REACT_ACTIVITY_TYPE:
1474
- return "Activity";
1475
- }
1476
- if ("object" === typeof type)
1477
- switch (
1478
- ("number" === typeof type.tag &&
1479
- console.error(
1480
- "Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."
1481
- ),
1482
- type.$$typeof)
1483
- ) {
1484
- case REACT_PORTAL_TYPE:
1485
- return "Portal";
1486
- case REACT_CONTEXT_TYPE:
1487
- return type.displayName || "Context";
1488
- case REACT_CONSUMER_TYPE:
1489
- return (type._context.displayName || "Context") + ".Consumer";
1490
- case REACT_FORWARD_REF_TYPE:
1491
- var innerType = type.render;
1492
- type = type.displayName;
1493
- type ||
1494
- ((type = innerType.displayName || innerType.name || ""),
1495
- (type = "" !== type ? "ForwardRef(" + type + ")" : "ForwardRef"));
1496
- return type;
1497
- case REACT_MEMO_TYPE:
1498
- return (
1499
- (innerType = type.displayName || null),
1500
- null !== innerType
1501
- ? innerType
1502
- : getComponentNameFromType(type.type) || "Memo"
1503
- );
1504
- case REACT_LAZY_TYPE:
1505
- innerType = type._payload;
1506
- type = type._init;
1507
- try {
1508
- return getComponentNameFromType(type(innerType));
1509
- } catch (x) {}
1510
- }
1511
- return null;
1512
- }
1513
- function getTaskName(type) {
1514
- if (type === REACT_FRAGMENT_TYPE) return "<>";
1515
- if (
1516
- "object" === typeof type &&
1517
- null !== type &&
1518
- type.$$typeof === REACT_LAZY_TYPE
1519
- )
1520
- return "<...>";
1521
- try {
1522
- var name = getComponentNameFromType(type);
1523
- return name ? "<" + name + ">" : "<...>";
1524
- } catch (x) {
1525
- return "<...>";
1526
- }
1527
- }
1528
- function getOwner() {
1529
- var dispatcher = ReactSharedInternals.A;
1530
- return null === dispatcher ? null : dispatcher.getOwner();
1531
- }
1532
- function UnknownOwner() {
1533
- return Error("react-stack-top-frame");
1534
- }
1535
- function hasValidKey(config) {
1536
- if (hasOwnProperty.call(config, "key")) {
1537
- var getter = Object.getOwnPropertyDescriptor(config, "key").get;
1538
- if (getter && getter.isReactWarning) return false;
1539
- }
1540
- return void 0 !== config.key;
1541
- }
1542
- function defineKeyPropWarningGetter(props, displayName) {
1543
- function warnAboutAccessingKey() {
1544
- specialPropKeyWarningShown ||
1545
- ((specialPropKeyWarningShown = true),
1546
- console.error(
1547
- "%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",
1548
- displayName
1549
- ));
1550
- }
1551
- warnAboutAccessingKey.isReactWarning = true;
1552
- Object.defineProperty(props, "key", {
1553
- get: warnAboutAccessingKey,
1554
- configurable: true
1555
- });
1556
- }
1557
- function elementRefGetterWithDeprecationWarning() {
1558
- var componentName = getComponentNameFromType(this.type);
1559
- didWarnAboutElementRef[componentName] ||
1560
- ((didWarnAboutElementRef[componentName] = true),
1561
- console.error(
1562
- "Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release."
1563
- ));
1564
- componentName = this.props.ref;
1565
- return void 0 !== componentName ? componentName : null;
1566
- }
1567
- function ReactElement(type, key, props, owner, debugStack, debugTask) {
1568
- var refProp = props.ref;
1569
- type = {
1570
- $$typeof: REACT_ELEMENT_TYPE,
1571
- type: type,
1572
- key: key,
1573
- props: props,
1574
- _owner: owner
1575
- };
1576
- null !== (void 0 !== refProp ? refProp : null)
1577
- ? Object.defineProperty(type, "ref", {
1578
- enumerable: false,
1579
- get: elementRefGetterWithDeprecationWarning
1580
- })
1581
- : Object.defineProperty(type, "ref", { enumerable: false, value: null });
1582
- type._store = {};
1583
- Object.defineProperty(type._store, "validated", {
1584
- configurable: false,
1585
- enumerable: false,
1586
- writable: true,
1587
- value: 0
1588
- });
1589
- Object.defineProperty(type, "_debugInfo", {
1590
- configurable: false,
1591
- enumerable: false,
1592
- writable: true,
1593
- value: null
1594
- });
1595
- Object.defineProperty(type, "_debugStack", {
1596
- configurable: false,
1597
- enumerable: false,
1598
- writable: true,
1599
- value: debugStack
1600
- });
1601
- Object.defineProperty(type, "_debugTask", {
1602
- configurable: false,
1603
- enumerable: false,
1604
- writable: true,
1605
- value: debugTask
1606
- });
1607
- Object.freeze && (Object.freeze(type.props), Object.freeze(type));
1608
- return type;
1609
- }
1610
- function cloneAndReplaceKey(oldElement, newKey) {
1611
- newKey = ReactElement(
1612
- oldElement.type,
1613
- newKey,
1614
- oldElement.props,
1615
- oldElement._owner,
1616
- oldElement._debugStack,
1617
- oldElement._debugTask
1618
- );
1619
- oldElement._store &&
1620
- (newKey._store.validated = oldElement._store.validated);
1621
- return newKey;
1622
- }
1623
- function validateChildKeys(node) {
1624
- isValidElement(node)
1625
- ? node._store && (node._store.validated = 1)
1626
- : "object" === typeof node &&
1627
- null !== node &&
1628
- node.$$typeof === REACT_LAZY_TYPE &&
1629
- ("fulfilled" === node._payload.status
1630
- ? isValidElement(node._payload.value) &&
1631
- node._payload.value._store &&
1632
- (node._payload.value._store.validated = 1)
1633
- : node._store && (node._store.validated = 1));
1634
- }
1635
- function isValidElement(object) {
1636
- return (
1637
- "object" === typeof object &&
1638
- null !== object &&
1639
- object.$$typeof === REACT_ELEMENT_TYPE
1640
- );
1641
- }
1642
- function escape(key) {
1643
- var escaperLookup = { "=": "=0", ":": "=2" };
1644
- return (
1645
- "$" +
1646
- key.replace(/[=:]/g, function (match) {
1647
- return escaperLookup[match];
1648
- })
1649
- );
1650
- }
1651
- function getElementKey(element, index) {
1652
- return "object" === typeof element &&
1653
- null !== element &&
1654
- null != element.key
1655
- ? (checkKeyStringCoercion(element.key), escape("" + element.key))
1656
- : index.toString(36);
1657
- }
1658
- function resolveThenable(thenable) {
1659
- switch (thenable.status) {
1660
- case "fulfilled":
1661
- return thenable.value;
1662
- case "rejected":
1663
- throw thenable.reason;
1664
- default:
1665
- switch (
1666
- ("string" === typeof thenable.status
1667
- ? thenable.then(noop, noop)
1668
- : ((thenable.status = "pending"),
1669
- thenable.then(
1670
- function (fulfilledValue) {
1671
- "pending" === thenable.status &&
1672
- ((thenable.status = "fulfilled"),
1673
- (thenable.value = fulfilledValue));
1674
- },
1675
- function (error) {
1676
- "pending" === thenable.status &&
1677
- ((thenable.status = "rejected"),
1678
- (thenable.reason = error));
1679
- }
1680
- )),
1681
- thenable.status)
1682
- ) {
1683
- case "fulfilled":
1684
- return thenable.value;
1685
- case "rejected":
1686
- throw thenable.reason;
1687
- }
1688
- }
1689
- throw thenable;
1690
- }
1691
- function mapIntoArray(children, array, escapedPrefix, nameSoFar, callback) {
1692
- var type = typeof children;
1693
- if ("undefined" === type || "boolean" === type) children = null;
1694
- var invokeCallback = false;
1695
- if (null === children) invokeCallback = true;
1696
- else
1697
- switch (type) {
1698
- case "bigint":
1699
- case "string":
1700
- case "number":
1701
- invokeCallback = true;
1702
- break;
1703
- case "object":
1704
- switch (children.$$typeof) {
1705
- case REACT_ELEMENT_TYPE:
1706
- case REACT_PORTAL_TYPE:
1707
- invokeCallback = true;
1708
- break;
1709
- case REACT_LAZY_TYPE:
1710
- return (
1711
- (invokeCallback = children._init),
1712
- mapIntoArray(
1713
- invokeCallback(children._payload),
1714
- array,
1715
- escapedPrefix,
1716
- nameSoFar,
1717
- callback
1718
- )
1719
- );
1720
- }
1721
- }
1722
- if (invokeCallback) {
1723
- invokeCallback = children;
1724
- callback = callback(invokeCallback);
1725
- var childKey =
1726
- "" === nameSoFar ? "." + getElementKey(invokeCallback, 0) : nameSoFar;
1727
- isArrayImpl(callback)
1728
- ? ((escapedPrefix = ""),
1729
- null != childKey &&
1730
- (escapedPrefix =
1731
- childKey.replace(userProvidedKeyEscapeRegex, "$&/") + "/"),
1732
- mapIntoArray(callback, array, escapedPrefix, "", function (c) {
1733
- return c;
1734
- }))
1735
- : null != callback &&
1736
- (isValidElement(callback) &&
1737
- (null != callback.key &&
1738
- ((invokeCallback && invokeCallback.key === callback.key) ||
1739
- checkKeyStringCoercion(callback.key)),
1740
- (escapedPrefix = cloneAndReplaceKey(
1741
- callback,
1742
- escapedPrefix +
1743
- (null == callback.key ||
1744
- (invokeCallback && invokeCallback.key === callback.key)
1745
- ? ""
1746
- : ("" + callback.key).replace(
1747
- userProvidedKeyEscapeRegex,
1748
- "$&/"
1749
- ) + "/") +
1750
- childKey
1751
- )),
1752
- "" !== nameSoFar &&
1753
- null != invokeCallback &&
1754
- isValidElement(invokeCallback) &&
1755
- null == invokeCallback.key &&
1756
- invokeCallback._store &&
1757
- !invokeCallback._store.validated &&
1758
- (escapedPrefix._store.validated = 2),
1759
- (callback = escapedPrefix)),
1760
- array.push(callback));
1761
- return 1;
1762
- }
1763
- invokeCallback = 0;
1764
- childKey = "" === nameSoFar ? "." : nameSoFar + ":";
1765
- if (isArrayImpl(children))
1766
- for (var i = 0; i < children.length; i++)
1767
- (nameSoFar = children[i]),
1768
- (type = childKey + getElementKey(nameSoFar, i)),
1769
- (invokeCallback += mapIntoArray(
1770
- nameSoFar,
1771
- array,
1772
- escapedPrefix,
1773
- type,
1774
- callback
1775
- ));
1776
- else if (((i = getIteratorFn(children)), "function" === typeof i))
1777
- for (
1778
- i === children.entries &&
1779
- (didWarnAboutMaps ||
1780
- console.warn(
1781
- "Using Maps as children is not supported. Use an array of keyed ReactElements instead."
1782
- ),
1783
- (didWarnAboutMaps = true)),
1784
- children = i.call(children),
1785
- i = 0;
1786
- !(nameSoFar = children.next()).done;
1787
-
1788
- )
1789
- (nameSoFar = nameSoFar.value),
1790
- (type = childKey + getElementKey(nameSoFar, i++)),
1791
- (invokeCallback += mapIntoArray(
1792
- nameSoFar,
1793
- array,
1794
- escapedPrefix,
1795
- type,
1796
- callback
1797
- ));
1798
- else if ("object" === type) {
1799
- if ("function" === typeof children.then)
1800
- return mapIntoArray(
1801
- resolveThenable(children),
1802
- array,
1803
- escapedPrefix,
1804
- nameSoFar,
1805
- callback
1806
- );
1807
- array = String(children);
1808
- throw Error(
1809
- "Objects are not valid as a React child (found: " +
1810
- ("[object Object]" === array
1811
- ? "object with keys {" + Object.keys(children).join(", ") + "}"
1812
- : array) +
1813
- "). If you meant to render a collection of children, use an array instead."
1814
- );
1815
- }
1816
- return invokeCallback;
1817
- }
1818
- function mapChildren(children, func, context) {
1819
- if (null == children) return children;
1820
- var result = [],
1821
- count = 0;
1822
- mapIntoArray(children, result, "", "", function (child) {
1823
- return func.call(context, child, count++);
1824
- });
1825
- return result;
1826
- }
1827
- function lazyInitializer(payload) {
1828
- if (-1 === payload._status) {
1829
- var ioInfo = payload._ioInfo;
1830
- null != ioInfo && (ioInfo.start = ioInfo.end = performance.now());
1831
- ioInfo = payload._result;
1832
- var thenable = ioInfo();
1833
- thenable.then(
1834
- function (moduleObject) {
1835
- if (0 === payload._status || -1 === payload._status) {
1836
- payload._status = 1;
1837
- payload._result = moduleObject;
1838
- var _ioInfo = payload._ioInfo;
1839
- null != _ioInfo && (_ioInfo.end = performance.now());
1840
- void 0 === thenable.status &&
1841
- ((thenable.status = "fulfilled"),
1842
- (thenable.value = moduleObject));
1843
- }
1844
- },
1845
- function (error) {
1846
- if (0 === payload._status || -1 === payload._status) {
1847
- payload._status = 2;
1848
- payload._result = error;
1849
- var _ioInfo2 = payload._ioInfo;
1850
- null != _ioInfo2 && (_ioInfo2.end = performance.now());
1851
- void 0 === thenable.status &&
1852
- ((thenable.status = "rejected"), (thenable.reason = error));
1853
- }
1854
- }
1855
- );
1856
- ioInfo = payload._ioInfo;
1857
- if (null != ioInfo) {
1858
- ioInfo.value = thenable;
1859
- var displayName = thenable.displayName;
1860
- "string" === typeof displayName && (ioInfo.name = displayName);
1861
- }
1862
- -1 === payload._status &&
1863
- ((payload._status = 0), (payload._result = thenable));
1864
- }
1865
- if (1 === payload._status)
1866
- return (
1867
- (ioInfo = payload._result),
1868
- void 0 === ioInfo &&
1869
- console.error(
1870
- "lazy: Expected the result of a dynamic import() call. Instead received: %s\n\nYour code should look like: \n const MyComponent = lazy(() => import('./MyComponent'))\n\nDid you accidentally put curly braces around the import?",
1871
- ioInfo
1872
- ),
1873
- "default" in ioInfo ||
1874
- console.error(
1875
- "lazy: Expected the result of a dynamic import() call. Instead received: %s\n\nYour code should look like: \n const MyComponent = lazy(() => import('./MyComponent'))",
1876
- ioInfo
1877
- ),
1878
- ioInfo.default
1879
- );
1880
- throw payload._result;
1881
- }
1882
- function resolveDispatcher() {
1883
- var dispatcher = ReactSharedInternals.H;
1884
- null === dispatcher &&
1885
- console.error(
1886
- "Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:\n1. You might have mismatching versions of React and the renderer (such as React DOM)\n2. You might be breaking the Rules of Hooks\n3. You might have more than one copy of React in the same app\nSee https://react.dev/link/invalid-hook-call for tips about how to debug and fix this problem."
1887
- );
1888
- return dispatcher;
1889
- }
1890
- function releaseAsyncTransition() {
1891
- ReactSharedInternals.asyncTransitions--;
1892
- }
1893
- function enqueueTask(task) {
1894
- if (null === enqueueTaskImpl)
1895
- try {
1896
- var requireString = ("require" + Math.random()).slice(0, 7);
1897
- enqueueTaskImpl = (module && module[requireString]).call(
1898
- module,
1899
- "timers"
1900
- ).setImmediate;
1901
- } catch (_err) {
1902
- enqueueTaskImpl = function (callback) {
1903
- false === didWarnAboutMessageChannel &&
1904
- ((didWarnAboutMessageChannel = true),
1905
- "undefined" === typeof MessageChannel &&
1906
- console.error(
1907
- "This browser does not have a MessageChannel implementation, so enqueuing tasks via await act(async () => ...) will fail. Please file an issue at https://github.com/facebook/react/issues if you encounter this warning."
1908
- ));
1909
- var channel = new MessageChannel();
1910
- channel.port1.onmessage = callback;
1911
- channel.port2.postMessage(void 0);
1912
- };
1913
- }
1914
- return enqueueTaskImpl(task);
1915
- }
1916
- function aggregateErrors(errors) {
1917
- return 1 < errors.length && "function" === typeof AggregateError
1918
- ? new AggregateError(errors)
1919
- : errors[0];
1920
- }
1921
- function popActScope(prevActQueue, prevActScopeDepth) {
1922
- prevActScopeDepth !== actScopeDepth - 1 &&
1923
- console.error(
1924
- "You seem to have overlapping act() calls, this is not supported. Be sure to await previous act() calls before making a new one. "
1925
- );
1926
- actScopeDepth = prevActScopeDepth;
1927
- }
1928
- function recursivelyFlushAsyncActWork(returnValue, resolve, reject) {
1929
- var queue = ReactSharedInternals.actQueue;
1930
- if (null !== queue)
1931
- if (0 !== queue.length)
1932
- try {
1933
- flushActQueue(queue);
1934
- enqueueTask(function () {
1935
- return recursivelyFlushAsyncActWork(returnValue, resolve, reject);
1936
- });
1937
- return;
1938
- } catch (error) {
1939
- ReactSharedInternals.thrownErrors.push(error);
1940
- }
1941
- else ReactSharedInternals.actQueue = null;
1942
- 0 < ReactSharedInternals.thrownErrors.length
1943
- ? ((queue = aggregateErrors(ReactSharedInternals.thrownErrors)),
1944
- (ReactSharedInternals.thrownErrors.length = 0),
1945
- reject(queue))
1946
- : resolve(returnValue);
1947
- }
1948
- function flushActQueue(queue) {
1949
- if (!isFlushing) {
1950
- isFlushing = true;
1951
- var i = 0;
1952
- try {
1953
- for (; i < queue.length; i++) {
1954
- var callback = queue[i];
1955
- do {
1956
- ReactSharedInternals.didUsePromise = !1;
1957
- var continuation = callback(!1);
1958
- if (null !== continuation) {
1959
- if (ReactSharedInternals.didUsePromise) {
1960
- queue[i] = callback;
1961
- queue.splice(0, i);
1962
- return;
1963
- }
1964
- callback = continuation;
1965
- } else break;
1966
- } while (1);
1967
- }
1968
- queue.length = 0;
1969
- } catch (error) {
1970
- queue.splice(0, i + 1), ReactSharedInternals.thrownErrors.push(error);
1971
- } finally {
1972
- isFlushing = false;
1973
- }
1974
- }
1975
- }
1976
- "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
1977
- "function" ===
1978
- typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart &&
1979
- __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());
1980
- var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element"),
1981
- REACT_PORTAL_TYPE = Symbol.for("react.portal"),
1982
- REACT_FRAGMENT_TYPE = Symbol.for("react.fragment"),
1983
- REACT_STRICT_MODE_TYPE = Symbol.for("react.strict_mode"),
1984
- REACT_PROFILER_TYPE = Symbol.for("react.profiler"),
1985
- REACT_CONSUMER_TYPE = Symbol.for("react.consumer"),
1986
- REACT_CONTEXT_TYPE = Symbol.for("react.context"),
1987
- REACT_FORWARD_REF_TYPE = Symbol.for("react.forward_ref"),
1988
- REACT_SUSPENSE_TYPE = Symbol.for("react.suspense"),
1989
- REACT_SUSPENSE_LIST_TYPE = Symbol.for("react.suspense_list"),
1990
- REACT_MEMO_TYPE = Symbol.for("react.memo"),
1991
- REACT_LAZY_TYPE = Symbol.for("react.lazy"),
1992
- REACT_ACTIVITY_TYPE = Symbol.for("react.activity"),
1993
- MAYBE_ITERATOR_SYMBOL = Symbol.iterator,
1994
- didWarnStateUpdateForUnmountedComponent = {},
1995
- ReactNoopUpdateQueue = {
1996
- isMounted: function () {
1997
- return false;
1998
- },
1999
- enqueueForceUpdate: function (publicInstance) {
2000
- warnNoop(publicInstance, "forceUpdate");
2001
- },
2002
- enqueueReplaceState: function (publicInstance) {
2003
- warnNoop(publicInstance, "replaceState");
2004
- },
2005
- enqueueSetState: function (publicInstance) {
2006
- warnNoop(publicInstance, "setState");
2007
- }
2008
- },
2009
- assign = Object.assign,
2010
- emptyObject = {};
2011
- Object.freeze(emptyObject);
2012
- Component.prototype.isReactComponent = {};
2013
- Component.prototype.setState = function (partialState, callback) {
2014
- if (
2015
- "object" !== typeof partialState &&
2016
- "function" !== typeof partialState &&
2017
- null != partialState
2018
- )
2019
- throw Error(
2020
- "takes an object of state variables to update or a function which returns an object of state variables."
2021
- );
2022
- this.updater.enqueueSetState(this, partialState, callback, "setState");
2023
- };
2024
- Component.prototype.forceUpdate = function (callback) {
2025
- this.updater.enqueueForceUpdate(this, callback, "forceUpdate");
2026
- };
2027
- var deprecatedAPIs = {
2028
- isMounted: [
2029
- "isMounted",
2030
- "Instead, make sure to clean up subscriptions and pending requests in componentWillUnmount to prevent memory leaks."
2031
- ],
2032
- replaceState: [
2033
- "replaceState",
2034
- "Refactor your code to use setState instead (see https://github.com/facebook/react/issues/3236)."
2035
- ]
2036
- };
2037
- for (fnName in deprecatedAPIs)
2038
- deprecatedAPIs.hasOwnProperty(fnName) &&
2039
- defineDeprecationWarning(fnName, deprecatedAPIs[fnName]);
2040
- ComponentDummy.prototype = Component.prototype;
2041
- deprecatedAPIs = PureComponent.prototype = new ComponentDummy();
2042
- deprecatedAPIs.constructor = PureComponent;
2043
- assign(deprecatedAPIs, Component.prototype);
2044
- deprecatedAPIs.isPureReactComponent = true;
2045
- var isArrayImpl = Array.isArray,
2046
- REACT_CLIENT_REFERENCE = Symbol.for("react.client.reference"),
2047
- ReactSharedInternals = {
2048
- H: null,
2049
- A: null,
2050
- T: null,
2051
- S: null,
2052
- actQueue: null,
2053
- asyncTransitions: 0,
2054
- isBatchingLegacy: false,
2055
- didScheduleLegacyUpdate: false,
2056
- didUsePromise: false,
2057
- thrownErrors: [],
2058
- getCurrentStack: null,
2059
- recentlyCreatedOwnerStacks: 0
2060
- },
2061
- hasOwnProperty = Object.prototype.hasOwnProperty,
2062
- createTask = console.createTask
2063
- ? console.createTask
2064
- : function () {
2065
- return null;
2066
- };
2067
- deprecatedAPIs = {
2068
- react_stack_bottom_frame: function (callStackForError) {
2069
- return callStackForError();
2070
- }
2071
- };
2072
- var specialPropKeyWarningShown, didWarnAboutOldJSXRuntime;
2073
- var didWarnAboutElementRef = {};
2074
- var unknownOwnerDebugStack = deprecatedAPIs.react_stack_bottom_frame.bind(
2075
- deprecatedAPIs,
2076
- UnknownOwner
2077
- )();
2078
- var unknownOwnerDebugTask = createTask(getTaskName(UnknownOwner));
2079
- var didWarnAboutMaps = false,
2080
- userProvidedKeyEscapeRegex = /\/+/g,
2081
- reportGlobalError =
2082
- "function" === typeof reportError
2083
- ? reportError
2084
- : function (error) {
2085
- if (
2086
- "object" === typeof window &&
2087
- "function" === typeof window.ErrorEvent
2088
- ) {
2089
- var event = new window.ErrorEvent("error", {
2090
- bubbles: true,
2091
- cancelable: true,
2092
- message:
2093
- "object" === typeof error &&
2094
- null !== error &&
2095
- "string" === typeof error.message
2096
- ? String(error.message)
2097
- : String(error),
2098
- error: error
2099
- });
2100
- if (!window.dispatchEvent(event)) return;
2101
- } else if (
2102
- "object" === typeof process &&
2103
- "function" === typeof process.emit
2104
- ) {
2105
- process.emit("uncaughtException", error);
2106
- return;
2107
- }
2108
- console.error(error);
2109
- },
2110
- didWarnAboutMessageChannel = false,
2111
- enqueueTaskImpl = null,
2112
- actScopeDepth = 0,
2113
- didWarnNoAwaitAct = false,
2114
- isFlushing = false,
2115
- queueSeveralMicrotasks =
2116
- "function" === typeof queueMicrotask
2117
- ? function (callback) {
2118
- queueMicrotask(function () {
2119
- return queueMicrotask(callback);
2120
- });
2121
- }
2122
- : enqueueTask;
2123
- deprecatedAPIs = Object.freeze({
2124
- __proto__: null,
2125
- c: function (size) {
2126
- return resolveDispatcher().useMemoCache(size);
2127
- }
2128
- });
2129
- var fnName = {
2130
- map: mapChildren,
2131
- forEach: function (children, forEachFunc, forEachContext) {
2132
- mapChildren(
2133
- children,
2134
- function () {
2135
- forEachFunc.apply(this, arguments);
2136
- },
2137
- forEachContext
2138
- );
2139
- },
2140
- count: function (children) {
2141
- var n = 0;
2142
- mapChildren(children, function () {
2143
- n++;
2144
- });
2145
- return n;
2146
- },
2147
- toArray: function (children) {
2148
- return (
2149
- mapChildren(children, function (child) {
2150
- return child;
2151
- }) || []
2152
- );
2153
- },
2154
- only: function (children) {
2155
- if (!isValidElement(children))
2156
- throw Error(
2157
- "React.Children.only expected to receive a single React element child."
2158
- );
2159
- return children;
2160
- }
2161
- };
2162
- exports.Activity = REACT_ACTIVITY_TYPE;
2163
- exports.Children = fnName;
2164
- exports.Component = Component;
2165
- exports.Fragment = REACT_FRAGMENT_TYPE;
2166
- exports.Profiler = REACT_PROFILER_TYPE;
2167
- exports.PureComponent = PureComponent;
2168
- exports.StrictMode = REACT_STRICT_MODE_TYPE;
2169
- exports.Suspense = REACT_SUSPENSE_TYPE;
2170
- exports.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE =
2171
- ReactSharedInternals;
2172
- exports.__COMPILER_RUNTIME = deprecatedAPIs;
2173
- exports.act = function (callback) {
2174
- var prevActQueue = ReactSharedInternals.actQueue,
2175
- prevActScopeDepth = actScopeDepth;
2176
- actScopeDepth++;
2177
- var queue = (ReactSharedInternals.actQueue =
2178
- null !== prevActQueue ? prevActQueue : []),
2179
- didAwaitActCall = false;
2180
- try {
2181
- var result = callback();
2182
- } catch (error) {
2183
- ReactSharedInternals.thrownErrors.push(error);
2184
- }
2185
- if (0 < ReactSharedInternals.thrownErrors.length)
2186
- throw (
2187
- (popActScope(prevActQueue, prevActScopeDepth),
2188
- (callback = aggregateErrors(ReactSharedInternals.thrownErrors)),
2189
- (ReactSharedInternals.thrownErrors.length = 0),
2190
- callback)
2191
- );
2192
- if (
2193
- null !== result &&
2194
- "object" === typeof result &&
2195
- "function" === typeof result.then
2196
- ) {
2197
- var thenable = result;
2198
- queueSeveralMicrotasks(function () {
2199
- didAwaitActCall ||
2200
- didWarnNoAwaitAct ||
2201
- ((didWarnNoAwaitAct = true),
2202
- console.error(
2203
- "You called act(async () => ...) without await. This could lead to unexpected testing behaviour, interleaving multiple act calls and mixing their scopes. You should - await act(async () => ...);"
2204
- ));
2205
- });
2206
- return {
2207
- then: function (resolve, reject) {
2208
- didAwaitActCall = true;
2209
- thenable.then(
2210
- function (returnValue) {
2211
- popActScope(prevActQueue, prevActScopeDepth);
2212
- if (0 === prevActScopeDepth) {
2213
- try {
2214
- flushActQueue(queue),
2215
- enqueueTask(function () {
2216
- return recursivelyFlushAsyncActWork(
2217
- returnValue,
2218
- resolve,
2219
- reject
2220
- );
2221
- });
2222
- } catch (error$0) {
2223
- ReactSharedInternals.thrownErrors.push(error$0);
2224
- }
2225
- if (0 < ReactSharedInternals.thrownErrors.length) {
2226
- var _thrownError = aggregateErrors(
2227
- ReactSharedInternals.thrownErrors
2228
- );
2229
- ReactSharedInternals.thrownErrors.length = 0;
2230
- reject(_thrownError);
2231
- }
2232
- } else resolve(returnValue);
2233
- },
2234
- function (error) {
2235
- popActScope(prevActQueue, prevActScopeDepth);
2236
- 0 < ReactSharedInternals.thrownErrors.length
2237
- ? ((error = aggregateErrors(
2238
- ReactSharedInternals.thrownErrors
2239
- )),
2240
- (ReactSharedInternals.thrownErrors.length = 0),
2241
- reject(error))
2242
- : reject(error);
2243
- }
2244
- );
2245
- }
2246
- };
2247
- }
2248
- var returnValue$jscomp$0 = result;
2249
- popActScope(prevActQueue, prevActScopeDepth);
2250
- 0 === prevActScopeDepth &&
2251
- (flushActQueue(queue),
2252
- 0 !== queue.length &&
2253
- queueSeveralMicrotasks(function () {
2254
- didAwaitActCall ||
2255
- didWarnNoAwaitAct ||
2256
- ((didWarnNoAwaitAct = true),
2257
- console.error(
2258
- "A component suspended inside an `act` scope, but the `act` call was not awaited. When testing React components that depend on asynchronous data, you must await the result:\n\nawait act(() => ...)"
2259
- ));
2260
- }),
2261
- (ReactSharedInternals.actQueue = null));
2262
- if (0 < ReactSharedInternals.thrownErrors.length)
2263
- throw (
2264
- ((callback = aggregateErrors(ReactSharedInternals.thrownErrors)),
2265
- (ReactSharedInternals.thrownErrors.length = 0),
2266
- callback)
2267
- );
2268
- return {
2269
- then: function (resolve, reject) {
2270
- didAwaitActCall = true;
2271
- 0 === prevActScopeDepth
2272
- ? ((ReactSharedInternals.actQueue = queue),
2273
- enqueueTask(function () {
2274
- return recursivelyFlushAsyncActWork(
2275
- returnValue$jscomp$0,
2276
- resolve,
2277
- reject
2278
- );
2279
- }))
2280
- : resolve(returnValue$jscomp$0);
2281
- }
2282
- };
2283
- };
2284
- exports.cache = function (fn) {
2285
- return function () {
2286
- return fn.apply(null, arguments);
2287
- };
2288
- };
2289
- exports.cacheSignal = function () {
2290
- return null;
2291
- };
2292
- exports.captureOwnerStack = function () {
2293
- var getCurrentStack = ReactSharedInternals.getCurrentStack;
2294
- return null === getCurrentStack ? null : getCurrentStack();
2295
- };
2296
- exports.cloneElement = function (element, config, children) {
2297
- if (null === element || void 0 === element)
2298
- throw Error(
2299
- "The argument must be a React element, but you passed " +
2300
- element +
2301
- "."
2302
- );
2303
- var props = assign({}, element.props),
2304
- key = element.key,
2305
- owner = element._owner;
2306
- if (null != config) {
2307
- var JSCompiler_inline_result;
2308
- a: {
2309
- if (
2310
- hasOwnProperty.call(config, "ref") &&
2311
- (JSCompiler_inline_result = Object.getOwnPropertyDescriptor(
2312
- config,
2313
- "ref"
2314
- ).get) &&
2315
- JSCompiler_inline_result.isReactWarning
2316
- ) {
2317
- JSCompiler_inline_result = false;
2318
- break a;
2319
- }
2320
- JSCompiler_inline_result = void 0 !== config.ref;
2321
- }
2322
- JSCompiler_inline_result && (owner = getOwner());
2323
- hasValidKey(config) &&
2324
- (checkKeyStringCoercion(config.key), (key = "" + config.key));
2325
- for (propName in config)
2326
- !hasOwnProperty.call(config, propName) ||
2327
- "key" === propName ||
2328
- "__self" === propName ||
2329
- "__source" === propName ||
2330
- ("ref" === propName && void 0 === config.ref) ||
2331
- (props[propName] = config[propName]);
2332
- }
2333
- var propName = arguments.length - 2;
2334
- if (1 === propName) props.children = children;
2335
- else if (1 < propName) {
2336
- JSCompiler_inline_result = Array(propName);
2337
- for (var i = 0; i < propName; i++)
2338
- JSCompiler_inline_result[i] = arguments[i + 2];
2339
- props.children = JSCompiler_inline_result;
2340
- }
2341
- props = ReactElement(
2342
- element.type,
2343
- key,
2344
- props,
2345
- owner,
2346
- element._debugStack,
2347
- element._debugTask
2348
- );
2349
- for (key = 2; key < arguments.length; key++)
2350
- validateChildKeys(arguments[key]);
2351
- return props;
2352
- };
2353
- exports.createContext = function (defaultValue) {
2354
- defaultValue = {
2355
- $$typeof: REACT_CONTEXT_TYPE,
2356
- _currentValue: defaultValue,
2357
- _currentValue2: defaultValue,
2358
- _threadCount: 0,
2359
- Provider: null,
2360
- Consumer: null
2361
- };
2362
- defaultValue.Provider = defaultValue;
2363
- defaultValue.Consumer = {
2364
- $$typeof: REACT_CONSUMER_TYPE,
2365
- _context: defaultValue
2366
- };
2367
- defaultValue._currentRenderer = null;
2368
- defaultValue._currentRenderer2 = null;
2369
- return defaultValue;
2370
- };
2371
- exports.createElement = function (type, config, children) {
2372
- for (var i = 2; i < arguments.length; i++)
2373
- validateChildKeys(arguments[i]);
2374
- i = {};
2375
- var key = null;
2376
- if (null != config)
2377
- for (propName in (didWarnAboutOldJSXRuntime ||
2378
- !("__self" in config) ||
2379
- "key" in config ||
2380
- ((didWarnAboutOldJSXRuntime = true),
2381
- console.warn(
2382
- "Your app (or one of its dependencies) is using an outdated JSX transform. Update to the modern JSX transform for faster performance: https://react.dev/link/new-jsx-transform"
2383
- )),
2384
- hasValidKey(config) &&
2385
- (checkKeyStringCoercion(config.key), (key = "" + config.key)),
2386
- config))
2387
- hasOwnProperty.call(config, propName) &&
2388
- "key" !== propName &&
2389
- "__self" !== propName &&
2390
- "__source" !== propName &&
2391
- (i[propName] = config[propName]);
2392
- var childrenLength = arguments.length - 2;
2393
- if (1 === childrenLength) i.children = children;
2394
- else if (1 < childrenLength) {
2395
- for (
2396
- var childArray = Array(childrenLength), _i = 0;
2397
- _i < childrenLength;
2398
- _i++
2399
- )
2400
- childArray[_i] = arguments[_i + 2];
2401
- Object.freeze && Object.freeze(childArray);
2402
- i.children = childArray;
2403
- }
2404
- if (type && type.defaultProps)
2405
- for (propName in ((childrenLength = type.defaultProps), childrenLength))
2406
- void 0 === i[propName] && (i[propName] = childrenLength[propName]);
2407
- key &&
2408
- defineKeyPropWarningGetter(
2409
- i,
2410
- "function" === typeof type
2411
- ? type.displayName || type.name || "Unknown"
2412
- : type
2413
- );
2414
- var propName = 1e4 > ReactSharedInternals.recentlyCreatedOwnerStacks++;
2415
- return ReactElement(
2416
- type,
2417
- key,
2418
- i,
2419
- getOwner(),
2420
- propName ? Error("react-stack-top-frame") : unknownOwnerDebugStack,
2421
- propName ? createTask(getTaskName(type)) : unknownOwnerDebugTask
2422
- );
2423
- };
2424
- exports.createRef = function () {
2425
- var refObject = { current: null };
2426
- Object.seal(refObject);
2427
- return refObject;
2428
- };
2429
- exports.forwardRef = function (render) {
2430
- null != render && render.$$typeof === REACT_MEMO_TYPE
2431
- ? console.error(
2432
- "forwardRef requires a render function but received a `memo` component. Instead of forwardRef(memo(...)), use memo(forwardRef(...))."
2433
- )
2434
- : "function" !== typeof render
2435
- ? console.error(
2436
- "forwardRef requires a render function but was given %s.",
2437
- null === render ? "null" : typeof render
2438
- )
2439
- : 0 !== render.length &&
2440
- 2 !== render.length &&
2441
- console.error(
2442
- "forwardRef render functions accept exactly two parameters: props and ref. %s",
2443
- 1 === render.length
2444
- ? "Did you forget to use the ref parameter?"
2445
- : "Any additional parameter will be undefined."
2446
- );
2447
- null != render &&
2448
- null != render.defaultProps &&
2449
- console.error(
2450
- "forwardRef render functions do not support defaultProps. Did you accidentally pass a React component?"
2451
- );
2452
- var elementType = { $$typeof: REACT_FORWARD_REF_TYPE, render: render },
2453
- ownName;
2454
- Object.defineProperty(elementType, "displayName", {
2455
- enumerable: false,
2456
- configurable: true,
2457
- get: function () {
2458
- return ownName;
2459
- },
2460
- set: function (name) {
2461
- ownName = name;
2462
- render.name ||
2463
- render.displayName ||
2464
- (Object.defineProperty(render, "name", { value: name }),
2465
- (render.displayName = name));
2466
- }
2467
- });
2468
- return elementType;
2469
- };
2470
- exports.isValidElement = isValidElement;
2471
- exports.lazy = function (ctor) {
2472
- ctor = { _status: -1, _result: ctor };
2473
- var lazyType = {
2474
- $$typeof: REACT_LAZY_TYPE,
2475
- _payload: ctor,
2476
- _init: lazyInitializer
2477
- },
2478
- ioInfo = {
2479
- name: "lazy",
2480
- start: -1,
2481
- end: -1,
2482
- value: null,
2483
- owner: null,
2484
- debugStack: Error("react-stack-top-frame"),
2485
- debugTask: console.createTask ? console.createTask("lazy()") : null
2486
- };
2487
- ctor._ioInfo = ioInfo;
2488
- lazyType._debugInfo = [{ awaited: ioInfo }];
2489
- return lazyType;
2490
- };
2491
- exports.memo = function (type, compare) {
2492
- null == type &&
2493
- console.error(
2494
- "memo: The first argument must be a component. Instead received: %s",
2495
- null === type ? "null" : typeof type
2496
- );
2497
- compare = {
2498
- $$typeof: REACT_MEMO_TYPE,
2499
- type: type,
2500
- compare: void 0 === compare ? null : compare
2501
- };
2502
- var ownName;
2503
- Object.defineProperty(compare, "displayName", {
2504
- enumerable: false,
2505
- configurable: true,
2506
- get: function () {
2507
- return ownName;
2508
- },
2509
- set: function (name) {
2510
- ownName = name;
2511
- type.name ||
2512
- type.displayName ||
2513
- (Object.defineProperty(type, "name", { value: name }),
2514
- (type.displayName = name));
2515
- }
2516
- });
2517
- return compare;
2518
- };
2519
- exports.startTransition = function (scope) {
2520
- var prevTransition = ReactSharedInternals.T,
2521
- currentTransition = {};
2522
- currentTransition._updatedFibers = new Set();
2523
- ReactSharedInternals.T = currentTransition;
2524
- try {
2525
- var returnValue = scope(),
2526
- onStartTransitionFinish = ReactSharedInternals.S;
2527
- null !== onStartTransitionFinish &&
2528
- onStartTransitionFinish(currentTransition, returnValue);
2529
- "object" === typeof returnValue &&
2530
- null !== returnValue &&
2531
- "function" === typeof returnValue.then &&
2532
- (ReactSharedInternals.asyncTransitions++,
2533
- returnValue.then(releaseAsyncTransition, releaseAsyncTransition),
2534
- returnValue.then(noop, reportGlobalError));
2535
- } catch (error) {
2536
- reportGlobalError(error);
2537
- } finally {
2538
- null === prevTransition &&
2539
- currentTransition._updatedFibers &&
2540
- ((scope = currentTransition._updatedFibers.size),
2541
- currentTransition._updatedFibers.clear(),
2542
- 10 < scope &&
2543
- console.warn(
2544
- "Detected a large number of updates inside startTransition. If this is due to a subscription please re-write it to use React provided hooks. Otherwise concurrent mode guarantees are off the table."
2545
- )),
2546
- null !== prevTransition &&
2547
- null !== currentTransition.types &&
2548
- (null !== prevTransition.types &&
2549
- prevTransition.types !== currentTransition.types &&
2550
- console.error(
2551
- "We expected inner Transitions to have transferred the outer types set and that you cannot add to the outer Transition while inside the inner.This is a bug in React."
2552
- ),
2553
- (prevTransition.types = currentTransition.types)),
2554
- (ReactSharedInternals.T = prevTransition);
2555
- }
2556
- };
2557
- exports.unstable_useCacheRefresh = function () {
2558
- return resolveDispatcher().useCacheRefresh();
2559
- };
2560
- exports.use = function (usable) {
2561
- return resolveDispatcher().use(usable);
2562
- };
2563
- exports.useActionState = function (action, initialState, permalink) {
2564
- return resolveDispatcher().useActionState(
2565
- action,
2566
- initialState,
2567
- permalink
2568
- );
2569
- };
2570
- exports.useCallback = function (callback, deps) {
2571
- return resolveDispatcher().useCallback(callback, deps);
2572
- };
2573
- exports.useContext = function (Context) {
2574
- var dispatcher = resolveDispatcher();
2575
- Context.$$typeof === REACT_CONSUMER_TYPE &&
2576
- console.error(
2577
- "Calling useContext(Context.Consumer) is not supported and will cause bugs. Did you mean to call useContext(Context) instead?"
2578
- );
2579
- return dispatcher.useContext(Context);
2580
- };
2581
- exports.useDebugValue = function (value, formatterFn) {
2582
- return resolveDispatcher().useDebugValue(value, formatterFn);
2583
- };
2584
- exports.useDeferredValue = function (value, initialValue) {
2585
- return resolveDispatcher().useDeferredValue(value, initialValue);
2586
- };
2587
- exports.useEffect = function (create, deps) {
2588
- null == create &&
2589
- console.warn(
2590
- "React Hook useEffect requires an effect callback. Did you forget to pass a callback to the hook?"
2591
- );
2592
- return resolveDispatcher().useEffect(create, deps);
2593
- };
2594
- exports.useEffectEvent = function (callback) {
2595
- return resolveDispatcher().useEffectEvent(callback);
2596
- };
2597
- exports.useId = function () {
2598
- return resolveDispatcher().useId();
2599
- };
2600
- exports.useImperativeHandle = function (ref, create, deps) {
2601
- return resolveDispatcher().useImperativeHandle(ref, create, deps);
2602
- };
2603
- exports.useInsertionEffect = function (create, deps) {
2604
- null == create &&
2605
- console.warn(
2606
- "React Hook useInsertionEffect requires an effect callback. Did you forget to pass a callback to the hook?"
2607
- );
2608
- return resolveDispatcher().useInsertionEffect(create, deps);
2609
- };
2610
- exports.useLayoutEffect = function (create, deps) {
2611
- null == create &&
2612
- console.warn(
2613
- "React Hook useLayoutEffect requires an effect callback. Did you forget to pass a callback to the hook?"
2614
- );
2615
- return resolveDispatcher().useLayoutEffect(create, deps);
2616
- };
2617
- exports.useMemo = function (create, deps) {
2618
- return resolveDispatcher().useMemo(create, deps);
2619
- };
2620
- exports.useOptimistic = function (passthrough, reducer) {
2621
- return resolveDispatcher().useOptimistic(passthrough, reducer);
2622
- };
2623
- exports.useReducer = function (reducer, initialArg, init) {
2624
- return resolveDispatcher().useReducer(reducer, initialArg, init);
2625
- };
2626
- exports.useRef = function (initialValue) {
2627
- return resolveDispatcher().useRef(initialValue);
2628
- };
2629
- exports.useState = function (initialState) {
2630
- return resolveDispatcher().useState(initialState);
2631
- };
2632
- exports.useSyncExternalStore = function (
2633
- subscribe,
2634
- getSnapshot,
2635
- getServerSnapshot
2636
- ) {
2637
- return resolveDispatcher().useSyncExternalStore(
2638
- subscribe,
2639
- getSnapshot,
2640
- getServerSnapshot
2641
- );
2642
- };
2643
- exports.useTransition = function () {
2644
- return resolveDispatcher().useTransition();
2645
- };
2646
- exports.version = "19.2.0";
2647
- "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
2648
- "function" ===
2649
- typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&
2650
- __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error());
2651
- })();
2652
- } (react_development, react_development.exports));
2653
- return react_development.exports;
2654
- }
2655
-
2656
- var hasRequiredReact;
2657
-
2658
- function requireReact () {
2659
- if (hasRequiredReact) return react.exports;
2660
- hasRequiredReact = 1;
2661
-
2662
- if (process.env.NODE_ENV === 'production') {
2663
- react.exports = requireReact_production();
2664
- } else {
2665
- react.exports = requireReact_development();
2666
- }
2667
- return react.exports;
2668
- }
2669
-
2670
- var reactExports = requireReact();
2671
-
2672
- /**
2673
- * React Hook: handles <input type="file" onChange> with validation + scanning.
2674
- */
2675
- function useFileScanner() {
2676
- const [results, setResults] = reactExports.useState([]);
2677
- const [errors, setErrors] = reactExports.useState([]);
2678
- const onChange = reactExports.useCallback(async (e) => {
2679
- const fileList = Array.from(e.target.files || []);
2680
- const good = [];
2681
- const bad = [];
2682
- for (const file of fileList) {
2683
- const { valid, error } = validateFile(file);
2684
- if (valid)
2685
- good.push(file);
2686
- else
2687
- bad.push({ file, error: error });
2688
- }
2689
- setErrors(bad);
2690
- if (good.length) {
2691
- const scanned = await scanFiles(good);
2692
- setResults(scanned.map((r, i) => ({ file: good[i], report: r })));
2693
- }
2694
- else {
2695
- setResults([]);
2696
- }
2697
- }, []);
2698
- return { results, errors, onChange };
2699
- }
2700
-
2701
847
  async function createRemoteEngine(opts) {
2702
848
  const { endpoint, headers = {}, rulesField = 'rules', fileField = 'file', mode = 'multipart', rulesAsBase64 = false, } = opts;
2703
849
  const engine = {
@@ -2786,548 +932,121 @@ async function scanFilesWithRemoteYara(files, rulesSource, remote) {
2786
932
  return results;
2787
933
  }
2788
934
 
2789
- /** Decompilation-specific types for Pompelmi */
2790
- const SUSPICIOUS_PATTERNS = [
2791
- {
2792
- name: 'syscall_direct',
2793
- description: 'Direct system call without library wrapper',
2794
- severity: 'medium',
2795
- pattern: /syscall|sysenter|int\s+0x80/i
2796
- },
2797
- {
2798
- name: 'process_injection',
2799
- description: 'Process injection techniques',
2800
- severity: 'high',
2801
- pattern: /CreateRemoteThread|WriteProcessMemory|VirtualAllocEx/i
2802
- },
2803
- {
2804
- name: 'anti_debug',
2805
- description: 'Anti-debugging techniques',
2806
- severity: 'medium',
2807
- pattern: /IsDebuggerPresent|CheckRemoteDebuggerPresent|OutputDebugString/i
2808
- },
2809
- {
2810
- name: 'obfuscation_xor',
2811
- description: 'XOR-based obfuscation pattern',
2812
- severity: 'medium',
2813
- pattern: /xor.*0x[0-9a-f]+.*xor/i
2814
- },
2815
- {
2816
- name: 'crypto_constants',
2817
- description: 'Cryptographic constants',
2818
- severity: 'low',
2819
- pattern: /0x67452301|0xefcdab89|0x98badcfe|0x10325476/i
2820
- }
2821
- ];
2822
-
2823
- /**
2824
- * HIPAA Compliance Module for Pompelmi
2825
- *
2826
- * This module provides comprehensive HIPAA compliance features for healthcare environments
2827
- * where Pompelmi is used to analyze potentially compromised systems containing PHI.
2828
- *
2829
- * Key protections:
2830
- * - Data sanitization and redaction
2831
- * - Secure temporary file handling
2832
- * - Audit logging
2833
- * - Memory protection
2834
- * - Error message sanitization
2835
- */
2836
- class HipaaComplianceManager {
2837
- constructor(config) {
2838
- this.auditEvents = [];
2839
- this.config = {
2840
- sanitizeErrors: true,
2841
- sanitizeFilenames: true,
2842
- encryptTempFiles: true,
2843
- memoryProtection: true,
2844
- requireSecureTransport: true,
2845
- ...config,
2846
- enabled: config.enabled !== undefined ? config.enabled : true
2847
- };
2848
- this.sessionId = this.generateSessionId();
2849
- }
2850
- /**
2851
- * Sanitize filename to prevent PHI leakage in logs
2852
- */
2853
- sanitizeFilename(filename) {
2854
- if (!this.config.enabled || !this.config.sanitizeFilenames || !filename) {
2855
- return filename || 'unknown';
2856
- }
2857
- // Remove potentially sensitive path information
2858
- const basename = path__namespace.basename(filename);
2859
- // Hash the filename to create a consistent but non-revealing identifier
2860
- const hash = crypto__namespace.createHash('sha256').update(basename).digest('hex').substring(0, 8);
2861
- // Preserve file extension for analysis purposes
2862
- const ext = path__namespace.extname(basename);
2863
- return `file_${hash}${ext}`;
2864
- }
2865
- /**
2866
- * Sanitize error messages to prevent PHI exposure
2867
- */
2868
- sanitizeError(error) {
2869
- if (!this.config.enabled || !this.config.sanitizeErrors) {
2870
- return typeof error === 'string' ? error : error.message;
2871
- }
2872
- const message = typeof error === 'string' ? error : error.message;
2873
- // Remove common patterns that might contain PHI
2874
- let sanitized = message
2875
- // Remove file paths
2876
- .replace(/[A-Za-z]:\\\\[^\\s]+/g, '[REDACTED_PATH]')
2877
- .replace(/\/[^\\s]+/g, '[REDACTED_PATH]')
2878
- // Remove potential patient identifiers (numbers that could be MRNs, SSNs)
2879
- .replace(/\\b\\d{3}-?\\d{2}-?\\d{4}\\b/g, '[REDACTED_ID]')
2880
- .replace(/\\b\\d{6,}\\b/g, '[REDACTED_ID]')
2881
- // Remove email addresses
2882
- .replace(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/g, '[REDACTED_EMAIL]')
2883
- // Remove potential names (capitalize words in error messages)
2884
- .replace(/\\b[A-Z][a-z]+\\s+[A-Z][a-z]+\\b/g, '[REDACTED_NAME]')
2885
- // Remove IP addresses
2886
- .replace(/\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b/g, '[REDACTED_IP]');
2887
- return sanitized;
2888
- }
2889
- /**
2890
- * Create secure temporary file path with encryption if enabled
2891
- */
2892
- createSecureTempPath(prefix = 'pompelmi') {
2893
- if (!this.config.enabled) {
2894
- return path__namespace.join(os__namespace.tmpdir(), `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2)}`);
2895
- }
2896
- // Use cryptographically secure random names
2897
- const randomId = crypto__namespace.randomBytes(16).toString('hex');
2898
- const timestamp = Date.now();
2899
- // Create path in secure temp directory
2900
- const secureTempDir = this.getSecureTempDir();
2901
- const tempPath = path__namespace.join(secureTempDir, `${prefix}-${timestamp}-${randomId}`);
2902
- this.auditLog('temp_file_created', {
2903
- action: 'create_temp_file',
2904
- success: true,
2905
- metadata: { path: this.sanitizeFilename(tempPath) }
2906
- });
2907
- return tempPath;
2908
- }
2909
- /**
2910
- * Get or create secure temporary directory with restricted permissions
2911
- */
2912
- getSecureTempDir() {
2913
- const secureTempPath = path__namespace.join(os__namespace.tmpdir(), 'pompelmi-secure');
2914
- try {
2915
- const fs = require('fs');
2916
- if (!fs.existsSync(secureTempPath)) {
2917
- fs.mkdirSync(secureTempPath, { mode: 0o700 }); // Owner read/write/execute only
2918
- }
2919
- }
2920
- catch (error) {
2921
- // Fallback to system temp
2922
- return os__namespace.tmpdir();
2923
- }
2924
- return secureTempPath;
2925
- }
2926
- /**
2927
- * Secure file cleanup with multiple overwrite passes
2928
- */
2929
- async secureFileCleanup(filePath) {
2930
- if (!this.config.enabled) {
2931
- try {
2932
- const fs = await import('fs/promises');
2933
- await fs.unlink(filePath);
935
+ const SIG_CEN = 0x02014b50;
936
+ const DEFAULTS = {
937
+ maxEntries: 1000,
938
+ maxTotalUncompressedBytes: 500 * 1024 * 1024,
939
+ maxEntryNameLength: 255,
940
+ maxCompressionRatio: 1000,
941
+ eocdSearchWindow: 70000,
942
+ };
943
+ function r16(buf, off) {
944
+ return buf.readUInt16LE(off);
945
+ }
946
+ function r32(buf, off) {
947
+ return buf.readUInt32LE(off);
948
+ }
949
+ function isZipLike(buf) {
950
+ // local file header at start is common
951
+ return buf.length >= 4 && buf[0] === 0x50 && buf[1] === 0x4b && buf[2] === 0x03 && buf[3] === 0x04;
952
+ }
953
+ function lastIndexOfEOCD(buf, window) {
954
+ const sig = Buffer.from([0x50, 0x4b, 0x05, 0x06]);
955
+ const start = Math.max(0, buf.length - window);
956
+ const idx = buf.lastIndexOf(sig, Math.min(buf.length - sig.length, buf.length - 1));
957
+ return idx >= start ? idx : -1;
958
+ }
959
+ function hasTraversal(name) {
960
+ return name.includes('../') || name.includes('..\\') || name.startsWith('/') || /^[A-Za-z]:/.test(name);
961
+ }
962
+ function createZipBombGuard(opts = {}) {
963
+ const cfg = { ...DEFAULTS, ...opts };
964
+ return {
965
+ async scan(input) {
966
+ const buf = Buffer.from(input);
967
+ const matches = [];
968
+ if (!isZipLike(buf))
969
+ return matches;
970
+ // Find EOCD near the end
971
+ const eocdPos = lastIndexOfEOCD(buf, cfg.eocdSearchWindow);
972
+ if (eocdPos < 0 || eocdPos + 22 > buf.length) {
973
+ // ZIP but no EOCD malformed or polyglot suspicious
974
+ matches.push({ rule: 'zip_eocd_not_found', severity: 'medium' });
975
+ return matches;
2934
976
  }
2935
- catch {
2936
- // Ignore cleanup errors
977
+ const totalEntries = r16(buf, eocdPos + 10);
978
+ const cdSize = r32(buf, eocdPos + 12);
979
+ const cdOffset = r32(buf, eocdPos + 16);
980
+ // Bounds check
981
+ if (cdOffset + cdSize > buf.length) {
982
+ matches.push({ rule: 'zip_cd_out_of_bounds', severity: 'medium' });
983
+ return matches;
2937
984
  }
2938
- return;
2939
- }
2940
- try {
2941
- const fs = await import('fs/promises');
2942
- const stats = await fs.stat(filePath);
2943
- if (this.config.memoryProtection) {
2944
- // Overwrite file with random data multiple times (DoD 5220.22-M standard)
2945
- const fileSize = stats.size;
2946
- const buffer = crypto__namespace.randomBytes(Math.min(fileSize, 64 * 1024)); // 64KB chunks
2947
- for (let pass = 0; pass < 3; pass++) {
2948
- const handle = await fs.open(filePath, 'r+');
2949
- try {
2950
- for (let offset = 0; offset < fileSize; offset += buffer.length) {
2951
- const chunk = offset + buffer.length > fileSize
2952
- ? buffer.subarray(0, fileSize - offset)
2953
- : buffer;
2954
- await handle.write(chunk, 0, chunk.length, offset);
2955
- }
2956
- await handle.sync();
2957
- }
2958
- finally {
2959
- await handle.close();
2960
- }
985
+ // Iterate central directory entries
986
+ let ptr = cdOffset;
987
+ let seen = 0;
988
+ let sumComp = 0;
989
+ let sumUnc = 0;
990
+ while (ptr + 46 <= cdOffset + cdSize && seen < totalEntries) {
991
+ const sig = r32(buf, ptr);
992
+ if (sig !== SIG_CEN)
993
+ break; // stop if structure breaks
994
+ const compSize = r32(buf, ptr + 20);
995
+ const uncSize = r32(buf, ptr + 24);
996
+ const fnLen = r16(buf, ptr + 28);
997
+ const exLen = r16(buf, ptr + 30);
998
+ const cmLen = r16(buf, ptr + 32);
999
+ const nameStart = ptr + 46;
1000
+ const nameEnd = nameStart + fnLen;
1001
+ if (nameEnd > buf.length)
1002
+ break;
1003
+ const name = buf.toString('utf8', nameStart, nameEnd);
1004
+ sumComp += compSize;
1005
+ sumUnc += uncSize;
1006
+ seen++;
1007
+ if (name.length > cfg.maxEntryNameLength) {
1008
+ matches.push({ rule: 'zip_entry_name_too_long', severity: 'medium', meta: { name, length: name.length } });
2961
1009
  }
2962
- }
2963
- // Final deletion
2964
- await fs.unlink(filePath);
2965
- this.auditLog('temp_file_deleted', {
2966
- action: 'secure_delete',
2967
- success: true,
2968
- metadata: {
2969
- path: this.sanitizeFilename(filePath),
2970
- overwritePasses: this.config.memoryProtection ? 3 : 0
1010
+ if (hasTraversal(name)) {
1011
+ matches.push({ rule: 'zip_path_traversal_entry', severity: 'medium', meta: { name } });
2971
1012
  }
2972
- });
2973
- }
2974
- catch (error) {
2975
- this.auditLog('temp_file_deleted', {
2976
- action: 'secure_delete',
2977
- success: false,
2978
- sanitizedError: this.sanitizeError(error),
2979
- metadata: { path: this.sanitizeFilename(filePath) }
2980
- });
2981
- }
2982
- }
2983
- /**
2984
- * Calculate secure file hash for audit purposes
2985
- */
2986
- calculateFileHash(data) {
2987
- return crypto__namespace.createHash('sha256').update(data).digest('hex');
2988
- }
2989
- /**
2990
- * Log audit event
2991
- */
2992
- auditLog(eventType, details) {
2993
- if (!this.config.enabled)
2994
- return;
2995
- const event = {
2996
- timestamp: new Date().toISOString(),
2997
- eventType,
2998
- sessionId: this.sessionId,
2999
- details: {
3000
- action: details.action || 'unknown',
3001
- success: details.success ?? true,
3002
- ...details
1013
+ // move to next entry
1014
+ ptr = nameEnd + exLen + cmLen;
3003
1015
  }
3004
- };
3005
- this.auditEvents.push(event);
3006
- // Write to audit log file if configured
3007
- if (this.config.auditLogPath) {
3008
- this.writeAuditLog(event).catch(() => {
3009
- // Silent failure to prevent error loops
3010
- });
3011
- }
3012
- }
3013
- /**
3014
- * Write audit event to file
3015
- */
3016
- async writeAuditLog(event) {
3017
- if (!this.config.auditLogPath)
3018
- return;
3019
- try {
3020
- const fs = await import('fs/promises');
3021
- const logLine = JSON.stringify(event) + '\\n';
3022
- await fs.appendFile(this.config.auditLogPath, logLine, { flag: 'a' });
3023
- }
3024
- catch {
3025
- // Silent failure
3026
- }
3027
- }
3028
- /**
3029
- * Generate cryptographically secure session ID
3030
- */
3031
- generateSessionId() {
3032
- return crypto__namespace.randomBytes(16).toString('hex');
3033
- }
3034
- /**
3035
- * Get current audit events for this session
3036
- */
3037
- getAuditEvents() {
3038
- return [...this.auditEvents];
3039
- }
3040
- /**
3041
- * Clear sensitive data from memory
3042
- */
3043
- clearSensitiveData() {
3044
- if (!this.config.enabled || !this.config.memoryProtection)
3045
- return;
3046
- // Clear audit events
3047
- this.auditEvents.length = 0;
3048
- // Force garbage collection if available
3049
- if (global.gc) {
3050
- global.gc();
3051
- }
3052
- }
3053
- /**
3054
- * Validate transport security
3055
- */
3056
- validateTransportSecurity(url) {
3057
- if (!this.config.enabled || !this.config.requireSecureTransport) {
3058
- return true;
3059
- }
3060
- if (!url)
3061
- return true;
3062
- try {
3063
- const urlObj = new URL(url);
3064
- const isSecure = urlObj.protocol === 'https:' || urlObj.hostname === 'localhost' || urlObj.hostname === '127.0.0.1';
3065
- if (!isSecure) {
3066
- this.auditLog('security_violation', {
3067
- action: 'insecure_transport',
3068
- success: false,
3069
- metadata: { protocol: urlObj.protocol, hostname: urlObj.hostname }
1016
+ if (seen !== totalEntries) {
1017
+ // central dir truncated/odd, still report what we found
1018
+ matches.push({ rule: 'zip_cd_truncated', severity: 'medium', meta: { seen, totalEntries } });
1019
+ }
1020
+ // Heuristics thresholds
1021
+ if (seen > cfg.maxEntries) {
1022
+ matches.push({ rule: 'zip_too_many_entries', severity: 'medium', meta: { seen, limit: cfg.maxEntries } });
1023
+ }
1024
+ if (sumUnc > cfg.maxTotalUncompressedBytes) {
1025
+ matches.push({
1026
+ rule: 'zip_total_uncompressed_too_large',
1027
+ severity: 'medium',
1028
+ meta: { totalUncompressed: sumUnc, limit: cfg.maxTotalUncompressedBytes }
3070
1029
  });
3071
1030
  }
3072
- return isSecure;
3073
- }
3074
- catch {
3075
- return false;
1031
+ if (sumComp === 0 && sumUnc > 0) {
1032
+ matches.push({ rule: 'zip_suspicious_ratio', severity: 'medium', meta: { ratio: Infinity } });
1033
+ }
1034
+ else if (sumComp > 0) {
1035
+ const ratio = sumUnc / Math.max(1, sumComp);
1036
+ if (ratio >= cfg.maxCompressionRatio) {
1037
+ matches.push({ rule: 'zip_suspicious_ratio', severity: 'medium', meta: { ratio, limit: cfg.maxCompressionRatio } });
1038
+ }
1039
+ }
1040
+ return matches;
3076
1041
  }
3077
- }
3078
- }
3079
- // Global HIPAA compliance instance
3080
- let hipaaManager = null;
3081
- /**
3082
- * Initialize HIPAA compliance
3083
- */
3084
- function initializeHipaaCompliance(config) {
3085
- hipaaManager = new HipaaComplianceManager(config);
3086
- return hipaaManager;
3087
- }
3088
- /**
3089
- * Get current HIPAA compliance manager
3090
- */
3091
- function getHipaaManager() {
3092
- return hipaaManager;
3093
- }
3094
- /**
3095
- * HIPAA-compliant error wrapper
3096
- */
3097
- function createHipaaError(error, context) {
3098
- const manager = getHipaaManager();
3099
- if (!manager) {
3100
- return typeof error === 'string' ? new Error(error) : error;
3101
- }
3102
- const sanitizedMessage = manager.sanitizeError(error);
3103
- const hipaaError = new Error(sanitizedMessage);
3104
- manager.auditLog('error_occurred', {
3105
- action: context || 'error',
3106
- success: false,
3107
- sanitizedError: sanitizedMessage
3108
- });
3109
- return hipaaError;
3110
- }
3111
- /**
3112
- * HIPAA-compliant temporary file utilities
3113
- */
3114
- const HipaaTemp = {
3115
- createPath: (prefix) => {
3116
- const manager = getHipaaManager();
3117
- return manager ? manager.createSecureTempPath(prefix) : path__namespace.join(os__namespace.tmpdir(), `${prefix || 'pompelmi'}-${Date.now()}`);
3118
- },
3119
- cleanup: async (filePath) => {
3120
- const manager = getHipaaManager();
3121
- if (manager) {
3122
- await manager.secureFileCleanup(filePath);
3123
- }
3124
- else {
3125
- try {
3126
- const fs = await import('fs/promises');
3127
- await fs.unlink(filePath);
3128
- }
3129
- catch {
3130
- // Ignore errors
3131
- }
3132
- }
3133
- }
3134
- };
3135
-
3136
- function mapMatchesToVerdict(matches = []) {
3137
- if (!matches.length)
3138
- return 'clean';
3139
- const malHints = ['trojan', 'ransom', 'worm', 'spy', 'rootkit', 'keylog', 'botnet'];
3140
- const tagSet = new Set(matches.flatMap(m => (m.tags ?? []).map(t => t.toLowerCase())));
3141
- const nameHit = (r) => malHints.some(h => r.toLowerCase().includes(h));
3142
- const isMal = matches.some(m => nameHit(m.rule)) || tagSet.has('malware') || tagSet.has('critical');
3143
- return isMal ? 'malicious' : 'suspicious';
3144
- }
3145
-
3146
- function hasAsciiToken(buf, token) {
3147
- // Use latin1 so we can safely search binary
3148
- return buf.indexOf(token, 0, 'latin1') !== -1;
3149
- }
3150
- function startsWith(buf, bytes) {
3151
- if (buf.length < bytes.length)
3152
- return false;
3153
- for (let i = 0; i < bytes.length; i++)
3154
- if (buf[i] !== bytes[i])
3155
- return false;
3156
- return true;
3157
- }
3158
- function isPDF(buf) {
3159
- // %PDF-
3160
- return startsWith(buf, [0x25, 0x50, 0x44, 0x46, 0x2d]);
3161
- }
3162
- function isOleCfb(buf) {
3163
- // D0 CF 11 E0 A1 B1 1A E1
3164
- const sig = [0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1];
3165
- return startsWith(buf, sig);
3166
- }
3167
- function isZipLike$1(buf) {
3168
- // PK\x03\x04
3169
- return startsWith(buf, [0x50, 0x4b, 0x03, 0x04]);
3170
- }
3171
- function isPeExecutable(buf) {
3172
- // "MZ"
3173
- return startsWith(buf, [0x4d, 0x5a]);
3174
- }
3175
- /** OOXML macro hint via filename token in ZIP container */
3176
- function hasOoxmlMacros(buf) {
3177
- if (!isZipLike$1(buf))
3178
- return false;
3179
- return hasAsciiToken(buf, 'vbaProject.bin');
3180
- }
3181
- /** PDF risky features (/JavaScript, /OpenAction, /AA, /Launch) */
3182
- function pdfRiskTokens(buf) {
3183
- const tokens = ['/JavaScript', '/OpenAction', '/AA', '/Launch'];
3184
- return tokens.filter(t => hasAsciiToken(buf, t));
3185
- }
3186
- const CommonHeuristicsScanner = {
3187
- async scan(input) {
3188
- const buf = Buffer.from(input);
3189
- const matches = [];
3190
- // Office macros (OLE / OOXML)
3191
- if (isOleCfb(buf)) {
3192
- matches.push({ rule: 'office_ole_container', severity: 'suspicious' });
3193
- }
3194
- if (hasOoxmlMacros(buf)) {
3195
- matches.push({ rule: 'office_ooxml_macros', severity: 'suspicious' });
3196
- }
3197
- // PDF risky tokens
3198
- if (isPDF(buf)) {
3199
- const toks = pdfRiskTokens(buf);
3200
- if (toks.length) {
3201
- matches.push({
3202
- rule: 'pdf_risky_actions',
3203
- severity: 'suspicious',
3204
- meta: { tokens: toks }
3205
- });
3206
- }
3207
- }
3208
- // Executable header
3209
- if (isPeExecutable(buf)) {
3210
- matches.push({ rule: 'pe_executable_signature', severity: 'suspicious' });
3211
- }
3212
- return matches;
3213
- }
3214
- };
3215
-
3216
- const SIG_CEN = 0x02014b50;
3217
- const DEFAULTS = {
3218
- maxEntries: 1000,
3219
- maxTotalUncompressedBytes: 500 * 1024 * 1024,
3220
- maxEntryNameLength: 255,
3221
- maxCompressionRatio: 1000,
3222
- eocdSearchWindow: 70000,
3223
- };
3224
- function r16(buf, off) {
3225
- return buf.readUInt16LE(off);
3226
- }
3227
- function r32(buf, off) {
3228
- return buf.readUInt32LE(off);
3229
- }
3230
- function isZipLike(buf) {
3231
- // local file header at start is common
3232
- return buf.length >= 4 && buf[0] === 0x50 && buf[1] === 0x4b && buf[2] === 0x03 && buf[3] === 0x04;
3233
- }
3234
- function lastIndexOfEOCD(buf, window) {
3235
- const sig = Buffer.from([0x50, 0x4b, 0x05, 0x06]);
3236
- const start = Math.max(0, buf.length - window);
3237
- const idx = buf.lastIndexOf(sig, Math.min(buf.length - sig.length, buf.length - 1));
3238
- return idx >= start ? idx : -1;
3239
- }
3240
- function hasTraversal(name) {
3241
- return name.includes('../') || name.includes('..\\') || name.startsWith('/') || /^[A-Za-z]:/.test(name);
3242
- }
3243
- function createZipBombGuard(opts = {}) {
3244
- const cfg = { ...DEFAULTS, ...opts };
3245
- return {
3246
- async scan(input) {
3247
- const buf = Buffer.from(input);
3248
- const matches = [];
3249
- if (!isZipLike(buf))
3250
- return matches;
3251
- // Find EOCD near the end
3252
- const eocdPos = lastIndexOfEOCD(buf, cfg.eocdSearchWindow);
3253
- if (eocdPos < 0 || eocdPos + 22 > buf.length) {
3254
- // ZIP but no EOCD — malformed or polyglot → suspicious
3255
- matches.push({ rule: 'zip_eocd_not_found', severity: 'medium' });
3256
- return matches;
3257
- }
3258
- const totalEntries = r16(buf, eocdPos + 10);
3259
- const cdSize = r32(buf, eocdPos + 12);
3260
- const cdOffset = r32(buf, eocdPos + 16);
3261
- // Bounds check
3262
- if (cdOffset + cdSize > buf.length) {
3263
- matches.push({ rule: 'zip_cd_out_of_bounds', severity: 'medium' });
3264
- return matches;
3265
- }
3266
- // Iterate central directory entries
3267
- let ptr = cdOffset;
3268
- let seen = 0;
3269
- let sumComp = 0;
3270
- let sumUnc = 0;
3271
- while (ptr + 46 <= cdOffset + cdSize && seen < totalEntries) {
3272
- const sig = r32(buf, ptr);
3273
- if (sig !== SIG_CEN)
3274
- break; // stop if structure breaks
3275
- const compSize = r32(buf, ptr + 20);
3276
- const uncSize = r32(buf, ptr + 24);
3277
- const fnLen = r16(buf, ptr + 28);
3278
- const exLen = r16(buf, ptr + 30);
3279
- const cmLen = r16(buf, ptr + 32);
3280
- const nameStart = ptr + 46;
3281
- const nameEnd = nameStart + fnLen;
3282
- if (nameEnd > buf.length)
3283
- break;
3284
- const name = buf.toString('utf8', nameStart, nameEnd);
3285
- sumComp += compSize;
3286
- sumUnc += uncSize;
3287
- seen++;
3288
- if (name.length > cfg.maxEntryNameLength) {
3289
- matches.push({ rule: 'zip_entry_name_too_long', severity: 'medium', meta: { name, length: name.length } });
3290
- }
3291
- if (hasTraversal(name)) {
3292
- matches.push({ rule: 'zip_path_traversal_entry', severity: 'medium', meta: { name } });
3293
- }
3294
- // move to next entry
3295
- ptr = nameEnd + exLen + cmLen;
3296
- }
3297
- if (seen !== totalEntries) {
3298
- // central dir truncated/odd, still report what we found
3299
- matches.push({ rule: 'zip_cd_truncated', severity: 'medium', meta: { seen, totalEntries } });
3300
- }
3301
- // Heuristics thresholds
3302
- if (seen > cfg.maxEntries) {
3303
- matches.push({ rule: 'zip_too_many_entries', severity: 'medium', meta: { seen, limit: cfg.maxEntries } });
3304
- }
3305
- if (sumUnc > cfg.maxTotalUncompressedBytes) {
3306
- matches.push({
3307
- rule: 'zip_total_uncompressed_too_large',
3308
- severity: 'medium',
3309
- meta: { totalUncompressed: sumUnc, limit: cfg.maxTotalUncompressedBytes }
3310
- });
3311
- }
3312
- if (sumComp === 0 && sumUnc > 0) {
3313
- matches.push({ rule: 'zip_suspicious_ratio', severity: 'medium', meta: { ratio: Infinity } });
3314
- }
3315
- else if (sumComp > 0) {
3316
- const ratio = sumUnc / Math.max(1, sumComp);
3317
- if (ratio >= cfg.maxCompressionRatio) {
3318
- matches.push({ rule: 'zip_suspicious_ratio', severity: 'medium', meta: { ratio, limit: cfg.maxCompressionRatio } });
3319
- }
3320
- }
3321
- return matches;
3322
- }
3323
- };
1042
+ };
3324
1043
  }
3325
1044
 
3326
- const MB = 1024 * 1024;
1045
+ const MB$1 = 1024 * 1024;
3327
1046
  const DEFAULT_POLICY = {
3328
1047
  includeExtensions: ['zip', 'png', 'jpg', 'jpeg', 'pdf'],
3329
1048
  allowedMimeTypes: ['application/zip', 'image/png', 'image/jpeg', 'application/pdf', 'text/plain'],
3330
- maxFileSizeBytes: 20 * MB,
1049
+ maxFileSizeBytes: 20 * MB$1,
3331
1050
  timeoutMs: 5000,
3332
1051
  concurrency: 4,
3333
1052
  failClosed: true
@@ -3348,25 +1067,274 @@ function definePolicy(input = {}) {
3348
1067
  }
3349
1068
 
3350
1069
  /**
3351
- * Batch scanning with concurrency control
3352
- * @module utils/batch-scanner
1070
+ * Policy packs for Pompelmi.
1071
+ *
1072
+ * Pre-configured, named policies for common upload scenarios. Each pack
1073
+ * defines the file type allowlist, size limits, and timeout appropriate for
1074
+ * its use case.
1075
+ *
1076
+ * All packs are built on `definePolicy` and are fully overridable:
1077
+ *
1078
+ * ```ts
1079
+ * import { POLICY_PACKS } from 'pompelmi/policy-packs';
1080
+ *
1081
+ * // Use a pack as-is:
1082
+ * const policy = POLICY_PACKS['images-only'];
1083
+ *
1084
+ * // Or override individual fields:
1085
+ * import { definePolicy } from 'pompelmi';
1086
+ * const custom = definePolicy({ ...POLICY_PACKS['documents-only'], maxFileSizeBytes: 5 * 1024 * 1024 });
1087
+ * ```
1088
+ *
1089
+ * These packs are *deterministic* and *descriptor-based* — they do not
1090
+ * depend on any external threat intelligence feed.
1091
+ *
1092
+ * @module policy-packs
3353
1093
  */
1094
+ const KB = 1024;
1095
+ const MB = 1024 * KB;
1096
+ // ── Policy packs ──────────────────────────────────────────────────────────────
3354
1097
  /**
3355
- * Batch file scanner with concurrency control and progress tracking
1098
+ * Documents-only policy.
1099
+ *
1100
+ * Appropriate for: document management APIs, PDF/Office file upload endpoints,
1101
+ * data import pipelines.
1102
+ *
1103
+ * Allowed: PDF, Word (.docx/.doc), Excel (.xlsx/.xls), PowerPoint (.pptx/.ppt),
1104
+ * CSV, plain text, JSON, YAML, ODT/ODS/ODP (OpenDocument).
1105
+ * Max size: 25 MB.
3356
1106
  */
3357
- class BatchScanner {
3358
- constructor(options = {}) {
3359
- this.options = {
3360
- concurrency: 5,
3361
- continueOnError: true,
3362
- ...options,
3363
- };
3364
- }
3365
- /**
3366
- * Scan multiple files with controlled concurrency
3367
- */
3368
- async scanBatch(tasks) {
3369
- const startTime = Date.now();
1107
+ const DOCUMENTS_ONLY = definePolicy({
1108
+ includeExtensions: [
1109
+ 'pdf',
1110
+ 'doc', 'docx',
1111
+ 'xls', 'xlsx',
1112
+ 'ppt', 'pptx',
1113
+ 'odt', 'ods', 'odp',
1114
+ 'csv',
1115
+ 'txt',
1116
+ 'json',
1117
+ 'yaml', 'yml',
1118
+ 'md',
1119
+ ],
1120
+ allowedMimeTypes: [
1121
+ 'application/pdf',
1122
+ 'application/msword',
1123
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
1124
+ 'application/vnd.ms-excel',
1125
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
1126
+ 'application/vnd.ms-powerpoint',
1127
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
1128
+ 'application/vnd.oasis.opendocument.text',
1129
+ 'application/vnd.oasis.opendocument.spreadsheet',
1130
+ 'application/vnd.oasis.opendocument.presentation',
1131
+ 'text/csv',
1132
+ 'text/plain',
1133
+ 'application/json',
1134
+ 'text/yaml',
1135
+ 'text/markdown',
1136
+ ],
1137
+ maxFileSizeBytes: 25 * MB,
1138
+ timeoutMs: 10000,
1139
+ concurrency: 4,
1140
+ failClosed: true,
1141
+ });
1142
+ /**
1143
+ * Images-only policy.
1144
+ *
1145
+ * Appropriate for: avatar uploads, product image APIs, content platforms with
1146
+ * user-generated imagery.
1147
+ *
1148
+ * Allowed: JPEG, PNG, GIF, WebP, AVIF, TIFF, BMP, ICO.
1149
+ * Max size: 10 MB.
1150
+ * Note: SVG is intentionally excluded — inline SVGs can contain scripts.
1151
+ */
1152
+ const IMAGES_ONLY = definePolicy({
1153
+ includeExtensions: ['jpg', 'jpeg', 'png', 'gif', 'webp', 'avif', 'tiff', 'tif', 'bmp', 'ico'],
1154
+ allowedMimeTypes: [
1155
+ 'image/jpeg',
1156
+ 'image/png',
1157
+ 'image/gif',
1158
+ 'image/webp',
1159
+ 'image/avif',
1160
+ 'image/tiff',
1161
+ 'image/bmp',
1162
+ 'image/x-icon',
1163
+ 'image/vnd.microsoft.icon',
1164
+ ],
1165
+ maxFileSizeBytes: 10 * MB,
1166
+ timeoutMs: 5000,
1167
+ concurrency: 8,
1168
+ failClosed: true,
1169
+ });
1170
+ /**
1171
+ * Strict public-upload policy.
1172
+ *
1173
+ * Appropriate for: anonymous or low-trust upload endpoints, public APIs,
1174
+ * any surface exposed to untrusted users.
1175
+ *
1176
+ * Aggressive size limit (5 MB), short timeout, fail-closed, narrow MIME
1177
+ * allowlist. Only allows plain images and PDF.
1178
+ */
1179
+ const STRICT_PUBLIC_UPLOAD = definePolicy({
1180
+ includeExtensions: ['jpg', 'jpeg', 'png', 'webp', 'pdf'],
1181
+ allowedMimeTypes: [
1182
+ 'image/jpeg',
1183
+ 'image/png',
1184
+ 'image/webp',
1185
+ 'application/pdf',
1186
+ ],
1187
+ maxFileSizeBytes: 5 * MB,
1188
+ timeoutMs: 4000,
1189
+ concurrency: 2,
1190
+ failClosed: true,
1191
+ });
1192
+ /**
1193
+ * Conservative default policy.
1194
+ *
1195
+ * A hardened version of the built-in `DEFAULT_POLICY` suitable for
1196
+ * production without further customisation. Stricter size limit and
1197
+ * shorter timeout than the permissive default.
1198
+ */
1199
+ const CONSERVATIVE_DEFAULT = definePolicy({
1200
+ includeExtensions: ['zip', 'png', 'jpg', 'jpeg', 'pdf', 'txt', 'csv', 'docx', 'xlsx'],
1201
+ allowedMimeTypes: [
1202
+ 'application/zip',
1203
+ 'image/png',
1204
+ 'image/jpeg',
1205
+ 'application/pdf',
1206
+ 'text/plain',
1207
+ 'text/csv',
1208
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
1209
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
1210
+ ],
1211
+ maxFileSizeBytes: 10 * MB,
1212
+ timeoutMs: 8000,
1213
+ concurrency: 4,
1214
+ failClosed: true,
1215
+ });
1216
+ /**
1217
+ * Archives policy.
1218
+ *
1219
+ * Appropriate for: endpoints that accept ZIP, tar, or compressed archives.
1220
+ * Combines a generous size allowance with a longer timeout for deep inspection.
1221
+ *
1222
+ * NOTE: Pair this policy with `createZipBombGuard()` to defend against
1223
+ * decompression-bomb attacks:
1224
+ *
1225
+ * ```ts
1226
+ * import { composeScanners, createZipBombGuard, CommonHeuristicsScanner } from 'pompelmi';
1227
+ * const scanner = composeScanners(
1228
+ * [['zipGuard', createZipBombGuard()], ['heuristics', CommonHeuristicsScanner]]
1229
+ * );
1230
+ * ```
1231
+ */
1232
+ const ARCHIVES = definePolicy({
1233
+ includeExtensions: ['zip', 'tar', 'gz', 'tgz', 'bz2', 'xz', '7z', 'rar'],
1234
+ allowedMimeTypes: [
1235
+ 'application/zip',
1236
+ 'application/x-tar',
1237
+ 'application/gzip',
1238
+ 'application/x-bzip2',
1239
+ 'application/x-xz',
1240
+ 'application/x-7z-compressed',
1241
+ 'application/x-rar-compressed',
1242
+ ],
1243
+ maxFileSizeBytes: 100 * MB,
1244
+ timeoutMs: 30000,
1245
+ concurrency: 2,
1246
+ failClosed: true,
1247
+ });
1248
+ /**
1249
+ * Named map of all built-in policy packs.
1250
+ *
1251
+ * ```ts
1252
+ * import { POLICY_PACKS } from 'pompelmi/policy-packs';
1253
+ * const policy = POLICY_PACKS['strict-public-upload'];
1254
+ * ```
1255
+ */
1256
+ const POLICY_PACKS = {
1257
+ 'documents-only': DOCUMENTS_ONLY,
1258
+ 'images-only': IMAGES_ONLY,
1259
+ 'strict-public-upload': STRICT_PUBLIC_UPLOAD,
1260
+ 'conservative-default': CONSERVATIVE_DEFAULT,
1261
+ 'archives': ARCHIVES,
1262
+ };
1263
+ /**
1264
+ * Look up a policy pack by name.
1265
+ * Throws if the name is not recognised.
1266
+ */
1267
+ function getPolicyPack(name) {
1268
+ const policy = POLICY_PACKS[name];
1269
+ if (!policy)
1270
+ throw new Error(`Unknown policy pack: '${name}'. Valid names: ${Object.keys(POLICY_PACKS).join(', ')}`);
1271
+ return policy;
1272
+ }
1273
+
1274
+ function mapMatchesToVerdict(matches = []) {
1275
+ if (!matches.length)
1276
+ return 'clean';
1277
+ const malHints = ['trojan', 'ransom', 'worm', 'spy', 'rootkit', 'keylog', 'botnet'];
1278
+ const tagSet = new Set(matches.flatMap(m => (m.tags ?? []).map(t => t.toLowerCase())));
1279
+ const nameHit = (r) => malHints.some(h => r.toLowerCase().includes(h));
1280
+ const isMal = matches.some(m => nameHit(m.rule)) || tagSet.has('malware') || tagSet.has('critical');
1281
+ return isMal ? 'malicious' : 'suspicious';
1282
+ }
1283
+
1284
+ /** Decompilation-specific types for Pompelmi */
1285
+ const SUSPICIOUS_PATTERNS = [
1286
+ {
1287
+ name: 'syscall_direct',
1288
+ description: 'Direct system call without library wrapper',
1289
+ severity: 'medium',
1290
+ pattern: /syscall|sysenter|int\s+0x80/i
1291
+ },
1292
+ {
1293
+ name: 'process_injection',
1294
+ description: 'Process injection techniques',
1295
+ severity: 'high',
1296
+ pattern: /CreateRemoteThread|WriteProcessMemory|VirtualAllocEx/i
1297
+ },
1298
+ {
1299
+ name: 'anti_debug',
1300
+ description: 'Anti-debugging techniques',
1301
+ severity: 'medium',
1302
+ pattern: /IsDebuggerPresent|CheckRemoteDebuggerPresent|OutputDebugString/i
1303
+ },
1304
+ {
1305
+ name: 'obfuscation_xor',
1306
+ description: 'XOR-based obfuscation pattern',
1307
+ severity: 'medium',
1308
+ pattern: /xor.*0x[0-9a-f]+.*xor/i
1309
+ },
1310
+ {
1311
+ name: 'crypto_constants',
1312
+ description: 'Cryptographic constants',
1313
+ severity: 'low',
1314
+ pattern: /0x67452301|0xefcdab89|0x98badcfe|0x10325476/i
1315
+ }
1316
+ ];
1317
+
1318
+ /**
1319
+ * Batch scanning with concurrency control
1320
+ * @module utils/batch-scanner
1321
+ */
1322
+ /**
1323
+ * Batch file scanner with concurrency control and progress tracking
1324
+ */
1325
+ class BatchScanner {
1326
+ constructor(options = {}) {
1327
+ this.options = {
1328
+ concurrency: 5,
1329
+ continueOnError: true,
1330
+ ...options,
1331
+ };
1332
+ }
1333
+ /**
1334
+ * Scan multiple files with controlled concurrency
1335
+ */
1336
+ async scanBatch(tasks) {
1337
+ const startTime = Date.now();
3370
1338
  const results = new Array(tasks.length);
3371
1339
  const errors = [];
3372
1340
  let successCount = 0;
@@ -3434,748 +1402,1065 @@ class BatchScanner {
3434
1402
  };
3435
1403
  }
3436
1404
  /**
3437
- * Scan files from File objects (browser environment)
1405
+ * Scan files from File objects (browser environment)
1406
+ */
1407
+ async scanFiles(files) {
1408
+ const tasks = await Promise.all(files.map(async (file) => ({
1409
+ content: new Uint8Array(await file.arrayBuffer()),
1410
+ context: {
1411
+ filename: file.name,
1412
+ mimeType: file.type,
1413
+ size: file.size,
1414
+ },
1415
+ })));
1416
+ return this.scanBatch(tasks);
1417
+ }
1418
+ /**
1419
+ * Scan files from file paths (Node.js environment)
1420
+ */
1421
+ async scanFilePaths(filePaths) {
1422
+ const fs = await import('fs/promises');
1423
+ const path = await import('path');
1424
+ const tasks = await Promise.all(filePaths.map(async (filePath) => {
1425
+ const [content, stats] = await Promise.all([
1426
+ fs.readFile(filePath),
1427
+ fs.stat(filePath),
1428
+ ]);
1429
+ return {
1430
+ content: new Uint8Array(content),
1431
+ context: {
1432
+ filename: path.basename(filePath),
1433
+ size: stats.size,
1434
+ },
1435
+ };
1436
+ }));
1437
+ return this.scanBatch(tasks);
1438
+ }
1439
+ }
1440
+ /**
1441
+ * Quick helper for batch scanning with default options
1442
+ */
1443
+ async function batchScan(tasks, options) {
1444
+ const scanner = new BatchScanner(options);
1445
+ return scanner.scanBatch(tasks);
1446
+ }
1447
+
1448
+ /**
1449
+ * Threat intelligence integration and enhanced detection
1450
+ * @module utils/threat-intelligence
1451
+ */
1452
+ /**
1453
+ * Built-in threat intelligence - known malware hashes
1454
+ * In production, this would connect to real threat intel APIs
1455
+ */
1456
+ class LocalThreatIntelligence {
1457
+ constructor() {
1458
+ this.name = 'Local Database';
1459
+ this.knownThreats = new Map();
1460
+ // Initialize with some example known threats (in production, load from database)
1461
+ this.initializeKnownThreats();
1462
+ }
1463
+ initializeKnownThreats() {
1464
+ // Example: EICAR test file hash
1465
+ this.knownThreats.set('275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f', {
1466
+ threatLevel: 100,
1467
+ category: 'test-malware',
1468
+ source: 'local',
1469
+ metadata: { name: 'EICAR Test File' },
1470
+ });
1471
+ }
1472
+ async checkHash(hash) {
1473
+ return this.knownThreats.get(hash.toLowerCase()) || null;
1474
+ }
1475
+ /**
1476
+ * Add a known threat to the local database
1477
+ */
1478
+ addThreat(hash, info) {
1479
+ this.knownThreats.set(hash.toLowerCase(), info);
1480
+ }
1481
+ /**
1482
+ * Remove a threat from the local database
1483
+ */
1484
+ removeThreat(hash) {
1485
+ return this.knownThreats.delete(hash.toLowerCase());
1486
+ }
1487
+ /**
1488
+ * Get all known threats
1489
+ */
1490
+ getAllThreats() {
1491
+ return new Map(this.knownThreats);
1492
+ }
1493
+ }
1494
+ /**
1495
+ * Threat intelligence aggregator
1496
+ */
1497
+ class ThreatIntelligenceAggregator {
1498
+ constructor(sources) {
1499
+ this.sources = [];
1500
+ if (sources) {
1501
+ this.sources = sources;
1502
+ }
1503
+ else {
1504
+ // Default to local intelligence
1505
+ this.sources = [new LocalThreatIntelligence()];
1506
+ }
1507
+ }
1508
+ /**
1509
+ * Add a threat intelligence source
1510
+ */
1511
+ addSource(source) {
1512
+ this.sources.push(source);
1513
+ }
1514
+ /**
1515
+ * Check file hash against all sources
1516
+ */
1517
+ async checkHash(hash) {
1518
+ const results = await Promise.allSettled(this.sources.map(source => source.checkHash(hash)));
1519
+ const threats = [];
1520
+ for (const result of results) {
1521
+ if (result.status === 'fulfilled' && result.value) {
1522
+ threats.push(result.value);
1523
+ }
1524
+ }
1525
+ return threats;
1526
+ }
1527
+ /**
1528
+ * Enhance scan report with threat intelligence
1529
+ */
1530
+ async enhanceScanReport(content, report) {
1531
+ // Calculate file hash
1532
+ const hash = crypto.createHash('sha256').update(content).digest('hex');
1533
+ // Check threat intelligence
1534
+ const threatIntel = await this.checkHash(hash);
1535
+ // Calculate risk score
1536
+ const riskScore = this.calculateRiskScore(report, threatIntel);
1537
+ return {
1538
+ ...report,
1539
+ fileHash: hash,
1540
+ threatIntel: threatIntel.length > 0 ? threatIntel : undefined,
1541
+ riskScore,
1542
+ };
1543
+ }
1544
+ /**
1545
+ * Calculate overall risk score based on scan results and threat intel
1546
+ */
1547
+ calculateRiskScore(report, threats) {
1548
+ let score = 0;
1549
+ // Base score from verdict
1550
+ switch (report.verdict) {
1551
+ case 'malicious':
1552
+ score += 70;
1553
+ break;
1554
+ case 'suspicious':
1555
+ score += 40;
1556
+ break;
1557
+ case 'clean':
1558
+ score += 0;
1559
+ break;
1560
+ }
1561
+ // Add points for number of matches
1562
+ score += Math.min(report.matches.length * 5, 20);
1563
+ // Add points from threat intelligence
1564
+ if (threats.length > 0) {
1565
+ const maxThreat = Math.max(...threats.map(t => t.threatLevel));
1566
+ score = Math.max(score, maxThreat);
1567
+ }
1568
+ return Math.min(score, 100);
1569
+ }
1570
+ }
1571
+ /**
1572
+ * Create default threat intelligence aggregator
1573
+ */
1574
+ function createThreatIntelligence() {
1575
+ return new ThreatIntelligenceAggregator();
1576
+ }
1577
+ /**
1578
+ * Helper to get file hash
1579
+ */
1580
+ function getFileHash(content) {
1581
+ return crypto.createHash('sha256').update(content).digest('hex');
1582
+ }
1583
+
1584
+ /**
1585
+ * Export utilities for scan results
1586
+ * @module utils/export
1587
+ */
1588
+ /**
1589
+ * Export scan results to various formats
1590
+ */
1591
+ class ScanResultExporter {
1592
+ /**
1593
+ * Export to JSON format
1594
+ */
1595
+ toJSON(reports, options = {}) {
1596
+ const data = Array.isArray(reports) ? reports : [reports];
1597
+ if (!options.includeDetails) {
1598
+ // Simplified output
1599
+ const simplified = data.map(r => ({
1600
+ verdict: r.verdict,
1601
+ file: r.file?.name,
1602
+ matches: r.matches.length,
1603
+ durationMs: r.durationMs,
1604
+ }));
1605
+ return options.prettyPrint
1606
+ ? JSON.stringify(simplified, null, 2)
1607
+ : JSON.stringify(simplified);
1608
+ }
1609
+ return options.prettyPrint
1610
+ ? JSON.stringify(data, null, 2)
1611
+ : JSON.stringify(data);
1612
+ }
1613
+ /**
1614
+ * Export to CSV format
1615
+ */
1616
+ toCSV(reports, options = {}) {
1617
+ const data = Array.isArray(reports) ? reports : [reports];
1618
+ const headers = [
1619
+ 'filename',
1620
+ 'verdict',
1621
+ 'matches_count',
1622
+ 'file_size',
1623
+ 'mime_type',
1624
+ 'duration_ms',
1625
+ 'engine',
1626
+ ];
1627
+ if (options.includeDetails) {
1628
+ headers.push('reasons', 'match_rules');
1629
+ }
1630
+ const rows = data.map(report => {
1631
+ const row = [
1632
+ this.escapeCsv(report.file?.name || 'unknown'),
1633
+ report.verdict,
1634
+ report.matches.length.toString(),
1635
+ (report.file?.size || 0).toString(),
1636
+ this.escapeCsv(report.file?.mimeType || 'unknown'),
1637
+ (report.durationMs || 0).toString(),
1638
+ report.engine || 'unknown',
1639
+ ];
1640
+ if (options.includeDetails) {
1641
+ row.push(this.escapeCsv((report.reasons || []).join('; ')), this.escapeCsv(report.matches.map(m => m.rule).join('; ')));
1642
+ }
1643
+ return row.join(',');
1644
+ });
1645
+ return [headers.join(','), ...rows].join('\n');
1646
+ }
1647
+ /**
1648
+ * Export to Markdown format
1649
+ */
1650
+ toMarkdown(reports, options = {}) {
1651
+ const data = Array.isArray(reports) ? reports : [reports];
1652
+ let md = '# Scan Results\n\n';
1653
+ md += `**Total Scans:** ${data.length}\n\n`;
1654
+ const clean = data.filter(r => r.verdict === 'clean').length;
1655
+ const suspicious = data.filter(r => r.verdict === 'suspicious').length;
1656
+ const malicious = data.filter(r => r.verdict === 'malicious').length;
1657
+ md += '## Summary\n\n';
1658
+ md += `- ✅ Clean: ${clean}\n`;
1659
+ md += `- ⚠️ Suspicious: ${suspicious}\n`;
1660
+ md += `- ❌ Malicious: ${malicious}\n\n`;
1661
+ md += '## Detailed Results\n\n';
1662
+ for (const report of data) {
1663
+ const icon = report.verdict === 'clean' ? '✅' : report.verdict === 'suspicious' ? '⚠️' : '❌';
1664
+ md += `### ${icon} ${report.file?.name || 'Unknown'}\n\n`;
1665
+ md += `- **Verdict:** ${report.verdict}\n`;
1666
+ md += `- **Size:** ${this.formatBytes(report.file?.size || 0)}\n`;
1667
+ md += `- **MIME Type:** ${report.file?.mimeType || 'unknown'}\n`;
1668
+ md += `- **Duration:** ${report.durationMs || 0}ms\n`;
1669
+ md += `- **Matches:** ${report.matches.length}\n`;
1670
+ if (options.includeDetails && report.matches.length > 0) {
1671
+ md += '\n**Match Details:**\n';
1672
+ for (const match of report.matches) {
1673
+ md += `- ${match.rule}`;
1674
+ if (match.tags && match.tags.length > 0) {
1675
+ md += ` (${match.tags.join(', ')})`;
1676
+ }
1677
+ md += '\n';
1678
+ }
1679
+ }
1680
+ md += '\n';
1681
+ }
1682
+ return md;
1683
+ }
1684
+ /**
1685
+ * Export to SARIF format (Static Analysis Results Interchange Format)
1686
+ * Useful for CI/CD integration
1687
+ */
1688
+ toSARIF(reports, options = {}) {
1689
+ const data = Array.isArray(reports) ? reports : [reports];
1690
+ const results = data.flatMap(report => {
1691
+ if (report.verdict === 'clean')
1692
+ return [];
1693
+ return report.matches.map(match => ({
1694
+ ruleId: match.rule,
1695
+ level: report.verdict === 'malicious' ? 'error' : 'warning',
1696
+ message: {
1697
+ text: `${match.rule} detected in ${report.file?.name || 'unknown file'}`,
1698
+ },
1699
+ locations: [
1700
+ {
1701
+ physicalLocation: {
1702
+ artifactLocation: {
1703
+ uri: report.file?.name || 'unknown',
1704
+ },
1705
+ },
1706
+ },
1707
+ ],
1708
+ properties: {
1709
+ tags: match.tags,
1710
+ metadata: match.meta,
1711
+ },
1712
+ }));
1713
+ });
1714
+ const sarif = {
1715
+ version: '2.1.0',
1716
+ $schema: 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json',
1717
+ runs: [
1718
+ {
1719
+ tool: {
1720
+ driver: {
1721
+ name: 'Pompelmi',
1722
+ version: '0.29.0',
1723
+ informationUri: 'https://pompelmi.github.io/pompelmi/',
1724
+ },
1725
+ },
1726
+ results,
1727
+ },
1728
+ ],
1729
+ };
1730
+ return options.prettyPrint
1731
+ ? JSON.stringify(sarif, null, 2)
1732
+ : JSON.stringify(sarif);
1733
+ }
1734
+ /**
1735
+ * Export to HTML format
1736
+ */
1737
+ toHTML(reports, options = {}) {
1738
+ const data = Array.isArray(reports) ? reports : [reports];
1739
+ const clean = data.filter(r => r.verdict === 'clean').length;
1740
+ const suspicious = data.filter(r => r.verdict === 'suspicious').length;
1741
+ const malicious = data.filter(r => r.verdict === 'malicious').length;
1742
+ let html = `<!DOCTYPE html>
1743
+ <html lang="en">
1744
+ <head>
1745
+ <meta charset="UTF-8">
1746
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1747
+ <title>Pompelmi Scan Results</title>
1748
+ <style>
1749
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; max-width: 1200px; margin: 0 auto; padding: 20px; }
1750
+ .summary { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; margin: 20px 0; }
1751
+ .card { padding: 20px; border-radius: 8px; text-align: center; }
1752
+ .clean { background: #d4edda; color: #155724; }
1753
+ .suspicious { background: #fff3cd; color: #856404; }
1754
+ .malicious { background: #f8d7da; color: #721c24; }
1755
+ .result { border: 1px solid #ddd; border-radius: 8px; padding: 15px; margin: 10px 0; }
1756
+ .result h3 { margin-top: 0; }
1757
+ .badge { display: inline-block; padding: 4px 8px; border-radius: 4px; font-size: 0.8em; margin: 2px; }
1758
+ table { width: 100%; border-collapse: collapse; }
1759
+ th, td { padding: 8px; text-align: left; border-bottom: 1px solid #ddd; }
1760
+ </style>
1761
+ </head>
1762
+ <body>
1763
+ <h1>🛡️ Pompelmi Scan Results</h1>
1764
+ <div class="summary">
1765
+ <div class="card clean"><h2>${clean}</h2><p>Clean Files</p></div>
1766
+ <div class="card suspicious"><h2>${suspicious}</h2><p>Suspicious Files</p></div>
1767
+ <div class="card malicious"><h2>${malicious}</h2><p>Malicious Files</p></div>
1768
+ </div>
1769
+ <h2>Detailed Results</h2>`;
1770
+ for (const report of data) {
1771
+ const statusClass = report.verdict;
1772
+ html += `<div class="result ${statusClass}">`;
1773
+ html += `<h3>${this.escapeHtml(report.file?.name || 'Unknown')}</h3>`;
1774
+ html += `<table>`;
1775
+ html += `<tr><th>Verdict</th><td>${report.verdict.toUpperCase()}</td></tr>`;
1776
+ html += `<tr><th>Size</th><td>${this.formatBytes(report.file?.size || 0)}</td></tr>`;
1777
+ html += `<tr><th>MIME Type</th><td>${this.escapeHtml(report.file?.mimeType || 'unknown')}</td></tr>`;
1778
+ html += `<tr><th>Duration</th><td>${report.durationMs || 0}ms</td></tr>`;
1779
+ html += `<tr><th>Matches</th><td>${report.matches.length}</td></tr>`;
1780
+ html += `</table>`;
1781
+ if (options.includeDetails && report.matches.length > 0) {
1782
+ html += `<h4>Match Details:</h4><ul>`;
1783
+ for (const match of report.matches) {
1784
+ html += `<li><strong>${this.escapeHtml(match.rule)}</strong>`;
1785
+ if (match.tags && match.tags.length > 0) {
1786
+ html += ` ${match.tags.map(tag => `<span class="badge">${this.escapeHtml(tag)}</span>`).join('')}`;
1787
+ }
1788
+ html += `</li>`;
1789
+ }
1790
+ html += `</ul>`;
1791
+ }
1792
+ html += `</div>`;
1793
+ }
1794
+ html += `</body></html>`;
1795
+ return html;
1796
+ }
1797
+ /**
1798
+ * Export to specified format
1799
+ */
1800
+ export(reports, format, options = {}) {
1801
+ switch (format) {
1802
+ case 'json':
1803
+ return this.toJSON(reports, options);
1804
+ case 'csv':
1805
+ return this.toCSV(reports, options);
1806
+ case 'markdown':
1807
+ return this.toMarkdown(reports, options);
1808
+ case 'html':
1809
+ return this.toHTML(reports, options);
1810
+ case 'sarif':
1811
+ return this.toSARIF(reports, options);
1812
+ default:
1813
+ throw new Error(`Unsupported export format: ${format}`);
1814
+ }
1815
+ }
1816
+ escapeCsv(value) {
1817
+ if (value.includes(',') || value.includes('"') || value.includes('\n')) {
1818
+ return `"${value.replace(/"/g, '""')}"`;
1819
+ }
1820
+ return value;
1821
+ }
1822
+ escapeHtml(value) {
1823
+ return value
1824
+ .replace(/&/g, '&amp;')
1825
+ .replace(/</g, '&lt;')
1826
+ .replace(/>/g, '&gt;')
1827
+ .replace(/"/g, '&quot;')
1828
+ .replace(/'/g, '&#039;');
1829
+ }
1830
+ formatBytes(bytes) {
1831
+ if (bytes === 0)
1832
+ return '0 Bytes';
1833
+ const k = 1024;
1834
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
1835
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
1836
+ return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
1837
+ }
1838
+ }
1839
+ /**
1840
+ * Quick export helper
1841
+ */
1842
+ function exportScanResults(reports, format, options) {
1843
+ const exporter = new ScanResultExporter();
1844
+ return exporter.export(reports, format, options);
1845
+ }
1846
+
1847
+ /**
1848
+ * Advanced configuration system for pompelmi
1849
+ * @module config
1850
+ */
1851
+ /**
1852
+ * Default configuration
1853
+ */
1854
+ const DEFAULT_CONFIG = {
1855
+ defaultPreset: 'zip-basic',
1856
+ performance: {
1857
+ enableCache: false,
1858
+ enablePerformanceTracking: false,
1859
+ enableParallel: true,
1860
+ maxConcurrency: 5,
1861
+ cacheOptions: {
1862
+ maxSize: 1000,
1863
+ ttl: 3600000, // 1 hour
1864
+ enableLRU: true,
1865
+ enableStats: false,
1866
+ },
1867
+ },
1868
+ security: {
1869
+ maxFileSize: 100 * 1024 * 1024, // 100MB
1870
+ enableThreatIntel: false,
1871
+ scanTimeout: 30000, // 30 seconds
1872
+ strictMode: false,
1873
+ },
1874
+ advanced: {
1875
+ enablePolyglotDetection: true,
1876
+ enableObfuscationDetection: true,
1877
+ enableNestedArchiveAnalysis: true,
1878
+ maxArchiveDepth: 5,
1879
+ },
1880
+ logging: {
1881
+ verbose: false,
1882
+ level: 'info',
1883
+ enableStats: false,
1884
+ },
1885
+ };
1886
+ /**
1887
+ * Configuration presets for common use cases
1888
+ */
1889
+ const CONFIG_PRESETS = {
1890
+ /** Fast scanning with minimal features */
1891
+ fast: {
1892
+ defaultPreset: 'basic',
1893
+ performance: {
1894
+ enableCache: true,
1895
+ enablePerformanceTracking: false,
1896
+ maxConcurrency: 10,
1897
+ },
1898
+ advanced: {
1899
+ enablePolyglotDetection: false,
1900
+ enableObfuscationDetection: false,
1901
+ enableNestedArchiveAnalysis: false,
1902
+ },
1903
+ },
1904
+ /** Balanced scanning (recommended) */
1905
+ balanced: DEFAULT_CONFIG,
1906
+ /** Thorough scanning with all features */
1907
+ thorough: {
1908
+ defaultPreset: 'advanced',
1909
+ performance: {
1910
+ enableCache: true,
1911
+ enablePerformanceTracking: true,
1912
+ maxConcurrency: 3,
1913
+ },
1914
+ security: {
1915
+ maxFileSize: 500 * 1024 * 1024, // 500MB
1916
+ enableThreatIntel: true,
1917
+ scanTimeout: 60000, // 60 seconds
1918
+ strictMode: true,
1919
+ },
1920
+ advanced: {
1921
+ enablePolyglotDetection: true,
1922
+ enableObfuscationDetection: true,
1923
+ enableNestedArchiveAnalysis: true,
1924
+ maxArchiveDepth: 10,
1925
+ },
1926
+ logging: {
1927
+ verbose: true,
1928
+ level: 'debug',
1929
+ enableStats: true,
1930
+ },
1931
+ },
1932
+ /** Production-ready configuration */
1933
+ production: {
1934
+ defaultPreset: 'advanced',
1935
+ performance: {
1936
+ enableCache: true,
1937
+ enablePerformanceTracking: true,
1938
+ maxConcurrency: 5,
1939
+ cacheOptions: {
1940
+ maxSize: 5000,
1941
+ ttl: 7200000, // 2 hours
1942
+ enableLRU: true,
1943
+ enableStats: true,
1944
+ },
1945
+ },
1946
+ security: {
1947
+ maxFileSize: 200 * 1024 * 1024, // 200MB
1948
+ enableThreatIntel: true,
1949
+ scanTimeout: 45000,
1950
+ strictMode: false,
1951
+ },
1952
+ advanced: {
1953
+ enablePolyglotDetection: true,
1954
+ enableObfuscationDetection: true,
1955
+ enableNestedArchiveAnalysis: true,
1956
+ maxArchiveDepth: 7,
1957
+ },
1958
+ logging: {
1959
+ verbose: false,
1960
+ level: 'warn',
1961
+ enableStats: true,
1962
+ },
1963
+ },
1964
+ /** Development configuration */
1965
+ development: {
1966
+ defaultPreset: 'basic',
1967
+ performance: {
1968
+ enableCache: false,
1969
+ enablePerformanceTracking: true,
1970
+ maxConcurrency: 3,
1971
+ },
1972
+ security: {
1973
+ maxFileSize: 50 * 1024 * 1024, // 50MB
1974
+ scanTimeout: 15000,
1975
+ strictMode: false,
1976
+ },
1977
+ logging: {
1978
+ verbose: true,
1979
+ level: 'debug',
1980
+ enableStats: true,
1981
+ },
1982
+ },
1983
+ };
1984
+ /**
1985
+ * Configuration manager
1986
+ */
1987
+ class ConfigManager {
1988
+ constructor(initialConfig) {
1989
+ this.config = this.mergeConfig(DEFAULT_CONFIG, initialConfig || {});
1990
+ }
1991
+ /**
1992
+ * Get current configuration
3438
1993
  */
3439
- async scanFiles(files) {
3440
- const tasks = await Promise.all(files.map(async (file) => ({
3441
- content: new Uint8Array(await file.arrayBuffer()),
3442
- context: {
3443
- filename: file.name,
3444
- mimeType: file.type,
3445
- size: file.size,
3446
- },
3447
- })));
3448
- return this.scanBatch(tasks);
1994
+ getConfig() {
1995
+ return { ...this.config };
3449
1996
  }
3450
1997
  /**
3451
- * Scan files from file paths (Node.js environment)
1998
+ * Update configuration
3452
1999
  */
3453
- async scanFilePaths(filePaths) {
3454
- const fs = await import('fs/promises');
3455
- const path = await import('path');
3456
- const tasks = await Promise.all(filePaths.map(async (filePath) => {
3457
- const [content, stats] = await Promise.all([
3458
- fs.readFile(filePath),
3459
- fs.stat(filePath),
3460
- ]);
3461
- return {
3462
- content: new Uint8Array(content),
3463
- context: {
3464
- filename: path.basename(filePath),
3465
- size: stats.size,
3466
- },
3467
- };
3468
- }));
3469
- return this.scanBatch(tasks);
3470
- }
3471
- }
3472
- /**
3473
- * Quick helper for batch scanning with default options
3474
- */
3475
- async function batchScan(tasks, options) {
3476
- const scanner = new BatchScanner(options);
3477
- return scanner.scanBatch(tasks);
3478
- }
3479
-
3480
- /**
3481
- * Threat intelligence integration and enhanced detection
3482
- * @module utils/threat-intelligence
3483
- */
3484
- /**
3485
- * Built-in threat intelligence - known malware hashes
3486
- * In production, this would connect to real threat intel APIs
3487
- */
3488
- class LocalThreatIntelligence {
3489
- constructor() {
3490
- this.name = 'Local Database';
3491
- this.knownThreats = new Map();
3492
- // Initialize with some example known threats (in production, load from database)
3493
- this.initializeKnownThreats();
3494
- }
3495
- initializeKnownThreats() {
3496
- // Example: EICAR test file hash
3497
- this.knownThreats.set('275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f', {
3498
- threatLevel: 100,
3499
- category: 'test-malware',
3500
- source: 'local',
3501
- metadata: { name: 'EICAR Test File' },
3502
- });
3503
- }
3504
- async checkHash(hash) {
3505
- return this.knownThreats.get(hash.toLowerCase()) || null;
2000
+ updateConfig(updates) {
2001
+ this.config = this.mergeConfig(this.config, updates);
3506
2002
  }
3507
2003
  /**
3508
- * Add a known threat to the local database
2004
+ * Load a preset configuration
3509
2005
  */
3510
- addThreat(hash, info) {
3511
- this.knownThreats.set(hash.toLowerCase(), info);
2006
+ loadPreset(preset) {
2007
+ const presetConfig = CONFIG_PRESETS[preset];
2008
+ this.config = this.mergeConfig(DEFAULT_CONFIG, presetConfig);
3512
2009
  }
3513
2010
  /**
3514
- * Remove a threat from the local database
2011
+ * Reset to default configuration
3515
2012
  */
3516
- removeThreat(hash) {
3517
- return this.knownThreats.delete(hash.toLowerCase());
2013
+ reset() {
2014
+ this.config = { ...DEFAULT_CONFIG };
3518
2015
  }
3519
2016
  /**
3520
- * Get all known threats
2017
+ * Get a specific configuration value
3521
2018
  */
3522
- getAllThreats() {
3523
- return new Map(this.knownThreats);
3524
- }
3525
- }
3526
- /**
3527
- * Threat intelligence aggregator
3528
- */
3529
- class ThreatIntelligenceAggregator {
3530
- constructor(sources) {
3531
- this.sources = [];
3532
- if (sources) {
3533
- this.sources = sources;
3534
- }
3535
- else {
3536
- // Default to local intelligence
3537
- this.sources = [new LocalThreatIntelligence()];
3538
- }
2019
+ get(key) {
2020
+ return this.config[key];
3539
2021
  }
3540
2022
  /**
3541
- * Add a threat intelligence source
2023
+ * Set a specific configuration value
3542
2024
  */
3543
- addSource(source) {
3544
- this.sources.push(source);
2025
+ set(key, value) {
2026
+ this.config[key] = value;
3545
2027
  }
3546
2028
  /**
3547
- * Check file hash against all sources
2029
+ * Validate configuration
3548
2030
  */
3549
- async checkHash(hash) {
3550
- const results = await Promise.allSettled(this.sources.map(source => source.checkHash(hash)));
3551
- const threats = [];
3552
- for (const result of results) {
3553
- if (result.status === 'fulfilled' && result.value) {
3554
- threats.push(result.value);
2031
+ validate() {
2032
+ const errors = [];
2033
+ // Validate performance settings
2034
+ if (this.config.performance?.maxConcurrency !== undefined) {
2035
+ if (this.config.performance.maxConcurrency < 1) {
2036
+ errors.push('maxConcurrency must be at least 1');
2037
+ }
2038
+ if (this.config.performance.maxConcurrency > 50) {
2039
+ errors.push('maxConcurrency should not exceed 50');
3555
2040
  }
3556
2041
  }
3557
- return threats;
2042
+ // Validate security settings
2043
+ if (this.config.security?.maxFileSize !== undefined) {
2044
+ if (this.config.security.maxFileSize < 1024) {
2045
+ errors.push('maxFileSize must be at least 1KB');
2046
+ }
2047
+ }
2048
+ if (this.config.security?.scanTimeout !== undefined) {
2049
+ if (this.config.security.scanTimeout < 1000) {
2050
+ errors.push('scanTimeout must be at least 1000ms');
2051
+ }
2052
+ }
2053
+ // Validate advanced settings
2054
+ if (this.config.advanced?.maxArchiveDepth !== undefined) {
2055
+ if (this.config.advanced.maxArchiveDepth < 1) {
2056
+ errors.push('maxArchiveDepth must be at least 1');
2057
+ }
2058
+ if (this.config.advanced.maxArchiveDepth > 20) {
2059
+ errors.push('maxArchiveDepth should not exceed 20');
2060
+ }
2061
+ }
2062
+ return {
2063
+ valid: errors.length === 0,
2064
+ errors,
2065
+ };
3558
2066
  }
3559
2067
  /**
3560
- * Enhance scan report with threat intelligence
2068
+ * Deep merge configuration objects
3561
2069
  */
3562
- async enhanceScanReport(content, report) {
3563
- // Calculate file hash
3564
- const hash = crypto.createHash('sha256').update(content).digest('hex');
3565
- // Check threat intelligence
3566
- const threatIntel = await this.checkHash(hash);
3567
- // Calculate risk score
3568
- const riskScore = this.calculateRiskScore(report, threatIntel);
2070
+ mergeConfig(base, updates) {
3569
2071
  return {
3570
- ...report,
3571
- fileHash: hash,
3572
- threatIntel: threatIntel.length > 0 ? threatIntel : undefined,
3573
- riskScore,
2072
+ ...base,
2073
+ ...updates,
2074
+ performance: {
2075
+ ...base.performance,
2076
+ ...updates.performance,
2077
+ cacheOptions: {
2078
+ ...base.performance?.cacheOptions,
2079
+ ...updates.performance?.cacheOptions,
2080
+ },
2081
+ },
2082
+ security: {
2083
+ ...base.security,
2084
+ ...updates.security,
2085
+ },
2086
+ advanced: {
2087
+ ...base.advanced,
2088
+ ...updates.advanced,
2089
+ },
2090
+ logging: {
2091
+ ...base.logging,
2092
+ ...updates.logging,
2093
+ },
2094
+ callbacks: {
2095
+ ...base.callbacks,
2096
+ ...updates.callbacks,
2097
+ },
2098
+ presetOptions: {
2099
+ ...base.presetOptions,
2100
+ ...updates.presetOptions,
2101
+ },
3574
2102
  };
3575
2103
  }
3576
2104
  /**
3577
- * Calculate overall risk score based on scan results and threat intel
2105
+ * Export configuration as JSON
3578
2106
  */
3579
- calculateRiskScore(report, threats) {
3580
- let score = 0;
3581
- // Base score from verdict
3582
- switch (report.verdict) {
3583
- case 'malicious':
3584
- score += 70;
3585
- break;
3586
- case 'suspicious':
3587
- score += 40;
3588
- break;
3589
- case 'clean':
3590
- score += 0;
3591
- break;
2107
+ toJSON() {
2108
+ return JSON.stringify(this.config, null, 2);
2109
+ }
2110
+ /**
2111
+ * Load configuration from JSON
2112
+ */
2113
+ fromJSON(json) {
2114
+ try {
2115
+ const parsed = JSON.parse(json);
2116
+ this.config = this.mergeConfig(DEFAULT_CONFIG, parsed);
3592
2117
  }
3593
- // Add points for number of matches
3594
- score += Math.min(report.matches.length * 5, 20);
3595
- // Add points from threat intelligence
3596
- if (threats.length > 0) {
3597
- const maxThreat = Math.max(...threats.map(t => t.threatLevel));
3598
- score = Math.max(score, maxThreat);
2118
+ catch (error) {
2119
+ throw new Error(`Failed to parse configuration JSON: ${error}`);
3599
2120
  }
3600
- return Math.min(score, 100);
3601
2121
  }
3602
2122
  }
3603
2123
  /**
3604
- * Create default threat intelligence aggregator
3605
- */
3606
- function createThreatIntelligence() {
3607
- return new ThreatIntelligenceAggregator();
2124
+ * Create a new configuration manager
2125
+ */
2126
+ function createConfig(config) {
2127
+ return new ConfigManager(config);
3608
2128
  }
3609
2129
  /**
3610
- * Helper to get file hash
2130
+ * Get a preset configuration
3611
2131
  */
3612
- function getFileHash(content) {
3613
- return crypto.createHash('sha256').update(content).digest('hex');
2132
+ function getPresetConfig(preset) {
2133
+ return { ...DEFAULT_CONFIG, ...CONFIG_PRESETS[preset] };
3614
2134
  }
3615
2135
 
3616
2136
  /**
3617
- * Export utilities for scan results
3618
- * @module utils/export
3619
- */
3620
- /**
3621
- * Export scan results to various formats
2137
+ * HIPAA Compliance Module for Pompelmi
2138
+ *
2139
+ * This module provides comprehensive HIPAA compliance features for healthcare environments
2140
+ * where Pompelmi is used to analyze potentially compromised systems containing PHI.
2141
+ *
2142
+ * Key protections:
2143
+ * - Data sanitization and redaction
2144
+ * - Secure temporary file handling
2145
+ * - Audit logging
2146
+ * - Memory protection
2147
+ * - Error message sanitization
3622
2148
  */
3623
- class ScanResultExporter {
3624
- /**
3625
- * Export to JSON format
3626
- */
3627
- toJSON(reports, options = {}) {
3628
- const data = Array.isArray(reports) ? reports : [reports];
3629
- if (!options.includeDetails) {
3630
- // Simplified output
3631
- const simplified = data.map(r => ({
3632
- verdict: r.verdict,
3633
- file: r.file?.name,
3634
- matches: r.matches.length,
3635
- durationMs: r.durationMs,
3636
- }));
3637
- return options.prettyPrint
3638
- ? JSON.stringify(simplified, null, 2)
3639
- : JSON.stringify(simplified);
3640
- }
3641
- return options.prettyPrint
3642
- ? JSON.stringify(data, null, 2)
3643
- : JSON.stringify(data);
3644
- }
3645
- /**
3646
- * Export to CSV format
3647
- */
3648
- toCSV(reports, options = {}) {
3649
- const data = Array.isArray(reports) ? reports : [reports];
3650
- const headers = [
3651
- 'filename',
3652
- 'verdict',
3653
- 'matches_count',
3654
- 'file_size',
3655
- 'mime_type',
3656
- 'duration_ms',
3657
- 'engine',
3658
- ];
3659
- if (options.includeDetails) {
3660
- headers.push('reasons', 'match_rules');
3661
- }
3662
- const rows = data.map(report => {
3663
- const row = [
3664
- this.escapeCsv(report.file?.name || 'unknown'),
3665
- report.verdict,
3666
- report.matches.length.toString(),
3667
- (report.file?.size || 0).toString(),
3668
- this.escapeCsv(report.file?.mimeType || 'unknown'),
3669
- (report.durationMs || 0).toString(),
3670
- report.engine || 'unknown',
3671
- ];
3672
- if (options.includeDetails) {
3673
- row.push(this.escapeCsv((report.reasons || []).join('; ')), this.escapeCsv(report.matches.map(m => m.rule).join('; ')));
3674
- }
3675
- return row.join(',');
3676
- });
3677
- return [headers.join(','), ...rows].join('\n');
2149
+ class HipaaComplianceManager {
2150
+ constructor(config) {
2151
+ this.auditEvents = [];
2152
+ this.config = {
2153
+ sanitizeErrors: true,
2154
+ sanitizeFilenames: true,
2155
+ encryptTempFiles: true,
2156
+ memoryProtection: true,
2157
+ requireSecureTransport: true,
2158
+ ...config,
2159
+ enabled: config.enabled !== undefined ? config.enabled : true
2160
+ };
2161
+ this.sessionId = this.generateSessionId();
3678
2162
  }
3679
2163
  /**
3680
- * Export to Markdown format
2164
+ * Sanitize filename to prevent PHI leakage in logs
3681
2165
  */
3682
- toMarkdown(reports, options = {}) {
3683
- const data = Array.isArray(reports) ? reports : [reports];
3684
- let md = '# Scan Results\n\n';
3685
- md += `**Total Scans:** ${data.length}\n\n`;
3686
- const clean = data.filter(r => r.verdict === 'clean').length;
3687
- const suspicious = data.filter(r => r.verdict === 'suspicious').length;
3688
- const malicious = data.filter(r => r.verdict === 'malicious').length;
3689
- md += '## Summary\n\n';
3690
- md += `- ✅ Clean: ${clean}\n`;
3691
- md += `- ⚠️ Suspicious: ${suspicious}\n`;
3692
- md += `- ❌ Malicious: ${malicious}\n\n`;
3693
- md += '## Detailed Results\n\n';
3694
- for (const report of data) {
3695
- const icon = report.verdict === 'clean' ? '✅' : report.verdict === 'suspicious' ? '⚠️' : '❌';
3696
- md += `### ${icon} ${report.file?.name || 'Unknown'}\n\n`;
3697
- md += `- **Verdict:** ${report.verdict}\n`;
3698
- md += `- **Size:** ${this.formatBytes(report.file?.size || 0)}\n`;
3699
- md += `- **MIME Type:** ${report.file?.mimeType || 'unknown'}\n`;
3700
- md += `- **Duration:** ${report.durationMs || 0}ms\n`;
3701
- md += `- **Matches:** ${report.matches.length}\n`;
3702
- if (options.includeDetails && report.matches.length > 0) {
3703
- md += '\n**Match Details:**\n';
3704
- for (const match of report.matches) {
3705
- md += `- ${match.rule}`;
3706
- if (match.tags && match.tags.length > 0) {
3707
- md += ` (${match.tags.join(', ')})`;
3708
- }
3709
- md += '\n';
3710
- }
3711
- }
3712
- md += '\n';
2166
+ sanitizeFilename(filename) {
2167
+ if (!this.config.enabled || !this.config.sanitizeFilenames || !filename) {
2168
+ return filename || 'unknown';
3713
2169
  }
3714
- return md;
3715
- }
3716
- /**
3717
- * Export to SARIF format (Static Analysis Results Interchange Format)
3718
- * Useful for CI/CD integration
3719
- */
3720
- toSARIF(reports, options = {}) {
3721
- const data = Array.isArray(reports) ? reports : [reports];
3722
- const results = data.flatMap(report => {
3723
- if (report.verdict === 'clean')
3724
- return [];
3725
- return report.matches.map(match => ({
3726
- ruleId: match.rule,
3727
- level: report.verdict === 'malicious' ? 'error' : 'warning',
3728
- message: {
3729
- text: `${match.rule} detected in ${report.file?.name || 'unknown file'}`,
3730
- },
3731
- locations: [
3732
- {
3733
- physicalLocation: {
3734
- artifactLocation: {
3735
- uri: report.file?.name || 'unknown',
3736
- },
3737
- },
3738
- },
3739
- ],
3740
- properties: {
3741
- tags: match.tags,
3742
- metadata: match.meta,
3743
- },
3744
- }));
3745
- });
3746
- const sarif = {
3747
- version: '2.1.0',
3748
- $schema: 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json',
3749
- runs: [
3750
- {
3751
- tool: {
3752
- driver: {
3753
- name: 'Pompelmi',
3754
- version: '0.29.0',
3755
- informationUri: 'https://pompelmi.github.io/pompelmi/',
3756
- },
3757
- },
3758
- results,
3759
- },
3760
- ],
3761
- };
3762
- return options.prettyPrint
3763
- ? JSON.stringify(sarif, null, 2)
3764
- : JSON.stringify(sarif);
2170
+ // Remove potentially sensitive path information
2171
+ const basename = path__namespace.basename(filename);
2172
+ // Hash the filename to create a consistent but non-revealing identifier
2173
+ const hash = crypto__namespace.createHash('sha256').update(basename).digest('hex').substring(0, 8);
2174
+ // Preserve file extension for analysis purposes
2175
+ const ext = path__namespace.extname(basename);
2176
+ return `file_${hash}${ext}`;
3765
2177
  }
3766
2178
  /**
3767
- * Export to HTML format
2179
+ * Sanitize error messages to prevent PHI exposure
3768
2180
  */
3769
- toHTML(reports, options = {}) {
3770
- const data = Array.isArray(reports) ? reports : [reports];
3771
- const clean = data.filter(r => r.verdict === 'clean').length;
3772
- const suspicious = data.filter(r => r.verdict === 'suspicious').length;
3773
- const malicious = data.filter(r => r.verdict === 'malicious').length;
3774
- let html = `<!DOCTYPE html>
3775
- <html lang="en">
3776
- <head>
3777
- <meta charset="UTF-8">
3778
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
3779
- <title>Pompelmi Scan Results</title>
3780
- <style>
3781
- body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; max-width: 1200px; margin: 0 auto; padding: 20px; }
3782
- .summary { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; margin: 20px 0; }
3783
- .card { padding: 20px; border-radius: 8px; text-align: center; }
3784
- .clean { background: #d4edda; color: #155724; }
3785
- .suspicious { background: #fff3cd; color: #856404; }
3786
- .malicious { background: #f8d7da; color: #721c24; }
3787
- .result { border: 1px solid #ddd; border-radius: 8px; padding: 15px; margin: 10px 0; }
3788
- .result h3 { margin-top: 0; }
3789
- .badge { display: inline-block; padding: 4px 8px; border-radius: 4px; font-size: 0.8em; margin: 2px; }
3790
- table { width: 100%; border-collapse: collapse; }
3791
- th, td { padding: 8px; text-align: left; border-bottom: 1px solid #ddd; }
3792
- </style>
3793
- </head>
3794
- <body>
3795
- <h1>🛡️ Pompelmi Scan Results</h1>
3796
- <div class="summary">
3797
- <div class="card clean"><h2>${clean}</h2><p>Clean Files</p></div>
3798
- <div class="card suspicious"><h2>${suspicious}</h2><p>Suspicious Files</p></div>
3799
- <div class="card malicious"><h2>${malicious}</h2><p>Malicious Files</p></div>
3800
- </div>
3801
- <h2>Detailed Results</h2>`;
3802
- for (const report of data) {
3803
- const statusClass = report.verdict;
3804
- html += `<div class="result ${statusClass}">`;
3805
- html += `<h3>${this.escapeHtml(report.file?.name || 'Unknown')}</h3>`;
3806
- html += `<table>`;
3807
- html += `<tr><th>Verdict</th><td>${report.verdict.toUpperCase()}</td></tr>`;
3808
- html += `<tr><th>Size</th><td>${this.formatBytes(report.file?.size || 0)}</td></tr>`;
3809
- html += `<tr><th>MIME Type</th><td>${this.escapeHtml(report.file?.mimeType || 'unknown')}</td></tr>`;
3810
- html += `<tr><th>Duration</th><td>${report.durationMs || 0}ms</td></tr>`;
3811
- html += `<tr><th>Matches</th><td>${report.matches.length}</td></tr>`;
3812
- html += `</table>`;
3813
- if (options.includeDetails && report.matches.length > 0) {
3814
- html += `<h4>Match Details:</h4><ul>`;
3815
- for (const match of report.matches) {
3816
- html += `<li><strong>${this.escapeHtml(match.rule)}</strong>`;
3817
- if (match.tags && match.tags.length > 0) {
3818
- html += ` ${match.tags.map(tag => `<span class="badge">${this.escapeHtml(tag)}</span>`).join('')}`;
3819
- }
3820
- html += `</li>`;
3821
- }
3822
- html += `</ul>`;
3823
- }
3824
- html += `</div>`;
2181
+ sanitizeError(error) {
2182
+ if (!this.config.enabled || !this.config.sanitizeErrors) {
2183
+ return typeof error === 'string' ? error : error.message;
3825
2184
  }
3826
- html += `</body></html>`;
3827
- return html;
2185
+ const message = typeof error === 'string' ? error : error.message;
2186
+ // Remove common patterns that might contain PHI
2187
+ let sanitized = message
2188
+ // Remove file paths
2189
+ .replace(/[A-Za-z]:\\\\[^\\s]+/g, '[REDACTED_PATH]')
2190
+ .replace(/\/[^\\s]+/g, '[REDACTED_PATH]')
2191
+ // Remove potential patient identifiers (numbers that could be MRNs, SSNs)
2192
+ .replace(/\\b\\d{3}-?\\d{2}-?\\d{4}\\b/g, '[REDACTED_ID]')
2193
+ .replace(/\\b\\d{6,}\\b/g, '[REDACTED_ID]')
2194
+ // Remove email addresses
2195
+ .replace(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/g, '[REDACTED_EMAIL]')
2196
+ // Remove potential names (capitalize words in error messages)
2197
+ .replace(/\\b[A-Z][a-z]+\\s+[A-Z][a-z]+\\b/g, '[REDACTED_NAME]')
2198
+ // Remove IP addresses
2199
+ .replace(/\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b/g, '[REDACTED_IP]');
2200
+ return sanitized;
3828
2201
  }
3829
2202
  /**
3830
- * Export to specified format
2203
+ * Create secure temporary file path with encryption if enabled
3831
2204
  */
3832
- export(reports, format, options = {}) {
3833
- switch (format) {
3834
- case 'json':
3835
- return this.toJSON(reports, options);
3836
- case 'csv':
3837
- return this.toCSV(reports, options);
3838
- case 'markdown':
3839
- return this.toMarkdown(reports, options);
3840
- case 'html':
3841
- return this.toHTML(reports, options);
3842
- case 'sarif':
3843
- return this.toSARIF(reports, options);
3844
- default:
3845
- throw new Error(`Unsupported export format: ${format}`);
2205
+ createSecureTempPath(prefix = 'pompelmi') {
2206
+ if (!this.config.enabled) {
2207
+ return path__namespace.join(os__namespace.tmpdir(), `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2)}`);
3846
2208
  }
2209
+ // Use cryptographically secure random names
2210
+ const randomId = crypto__namespace.randomBytes(16).toString('hex');
2211
+ const timestamp = Date.now();
2212
+ // Create path in secure temp directory
2213
+ const secureTempDir = this.getSecureTempDir();
2214
+ const tempPath = path__namespace.join(secureTempDir, `${prefix}-${timestamp}-${randomId}`);
2215
+ this.auditLog('temp_file_created', {
2216
+ action: 'create_temp_file',
2217
+ success: true,
2218
+ metadata: { path: this.sanitizeFilename(tempPath) }
2219
+ });
2220
+ return tempPath;
3847
2221
  }
3848
- escapeCsv(value) {
3849
- if (value.includes(',') || value.includes('"') || value.includes('\n')) {
3850
- return `"${value.replace(/"/g, '""')}"`;
2222
+ /**
2223
+ * Get or create secure temporary directory with restricted permissions
2224
+ */
2225
+ getSecureTempDir() {
2226
+ const secureTempPath = path__namespace.join(os__namespace.tmpdir(), 'pompelmi-secure');
2227
+ try {
2228
+ const fs = require('fs');
2229
+ if (!fs.existsSync(secureTempPath)) {
2230
+ fs.mkdirSync(secureTempPath, { mode: 0o700 }); // Owner read/write/execute only
2231
+ }
3851
2232
  }
3852
- return value;
3853
- }
3854
- escapeHtml(value) {
3855
- return value
3856
- .replace(/&/g, '&amp;')
3857
- .replace(/</g, '&lt;')
3858
- .replace(/>/g, '&gt;')
3859
- .replace(/"/g, '&quot;')
3860
- .replace(/'/g, '&#039;');
3861
- }
3862
- formatBytes(bytes) {
3863
- if (bytes === 0)
3864
- return '0 Bytes';
3865
- const k = 1024;
3866
- const sizes = ['Bytes', 'KB', 'MB', 'GB'];
3867
- const i = Math.floor(Math.log(bytes) / Math.log(k));
3868
- return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
3869
- }
3870
- }
3871
- /**
3872
- * Quick export helper
3873
- */
3874
- function exportScanResults(reports, format, options) {
3875
- const exporter = new ScanResultExporter();
3876
- return exporter.export(reports, format, options);
3877
- }
3878
-
3879
- /**
3880
- * Advanced configuration system for pompelmi
3881
- * @module config
3882
- */
3883
- /**
3884
- * Default configuration
3885
- */
3886
- const DEFAULT_CONFIG = {
3887
- defaultPreset: 'zip-basic',
3888
- performance: {
3889
- enableCache: false,
3890
- enablePerformanceTracking: false,
3891
- enableParallel: true,
3892
- maxConcurrency: 5,
3893
- cacheOptions: {
3894
- maxSize: 1000,
3895
- ttl: 3600000, // 1 hour
3896
- enableLRU: true,
3897
- enableStats: false,
3898
- },
3899
- },
3900
- security: {
3901
- maxFileSize: 100 * 1024 * 1024, // 100MB
3902
- enableThreatIntel: false,
3903
- scanTimeout: 30000, // 30 seconds
3904
- strictMode: false,
3905
- },
3906
- advanced: {
3907
- enablePolyglotDetection: true,
3908
- enableObfuscationDetection: true,
3909
- enableNestedArchiveAnalysis: true,
3910
- maxArchiveDepth: 5,
3911
- },
3912
- logging: {
3913
- verbose: false,
3914
- level: 'info',
3915
- enableStats: false,
3916
- },
3917
- };
3918
- /**
3919
- * Configuration presets for common use cases
3920
- */
3921
- const CONFIG_PRESETS = {
3922
- /** Fast scanning with minimal features */
3923
- fast: {
3924
- defaultPreset: 'basic',
3925
- performance: {
3926
- enableCache: true,
3927
- enablePerformanceTracking: false,
3928
- maxConcurrency: 10,
3929
- },
3930
- advanced: {
3931
- enablePolyglotDetection: false,
3932
- enableObfuscationDetection: false,
3933
- enableNestedArchiveAnalysis: false,
3934
- },
3935
- },
3936
- /** Balanced scanning (recommended) */
3937
- balanced: DEFAULT_CONFIG,
3938
- /** Thorough scanning with all features */
3939
- thorough: {
3940
- defaultPreset: 'advanced',
3941
- performance: {
3942
- enableCache: true,
3943
- enablePerformanceTracking: true,
3944
- maxConcurrency: 3,
3945
- },
3946
- security: {
3947
- maxFileSize: 500 * 1024 * 1024, // 500MB
3948
- enableThreatIntel: true,
3949
- scanTimeout: 60000, // 60 seconds
3950
- strictMode: true,
3951
- },
3952
- advanced: {
3953
- enablePolyglotDetection: true,
3954
- enableObfuscationDetection: true,
3955
- enableNestedArchiveAnalysis: true,
3956
- maxArchiveDepth: 10,
3957
- },
3958
- logging: {
3959
- verbose: true,
3960
- level: 'debug',
3961
- enableStats: true,
3962
- },
3963
- },
3964
- /** Production-ready configuration */
3965
- production: {
3966
- defaultPreset: 'advanced',
3967
- performance: {
3968
- enableCache: true,
3969
- enablePerformanceTracking: true,
3970
- maxConcurrency: 5,
3971
- cacheOptions: {
3972
- maxSize: 5000,
3973
- ttl: 7200000, // 2 hours
3974
- enableLRU: true,
3975
- enableStats: true,
3976
- },
3977
- },
3978
- security: {
3979
- maxFileSize: 200 * 1024 * 1024, // 200MB
3980
- enableThreatIntel: true,
3981
- scanTimeout: 45000,
3982
- strictMode: false,
3983
- },
3984
- advanced: {
3985
- enablePolyglotDetection: true,
3986
- enableObfuscationDetection: true,
3987
- enableNestedArchiveAnalysis: true,
3988
- maxArchiveDepth: 7,
3989
- },
3990
- logging: {
3991
- verbose: false,
3992
- level: 'warn',
3993
- enableStats: true,
3994
- },
3995
- },
3996
- /** Development configuration */
3997
- development: {
3998
- defaultPreset: 'basic',
3999
- performance: {
4000
- enableCache: false,
4001
- enablePerformanceTracking: true,
4002
- maxConcurrency: 3,
4003
- },
4004
- security: {
4005
- maxFileSize: 50 * 1024 * 1024, // 50MB
4006
- scanTimeout: 15000,
4007
- strictMode: false,
4008
- },
4009
- logging: {
4010
- verbose: true,
4011
- level: 'debug',
4012
- enableStats: true,
4013
- },
4014
- },
4015
- };
4016
- /**
4017
- * Configuration manager
4018
- */
4019
- class ConfigManager {
4020
- constructor(initialConfig) {
4021
- this.config = this.mergeConfig(DEFAULT_CONFIG, initialConfig || {});
2233
+ catch (error) {
2234
+ // Fallback to system temp
2235
+ return os__namespace.tmpdir();
2236
+ }
2237
+ return secureTempPath;
4022
2238
  }
4023
2239
  /**
4024
- * Get current configuration
2240
+ * Secure file cleanup with multiple overwrite passes
4025
2241
  */
4026
- getConfig() {
4027
- return { ...this.config };
2242
+ async secureFileCleanup(filePath) {
2243
+ if (!this.config.enabled) {
2244
+ try {
2245
+ const fs = await import('fs/promises');
2246
+ await fs.unlink(filePath);
2247
+ }
2248
+ catch {
2249
+ // Ignore cleanup errors
2250
+ }
2251
+ return;
2252
+ }
2253
+ try {
2254
+ const fs = await import('fs/promises');
2255
+ const stats = await fs.stat(filePath);
2256
+ if (this.config.memoryProtection) {
2257
+ // Overwrite file with random data multiple times (DoD 5220.22-M standard)
2258
+ const fileSize = stats.size;
2259
+ const buffer = crypto__namespace.randomBytes(Math.min(fileSize, 64 * 1024)); // 64KB chunks
2260
+ for (let pass = 0; pass < 3; pass++) {
2261
+ const handle = await fs.open(filePath, 'r+');
2262
+ try {
2263
+ for (let offset = 0; offset < fileSize; offset += buffer.length) {
2264
+ const chunk = offset + buffer.length > fileSize
2265
+ ? buffer.subarray(0, fileSize - offset)
2266
+ : buffer;
2267
+ await handle.write(chunk, 0, chunk.length, offset);
2268
+ }
2269
+ await handle.sync();
2270
+ }
2271
+ finally {
2272
+ await handle.close();
2273
+ }
2274
+ }
2275
+ }
2276
+ // Final deletion
2277
+ await fs.unlink(filePath);
2278
+ this.auditLog('temp_file_deleted', {
2279
+ action: 'secure_delete',
2280
+ success: true,
2281
+ metadata: {
2282
+ path: this.sanitizeFilename(filePath),
2283
+ overwritePasses: this.config.memoryProtection ? 3 : 0
2284
+ }
2285
+ });
2286
+ }
2287
+ catch (error) {
2288
+ this.auditLog('temp_file_deleted', {
2289
+ action: 'secure_delete',
2290
+ success: false,
2291
+ sanitizedError: this.sanitizeError(error),
2292
+ metadata: { path: this.sanitizeFilename(filePath) }
2293
+ });
2294
+ }
4028
2295
  }
4029
2296
  /**
4030
- * Update configuration
2297
+ * Calculate secure file hash for audit purposes
4031
2298
  */
4032
- updateConfig(updates) {
4033
- this.config = this.mergeConfig(this.config, updates);
2299
+ calculateFileHash(data) {
2300
+ return crypto__namespace.createHash('sha256').update(data).digest('hex');
4034
2301
  }
4035
2302
  /**
4036
- * Load a preset configuration
2303
+ * Log audit event
4037
2304
  */
4038
- loadPreset(preset) {
4039
- const presetConfig = CONFIG_PRESETS[preset];
4040
- this.config = this.mergeConfig(DEFAULT_CONFIG, presetConfig);
2305
+ auditLog(eventType, details) {
2306
+ if (!this.config.enabled)
2307
+ return;
2308
+ const event = {
2309
+ timestamp: new Date().toISOString(),
2310
+ eventType,
2311
+ sessionId: this.sessionId,
2312
+ details: {
2313
+ action: details.action || 'unknown',
2314
+ success: details.success ?? true,
2315
+ ...details
2316
+ }
2317
+ };
2318
+ this.auditEvents.push(event);
2319
+ // Write to audit log file if configured
2320
+ if (this.config.auditLogPath) {
2321
+ this.writeAuditLog(event).catch(() => {
2322
+ // Silent failure to prevent error loops
2323
+ });
2324
+ }
4041
2325
  }
4042
2326
  /**
4043
- * Reset to default configuration
2327
+ * Write audit event to file
4044
2328
  */
4045
- reset() {
4046
- this.config = { ...DEFAULT_CONFIG };
2329
+ async writeAuditLog(event) {
2330
+ if (!this.config.auditLogPath)
2331
+ return;
2332
+ try {
2333
+ const fs = await import('fs/promises');
2334
+ const logLine = JSON.stringify(event) + '\\n';
2335
+ await fs.appendFile(this.config.auditLogPath, logLine, { flag: 'a' });
2336
+ }
2337
+ catch {
2338
+ // Silent failure
2339
+ }
4047
2340
  }
4048
2341
  /**
4049
- * Get a specific configuration value
2342
+ * Generate cryptographically secure session ID
4050
2343
  */
4051
- get(key) {
4052
- return this.config[key];
2344
+ generateSessionId() {
2345
+ return crypto__namespace.randomBytes(16).toString('hex');
4053
2346
  }
4054
2347
  /**
4055
- * Set a specific configuration value
2348
+ * Get current audit events for this session
4056
2349
  */
4057
- set(key, value) {
4058
- this.config[key] = value;
2350
+ getAuditEvents() {
2351
+ return [...this.auditEvents];
4059
2352
  }
4060
2353
  /**
4061
- * Validate configuration
2354
+ * Clear sensitive data from memory
4062
2355
  */
4063
- validate() {
4064
- const errors = [];
4065
- // Validate performance settings
4066
- if (this.config.performance?.maxConcurrency !== undefined) {
4067
- if (this.config.performance.maxConcurrency < 1) {
4068
- errors.push('maxConcurrency must be at least 1');
4069
- }
4070
- if (this.config.performance.maxConcurrency > 50) {
4071
- errors.push('maxConcurrency should not exceed 50');
4072
- }
4073
- }
4074
- // Validate security settings
4075
- if (this.config.security?.maxFileSize !== undefined) {
4076
- if (this.config.security.maxFileSize < 1024) {
4077
- errors.push('maxFileSize must be at least 1KB');
4078
- }
4079
- }
4080
- if (this.config.security?.scanTimeout !== undefined) {
4081
- if (this.config.security.scanTimeout < 1000) {
4082
- errors.push('scanTimeout must be at least 1000ms');
4083
- }
4084
- }
4085
- // Validate advanced settings
4086
- if (this.config.advanced?.maxArchiveDepth !== undefined) {
4087
- if (this.config.advanced.maxArchiveDepth < 1) {
4088
- errors.push('maxArchiveDepth must be at least 1');
4089
- }
4090
- if (this.config.advanced.maxArchiveDepth > 20) {
4091
- errors.push('maxArchiveDepth should not exceed 20');
4092
- }
2356
+ clearSensitiveData() {
2357
+ if (!this.config.enabled || !this.config.memoryProtection)
2358
+ return;
2359
+ // Clear audit events
2360
+ this.auditEvents.length = 0;
2361
+ // Force garbage collection if available
2362
+ if (global.gc) {
2363
+ global.gc();
4093
2364
  }
4094
- return {
4095
- valid: errors.length === 0,
4096
- errors,
4097
- };
4098
- }
4099
- /**
4100
- * Deep merge configuration objects
4101
- */
4102
- mergeConfig(base, updates) {
4103
- return {
4104
- ...base,
4105
- ...updates,
4106
- performance: {
4107
- ...base.performance,
4108
- ...updates.performance,
4109
- cacheOptions: {
4110
- ...base.performance?.cacheOptions,
4111
- ...updates.performance?.cacheOptions,
4112
- },
4113
- },
4114
- security: {
4115
- ...base.security,
4116
- ...updates.security,
4117
- },
4118
- advanced: {
4119
- ...base.advanced,
4120
- ...updates.advanced,
4121
- },
4122
- logging: {
4123
- ...base.logging,
4124
- ...updates.logging,
4125
- },
4126
- callbacks: {
4127
- ...base.callbacks,
4128
- ...updates.callbacks,
4129
- },
4130
- presetOptions: {
4131
- ...base.presetOptions,
4132
- ...updates.presetOptions,
4133
- },
4134
- };
4135
- }
4136
- /**
4137
- * Export configuration as JSON
4138
- */
4139
- toJSON() {
4140
- return JSON.stringify(this.config, null, 2);
4141
2365
  }
4142
2366
  /**
4143
- * Load configuration from JSON
2367
+ * Validate transport security
4144
2368
  */
4145
- fromJSON(json) {
2369
+ validateTransportSecurity(url) {
2370
+ if (!this.config.enabled || !this.config.requireSecureTransport) {
2371
+ return true;
2372
+ }
2373
+ if (!url)
2374
+ return true;
4146
2375
  try {
4147
- const parsed = JSON.parse(json);
4148
- this.config = this.mergeConfig(DEFAULT_CONFIG, parsed);
2376
+ const urlObj = new URL(url);
2377
+ const isSecure = urlObj.protocol === 'https:' || urlObj.hostname === 'localhost' || urlObj.hostname === '127.0.0.1';
2378
+ if (!isSecure) {
2379
+ this.auditLog('security_violation', {
2380
+ action: 'insecure_transport',
2381
+ success: false,
2382
+ metadata: { protocol: urlObj.protocol, hostname: urlObj.hostname }
2383
+ });
2384
+ }
2385
+ return isSecure;
4149
2386
  }
4150
- catch (error) {
4151
- throw new Error(`Failed to parse configuration JSON: ${error}`);
2387
+ catch {
2388
+ return false;
4152
2389
  }
4153
2390
  }
4154
2391
  }
2392
+ // Global HIPAA compliance instance
2393
+ let hipaaManager = null;
4155
2394
  /**
4156
- * Create a new configuration manager
2395
+ * Initialize HIPAA compliance
4157
2396
  */
4158
- function createConfig(config) {
4159
- return new ConfigManager(config);
2397
+ function initializeHipaaCompliance(config) {
2398
+ hipaaManager = new HipaaComplianceManager(config);
2399
+ return hipaaManager;
4160
2400
  }
4161
2401
  /**
4162
- * Get a preset configuration
2402
+ * Get current HIPAA compliance manager
4163
2403
  */
4164
- function getPresetConfig(preset) {
4165
- return { ...DEFAULT_CONFIG, ...CONFIG_PRESETS[preset] };
2404
+ function getHipaaManager() {
2405
+ return hipaaManager;
2406
+ }
2407
+ /**
2408
+ * HIPAA-compliant error wrapper
2409
+ */
2410
+ function createHipaaError(error, context) {
2411
+ const manager = getHipaaManager();
2412
+ if (!manager) {
2413
+ return typeof error === 'string' ? new Error(error) : error;
2414
+ }
2415
+ const sanitizedMessage = manager.sanitizeError(error);
2416
+ const hipaaError = new Error(sanitizedMessage);
2417
+ manager.auditLog('error_occurred', {
2418
+ action: context || 'error',
2419
+ success: false,
2420
+ sanitizedError: sanitizedMessage
2421
+ });
2422
+ return hipaaError;
4166
2423
  }
2424
+ /**
2425
+ * HIPAA-compliant temporary file utilities
2426
+ */
2427
+ const HipaaTemp = {
2428
+ createPath: (prefix) => {
2429
+ const manager = getHipaaManager();
2430
+ return manager ? manager.createSecureTempPath(prefix) : path__namespace.join(os__namespace.tmpdir(), `${prefix || 'pompelmi'}-${Date.now()}`);
2431
+ },
2432
+ cleanup: async (filePath) => {
2433
+ const manager = getHipaaManager();
2434
+ if (manager) {
2435
+ await manager.secureFileCleanup(filePath);
2436
+ }
2437
+ else {
2438
+ try {
2439
+ const fs = await import('fs/promises');
2440
+ await fs.unlink(filePath);
2441
+ }
2442
+ catch {
2443
+ // Ignore errors
2444
+ }
2445
+ }
2446
+ }
2447
+ };
4167
2448
 
2449
+ exports.ARCHIVES = ARCHIVES;
4168
2450
  exports.BatchScanner = BatchScanner;
4169
2451
  exports.CONFIG_PRESETS = CONFIG_PRESETS;
2452
+ exports.CONSERVATIVE_DEFAULT = CONSERVATIVE_DEFAULT;
4170
2453
  exports.CommonHeuristicsScanner = CommonHeuristicsScanner;
4171
2454
  exports.ConfigManager = ConfigManager;
4172
2455
  exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
4173
2456
  exports.DEFAULT_POLICY = DEFAULT_POLICY;
4174
- exports.HipaaComplianceManager = HipaaComplianceManager;
2457
+ exports.DOCUMENTS_ONLY = DOCUMENTS_ONLY;
4175
2458
  exports.HipaaTemp = HipaaTemp;
2459
+ exports.IMAGES_ONLY = IMAGES_ONLY;
4176
2460
  exports.LocalThreatIntelligence = LocalThreatIntelligence;
4177
- exports.PRESET_CONFIGS = PRESET_CONFIGS;
2461
+ exports.POLICY_PACKS = POLICY_PACKS;
4178
2462
  exports.PerformanceTracker = PerformanceTracker;
2463
+ exports.STRICT_PUBLIC_UPLOAD = STRICT_PUBLIC_UPLOAD;
4179
2464
  exports.SUSPICIOUS_PATTERNS = SUSPICIOUS_PATTERNS;
4180
2465
  exports.ScanCacheManager = ScanCacheManager;
4181
2466
  exports.ScanResultExporter = ScanResultExporter;
@@ -4196,6 +2481,7 @@ exports.exportScanResults = exportScanResults;
4196
2481
  exports.getDefaultCache = getDefaultCache;
4197
2482
  exports.getFileHash = getFileHash;
4198
2483
  exports.getHipaaManager = getHipaaManager;
2484
+ exports.getPolicyPack = getPolicyPack;
4199
2485
  exports.getPresetConfig = getPresetConfig;
4200
2486
  exports.initializeHipaaCompliance = initializeHipaaCompliance;
4201
2487
  exports.mapMatchesToVerdict = mapMatchesToVerdict;
@@ -4204,6 +2490,5 @@ exports.scanBytes = scanBytes;
4204
2490
  exports.scanFile = scanFile;
4205
2491
  exports.scanFiles = scanFiles;
4206
2492
  exports.scanFilesWithRemoteYara = scanFilesWithRemoteYara;
4207
- exports.useFileScanner = useFileScanner;
4208
2493
  exports.validateFile = validateFile;
4209
2494
  //# sourceMappingURL=pompelmi.cjs.map