conexys-elements 1.1.3 → 1.1.26

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 (254) hide show
  1. package/README.md +462 -125
  2. package/dist/Auth.d.ts +0 -0
  3. package/dist/Auth.d.ts.map +0 -0
  4. package/dist/ThemeContext.d.ts +0 -0
  5. package/dist/ThemeContext.d.ts.map +0 -0
  6. package/dist/components/AppDataSettings.d.ts +0 -0
  7. package/dist/components/AppDataSettings.d.ts.map +0 -0
  8. package/dist/components/AppDataSettingsHTML.d.ts +0 -0
  9. package/dist/components/AppDataSettingsHTML.d.ts.map +1 -1
  10. package/dist/components/AppError.d.ts +0 -0
  11. package/dist/components/AppError.d.ts.map +0 -0
  12. package/dist/components/AppFormFields.d.ts +1 -1
  13. package/dist/components/AppFormFields.d.ts.map +1 -1
  14. package/dist/components/AppFormFieldsFromConfig.d.ts +0 -0
  15. package/dist/components/AppFormFieldsFromConfig.d.ts.map +1 -1
  16. package/dist/components/AppFormFieldsNoUser.d.ts +0 -0
  17. package/dist/components/AppFormFieldsNoUser.d.ts.map +1 -1
  18. package/dist/components/AppFormFieldsTable.d.ts +0 -0
  19. package/dist/components/AppFormFieldsTable.d.ts.map +1 -1
  20. package/dist/components/AppHeaderPage.d.ts +0 -0
  21. package/dist/components/AppHeaderPage.d.ts.map +1 -1
  22. package/dist/components/AppHeaderPage404.d.ts +0 -0
  23. package/dist/components/AppHeaderPage404.d.ts.map +0 -0
  24. package/dist/components/AppSetHeaderTitle.d.ts +0 -0
  25. package/dist/components/AppSetHeaderTitle.d.ts.map +0 -0
  26. package/dist/components/BackendStatus/BackendOfflinePage.d.ts +0 -0
  27. package/dist/components/BackendStatus/BackendOfflinePage.d.ts.map +0 -0
  28. package/dist/components/BackendStatus/BackendStatusProvider.d.ts +0 -0
  29. package/dist/components/BackendStatus/BackendStatusProvider.d.ts.map +0 -0
  30. package/dist/components/BackendStatus/index.d.ts +0 -0
  31. package/dist/components/BackendStatus/index.d.ts.map +0 -0
  32. package/dist/components/ErrorBoundary.d.ts +0 -0
  33. package/dist/components/ErrorBoundary.d.ts.map +0 -0
  34. package/dist/components/NotFound.d.ts +0 -0
  35. package/dist/components/NotFound.d.ts.map +0 -0
  36. package/dist/components/PermissionGate.d.ts +0 -0
  37. package/dist/components/PermissionGate.d.ts.map +0 -0
  38. package/dist/components/Uservalidationerror.d.ts +0 -0
  39. package/dist/components/Uservalidationerror.d.ts.map +0 -0
  40. package/dist/components/dialog/AppDialogModal.d.ts +0 -0
  41. package/dist/components/dialog/AppDialogModal.d.ts.map +0 -0
  42. package/dist/components/form/RenderForm.d.ts +0 -0
  43. package/dist/components/form/RenderForm.d.ts.map +0 -0
  44. package/dist/components/form/components/Button.d.ts +0 -0
  45. package/dist/components/form/components/Button.d.ts.map +0 -0
  46. package/dist/components/form/components/Checkbox.d.ts +0 -0
  47. package/dist/components/form/components/Checkbox.d.ts.map +0 -0
  48. package/dist/components/form/components/FormLogin.d.ts +0 -0
  49. package/dist/components/form/components/FormLogin.d.ts.map +1 -1
  50. package/dist/components/form/components/Heading.d.ts +0 -0
  51. package/dist/components/form/components/Heading.d.ts.map +0 -0
  52. package/dist/components/form/components/Image.d.ts +0 -0
  53. package/dist/components/form/components/Image.d.ts.map +0 -0
  54. package/dist/components/form/components/Info.d.ts +0 -0
  55. package/dist/components/form/components/Info.d.ts.map +1 -1
  56. package/dist/components/form/components/InputFile.d.ts +0 -0
  57. package/dist/components/form/components/InputFile.d.ts.map +0 -0
  58. package/dist/components/form/components/InputPassword.d.ts +0 -0
  59. package/dist/components/form/components/InputPassword.d.ts.map +1 -1
  60. package/dist/components/form/components/InputText.d.ts +0 -0
  61. package/dist/components/form/components/InputText.d.ts.map +1 -1
  62. package/dist/components/form/components/InputWYSIWYG.d.ts +0 -0
  63. package/dist/components/form/components/InputWYSIWYG.d.ts.map +1 -1
  64. package/dist/components/form/components/Radiobutton.d.ts +0 -0
  65. package/dist/components/form/components/Radiobutton.d.ts.map +0 -0
  66. package/dist/components/form/components/Select.d.ts +0 -0
  67. package/dist/components/form/components/Select.d.ts.map +0 -0
  68. package/dist/components/form/components/Switch.d.ts +0 -0
  69. package/dist/components/form/components/Switch.d.ts.map +0 -0
  70. package/dist/components/form/components/Text.d.ts +0 -0
  71. package/dist/components/form/components/Text.d.ts.map +0 -0
  72. package/dist/components/form/components/index.d.ts +0 -0
  73. package/dist/components/form/components/index.d.ts.map +0 -0
  74. package/dist/components/form/components/validation/Validation.d.ts +4 -1
  75. package/dist/components/form/components/validation/Validation.d.ts.map +1 -1
  76. package/dist/components/index.d.ts +0 -0
  77. package/dist/components/index.d.ts.map +0 -0
  78. package/dist/components/login/LanguageSet.d.ts +0 -0
  79. package/dist/components/login/LanguageSet.d.ts.map +1 -1
  80. package/dist/components/theme/Card.d.ts +0 -0
  81. package/dist/components/theme/Card.d.ts.map +0 -0
  82. package/dist/components/theme/CardDashboard.d.ts +0 -0
  83. package/dist/components/theme/CardDashboard.d.ts.map +0 -0
  84. package/dist/components/theme/CardUser.d.ts +0 -0
  85. package/dist/components/theme/CardUser.d.ts.map +0 -0
  86. package/dist/components/theme/Loading.d.ts +0 -0
  87. package/dist/components/theme/Loading.d.ts.map +0 -0
  88. package/dist/components/theme/MailTemplateWidget.d.ts +0 -0
  89. package/dist/components/theme/MailTemplateWidget.d.ts.map +1 -1
  90. package/dist/components/theme/Mailbox.d.ts +0 -0
  91. package/dist/components/theme/Mailbox.d.ts.map +1 -1
  92. package/dist/components/theme/MessagesBlock.d.ts +0 -0
  93. package/dist/components/theme/MessagesBlock.d.ts.map +0 -0
  94. package/dist/components/theme/Timeline.d.ts +0 -0
  95. package/dist/components/theme/Timeline.d.ts.map +0 -0
  96. package/dist/components/theme/UserWidget.d.ts +0 -0
  97. package/dist/components/theme/UserWidget.d.ts.map +1 -1
  98. package/dist/components/theme/index.d.ts +0 -0
  99. package/dist/components/theme/index.d.ts.map +0 -0
  100. package/dist/config/ConexysConfig.d.ts +0 -0
  101. package/dist/config/ConexysConfig.d.ts.map +0 -0
  102. package/dist/constants/global.d.ts +0 -0
  103. package/dist/constants/global.d.ts.map +1 -1
  104. package/dist/context/PermissionContext.d.ts +0 -0
  105. package/dist/context/PermissionContext.d.ts.map +0 -0
  106. package/dist/hooks/FormHooksNormal.d.ts +1 -1
  107. package/dist/hooks/FormHooksNormal.d.ts.map +1 -1
  108. package/dist/hooks/FormHooksNormalGetPost.d.ts +0 -0
  109. package/dist/hooks/FormHooksNormalGetPost.d.ts.map +1 -1
  110. package/dist/hooks/FormHooksNormalMulti.d.ts +0 -0
  111. package/dist/hooks/FormHooksNormalMulti.d.ts.map +0 -0
  112. package/dist/hooks/FormHooksNormalNoUser.d.ts +0 -0
  113. package/dist/hooks/FormHooksNormalNoUser.d.ts.map +1 -1
  114. package/dist/hooks/FormHooksUpload.d.ts +0 -0
  115. package/dist/hooks/FormHooksUpload.d.ts.map +1 -1
  116. package/dist/hooks/FormHooksUploadPatch.d.ts +0 -0
  117. package/dist/hooks/FormHooksUploadPatch.d.ts.map +1 -1
  118. package/dist/hooks/MRT_Localization.d.ts +0 -0
  119. package/dist/hooks/MRT_Localization.d.ts.map +0 -0
  120. package/dist/hooks/useFingerprintJS.d.ts +0 -0
  121. package/dist/hooks/useFingerprintJS.d.ts.map +0 -0
  122. package/dist/hooks/useLanguageSync.d.ts +0 -0
  123. package/dist/hooks/useLanguageSync.d.ts.map +0 -0
  124. package/dist/hooks/usePermission.d.ts +0 -0
  125. package/dist/hooks/usePermission.d.ts.map +0 -0
  126. package/dist/hooks/useSocket.d.ts +0 -0
  127. package/dist/hooks/useSocket.d.ts.map +1 -1
  128. package/dist/hooks/useZoomPrevention.d.ts +0 -0
  129. package/dist/hooks/useZoomPrevention.d.ts.map +0 -0
  130. package/dist/index.css +0 -0
  131. package/dist/index.css.map +1 -1
  132. package/dist/index.d.ts +1 -0
  133. package/dist/index.d.ts.map +1 -1
  134. package/dist/index.esm.css +0 -0
  135. package/dist/index.esm.css.map +1 -1
  136. package/dist/index.esm.js +3394 -293
  137. package/dist/index.esm.js.map +1 -1
  138. package/dist/index.js +3394 -292
  139. package/dist/index.js.map +1 -1
  140. package/dist/services/deleteFormService.d.ts +0 -0
  141. package/dist/services/deleteFormService.d.ts.map +1 -1
  142. package/dist/services/deleteService.d.ts +0 -0
  143. package/dist/services/deleteService.d.ts.map +1 -1
  144. package/dist/services/deleteServiceExtended.d.ts +0 -0
  145. package/dist/services/deleteServiceExtended.d.ts.map +0 -0
  146. package/dist/services/getService.d.ts +0 -0
  147. package/dist/services/getService.d.ts.map +1 -1
  148. package/dist/services/getServiceExtended.d.ts +0 -0
  149. package/dist/services/getServiceExtended.d.ts.map +1 -1
  150. package/dist/services/patchFormService.d.ts +0 -0
  151. package/dist/services/patchFormService.d.ts.map +1 -1
  152. package/dist/services/patchService.d.ts +0 -0
  153. package/dist/services/patchService.d.ts.map +1 -1
  154. package/dist/services/patchServiceExtended.d.ts +0 -0
  155. package/dist/services/patchServiceExtended.d.ts.map +0 -0
  156. package/dist/services/postFormService.d.ts +0 -0
  157. package/dist/services/postFormService.d.ts.map +1 -1
  158. package/dist/services/postFormServiceGetPost.d.ts +0 -0
  159. package/dist/services/postFormServiceGetPost.d.ts.map +1 -1
  160. package/dist/services/postService.d.ts +0 -0
  161. package/dist/services/postService.d.ts.map +1 -1
  162. package/dist/services/postServiceExtended.d.ts +0 -0
  163. package/dist/services/postServiceExtended.d.ts.map +1 -1
  164. package/dist/services/restoreFormService.d.ts +0 -0
  165. package/dist/services/restoreFormService.d.ts.map +1 -1
  166. package/dist/shared/baseFingerprintService.d.ts +0 -0
  167. package/dist/shared/baseFingerprintService.d.ts.map +0 -0
  168. package/dist/shared/crudtable/ActionButtons.d.ts +0 -0
  169. package/dist/shared/crudtable/ActionButtons.d.ts.map +0 -0
  170. package/dist/shared/crudtable/Table.d.ts +0 -0
  171. package/dist/shared/crudtable/Table.d.ts.map +0 -0
  172. package/dist/shared/crudtable/useTableData.d.ts +0 -0
  173. package/dist/shared/crudtable/useTableData.d.ts.map +0 -0
  174. package/dist/shared/crudtable/useTableDataInstall.d.ts +0 -0
  175. package/dist/shared/crudtable/useTableDataInstall.d.ts.map +0 -0
  176. package/dist/shared/sharedComponentsAPI.d.ts +0 -0
  177. package/dist/shared/sharedComponentsAPI.d.ts.map +0 -0
  178. package/dist/shared/sharedServicesAPI.d.ts +0 -0
  179. package/dist/shared/sharedServicesAPI.d.ts.map +0 -0
  180. package/dist/types/auth.types.d.ts +1 -1
  181. package/dist/types/auth.types.d.ts.map +1 -1
  182. package/dist/types/common/form.types.d.ts +0 -0
  183. package/dist/types/common/form.types.d.ts.map +0 -0
  184. package/dist/types/common/index.d.ts +0 -0
  185. package/dist/types/common/index.d.ts.map +0 -0
  186. package/dist/types/common/props.types.d.ts +0 -0
  187. package/dist/types/common/props.types.d.ts.map +0 -0
  188. package/dist/types/common/request.types.d.ts +1 -0
  189. package/dist/types/common/request.types.d.ts.map +1 -1
  190. package/dist/types/common/service.types.d.ts +0 -0
  191. package/dist/types/common/service.types.d.ts.map +0 -0
  192. package/dist/types/common/user.types.d.ts +0 -0
  193. package/dist/types/common/user.types.d.ts.map +0 -0
  194. package/dist/types/components/appFormFields.types.d.ts +2 -0
  195. package/dist/types/components/appFormFields.types.d.ts.map +1 -1
  196. package/dist/types/components/appFormFieldsFromConfig.types.d.ts +1 -0
  197. package/dist/types/components/appFormFieldsFromConfig.types.d.ts.map +1 -1
  198. package/dist/types/components/appFormFieldsNoUser.types.d.ts +1 -0
  199. package/dist/types/components/appFormFieldsNoUser.types.d.ts.map +1 -1
  200. package/dist/types/components/appFormFieldsTable.types.d.ts +1 -0
  201. package/dist/types/components/appFormFieldsTable.types.d.ts.map +1 -1
  202. package/dist/types/components/backendStatus.types.d.ts +0 -0
  203. package/dist/types/components/backendStatus.types.d.ts.map +0 -0
  204. package/dist/types/components/components.types.d.ts +0 -0
  205. package/dist/types/components/components.types.d.ts.map +0 -0
  206. package/dist/types/components/dialog.types.d.ts +0 -0
  207. package/dist/types/components/dialog.types.d.ts.map +0 -0
  208. package/dist/types/components/form.types.d.ts +20 -0
  209. package/dist/types/components/form.types.d.ts.map +1 -1
  210. package/dist/types/components/login.types.d.ts +0 -0
  211. package/dist/types/components/login.types.d.ts.map +0 -0
  212. package/dist/types/components/pages.types.d.ts +0 -0
  213. package/dist/types/components/pages.types.d.ts.map +0 -0
  214. package/dist/types/components/theme.types.d.ts +0 -0
  215. package/dist/types/components/theme.types.d.ts.map +0 -0
  216. package/dist/types/hooks/formHooksNormal.types.d.ts +0 -0
  217. package/dist/types/hooks/formHooksNormal.types.d.ts.map +0 -0
  218. package/dist/types/hooks/formHooksNormalGetPost.types.d.ts +0 -0
  219. package/dist/types/hooks/formHooksNormalGetPost.types.d.ts.map +0 -0
  220. package/dist/types/hooks/formHooksNormalMulti.types.d.ts +0 -0
  221. package/dist/types/hooks/formHooksNormalMulti.types.d.ts.map +0 -0
  222. package/dist/types/hooks/formHooksNormalNoUser.types.d.ts +0 -0
  223. package/dist/types/hooks/formHooksNormalNoUser.types.d.ts.map +0 -0
  224. package/dist/types/hooks/formHooksUpload.types.d.ts +0 -0
  225. package/dist/types/hooks/formHooksUpload.types.d.ts.map +0 -0
  226. package/dist/types/hooks/mrt_Localization.types.d.ts +0 -0
  227. package/dist/types/hooks/mrt_Localization.types.d.ts.map +0 -0
  228. package/dist/types/permission.types.d.ts +0 -0
  229. package/dist/types/permission.types.d.ts.map +0 -0
  230. package/dist/types/services/postFormService.types.d.ts +0 -0
  231. package/dist/types/services/postFormService.types.d.ts.map +0 -0
  232. package/dist/types/services/postFormServiceGetPost.types.d.ts +0 -0
  233. package/dist/types/services/postFormServiceGetPost.types.d.ts.map +0 -0
  234. package/dist/types/services/postServiceExtended.types.d.ts +0 -0
  235. package/dist/types/services/postServiceExtended.types.d.ts.map +0 -0
  236. package/dist/types/shared/crudtable.types.d.ts +0 -0
  237. package/dist/types/shared/crudtable.types.d.ts.map +0 -0
  238. package/dist/types/themecontext.types.d.ts +0 -0
  239. package/dist/types/themecontext.types.d.ts.map +0 -0
  240. package/dist/types/utilities/generateRandomPassword.types.d.ts +0 -0
  241. package/dist/types/utilities/generateRandomPassword.types.d.ts.map +0 -0
  242. package/dist/utilities/GenerateRandomPassword.d.ts +0 -0
  243. package/dist/utilities/GenerateRandomPassword.d.ts.map +0 -0
  244. package/dist/utilities/authStorage.d.ts +9 -5
  245. package/dist/utilities/authStorage.d.ts.map +1 -1
  246. package/dist/utilities/authStorage2.d.ts +20 -0
  247. package/dist/utilities/authStorage2.d.ts.map +1 -0
  248. package/dist/utilities/checkAuth.d.ts +0 -0
  249. package/dist/utilities/checkAuth.d.ts.map +1 -1
  250. package/dist/utilities/logConsole.d.ts +0 -0
  251. package/dist/utilities/logConsole.d.ts.map +0 -0
  252. package/dist/utilities/sanitizeHtml.d.ts +2 -0
  253. package/dist/utilities/sanitizeHtml.d.ts.map +1 -0
  254. package/package.json +83 -82
package/dist/index.esm.js CHANGED
@@ -40,8 +40,11 @@ import Checkboxmui from '@mui/material/Checkbox';
40
40
  import FormControlLabel from '@mui/material/FormControlLabel';
41
41
  import { useForm } from 'react-hook-form';
42
42
  import Button$3 from '@mui/material/Button';
43
+ import Visibility from '@mui/icons-material/Visibility';
44
+ import VisibilityOff from '@mui/icons-material/VisibilityOff';
43
45
  import TextField from '@mui/material/TextField';
44
46
  import InputLabel from '@mui/material/InputLabel';
47
+ import InputAdornment from '@mui/material/InputAdornment';
45
48
  import JoditEditor from 'jodit-react';
46
49
  import Radio from '@mui/material/Radio';
47
50
  import RadioGroup from '@mui/material/RadioGroup';
@@ -53,7 +56,30 @@ import { Stack, Button as Button$4, useTheme, darken, lighten } from '@mui/mater
53
56
  import { useMaterialReactTable, MaterialReactTable } from 'material-react-table';
54
57
  import { io } from 'socket.io-client';
55
58
 
56
- const Url = window.restAPI || '';
59
+ const REST_API_KEY = 'restAPI';
60
+ function resolveRestAPI() {
61
+ let value;
62
+ if (typeof window !== 'undefined') {
63
+ const existing = window[REST_API_KEY];
64
+ if (typeof existing === 'string' && existing.length > 0) {
65
+ value = existing;
66
+ } else {
67
+ value = '';
68
+ }
69
+ try {
70
+ Object.defineProperty(window, REST_API_KEY, {
71
+ value,
72
+ writable: false,
73
+ configurable: false,
74
+ enumerable: true
75
+ });
76
+ } catch {}
77
+ } else {
78
+ value = '';
79
+ }
80
+ return value;
81
+ }
82
+ const Url = resolveRestAPI();
57
83
 
58
84
  const logConsole = (configLogs, type, message, ...args) => {
59
85
  if (!configLogs) return;
@@ -96,6 +122,159 @@ const logConsole = (configLogs, type, message, ...args) => {
96
122
  }
97
123
  };
98
124
 
125
+ const config$4 = {
126
+ headers: {
127
+ 'Content-Type': 'application/json'
128
+ }
129
+ };
130
+ const TYPE_SESSION_CACHE_KEY = 'cx_type_session';
131
+ const COOKIE_AUTH_MARKER = 'cookie';
132
+ let USE_COOKIES_FOR_AUTH = false;
133
+ let SESSION_EXPIRATION = 365;
134
+ let isConfigInitialized = false;
135
+ let initPromise = null;
136
+ let isFetching = false;
137
+ const getCachedTypeSession = () => {
138
+ try {
139
+ return localStorage.getItem(TYPE_SESSION_CACHE_KEY);
140
+ } catch {
141
+ return null;
142
+ }
143
+ };
144
+ const resetAuthConfig = () => {
145
+ initPromise = null;
146
+ isConfigInitialized = false;
147
+ isFetching = false;
148
+ };
149
+ const setCachedTypeSession = value => {
150
+ try {
151
+ localStorage.setItem(TYPE_SESSION_CACHE_KEY, value);
152
+ } catch {}
153
+ };
154
+ const fetchTypeSession = async configLogs => {
155
+ const baseURL = Url + 'getsettings';
156
+ try {
157
+ await serviceData(baseURL, {
158
+ keys: 'type_session'
159
+ }, config$4, value => {
160
+ const resolved = value && typeof value === 'string' ? value : 'localstorage';
161
+ USE_COOKIES_FOR_AUTH = resolved === 'cookie';
162
+ setCachedTypeSession(resolved);
163
+ }, configLogs);
164
+ } catch (error) {
165
+ logConsole(configLogs, 'error', 'Error loading authentication configuration:', error);
166
+ }
167
+ };
168
+ const initializeAuthConfig = configLogs => {
169
+ if (isConfigInitialized) return Promise.resolve();
170
+ if (initPromise) return initPromise;
171
+ initPromise = (async () => {
172
+ if (isConfigInitialized) return;
173
+ const cached = getCachedTypeSession();
174
+ if (cached !== null) {
175
+ USE_COOKIES_FOR_AUTH = cached === 'cookie';
176
+ isConfigInitialized = true;
177
+ return;
178
+ }
179
+ if (isFetching) return;
180
+ isFetching = true;
181
+ try {
182
+ await fetchTypeSession(configLogs);
183
+ isConfigInitialized = true;
184
+ } finally {
185
+ isFetching = false;
186
+ initPromise = null;
187
+ }
188
+ })();
189
+ return initPromise;
190
+ };
191
+ const refreshTypeSessionFromDB = async configLogs => {
192
+ if (initPromise && !isFetching) {
193
+ await initPromise;
194
+ } else if (isFetching) {
195
+ if (initPromise) await initPromise;
196
+ }
197
+ if (isFetching) {
198
+ if (initPromise) await initPromise;
199
+ return;
200
+ }
201
+ isFetching = true;
202
+ initPromise = (async () => {
203
+ try {
204
+ await fetchTypeSession(configLogs);
205
+ isConfigInitialized = true;
206
+ } finally {
207
+ isFetching = false;
208
+ initPromise = null;
209
+ }
210
+ })();
211
+ await initPromise;
212
+ };
213
+ const authStorage = {
214
+ initialize(configLogs) {
215
+ return initializeAuthConfig(configLogs);
216
+ },
217
+ getSessionTypeValue(_configLogs) {
218
+ return 'cookie';
219
+ },
220
+ async setAuthToken(_token, _configLogs) {
221
+ localStorage.removeItem('cxauthxc');
222
+ this.deleteCookie('cxauthxc');
223
+ },
224
+ getAuthToken(_configLogs) {
225
+ return COOKIE_AUTH_MARKER;
226
+ },
227
+ hasActiveSession(_configLogs) {
228
+ const sessionId = this.getCookie('cx_session');
229
+ const csrfToken = this.getCookie('csrf_token');
230
+ return sessionId !== null && sessionId !== '' && csrfToken !== null && csrfToken !== '';
231
+ },
232
+ getCsrfToken() {
233
+ return this.getCookie('csrf_token');
234
+ },
235
+ async setSessionId(sessionId, _configLogs) {
236
+ this.setCookie('cx_session', sessionId, SESSION_EXPIRATION);
237
+ localStorage.removeItem('cx_session');
238
+ },
239
+ getSessionId(_configLogs) {
240
+ return this.getCookie('cx_session');
241
+ },
242
+ refreshTypeSession(configLogs) {
243
+ return refreshTypeSessionFromDB(configLogs);
244
+ },
245
+ async removeAuthData(configLogs) {
246
+ this.deleteCookie('cxauthxc');
247
+ this.deleteCookie('cx_session');
248
+ this.deleteCookie('csrf_token');
249
+ localStorage.removeItem('cxauthxc');
250
+ localStorage.removeItem('cx_session');
251
+ try {
252
+ localStorage.removeItem(TYPE_SESSION_CACHE_KEY);
253
+ } catch {}
254
+ resetAuthConfig();
255
+ },
256
+ setCookie(name, value, days) {
257
+ const date = new Date();
258
+ date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
259
+ const expires = `expires=${date.toUTCString()}`;
260
+ const secure = window.location.protocol === 'https:' ? ';Secure' : '';
261
+ document.cookie = `${name}=${value};${expires};path=/;SameSite=Strict${secure}`;
262
+ },
263
+ getCookie(name) {
264
+ const nameEQ = `${name}=`;
265
+ const ca = document.cookie.split(';');
266
+ for (let i = 0; i < ca.length; i++) {
267
+ let c = ca[i];
268
+ while (c.charAt(0) === ' ') c = c.substring(1, c.length);
269
+ if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
270
+ }
271
+ return null;
272
+ },
273
+ deleteCookie(name) {
274
+ document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/;`;
275
+ }
276
+ };
277
+
99
278
  const postservice = async (datauser, configLogs) => {
100
279
  const postURL = Url + datauser.postServerURL;
101
280
  const {
@@ -104,19 +283,25 @@ const postservice = async (datauser, configLogs) => {
104
283
  cxauthxc,
105
284
  ...requestData
106
285
  } = datauser;
286
+ const xSessionType = authStorage.getSessionTypeValue(configLogs);
107
287
  let config;
108
288
  if (authorization === true || authorization === 'true') {
109
289
  config = {
290
+ withCredentials: true,
110
291
  headers: {
111
292
  Authorization: `Bearer ${cxauthxc}`,
293
+ 'X-CSRF-Token': authStorage.getCsrfToken() || '',
112
294
  'X-Session-ID': datauser.sessionID || '',
113
295
  'X-Fingerprint': datauser.fingerprint || '',
296
+ 'X-Session-Type': xSessionType,
114
297
  'Content-Type': 'application/json'
115
298
  }
116
299
  };
117
300
  } else {
118
301
  config = {
302
+ withCredentials: true,
119
303
  headers: {
304
+ 'X-CSRF-Token': authStorage.getCsrfToken() || '',
120
305
  'Content-Type': 'application/json'
121
306
  }
122
307
  };
@@ -144,22 +329,27 @@ const deleteservice = async (datauser, configLogs) => {
144
329
  cxauthxc,
145
330
  ...requestData
146
331
  } = datauser;
332
+ const xSessionType = authStorage.getSessionTypeValue(configLogs);
147
333
  let headers;
148
334
  if (authorization === true || authorization === 'true') {
149
335
  headers = {
150
336
  Authorization: `Bearer ${cxauthxc}`,
337
+ 'X-CSRF-Token': authStorage.getCsrfToken() || '',
151
338
  'X-Session-ID': datauser.sessionID || '',
152
339
  'X-Fingerprint': datauser.fingerprint || '',
340
+ 'X-Session-Type': xSessionType,
153
341
  'Content-Type': 'application/json'
154
342
  };
155
343
  } else {
156
344
  headers = {
345
+ 'X-CSRF-Token': authStorage.getCsrfToken() || '',
157
346
  'Content-Type': 'application/json'
158
347
  };
159
348
  }
160
349
  logConsole(configLogs, 'debug', '[Payload] ', requestData);
161
350
  try {
162
351
  const response = await axios.delete(deleteURL, {
352
+ withCredentials: true,
163
353
  headers,
164
354
  data: requestData
165
355
  });
@@ -174,6 +364,7 @@ const deleteservice = async (datauser, configLogs) => {
174
364
 
175
365
  const getservice = async (datauser, configLogs) => {
176
366
  const getURL = Url + datauser.postServerURL;
367
+ const xSessionType = authStorage.getSessionTypeValue(configLogs);
177
368
  const {
178
369
  authorization,
179
370
  postServerURL,
@@ -185,15 +376,18 @@ const getservice = async (datauser, configLogs) => {
185
376
  let config;
186
377
  if (authorization === true || authorization === 'true') {
187
378
  config = {
379
+ withCredentials: true,
188
380
  headers: {
189
381
  Authorization: `Bearer ${cxauthxc}`,
190
382
  'X-Session-ID': sessionID || '',
191
- 'X-Fingerprint': fingerprint || ''
383
+ 'X-Fingerprint': fingerprint || '',
384
+ 'X-Session-Type': xSessionType
192
385
  },
193
386
  params: requestData
194
387
  };
195
388
  } else {
196
389
  config = {
390
+ withCredentials: true,
197
391
  params: requestData
198
392
  };
199
393
  }
@@ -235,105 +429,6 @@ const getOrSetFingerprint = async (fpHash = '', configLogs) => {
235
429
  return await loadFingerprint(configLogs);
236
430
  };
237
431
 
238
- const config$4 = {
239
- headers: {
240
- 'Content-Type': 'application/json'
241
- }
242
- };
243
- let USE_COOKIES_FOR_AUTH = false;
244
- let SESSION_EXPIRATION = 365;
245
- let isConfigInitialized = false;
246
- let initPromise = null;
247
- const initializeAuthConfig = configLogs => {
248
- if (initPromise) return initPromise;
249
- initPromise = (async () => {
250
- if (isConfigInitialized) return;
251
- const baseURL = Url + 'getsettings';
252
- try {
253
- await serviceData(baseURL, {
254
- keys: 'type_session'
255
- }, config$4, value => {
256
- USE_COOKIES_FOR_AUTH = value === 'cookie';
257
- isConfigInitialized = true;
258
- }, configLogs);
259
- } catch (error) {
260
- logConsole(configLogs, 'error', 'Error loading authentication configuration:', error);
261
- } finally {
262
- isConfigInitialized = true;
263
- }
264
- })();
265
- return initPromise;
266
- };
267
- const authStorage = {
268
- initialize(configLogs) {
269
- return initializeAuthConfig(configLogs);
270
- },
271
- setAuthToken(token, configLogs) {
272
- initializeAuthConfig(configLogs);
273
- if (USE_COOKIES_FOR_AUTH) {
274
- this.setCookie('cxauthxc', token, SESSION_EXPIRATION);
275
- } else {
276
- localStorage.setItem('cxauthxc', token);
277
- }
278
- },
279
- getAuthToken(configLogs) {
280
- initializeAuthConfig(configLogs);
281
- if (USE_COOKIES_FOR_AUTH) {
282
- return this.getCookie('cxauthxc');
283
- }
284
- const local = localStorage.getItem('cxauthxc');
285
- if (local !== null) return local;
286
- return this.getCookie('cxauthxc');
287
- },
288
- setSessionId(sessionId, configLogs) {
289
- initializeAuthConfig(configLogs);
290
- if (USE_COOKIES_FOR_AUTH) {
291
- this.setCookie('cx_session', sessionId, SESSION_EXPIRATION);
292
- } else {
293
- localStorage.setItem('cx_session', sessionId);
294
- }
295
- },
296
- getSessionId(configLogs) {
297
- initializeAuthConfig(configLogs);
298
- if (USE_COOKIES_FOR_AUTH) {
299
- return this.getCookie('cx_session');
300
- }
301
- const local = localStorage.getItem('cx_session');
302
- if (local !== null) return local;
303
- return this.getCookie('cx_session');
304
- },
305
- removeAuthData(configLogs) {
306
- initializeAuthConfig(configLogs);
307
- if (USE_COOKIES_FOR_AUTH) {
308
- this.deleteCookie('cxauthxc');
309
- this.deleteCookie('cx_session');
310
- } else {
311
- localStorage.removeItem('cxauthxc');
312
- localStorage.removeItem('cx_session');
313
- }
314
- },
315
- setCookie(name, value, days) {
316
- const date = new Date();
317
- date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
318
- const expires = `expires=${date.toUTCString()}`;
319
- const secure = window.location.protocol === 'https:' ? ';Secure' : '';
320
- document.cookie = `${name}=${value};${expires};path=/;SameSite=Strict${secure}`;
321
- },
322
- getCookie(name) {
323
- const nameEQ = `${name}=`;
324
- const ca = document.cookie.split(';');
325
- for (let i = 0; i < ca.length; i++) {
326
- let c = ca[i];
327
- while (c.charAt(0) === ' ') c = c.substring(1, c.length);
328
- if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
329
- }
330
- return null;
331
- },
332
- deleteCookie(name) {
333
- document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/;`;
334
- }
335
- };
336
-
337
432
  const handleError$3 = (err, t, configLogs, setIsError, setMessageerror) => {
338
433
  let errorMessage;
339
434
  if (!err?.response) {
@@ -361,7 +456,15 @@ const servicePostBasic = async (fpHash = '', sessionID, baseURL, config, setData
361
456
  };
362
457
  logConsole(configLogs, 'debug', '[Payload] ', key);
363
458
  try {
364
- const response = await axios.post(baseURL, key, config);
459
+ const response = await axios.post(baseURL, key, {
460
+ ...config,
461
+ withCredentials: true,
462
+ headers: {
463
+ ...(config?.headers || {}),
464
+ 'X-Session-Type': authStorage.getSessionTypeValue(configLogs),
465
+ 'X-CSRF-Token': authStorage.getCsrfToken() || ''
466
+ }
467
+ });
365
468
  logConsole(configLogs, 'info', '[Request] ', baseURL);
366
469
  logConsole(configLogs, 'data', '', response.data);
367
470
  setData(response.data);
@@ -373,7 +476,15 @@ const servicePostBasic = async (fpHash = '', sessionID, baseURL, config, setData
373
476
  const serviceData = async (baseURL, key, config, setData, configLogs) => {
374
477
  logConsole(configLogs, 'debug', '[Payload] ', key);
375
478
  try {
376
- const response = await axios.post(baseURL, key, config);
479
+ const response = await axios.post(baseURL, key, {
480
+ ...config,
481
+ withCredentials: true,
482
+ headers: {
483
+ ...(config?.headers || {}),
484
+ 'X-Session-Type': authStorage.getSessionTypeValue(configLogs),
485
+ 'X-CSRF-Token': authStorage.getCsrfToken() || ''
486
+ }
487
+ });
377
488
  logConsole(configLogs, 'info', '[Request] ', baseURL);
378
489
  logConsole(configLogs, 'data', '', response.data);
379
490
  const responseData = response.data;
@@ -411,7 +522,14 @@ const servicePost = async (fpHash = '', sessionID, id, permission, postURL, conf
411
522
  } else {
412
523
  logConsole(configLogs, 'debug', '[Payload] ', key);
413
524
  try {
414
- const response = await axios.post(postURL1, key, config);
525
+ const response = await axios.post(postURL1, key, {
526
+ ...config,
527
+ withCredentials: true,
528
+ headers: {
529
+ ...(config?.headers || {}),
530
+ 'X-CSRF-Token': authStorage.getCsrfToken() || ''
531
+ }
532
+ });
415
533
  logConsole(configLogs, 'info', '[Request] ', postURL1);
416
534
  logConsole(configLogs, 'data', '', response.data);
417
535
  setPost(response.data);
@@ -494,8 +612,16 @@ const servicePostData = async (fpHash = '', sessionID, id, permission, postURL,
494
612
  } else {
495
613
  logConsole(configLogs, 'debug', '[Payload] ', key);
496
614
  try {
497
- const response = await axios.post(postURL1, key, config);
615
+ const response = await axios.post(postURL1, key, {
616
+ ...config,
617
+ withCredentials: true,
618
+ headers: {
619
+ ...(config?.headers || {}),
620
+ 'X-CSRF-Token': authStorage.getCsrfToken() || ''
621
+ }
622
+ });
498
623
  logConsole(configLogs, 'info', '[Request] ', postURL1);
624
+ logConsole(configLogs, 'data', '', response.data);
499
625
  setPost(response.data);
500
626
  } catch (error) {
501
627
  logConsole(configLogs, 'error', '', error);
@@ -529,11 +655,6 @@ const serviceLogout = async (fpHash = '', sessionID, cxauthxc, t, setAuthTokens,
529
655
  const userLanguage = localStorage.getItem('userLanguage');
530
656
  const displaymode = localStorage.getItem('displaymode');
531
657
  const displayzoom = localStorage.getItem('displayzoom');
532
- localStorage.clear();
533
- authStorage.removeAuthData(configLogs);
534
- if (userLanguage) localStorage.setItem('userLanguage', userLanguage);
535
- if (displaymode) localStorage.setItem('displaymode', displaymode);
536
- if (displayzoom) localStorage.setItem('displayzoom', displayzoom);
537
658
  await postService.postservice({
538
659
  sessionID,
539
660
  cxauthxc,
@@ -541,6 +662,10 @@ const serviceLogout = async (fpHash = '', sessionID, cxauthxc, t, setAuthTokens,
541
662
  authorization: true,
542
663
  fingerprint: visitorIdHash
543
664
  }, configLogs);
665
+ authStorage.removeAuthData(configLogs);
666
+ if (userLanguage) localStorage.setItem('userLanguage', userLanguage);
667
+ if (displaymode) localStorage.setItem('displaymode', displaymode);
668
+ if (displayzoom) localStorage.setItem('displayzoom', displayzoom);
544
669
  setAuthTokens('');
545
670
  } catch (err) {
546
671
  const errorMessage = handleError$3(err, t, configLogs);
@@ -570,119 +695,2785 @@ const serviceFavorites = async (type = '', fpHash = '', sessionID, postURL, cxau
570
695
  } else {
571
696
  unreadcount = await postService.postservice(requestData, configLogs);
572
697
  }
573
- if (unreadcount && typeof unreadcount === 'object' && 'data' in unreadcount && !Array.isArray(unreadcount)) {
574
- setPost(unreadcount.data);
698
+ if (unreadcount && typeof unreadcount === 'object' && 'data' in unreadcount && !Array.isArray(unreadcount)) {
699
+ setPost(unreadcount.data);
700
+ } else {
701
+ setPost(unreadcount);
702
+ }
703
+ logConsole(configLogs, 'data', '', unreadcount);
704
+ const favoriteValue = unreadcount && typeof unreadcount === 'object' && 'data' in unreadcount && !Array.isArray(unreadcount) ? unreadcount.data : unreadcount;
705
+ if (type === 'get' && favoriteValue === true) {
706
+ setColor('#ffcd38');
707
+ } else if (type === 'set') {
708
+ setColor('#ffcd38');
709
+ localStorage.setItem('favoritessynch01', 'true');
710
+ localStorage.setItem('favoritessynch02', 'true');
711
+ window.dispatchEvent(new Event('favorites'));
712
+ } else if (type === 'del') {
713
+ setColor('');
714
+ localStorage.setItem('favoritessynch01', 'true');
715
+ localStorage.setItem('favoritessynch02', 'true');
716
+ window.dispatchEvent(new Event('favorites'));
717
+ }
718
+ setIsError(false);
719
+ } catch (err) {
720
+ setError(true);
721
+ handleError$3(err, t, configLogs, setIsError, setMessageerror);
722
+ } finally {
723
+ setLoading(false);
724
+ }
725
+ };
726
+ const serviceGetFavorites = async (fpHash = '', sessionID, postURL, cxauthxc, t, setPost, setIsError, setError, setMessageerror, setLoading, configLogs, ...rest) => {
727
+ const visitorIdHash = await getOrSetFingerprint(fpHash);
728
+ try {
729
+ const requestData = {
730
+ ...rest[0],
731
+ sessionID,
732
+ cxauthxc,
733
+ postServerURL: postURL,
734
+ authorization: true,
735
+ fingerprint: visitorIdHash
736
+ };
737
+ const unreadcount = await getservice(requestData, configLogs);
738
+ const responseData = unreadcount && typeof unreadcount === 'object' && 'data' in unreadcount && !Array.isArray(unreadcount) ? unreadcount.data : unreadcount;
739
+ setPost(responseData);
740
+ localStorage.setItem('favoritessynch03', JSON.stringify(responseData));
741
+ logConsole(configLogs, 'data', '', unreadcount);
742
+ setIsError(false);
743
+ } catch (err) {
744
+ setError(true);
745
+ handleError$3(err, t, configLogs, setIsError, setMessageerror);
746
+ } finally {
747
+ setLoading(false);
748
+ }
749
+ };
750
+
751
+ const defaultConfig$3 = {
752
+ enableLogs: false,
753
+ enableLogsError: false,
754
+ enableLogsWarning: false,
755
+ enableLogsInfo: false,
756
+ enableLogsData: false,
757
+ enableLogsDebug: false,
758
+ developmentMode: false
759
+ };
760
+ const ConexysConfigContext = /*#__PURE__*/createContext(defaultConfig$3);
761
+ const ConexysConfigProvider = ({
762
+ children,
763
+ config = {}
764
+ }) => {
765
+ const mergedConfig = {
766
+ ...defaultConfig$3,
767
+ ...config
768
+ };
769
+ return /*#__PURE__*/React__default.createElement(ConexysConfigContext.Provider, {
770
+ value: mergedConfig
771
+ }, children);
772
+ };
773
+ const useConexysConfig = () => {
774
+ return useContext(ConexysConfigContext);
775
+ };
776
+ const isDevelopmentMode = config => {
777
+ return config.developmentMode;
778
+ };
779
+
780
+ const config$3 = {
781
+ headers: {
782
+ 'Content-Type': 'application/json'
783
+ }
784
+ };
785
+ function AppDataSettings({
786
+ keys
787
+ }) {
788
+ const configLogs = useConexysConfig();
789
+ const postURL = Url + 'getsettings';
790
+ const [post, setPost] = useState(null);
791
+ const [error, setError] = useState(false);
792
+ const hasFetched = useRef(false);
793
+ const fetchData = useCallback(async () => {
794
+ if (hasFetched.current) return;
795
+ hasFetched.current = true;
796
+ try {
797
+ await serviceData(postURL, {
798
+ keys: keys
799
+ }, config$3, setPost, configLogs);
800
+ } catch (err) {
801
+ setError(true);
802
+ logConsole(configLogs, 'error', 'Error fetching data:', err);
803
+ console.error('Error fetching data:', err);
804
+ }
805
+ }, []);
806
+ useEffect(() => {
807
+ fetchData();
808
+ }, [fetchData]);
809
+ if (!post) return null;
810
+ return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, error ? /*#__PURE__*/React__default.createElement("div", null, "Error loading data") : post);
811
+ }
812
+
813
+ /*! @license DOMPurify 3.4.14 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.14/LICENSE */
814
+
815
+ function _arrayLikeToArray(r, a) {
816
+ (null == a || a > r.length) && (a = r.length);
817
+ for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
818
+ return n;
819
+ }
820
+ function _arrayWithHoles(r) {
821
+ if (Array.isArray(r)) return r;
822
+ }
823
+ function _iterableToArrayLimit(r, l) {
824
+ var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
825
+ if (null != t) {
826
+ var e,
827
+ n,
828
+ i,
829
+ u,
830
+ a = [],
831
+ f = true,
832
+ o = false;
833
+ try {
834
+ if (i = (t = t.call(r)).next, 0 === l) ; else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
835
+ } catch (r) {
836
+ o = true, n = r;
837
+ } finally {
838
+ try {
839
+ if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
840
+ } finally {
841
+ if (o) throw n;
842
+ }
843
+ }
844
+ return a;
845
+ }
846
+ }
847
+ function _nonIterableRest() {
848
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
849
+ }
850
+ function _slicedToArray(r, e) {
851
+ return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
852
+ }
853
+ function _unsupportedIterableToArray(r, a) {
854
+ if (r) {
855
+ if ("string" == typeof r) return _arrayLikeToArray(r, a);
856
+ var t = {}.toString.call(r).slice(8, -1);
857
+ return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
858
+ }
859
+ }
860
+
861
+ const entries = Object.entries,
862
+ setPrototypeOf = Object.setPrototypeOf,
863
+ isFrozen = Object.isFrozen,
864
+ getPrototypeOf = Object.getPrototypeOf,
865
+ getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
866
+ let freeze = Object.freeze,
867
+ seal = Object.seal,
868
+ create = Object.create; // eslint-disable-line import/no-mutable-exports
869
+ let _ref = typeof Reflect !== 'undefined' && Reflect,
870
+ apply = _ref.apply,
871
+ construct = _ref.construct;
872
+ if (!freeze) {
873
+ freeze = function freeze(x) {
874
+ return x;
875
+ };
876
+ }
877
+ if (!seal) {
878
+ seal = function seal(x) {
879
+ return x;
880
+ };
881
+ }
882
+ if (!apply) {
883
+ apply = function apply(func, thisArg) {
884
+ for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
885
+ args[_key - 2] = arguments[_key];
886
+ }
887
+ return func.apply(thisArg, args);
888
+ };
889
+ }
890
+ if (!construct) {
891
+ construct = function construct(Func) {
892
+ for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
893
+ args[_key2 - 1] = arguments[_key2];
894
+ }
895
+ return new Func(...args);
896
+ };
897
+ }
898
+ const arrayForEach = unapply(Array.prototype.forEach);
899
+ const arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);
900
+ const arrayPop = unapply(Array.prototype.pop);
901
+ const arrayPush = unapply(Array.prototype.push);
902
+ const arraySplice = unapply(Array.prototype.splice);
903
+ const arrayIsArray = Array.isArray;
904
+ const stringToLowerCase = unapply(String.prototype.toLowerCase);
905
+ const stringToString = unapply(String.prototype.toString);
906
+ const stringMatch = unapply(String.prototype.match);
907
+ const stringReplace = unapply(String.prototype.replace);
908
+ const stringIndexOf = unapply(String.prototype.indexOf);
909
+ const stringTrim = unapply(String.prototype.trim);
910
+ const numberToString = unapply(Number.prototype.toString);
911
+ const booleanToString = unapply(Boolean.prototype.toString);
912
+ const bigintToString = typeof BigInt === 'undefined' ? null : unapply(BigInt.prototype.toString);
913
+ const symbolToString = typeof Symbol === 'undefined' ? null : unapply(Symbol.prototype.toString);
914
+ const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
915
+ const objectToString = unapply(Object.prototype.toString);
916
+ const regExpTest = unapply(RegExp.prototype.test);
917
+ const typeErrorCreate = unconstruct(TypeError);
918
+ /**
919
+ * Creates a new function that calls the given function with a specified thisArg and arguments.
920
+ *
921
+ * @param func - The function to be wrapped and called.
922
+ * @returns A new function that calls the given function with a specified thisArg and arguments.
923
+ */
924
+ function unapply(func) {
925
+ return function (thisArg) {
926
+ if (thisArg instanceof RegExp) {
927
+ thisArg.lastIndex = 0;
928
+ }
929
+ for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
930
+ args[_key3 - 1] = arguments[_key3];
931
+ }
932
+ return apply(func, thisArg, args);
933
+ };
934
+ }
935
+ /**
936
+ * Creates a new function that constructs an instance of the given constructor function with the provided arguments.
937
+ *
938
+ * @param func - The constructor function to be wrapped and called.
939
+ * @returns A new function that constructs an instance of the given constructor function with the provided arguments.
940
+ */
941
+ function unconstruct(Func) {
942
+ return function () {
943
+ for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
944
+ args[_key4] = arguments[_key4];
945
+ }
946
+ return construct(Func, args);
947
+ };
948
+ }
949
+ /**
950
+ * Add properties to a lookup table
951
+ *
952
+ * @param set - The set to which elements will be added.
953
+ * @param array - The array containing elements to be added to the set.
954
+ * @param transformCaseFunc - An optional function to transform the case of each element before adding to the set.
955
+ * @returns The modified set with added elements.
956
+ */
957
+ function addToSet(set, array) {
958
+ let transformCaseFunc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : stringToLowerCase;
959
+ if (setPrototypeOf) {
960
+ // Make 'in' and truthy checks like Boolean(set.constructor)
961
+ // independent of any properties defined on Object.prototype.
962
+ // Prevent prototype setters from intercepting set as a this value.
963
+ setPrototypeOf(set, null);
964
+ }
965
+ if (!arrayIsArray(array)) {
966
+ return set;
967
+ }
968
+ let l = array.length;
969
+ while (l--) {
970
+ let element = array[l];
971
+ if (typeof element === 'string') {
972
+ const lcElement = transformCaseFunc(element);
973
+ if (lcElement !== element) {
974
+ // Config presets (e.g. tags.js, attrs.js) are immutable.
975
+ if (!isFrozen(array)) {
976
+ array[l] = lcElement;
977
+ }
978
+ element = lcElement;
979
+ }
980
+ }
981
+ set[element] = true;
982
+ }
983
+ return set;
984
+ }
985
+ /**
986
+ * Clean up an array to harden against CSPP
987
+ *
988
+ * @param array - The array to be cleaned.
989
+ * @returns The cleaned version of the array
990
+ */
991
+ function cleanArray(array) {
992
+ for (let index = 0; index < array.length; index++) {
993
+ const isPropertyExist = objectHasOwnProperty(array, index);
994
+ if (!isPropertyExist) {
995
+ array[index] = null;
996
+ }
997
+ }
998
+ return array;
999
+ }
1000
+ /**
1001
+ * Shallow clone an object
1002
+ *
1003
+ * @param object - The object to be cloned.
1004
+ * @returns A new object that copies the original.
1005
+ */
1006
+ function clone(object) {
1007
+ const newObject = create(null);
1008
+ for (const _ref2 of entries(object)) {
1009
+ var _ref3 = _slicedToArray(_ref2, 2);
1010
+ const property = _ref3[0];
1011
+ const value = _ref3[1];
1012
+ const isPropertyExist = objectHasOwnProperty(object, property);
1013
+ if (isPropertyExist) {
1014
+ if (arrayIsArray(value)) {
1015
+ newObject[property] = cleanArray(value);
1016
+ } else if (value && typeof value === 'object' && value.constructor === Object) {
1017
+ newObject[property] = clone(value);
1018
+ } else {
1019
+ newObject[property] = value;
1020
+ }
1021
+ }
1022
+ }
1023
+ return newObject;
1024
+ }
1025
+ /**
1026
+ * Convert non-node values into strings without depending on direct property access.
1027
+ *
1028
+ * @param value - The value to stringify.
1029
+ * @returns A string representation of the provided value.
1030
+ */
1031
+ function stringifyValue(value) {
1032
+ switch (typeof value) {
1033
+ case 'string':
1034
+ {
1035
+ return value;
1036
+ }
1037
+ case 'number':
1038
+ {
1039
+ return numberToString(value);
1040
+ }
1041
+ case 'boolean':
1042
+ {
1043
+ return booleanToString(value);
1044
+ }
1045
+ case 'bigint':
1046
+ {
1047
+ return bigintToString ? bigintToString(value) : '0';
1048
+ }
1049
+ case 'symbol':
1050
+ {
1051
+ return symbolToString ? symbolToString(value) : 'Symbol()';
1052
+ }
1053
+ case 'undefined':
1054
+ {
1055
+ return objectToString(value);
1056
+ }
1057
+ case 'function':
1058
+ case 'object':
1059
+ {
1060
+ if (value === null) {
1061
+ return objectToString(value);
1062
+ }
1063
+ const valueAsRecord = value;
1064
+ const valueToString = lookupGetter(valueAsRecord, 'toString');
1065
+ if (typeof valueToString === 'function') {
1066
+ const stringified = valueToString(valueAsRecord);
1067
+ return typeof stringified === 'string' ? stringified : objectToString(stringified);
1068
+ }
1069
+ return objectToString(value);
1070
+ }
1071
+ default:
1072
+ {
1073
+ return objectToString(value);
1074
+ }
1075
+ }
1076
+ }
1077
+ /**
1078
+ * This method automatically checks if the prop is function or getter and behaves accordingly.
1079
+ *
1080
+ * @param object - The object to look up the getter function in its prototype chain.
1081
+ * @param prop - The property name for which to find the getter function.
1082
+ * @returns The getter function found in the prototype chain or a fallback function.
1083
+ */
1084
+ function lookupGetter(object, prop) {
1085
+ while (object !== null) {
1086
+ const desc = getOwnPropertyDescriptor(object, prop);
1087
+ if (desc) {
1088
+ if (desc.get) {
1089
+ return unapply(desc.get);
1090
+ }
1091
+ if (typeof desc.value === 'function') {
1092
+ return unapply(desc.value);
1093
+ }
1094
+ }
1095
+ object = getPrototypeOf(object);
1096
+ }
1097
+ function fallbackValue() {
1098
+ return null;
1099
+ }
1100
+ return fallbackValue;
1101
+ }
1102
+ function isRegex(value) {
1103
+ try {
1104
+ regExpTest(value, '');
1105
+ return true;
1106
+ } catch (_unused) {
1107
+ return false;
1108
+ }
1109
+ }
1110
+
1111
+ const html$1 = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'search', 'section', 'select', 'shadow', 'slot', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']);
1112
+ const svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'enterkeyhint', 'exportparts', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'inputmode', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'part', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);
1113
+ const svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feDropShadow', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']);
1114
+ // List of SVG elements that are disallowed by default.
1115
+ // We still need to know them so that we can do namespace
1116
+ // checks properly in case one wants to add them to
1117
+ // allow-list.
1118
+ const svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignobject', 'hatch', 'hatchpath', 'mesh', 'meshgradient', 'meshpatch', 'meshrow', 'missing-glyph', 'script', 'set', 'solidcolor', 'unknown', 'use']);
1119
+ const mathMl$1 = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover', 'mprescripts']);
1120
+ // Similarly to SVG, we want to know all MathML elements,
1121
+ // even those that we disallow by default.
1122
+ const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
1123
+ const text = freeze(['#text']);
1124
+
1125
+ const html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'command', 'commandfor', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'exportparts', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inert', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'part', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'slot', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns']);
1126
+ const svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'amplitude', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dominant-baseline', 'dur', 'edgemode', 'elevation', 'end', 'exponent', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'intercept', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'mask-type', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'pointer-events', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'slope', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'tablevalues', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-orientation', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'vector-effect', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);
1127
+ const mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnalign', 'columnlines', 'columnspacing', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lquote', 'lspace', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);
1128
+ const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
1129
+
1130
+ const MUSTACHE_EXPR = seal(/{{[\w\W]*|^[\w\W]*}}/g);
1131
+ const ERB_EXPR = seal(/<%[\w\W]*|^[\w\W]*%>/g);
1132
+ const TMPLIT_EXPR = seal(/\${[\w\W]*/g);
1133
+ const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]+$/); // eslint-disable-line no-useless-escape
1134
+ const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
1135
+ const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
1136
+ );
1137
+ const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
1138
+ const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
1139
+ );
1140
+ const DOCTYPE_NAME = seal(/^html$/i);
1141
+ const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
1142
+ // Markup-significant character probes used by _sanitizeElements.
1143
+ // Shared module-level instances are safe despite the sticky /g flags:
1144
+ // unapply() resets lastIndex for RegExp receivers before every call.
1145
+ const ELEMENT_MARKUP_PROBE = seal(/<[/\w!]/g);
1146
+ const COMMENT_MARKUP_PROBE = seal(/<[/\w]/g);
1147
+ const FALLBACK_TAG_CLOSE = seal(/<\/no(script|embed|frames)/i);
1148
+ const SELF_CLOSING_TAG = seal(/\/>/i);
1149
+
1150
+ // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
1151
+ const NODE_TYPE = {
1152
+ element: 1,
1153
+ attribute: 2,
1154
+ text: 3,
1155
+ cdataSection: 4,
1156
+ entityReference: 5,
1157
+ // Deprecated
1158
+ entityNode: 6,
1159
+ // Deprecated
1160
+ processingInstruction: 7,
1161
+ comment: 8,
1162
+ document: 9,
1163
+ documentType: 10,
1164
+ documentFragment: 11,
1165
+ notation: 12 // Deprecated
1166
+ };
1167
+ /* HTML-namespace elements whose child text nodes are serialized *literally*
1168
+ (unescaped) by the HTML fragment-serialization algorithm. Two reparse-mXSS
1169
+ shapes ride on that literal serialization:
1170
+ (a) an element child - a tree the HTML parser can never build, but the DOM
1171
+ API and an XML/XHTML parse can - after which a `</tag>`-bearing text
1172
+ sibling breaks the element open on reparse; and
1173
+ (b) text-only content that already carries the element's OWN end tag, e.g.
1174
+ `<style>...</style><img onerror=x>` built as a node, which the literal
1175
+ serializer emits verbatim for the HTML parser to re-open.
1176
+ Shape (a) is handled by the firstElementChild branch in _isUnsafeNode; shape
1177
+ (b) by the LITERAL_TEXT_CLOSE probe. Both read textContent (the raw-serialized
1178
+ form for these elements) rather than innerHTML, because an XML/XHTML working
1179
+ document serializes innerHTML with `<` escaped, which silently blinds the
1180
+ innerHTML-based probes (rule 1's second probe and FALLBACK_TAG_CLOSE) there.
1181
+ `script` is never allow-listed, but is kept here so the guard matches the
1182
+ serializer's own literal-text list exactly. */
1183
+ const LITERAL_TEXT_ELEMENT_NAMES = ['style', 'script', 'xmp', 'iframe', 'noembed', 'noframes', 'plaintext', 'noscript'];
1184
+ const LITERAL_TEXT_ELEMENTS = freeze(addToSet({}, LITERAL_TEXT_ELEMENT_NAMES));
1185
+ /* Per-element end-tag matcher. On an HTML reparse the ONLY token that
1186
+ terminates a literal-text element's raw content is its own end tag; a foreign
1187
+ literal-text close (e.g. `</xmp>` sitting inside `<style>`) does not break
1188
+ out, so matching is per-element, not a shared alternation. The lookahead
1189
+ requires an HTML tag-name terminator (whitespace, `/` or `>`) so a longer
1190
+ name such as `</styles` is not mistaken for `</style`. */
1191
+ const LITERAL_TEXT_CLOSE = function () {
1192
+ const map = {};
1193
+ arrayForEach(LITERAL_TEXT_ELEMENT_NAMES, name => {
1194
+ map[name] = seal(new RegExp('</' + name + '(?=[\\t\\n\\f\\r />])', 'i'));
1195
+ });
1196
+ return freeze(map);
1197
+ }();
1198
+ const getGlobal = function getGlobal() {
1199
+ return typeof window === 'undefined' ? null : window;
1200
+ };
1201
+ /**
1202
+ * Creates a no-op policy for internal use only.
1203
+ * Don't export this function outside this module!
1204
+ * @param trustedTypes The policy factory.
1205
+ * @param purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
1206
+ * @return The policy created (or null, if Trusted Types
1207
+ * are not supported or creating the policy failed).
1208
+ */
1209
+ const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {
1210
+ if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
1211
+ return null;
1212
+ }
1213
+ // Allow the callers to control the unique policy name
1214
+ // by adding a data-tt-policy-suffix to the script element with the DOMPurify.
1215
+ // Policy creation with duplicate names throws in Trusted Types.
1216
+ let suffix = null;
1217
+ const ATTR_NAME = 'data-tt-policy-suffix';
1218
+ if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {
1219
+ suffix = purifyHostElement.getAttribute(ATTR_NAME);
1220
+ }
1221
+ const policyName = 'dompurify' + (suffix ? '#' + suffix : '');
1222
+ try {
1223
+ return trustedTypes.createPolicy(policyName, {
1224
+ createHTML(html) {
1225
+ return html;
1226
+ },
1227
+ createScriptURL(scriptUrl) {
1228
+ return scriptUrl;
1229
+ }
1230
+ });
1231
+ } catch (_) {
1232
+ // Policy creation failed (most likely another DOMPurify script has
1233
+ // already run). Skip creating the policy, as this will only cause errors
1234
+ // if TT are enforced.
1235
+ console.warn('TrustedTypes policy ' + policyName + ' could not be created.');
1236
+ return null;
1237
+ }
1238
+ };
1239
+ const _createHooksMap = function _createHooksMap() {
1240
+ return {
1241
+ afterSanitizeAttributes: [],
1242
+ afterSanitizeElements: [],
1243
+ afterSanitizeShadowDOM: [],
1244
+ beforeSanitizeAttributes: [],
1245
+ beforeSanitizeElements: [],
1246
+ beforeSanitizeShadowDOM: [],
1247
+ uponSanitizeAttribute: [],
1248
+ uponSanitizeElement: [],
1249
+ uponSanitizeShadowNode: []
1250
+ };
1251
+ };
1252
+ /**
1253
+ * Resolve a set-valued configuration option: a fresh set built from
1254
+ * cfg[key] when it is an own array property (seeded with a clone of
1255
+ * options.base when given, case-normalized via options.transform),
1256
+ * the fallback set otherwise.
1257
+ *
1258
+ * @param cfg the cloned, prototype-free configuration object
1259
+ * @param key the configuration property to read
1260
+ * @param fallback the set to use when the option is absent or not an array
1261
+ * @param options transform and optional base set to merge into
1262
+ * @returns the resolved set
1263
+ */
1264
+ const _resolveSetOption = function _resolveSetOption(cfg, key, fallback, options) {
1265
+ return objectHasOwnProperty(cfg, key) && arrayIsArray(cfg[key]) ? addToSet(options.base ? clone(options.base) : {}, cfg[key], options.transform) : fallback;
1266
+ };
1267
+ /**
1268
+ * Resolve an object-valued configuration option: a prototype-free clone
1269
+ * of cfg[key] when it is an own, truthy object property, else a fresh
1270
+ * fallback built by makeFallback (fresh on every parse, so a previous
1271
+ * parse can never leak state into the next one).
1272
+ *
1273
+ * @param cfg the cloned, prototype-free configuration object
1274
+ * @param key the configuration property to read
1275
+ * @param makeFallback builds the fallback value when the option is absent
1276
+ * @returns the resolved object
1277
+ */
1278
+ const _resolveObjectOption = function _resolveObjectOption(cfg, key, makeFallback) {
1279
+ const value = objectHasOwnProperty(cfg, key) ? cfg[key] : undefined;
1280
+ return value && typeof value === 'object' ? clone(value) : makeFallback();
1281
+ };
1282
+ function createDOMPurify() {
1283
+ let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
1284
+ const DOMPurify = root => createDOMPurify(root);
1285
+ DOMPurify.version = '3.4.14';
1286
+ DOMPurify.removed = [];
1287
+ if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
1288
+ // Not running in a browser, provide a factory function
1289
+ // so that you can pass your own Window
1290
+ DOMPurify.isSupported = false;
1291
+ return DOMPurify;
1292
+ }
1293
+ let document = window.document;
1294
+ const originalDocument = document;
1295
+ const currentScript = originalDocument.currentScript;
1296
+ window.DocumentFragment;
1297
+ const HTMLTemplateElement = window.HTMLTemplateElement,
1298
+ Node = window.Node,
1299
+ Element = window.Element,
1300
+ NodeFilter = window.NodeFilter,
1301
+ _window$NamedNodeMap = window.NamedNodeMap;
1302
+ _window$NamedNodeMap === void 0 ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap;
1303
+ window.HTMLFormElement;
1304
+ const DOMParser = window.DOMParser,
1305
+ trustedTypes = window.trustedTypes;
1306
+ const ElementPrototype = Element.prototype;
1307
+ const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
1308
+ const remove = lookupGetter(ElementPrototype, 'remove');
1309
+ const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
1310
+ const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
1311
+ const getParentNode = lookupGetter(ElementPrototype, 'parentNode');
1312
+ const getShadowRoot = lookupGetter(ElementPrototype, 'shadowRoot');
1313
+ const getAttributes = lookupGetter(ElementPrototype, 'attributes');
1314
+ const getNodeType = Node && Node.prototype ? lookupGetter(Node.prototype, 'nodeType') : null;
1315
+ const getNodeName = Node && Node.prototype ? lookupGetter(Node.prototype, 'nodeName') : null;
1316
+ const getOwnerDocument = Node && Node.prototype ? lookupGetter(Node.prototype, 'ownerDocument') : null;
1317
+ /* Clobber-safe nodeType / nodeName reads through the cached Node.prototype
1318
+ getters, with a direct-property fallback for environments that lack
1319
+ Node.prototype. Sites that need a different fallback (e.g. _isClobbered
1320
+ returns early on a null name) intentionally keep their own reads. */
1321
+ const _readNodeType = function _readNodeType(node) {
1322
+ return getNodeType ? getNodeType(node) : node.nodeType;
1323
+ };
1324
+ const _readNodeName = function _readNodeName(node) {
1325
+ return getNodeName ? getNodeName(node) : node.nodeName;
1326
+ };
1327
+ // As per issue #47, the web-components registry is inherited by a
1328
+ // new document created via createHTMLDocument. As per the spec
1329
+ // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
1330
+ // a new empty registry is used when creating a template contents owner
1331
+ // document, so we use that as our parent document to ensure nothing
1332
+ // is inherited.
1333
+ if (typeof HTMLTemplateElement === 'function') {
1334
+ const template = document.createElement('template');
1335
+ if (template.content && template.content.ownerDocument) {
1336
+ document = template.content.ownerDocument;
1337
+ }
1338
+ }
1339
+ let trustedTypesPolicy;
1340
+ let emptyHTML = '';
1341
+ // The instance's own internal Trusted Types policy. Unlike a caller-supplied
1342
+ // `TRUSTED_TYPES_POLICY`, this is created at most once — Trusted Types throws
1343
+ // on duplicate policy names — and is the only policy allowed to persist
1344
+ // across configurations and survive `clearConfig()`.
1345
+ let defaultTrustedTypesPolicy;
1346
+ let defaultTrustedTypesPolicyResolved = false;
1347
+ // Tracks whether we are already inside a call to the configured Trusted Types
1348
+ // policy (`createHTML` or `createScriptURL`). If a supplied policy callback
1349
+ // itself calls `DOMPurify.sanitize` (the cause of #1422), `sanitize` would
1350
+ // re-enter the policy and recurse until the stack overflows. We detect that
1351
+ // re-entry and throw a clear, actionable error instead. The guard is shared
1352
+ // across both callbacks, because either one re-entering `sanitize` triggers
1353
+ // the same unbounded recursion.
1354
+ let IN_TRUSTED_TYPES_POLICY = 0;
1355
+ const _assertNotInTrustedTypesPolicy = function _assertNotInTrustedTypesPolicy() {
1356
+ if (IN_TRUSTED_TYPES_POLICY > 0) {
1357
+ throw typeErrorCreate('A configured TRUSTED_TYPES_POLICY callback (createHTML or ' + 'createScriptURL) must not call DOMPurify.sanitize, as that causes ' + 'infinite recursion. Do not pass a policy whose callbacks wrap ' + 'DOMPurify as TRUSTED_TYPES_POLICY; see the "DOMPurify and Trusted ' + 'Types" section of the README.');
1358
+ }
1359
+ };
1360
+ const _createTrustedHTML = function _createTrustedHTML(html) {
1361
+ _assertNotInTrustedTypesPolicy();
1362
+ IN_TRUSTED_TYPES_POLICY++;
1363
+ try {
1364
+ return trustedTypesPolicy.createHTML(html);
1365
+ } finally {
1366
+ IN_TRUSTED_TYPES_POLICY--;
1367
+ }
1368
+ };
1369
+ const _createTrustedScriptURL = function _createTrustedScriptURL(scriptUrl) {
1370
+ _assertNotInTrustedTypesPolicy();
1371
+ IN_TRUSTED_TYPES_POLICY++;
1372
+ try {
1373
+ return trustedTypesPolicy.createScriptURL(scriptUrl);
1374
+ } finally {
1375
+ IN_TRUSTED_TYPES_POLICY--;
1376
+ }
1377
+ };
1378
+ // Lazily resolve (and cache) the instance's internal default policy.
1379
+ // Resolution is attempted at most once: a successful `createPolicy` cannot be
1380
+ // repeated (Trusted Types throws on duplicate names), and a failed or
1381
+ // unsupported attempt must not be retried on every parse.
1382
+ const _getDefaultTrustedTypesPolicy = function _getDefaultTrustedTypesPolicy() {
1383
+ if (!defaultTrustedTypesPolicyResolved) {
1384
+ defaultTrustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
1385
+ defaultTrustedTypesPolicyResolved = true;
1386
+ }
1387
+ return defaultTrustedTypesPolicy;
1388
+ };
1389
+ const _document = document,
1390
+ implementation = _document.implementation,
1391
+ createNodeIterator = _document.createNodeIterator,
1392
+ createDocumentFragment = _document.createDocumentFragment,
1393
+ getElementsByTagName = _document.getElementsByTagName;
1394
+ const importNode = originalDocument.importNode;
1395
+ let hooks = _createHooksMap();
1396
+ /**
1397
+ * Expose whether this browser supports running the full DOMPurify.
1398
+ */
1399
+ DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined;
1400
+ const MUSTACHE_EXPR$1 = MUSTACHE_EXPR,
1401
+ ERB_EXPR$1 = ERB_EXPR,
1402
+ TMPLIT_EXPR$1 = TMPLIT_EXPR,
1403
+ DATA_ATTR$1 = DATA_ATTR,
1404
+ ARIA_ATTR$1 = ARIA_ATTR,
1405
+ IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA,
1406
+ ATTR_WHITESPACE$1 = ATTR_WHITESPACE,
1407
+ CUSTOM_ELEMENT$1 = CUSTOM_ELEMENT;
1408
+ let IS_ALLOWED_URI$1 = IS_ALLOWED_URI;
1409
+ /**
1410
+ * We consider the elements and attributes below to be safe. Ideally
1411
+ * don't add any new ones but feel free to remove unwanted ones.
1412
+ */
1413
+ /* allowed element names */
1414
+ let ALLOWED_TAGS = null;
1415
+ const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text]);
1416
+ /* Allowed attribute names */
1417
+ let ALLOWED_ATTR = null;
1418
+ const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);
1419
+ /*
1420
+ * Configure how DOMPurify should handle custom elements and their attributes as well as customized built-in elements.
1421
+ * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
1422
+ * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
1423
+ * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
1424
+ */
1425
+ let CUSTOM_ELEMENT_HANDLING = Object.seal(create(null, {
1426
+ tagNameCheck: {
1427
+ writable: true,
1428
+ configurable: false,
1429
+ enumerable: true,
1430
+ value: null
1431
+ },
1432
+ attributeNameCheck: {
1433
+ writable: true,
1434
+ configurable: false,
1435
+ enumerable: true,
1436
+ value: null
1437
+ },
1438
+ allowCustomizedBuiltInElements: {
1439
+ writable: true,
1440
+ configurable: false,
1441
+ enumerable: true,
1442
+ value: false
1443
+ }
1444
+ }));
1445
+ /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
1446
+ let FORBID_TAGS = null;
1447
+ /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
1448
+ let FORBID_ATTR = null;
1449
+ /* Config object to store ADD_TAGS/ADD_ATTR functions (when used as functions) */
1450
+ const EXTRA_ELEMENT_HANDLING = Object.seal(create(null, {
1451
+ tagCheck: {
1452
+ writable: true,
1453
+ configurable: false,
1454
+ enumerable: true,
1455
+ value: null
1456
+ },
1457
+ attributeCheck: {
1458
+ writable: true,
1459
+ configurable: false,
1460
+ enumerable: true,
1461
+ value: null
1462
+ }
1463
+ }));
1464
+ /* Decide if ARIA attributes are okay */
1465
+ let ALLOW_ARIA_ATTR = true;
1466
+ /* Decide if custom data attributes are okay */
1467
+ let ALLOW_DATA_ATTR = true;
1468
+ /* Decide if unknown protocols are okay */
1469
+ let ALLOW_UNKNOWN_PROTOCOLS = false;
1470
+ /* Decide if self-closing tags in attributes are allowed.
1471
+ * Usually removed due to a mXSS issue in jQuery 3.0 */
1472
+ let ALLOW_SELF_CLOSE_IN_ATTR = true;
1473
+ /* Output should be safe for common template engines.
1474
+ * This means, DOMPurify removes data attributes, mustaches and ERB
1475
+ */
1476
+ let SAFE_FOR_TEMPLATES = false;
1477
+ /* Output should be safe even for XML used within HTML and alike.
1478
+ * This means, DOMPurify removes comments when containing risky content.
1479
+ */
1480
+ let SAFE_FOR_XML = true;
1481
+ /* Decide if document with <html>... should be returned */
1482
+ let WHOLE_DOCUMENT = false;
1483
+ /* Track whether config is already set on this instance of DOMPurify. */
1484
+ let SET_CONFIG = false;
1485
+ /* Pristine allowlist bindings captured at setConfig() time. On the
1486
+ * persistent-config path sanitize() restores the sets from these before
1487
+ * the per-walk hook clone-guard, so a hook's in-call widening cannot
1488
+ * carry across calls. Null until setConfig() is called; reset by
1489
+ * clearConfig(). */
1490
+ let SET_CONFIG_ALLOWED_TAGS = null;
1491
+ let SET_CONFIG_ALLOWED_ATTR = null;
1492
+ /* Decide if all elements (e.g. style, script) must be children of
1493
+ * document.body. By default, browsers might move them to document.head */
1494
+ let FORCE_BODY = false;
1495
+ /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
1496
+ * string (or a TrustedHTML object if Trusted Types are supported).
1497
+ * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
1498
+ */
1499
+ let RETURN_DOM = false;
1500
+ /* Decide if a DOM `DocumentFragment` should be returned, instead of a html
1501
+ * string (or a TrustedHTML object if Trusted Types are supported) */
1502
+ let RETURN_DOM_FRAGMENT = false;
1503
+ /* Try to return a Trusted Type object instead of a string, return a string in
1504
+ * case Trusted Types are not supported */
1505
+ let RETURN_TRUSTED_TYPE = false;
1506
+ /* Output should be free from DOM clobbering attacks?
1507
+ * This sanitizes markups named with colliding, clobberable built-in DOM APIs.
1508
+ */
1509
+ let SANITIZE_DOM = true;
1510
+ /* Achieve full DOM Clobbering protection by isolating the namespace of named
1511
+ * properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.
1512
+ *
1513
+ * HTML/DOM spec rules that enable DOM Clobbering:
1514
+ * - Named Access on Window (§7.3.3)
1515
+ * - DOM Tree Accessors (§3.1.5)
1516
+ * - Form Element Parent-Child Relations (§4.10.3)
1517
+ * - Iframe srcdoc / Nested WindowProxies (§4.8.5)
1518
+ * - HTMLCollection (§4.2.10.2)
1519
+ *
1520
+ * Namespace isolation is implemented by prefixing `id` and `name` attributes
1521
+ * with a constant string, i.e., `user-content-`
1522
+ */
1523
+ let SANITIZE_NAMED_PROPS = false;
1524
+ const SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';
1525
+ /* Keep element content when removing element? */
1526
+ let KEEP_CONTENT = true;
1527
+ /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
1528
+ * of importing it into a new Document and returning a sanitized copy */
1529
+ let IN_PLACE = false;
1530
+ /* Allow usage of profiles like html, svg and mathMl */
1531
+ let USE_PROFILES = {};
1532
+ /* Tags to ignore content of when KEEP_CONTENT is true */
1533
+ let FORBID_CONTENTS = null;
1534
+ const DEFAULT_FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'noscript', 'plaintext', 'script',
1535
+ // <selectedcontent> mirrors the selected <option>'s subtree, cloned by
1536
+ // the UA (customizable <select>) — including any on* handlers — and the
1537
+ // engine re-mirrors synchronously whenever a removal changes which
1538
+ // option/selectedcontent is current, even inside DOMPurify's inert
1539
+ // DOMParser document. Hoisting its children on removal re-inserts a fresh
1540
+ // mirror target ahead of the walk, which the engine refills, looping
1541
+ // forever (DoS) and amplifying output. Dropping its content on removal
1542
+ // (rather than hoisting) breaks that cascade; the content is a duplicate
1543
+ // of the option, which is sanitized on its own. See campaign-3 F1/F6.
1544
+ 'selectedcontent', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);
1545
+ /* Tags that are safe for data: URIs */
1546
+ let DATA_URI_TAGS = null;
1547
+ const DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
1548
+ /* Attributes safe for values like "javascript:" */
1549
+ let URI_SAFE_ATTRIBUTES = null;
1550
+ const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
1551
+ const MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
1552
+ const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
1553
+ const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
1554
+ /* Document namespace */
1555
+ let NAMESPACE = HTML_NAMESPACE;
1556
+ let IS_EMPTY_INPUT = false;
1557
+ /* Allowed XHTML+XML namespaces */
1558
+ let ALLOWED_NAMESPACES = null;
1559
+ const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
1560
+ const DEFAULT_MATHML_TEXT_INTEGRATION_POINTS = freeze(['mi', 'mo', 'mn', 'ms', 'mtext']);
1561
+ let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, DEFAULT_MATHML_TEXT_INTEGRATION_POINTS);
1562
+ const DEFAULT_HTML_INTEGRATION_POINTS = freeze(['annotation-xml']);
1563
+ let HTML_INTEGRATION_POINTS = addToSet({}, DEFAULT_HTML_INTEGRATION_POINTS);
1564
+ // Certain elements are allowed in both SVG and HTML
1565
+ // namespace. We need to specify them explicitly
1566
+ // so that they don't get erroneously deleted from
1567
+ // HTML namespace.
1568
+ const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
1569
+ /* Parsing of strict XHTML documents */
1570
+ let PARSER_MEDIA_TYPE = null;
1571
+ const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
1572
+ const DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
1573
+ let transformCaseFunc = null;
1574
+ /* Keep a reference to config to pass to hooks */
1575
+ let CONFIG = null;
1576
+ /* Ideally, do not touch anything below this line */
1577
+ /* ______________________________________________ */
1578
+ const formElement = document.createElement('form');
1579
+ const isRegexOrFunction = function isRegexOrFunction(testValue) {
1580
+ return testValue instanceof RegExp || testValue instanceof Function;
1581
+ };
1582
+ /**
1583
+ * _parseConfig
1584
+ *
1585
+ * @param cfg optional config literal
1586
+ */
1587
+ // eslint-disable-next-line complexity
1588
+ const _parseConfig = function _parseConfig() {
1589
+ let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1590
+ if (CONFIG && CONFIG === cfg) {
1591
+ return;
1592
+ }
1593
+ /* Shield configuration object from tampering */
1594
+ if (!cfg || typeof cfg !== 'object') {
1595
+ cfg = {};
1596
+ }
1597
+ /* Shield configuration object from prototype pollution */
1598
+ cfg = clone(cfg);
1599
+ PARSER_MEDIA_TYPE =
1600
+ // eslint-disable-next-line unicorn/prefer-includes
1601
+ SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE;
1602
+ // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
1603
+ transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
1604
+ /* Set configuration parameters */
1605
+ ALLOWED_TAGS = _resolveSetOption(cfg, 'ALLOWED_TAGS', DEFAULT_ALLOWED_TAGS, {
1606
+ transform: transformCaseFunc
1607
+ });
1608
+ ALLOWED_ATTR = _resolveSetOption(cfg, 'ALLOWED_ATTR', DEFAULT_ALLOWED_ATTR, {
1609
+ transform: transformCaseFunc
1610
+ });
1611
+ ALLOWED_NAMESPACES = _resolveSetOption(cfg, 'ALLOWED_NAMESPACES', DEFAULT_ALLOWED_NAMESPACES, {
1612
+ transform: stringToString
1613
+ });
1614
+ URI_SAFE_ATTRIBUTES = _resolveSetOption(cfg, 'ADD_URI_SAFE_ATTR', DEFAULT_URI_SAFE_ATTRIBUTES, {
1615
+ transform: transformCaseFunc,
1616
+ base: DEFAULT_URI_SAFE_ATTRIBUTES
1617
+ });
1618
+ DATA_URI_TAGS = _resolveSetOption(cfg, 'ADD_DATA_URI_TAGS', DEFAULT_DATA_URI_TAGS, {
1619
+ transform: transformCaseFunc,
1620
+ base: DEFAULT_DATA_URI_TAGS
1621
+ });
1622
+ FORBID_CONTENTS = _resolveSetOption(cfg, 'FORBID_CONTENTS', DEFAULT_FORBID_CONTENTS, {
1623
+ transform: transformCaseFunc
1624
+ });
1625
+ FORBID_TAGS = _resolveSetOption(cfg, 'FORBID_TAGS', clone({}), {
1626
+ transform: transformCaseFunc
1627
+ });
1628
+ FORBID_ATTR = _resolveSetOption(cfg, 'FORBID_ATTR', clone({}), {
1629
+ transform: transformCaseFunc
1630
+ });
1631
+ USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES && typeof cfg.USE_PROFILES === 'object' ? clone(cfg.USE_PROFILES) : cfg.USE_PROFILES : false;
1632
+ ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
1633
+ ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
1634
+ ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
1635
+ ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true
1636
+ SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
1637
+ SAFE_FOR_XML = cfg.SAFE_FOR_XML !== false; // Default true
1638
+ WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
1639
+ RETURN_DOM = cfg.RETURN_DOM || false; // Default false
1640
+ RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
1641
+ RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
1642
+ FORCE_BODY = cfg.FORCE_BODY || false; // Default false
1643
+ SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
1644
+ SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
1645
+ KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
1646
+ IN_PLACE = cfg.IN_PLACE || false; // Default false
1647
+ IS_ALLOWED_URI$1 = isRegex(cfg.ALLOWED_URI_REGEXP) ? cfg.ALLOWED_URI_REGEXP : IS_ALLOWED_URI; // Default regexp
1648
+ NAMESPACE = typeof cfg.NAMESPACE === 'string' ? cfg.NAMESPACE : HTML_NAMESPACE; // Default HTML namespace
1649
+ MATHML_TEXT_INTEGRATION_POINTS = _resolveObjectOption(cfg, 'MATHML_TEXT_INTEGRATION_POINTS', () => addToSet({}, DEFAULT_MATHML_TEXT_INTEGRATION_POINTS) // Default built-in map
1650
+ );
1651
+ HTML_INTEGRATION_POINTS = _resolveObjectOption(cfg, 'HTML_INTEGRATION_POINTS', () => addToSet({}, DEFAULT_HTML_INTEGRATION_POINTS) // Default built-in map
1652
+ );
1653
+ const customElementHandling = _resolveObjectOption(cfg, 'CUSTOM_ELEMENT_HANDLING', () => create(null));
1654
+ CUSTOM_ELEMENT_HANDLING = create(null);
1655
+ if (objectHasOwnProperty(customElementHandling, 'tagNameCheck') && isRegexOrFunction(customElementHandling.tagNameCheck)) {
1656
+ CUSTOM_ELEMENT_HANDLING.tagNameCheck = customElementHandling.tagNameCheck; // Default undefined
1657
+ }
1658
+ if (objectHasOwnProperty(customElementHandling, 'attributeNameCheck') && isRegexOrFunction(customElementHandling.attributeNameCheck)) {
1659
+ CUSTOM_ELEMENT_HANDLING.attributeNameCheck = customElementHandling.attributeNameCheck; // Default undefined
1660
+ }
1661
+ if (objectHasOwnProperty(customElementHandling, 'allowCustomizedBuiltInElements') && typeof customElementHandling.allowCustomizedBuiltInElements === 'boolean') {
1662
+ CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = customElementHandling.allowCustomizedBuiltInElements; // Default undefined
1663
+ }
1664
+ seal(CUSTOM_ELEMENT_HANDLING);
1665
+ if (SAFE_FOR_TEMPLATES) {
1666
+ ALLOW_DATA_ATTR = false;
1667
+ }
1668
+ if (RETURN_DOM_FRAGMENT) {
1669
+ RETURN_DOM = true;
1670
+ }
1671
+ /* Parse profile info */
1672
+ if (USE_PROFILES) {
1673
+ ALLOWED_TAGS = addToSet({}, text);
1674
+ ALLOWED_ATTR = create(null);
1675
+ if (USE_PROFILES.html === true) {
1676
+ addToSet(ALLOWED_TAGS, html$1);
1677
+ addToSet(ALLOWED_ATTR, html);
1678
+ }
1679
+ if (USE_PROFILES.svg === true) {
1680
+ addToSet(ALLOWED_TAGS, svg$1);
1681
+ addToSet(ALLOWED_ATTR, svg);
1682
+ addToSet(ALLOWED_ATTR, xml);
1683
+ }
1684
+ if (USE_PROFILES.svgFilters === true) {
1685
+ addToSet(ALLOWED_TAGS, svgFilters);
1686
+ addToSet(ALLOWED_ATTR, svg);
1687
+ addToSet(ALLOWED_ATTR, xml);
1688
+ }
1689
+ if (USE_PROFILES.mathMl === true) {
1690
+ addToSet(ALLOWED_TAGS, mathMl$1);
1691
+ addToSet(ALLOWED_ATTR, mathMl);
1692
+ addToSet(ALLOWED_ATTR, xml);
1693
+ }
1694
+ }
1695
+ /* Always reset function-based ADD_TAGS / ADD_ATTR checks to prevent
1696
+ * leaking across calls when switching from function to array config */
1697
+ EXTRA_ELEMENT_HANDLING.tagCheck = null;
1698
+ EXTRA_ELEMENT_HANDLING.attributeCheck = null;
1699
+ /* Merge configuration parameters */
1700
+ if (objectHasOwnProperty(cfg, 'ADD_TAGS')) {
1701
+ if (typeof cfg.ADD_TAGS === 'function') {
1702
+ EXTRA_ELEMENT_HANDLING.tagCheck = cfg.ADD_TAGS;
1703
+ } else if (arrayIsArray(cfg.ADD_TAGS)) {
1704
+ if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
1705
+ ALLOWED_TAGS = clone(ALLOWED_TAGS);
1706
+ }
1707
+ addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
1708
+ }
1709
+ }
1710
+ if (objectHasOwnProperty(cfg, 'ADD_ATTR')) {
1711
+ if (typeof cfg.ADD_ATTR === 'function') {
1712
+ EXTRA_ELEMENT_HANDLING.attributeCheck = cfg.ADD_ATTR;
1713
+ } else if (arrayIsArray(cfg.ADD_ATTR)) {
1714
+ if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
1715
+ ALLOWED_ATTR = clone(ALLOWED_ATTR);
1716
+ }
1717
+ addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
1718
+ }
1719
+ }
1720
+ if (objectHasOwnProperty(cfg, 'ADD_FORBID_CONTENTS') && arrayIsArray(cfg.ADD_FORBID_CONTENTS)) {
1721
+ if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
1722
+ FORBID_CONTENTS = clone(FORBID_CONTENTS);
1723
+ }
1724
+ addToSet(FORBID_CONTENTS, cfg.ADD_FORBID_CONTENTS, transformCaseFunc);
1725
+ }
1726
+ /* Add #text in case KEEP_CONTENT is set to true */
1727
+ if (KEEP_CONTENT) {
1728
+ ALLOWED_TAGS['#text'] = true;
1729
+ }
1730
+ /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
1731
+ if (WHOLE_DOCUMENT) {
1732
+ addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
1733
+ }
1734
+ /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
1735
+ if (ALLOWED_TAGS.table) {
1736
+ addToSet(ALLOWED_TAGS, ['tbody']);
1737
+ delete FORBID_TAGS.tbody;
1738
+ }
1739
+ // Re-derive the active Trusted Types policy from this configuration on
1740
+ // every parse. The active policy must never be sticky closure state that
1741
+ // outlives the config that set it: a caller-supplied policy left in place
1742
+ // after `clearConfig()` — or after a later call that supplied none, or
1743
+ // `TRUSTED_TYPES_POLICY: null` — could sign a subsequent "default"
1744
+ // `RETURN_TRUSTED_TYPE` result with a foreign, possibly unsafe policy.
1745
+ // See GHSA-vxr8-fq34-vvx9.
1746
+ if (cfg.TRUSTED_TYPES_POLICY) {
1747
+ if (typeof cfg.TRUSTED_TYPES_POLICY.createHTML !== 'function') {
1748
+ throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');
1749
+ }
1750
+ if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function') {
1751
+ throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
1752
+ }
1753
+ // A caller-supplied policy applies to this configuration only.
1754
+ const previousTrustedTypesPolicy = trustedTypesPolicy;
1755
+ trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;
1756
+ // Sign local variables required by `sanitize`. If the supplied policy's
1757
+ // `createHTML` is circular (i.e. it calls `DOMPurify.sanitize`), this
1758
+ // throws via the re-entrancy guard. Restore the previous policy first so
1759
+ // the instance is not left in a poisoned state. See #1422.
1760
+ try {
1761
+ emptyHTML = _createTrustedHTML('');
1762
+ } catch (error) {
1763
+ trustedTypesPolicy = previousTrustedTypesPolicy;
1764
+ throw error;
1765
+ }
1766
+ } else if (cfg.TRUSTED_TYPES_POLICY === null) {
1767
+ // Explicit opt-out for this call: perform no Trusted Types signing and
1768
+ // create nothing (so a strict `trusted-types` CSP that disallows a
1769
+ // `dompurify` policy can still call `sanitize` from inside its own
1770
+ // policy — see #1422). Resetting to `undefined` rather than a sticky
1771
+ // `null` also drops any previously retained caller policy, so it cannot
1772
+ // resurface on a later call, while still allowing the next config-less
1773
+ // call to restore the internal default policy. See GHSA-vxr8-fq34-vvx9.
1774
+ trustedTypesPolicy = undefined;
1775
+ emptyHTML = '';
1776
+ } else {
1777
+ // No policy supplied: keep the currently active policy if one is set — a
1778
+ // previously supplied policy is intentionally sticky across config-less
1779
+ // calls — otherwise fall back to the instance's own internal policy,
1780
+ // created at most once. (A policy supplied for a *single* call still
1781
+ // lingers by design; what must not linger is a policy whose configuration
1782
+ // has been torn down via `clearConfig()`, which restores the default.)
1783
+ if (trustedTypesPolicy === undefined) {
1784
+ trustedTypesPolicy = _getDefaultTrustedTypesPolicy();
1785
+ }
1786
+ // Sign internal variables only when a policy is active. A falsy policy
1787
+ // (Trusted Types unsupported, creation failed, or an explicit opt-out)
1788
+ // leaves `emptyHTML` as a plain string, so we never call `.createHTML` on
1789
+ // a non-policy and throw. See #1422.
1790
+ if (trustedTypesPolicy && typeof emptyHTML === 'string') {
1791
+ emptyHTML = _createTrustedHTML('');
1792
+ }
1793
+ }
1794
+ // Prevent further manipulation of configuration.
1795
+ // Not available in IE8, Safari 5, etc.
1796
+ if (freeze) {
1797
+ freeze(cfg);
1798
+ }
1799
+ CONFIG = cfg;
1800
+ };
1801
+ /* Keep track of all possible SVG and MathML tags
1802
+ * so that we can perform the namespace checks
1803
+ * correctly. */
1804
+ const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);
1805
+ const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);
1806
+ /**
1807
+ * Namespace rules for an element in the SVG namespace.
1808
+ *
1809
+ * @param tagName the element's lowercase tag name
1810
+ * @param parent the (possibly simulated) parent node
1811
+ * @param parentTagName the parent's lowercase tag name
1812
+ * @returns true if a spec-compliant parser could produce this element
1813
+ */
1814
+ const _checkSvgNamespace = function _checkSvgNamespace(tagName, parent, parentTagName) {
1815
+ // The only way to switch from HTML namespace to SVG
1816
+ // is via <svg>. If it happens via any other tag, then
1817
+ // it should be killed.
1818
+ if (parent.namespaceURI === HTML_NAMESPACE) {
1819
+ return tagName === 'svg';
1820
+ }
1821
+ // The only way to switch from MathML to SVG is via <svg>
1822
+ // if the parent is either <annotation-xml> or a MathML
1823
+ // text integration point.
1824
+ if (parent.namespaceURI === MATHML_NAMESPACE) {
1825
+ return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
1826
+ }
1827
+ // We only allow elements that are defined in SVG
1828
+ // spec. All others are disallowed in SVG namespace.
1829
+ return Boolean(ALL_SVG_TAGS[tagName]);
1830
+ };
1831
+ /**
1832
+ * Namespace rules for an element in the MathML namespace.
1833
+ *
1834
+ * @param tagName the element's lowercase tag name
1835
+ * @param parent the (possibly simulated) parent node
1836
+ * @param parentTagName the parent's lowercase tag name
1837
+ * @returns true if a spec-compliant parser could produce this element
1838
+ */
1839
+ const _checkMathMlNamespace = function _checkMathMlNamespace(tagName, parent, parentTagName) {
1840
+ // The only way to switch from HTML namespace to MathML
1841
+ // is via <math>. If it happens via any other tag, then
1842
+ // it should be killed.
1843
+ if (parent.namespaceURI === HTML_NAMESPACE) {
1844
+ return tagName === 'math';
1845
+ }
1846
+ // The only way to switch from SVG to MathML is via
1847
+ // <math> and HTML integration points
1848
+ if (parent.namespaceURI === SVG_NAMESPACE) {
1849
+ return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
1850
+ }
1851
+ // We only allow elements that are defined in MathML
1852
+ // spec. All others are disallowed in MathML namespace.
1853
+ return Boolean(ALL_MATHML_TAGS[tagName]);
1854
+ };
1855
+ /**
1856
+ * Namespace rules for an element in the HTML namespace.
1857
+ *
1858
+ * @param tagName the element's lowercase tag name
1859
+ * @param parent the (possibly simulated) parent node
1860
+ * @param parentTagName the parent's lowercase tag name
1861
+ * @returns true if a spec-compliant parser could produce this element
1862
+ */
1863
+ const _checkHtmlNamespace = function _checkHtmlNamespace(tagName, parent, parentTagName) {
1864
+ // The only way to switch from SVG to HTML is via
1865
+ // HTML integration points, and from MathML to HTML
1866
+ // is via MathML text integration points
1867
+ if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
1868
+ return false;
1869
+ }
1870
+ if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
1871
+ return false;
1872
+ }
1873
+ // We disallow tags that are specific for MathML
1874
+ // or SVG and should never appear in HTML namespace
1875
+ return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
1876
+ };
1877
+ /**
1878
+ * @param element a DOM element whose namespace is being checked
1879
+ * @returns Return false if the element has a
1880
+ * namespace that a spec-compliant parser would never
1881
+ * return. Return true otherwise.
1882
+ */
1883
+ const _checkValidNamespace = function _checkValidNamespace(element) {
1884
+ let parent = getParentNode(element);
1885
+ // In JSDOM, if we're inside shadow DOM, then parentNode
1886
+ // can be null. We just simulate parent in this case.
1887
+ if (!parent || !parent.tagName) {
1888
+ parent = {
1889
+ namespaceURI: NAMESPACE,
1890
+ tagName: 'template'
1891
+ };
1892
+ }
1893
+ const tagName = stringToLowerCase(element.tagName);
1894
+ const parentTagName = stringToLowerCase(parent.tagName);
1895
+ if (!ALLOWED_NAMESPACES[element.namespaceURI]) {
1896
+ return false;
1897
+ }
1898
+ if (element.namespaceURI === SVG_NAMESPACE) {
1899
+ return _checkSvgNamespace(tagName, parent, parentTagName);
1900
+ }
1901
+ if (element.namespaceURI === MATHML_NAMESPACE) {
1902
+ return _checkMathMlNamespace(tagName, parent, parentTagName);
1903
+ }
1904
+ if (element.namespaceURI === HTML_NAMESPACE) {
1905
+ return _checkHtmlNamespace(tagName, parent, parentTagName);
1906
+ }
1907
+ // For XHTML and XML documents that support custom namespaces
1908
+ if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {
1909
+ return true;
1910
+ }
1911
+ // The code should never reach this place (this means
1912
+ // that the element somehow got namespace that is not
1913
+ // HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).
1914
+ // Return false just in case.
1915
+ return false;
1916
+ };
1917
+ /**
1918
+ * _forceRemove
1919
+ *
1920
+ * @param node a DOM node
1921
+ */
1922
+ const _forceRemove = function _forceRemove(node) {
1923
+ arrayPush(DOMPurify.removed, {
1924
+ element: node
1925
+ });
1926
+ try {
1927
+ // eslint-disable-next-line unicorn/prefer-dom-node-remove
1928
+ getParentNode(node).removeChild(node);
1929
+ } catch (_) {
1930
+ /* The normal detach failed — this is reached for a parentless node
1931
+ (getParentNode() is null, so .removeChild throws). Element.prototype
1932
+ .remove() is itself a spec no-op on a parentless node, so a recorded
1933
+ "removal" would otherwise hand the caller back an intact,
1934
+ payload-bearing node (e.g. a detached IN_PLACE root the mXSS canary or
1935
+ the style-with-element-child rule decided to kill). Fail closed by
1936
+ throwing — exactly as a clobbered root does at the IN_PLACE entry —
1937
+ rather than trying to "neutralize" the node via its own methods.
1938
+ Neutralizing would mean calling getAttributeNames()/removeAttribute()
1939
+ on the node, both of which a <form> root can clobber via a named child
1940
+ (and _isClobbered does not even probe getAttributeNames), so the
1941
+ neutralize step could itself be silently defeated, leaving the payload
1942
+ intact. A throw touches only the cached, clobber-safe remove() and
1943
+ getParentNode(). Generalizes GHSA-r47g-fvhr-h676 (clobbered-form root)
1944
+ to every root-kill reason. REPORT-3.
1945
+ This lives inside the catch, so it never fires for a normally-removed
1946
+ in-tree node: those have a parent, removeChild() succeeds, and the
1947
+ catch is not entered. Only a kept (parentless) root reaches here. */
1948
+ remove(node);
1949
+ if (!getParentNode(node)) {
1950
+ throw typeErrorCreate('a node selected for removal could not be detached from its tree ' + 'and cannot be safely returned; refusing to sanitize in place');
1951
+ }
1952
+ }
1953
+ };
1954
+ /**
1955
+ * _stripAttributeNode
1956
+ *
1957
+ * Remove a single Attr node case/namespace-exactly on an attribute-teardown
1958
+ * path. Name-based removeAttribute() ASCII-lowercases its lookup key for an
1959
+ * HTML element in an HTML document and so silently misses a case-preserved
1960
+ * handler (e.g. `ONERROR` off an XML/XHTML import) - the same defect
1961
+ * _removeAttribute() was fixed for, which a name-based call would reintroduce
1962
+ * on these IN_PLACE teardown paths. Unlike _removeAttribute this does not
1963
+ * record into DOMPurify.removed: the neutralize passes intentionally do not
1964
+ * book-keep. A clobbered/detached node falls back to best-effort name-based
1965
+ * removal.
1966
+ *
1967
+ * @param element the element to strip the attribute from
1968
+ * @param attribute the Attr node to remove
1969
+ * @param name the attribute's name, for the fallback path
1970
+ */
1971
+ const _stripAttributeNode = function _stripAttributeNode(element, attribute, name) {
1972
+ try {
1973
+ element.removeAttributeNode(attribute);
1974
+ } catch (_) {
1975
+ try {
1976
+ element.removeAttribute(name);
1977
+ } catch (_) {}
1978
+ }
1979
+ };
1980
+ /**
1981
+ * _neutralizeRoot
1982
+ *
1983
+ * Fail-closed teardown of an in-place root after the sanitize walk aborts
1984
+ * (campaign-3 F2). An internal throw mid-walk — e.g. a page-registered
1985
+ * custom element's reaction detaches a node so `_forceRemove`'s deliberate
1986
+ * parentless guard throws, or any other re-entrant engine mutation — would
1987
+ * otherwise leave the caller's *live* tree half-sanitized, with everything
1988
+ * after the abort point still carrying its handlers. There is no safe way
1989
+ * to resume the walk (the tree mutated under us), so we strip the root bare:
1990
+ * remove every child and every attribute, then let the caller's catch see
1991
+ * the original error. Clobber-safe (cached `remove`/`childNodes`/`attributes`
1992
+ * getters; the root was already clobber-pre-flighted at the IN_PLACE entry).
1993
+ *
1994
+ * @param root the in-place root to empty
1995
+ */
1996
+ const _neutralizeRoot = function _neutralizeRoot(root) {
1997
+ /* Strip every disallowed attribute (on* handlers included) off the whole
1998
+ subtree BEFORE detaching anything. Detaching first would hand back
1999
+ handler-bearing originals (e.g. an already-loading `<img onerror>`)
2000
+ whose queued resource event still fires in page scope after we throw.
2001
+ Clobber-safe reads; a doomed clobbered node's own attributes are
2002
+ irrelevant while its non-clobbered descendants are reached and scrubbed. */
2003
+ _neutralizeSubtree(root);
2004
+ const childNodes = getChildNodes(root);
2005
+ if (childNodes) {
2006
+ const snapshot = [];
2007
+ arrayForEach(childNodes, child => {
2008
+ arrayPush(snapshot, child);
2009
+ });
2010
+ arrayForEach(snapshot, child => {
2011
+ try {
2012
+ remove(child);
2013
+ } catch (_) {
2014
+ /* Best-effort teardown; a still-attached child is handled below */
2015
+ }
2016
+ });
2017
+ }
2018
+ const attributes = getAttributes(root);
2019
+ if (attributes) {
2020
+ for (let i = attributes.length - 1; i >= 0; --i) {
2021
+ const attribute = attributes[i];
2022
+ const name = attribute && attribute.name;
2023
+ if (typeof name === 'string') {
2024
+ _stripAttributeNode(root, attribute, name);
2025
+ }
2026
+ }
2027
+ }
2028
+ };
2029
+ /**
2030
+ * _removeAttribute
2031
+ *
2032
+ * Name-based getAttributeNode()/removeAttribute() ASCII-lowercase their
2033
+ * lookup key for HTML elements in an HTML document, so they silently miss an
2034
+ * attribute whose stored qualified name still contains uppercase ASCII
2035
+ * letters. That happens when the node came from a case-preserving source
2036
+ * (an XML/XHTML document imported via importNode(), or createAttributeNS()),
2037
+ * where e.g. `ONERROR` survives the walk: the policy check lowercases to
2038
+ * `onerror` and rejects it, but `removeAttribute('ONERROR')` looks up
2039
+ * `onerror` and finds nothing. Remove the exact Attr node instead, which is
2040
+ * case- and namespace-exact, and fall back to name-based removal only when
2041
+ * the caller could not supply the node.
2042
+ *
2043
+ * @param name an Attribute name
2044
+ * @param element a DOM node
2045
+ * @param attr the exact Attr node to remove, when the caller has it
2046
+ */
2047
+ const _removeAttribute = function _removeAttribute(name, element, attr) {
2048
+ if (!attr) {
2049
+ try {
2050
+ attr = element.getAttributeNode(name);
2051
+ } catch (_) {
2052
+ attr = null;
2053
+ }
2054
+ }
2055
+ arrayPush(DOMPurify.removed, {
2056
+ attribute: attr || null,
2057
+ from: element
2058
+ });
2059
+ try {
2060
+ if (attr) {
2061
+ element.removeAttributeNode(attr);
2062
+ } else {
2063
+ element.removeAttribute(name);
2064
+ }
2065
+ } catch (_) {
2066
+ /* Clobbered or already-detached node - best-effort fall back to a
2067
+ name-based removal so the "is" handling below still runs. */
2068
+ try {
2069
+ element.removeAttribute(name);
2070
+ } catch (_) {}
2071
+ }
2072
+ // We void attribute values for unremovable "is" attributes
2073
+ if (name === 'is') {
2074
+ if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
2075
+ try {
2076
+ _forceRemove(element);
2077
+ } catch (_) {}
2078
+ } else {
2079
+ try {
2080
+ element.setAttribute(name, '');
2081
+ } catch (_) {}
2082
+ }
2083
+ }
2084
+ };
2085
+ /**
2086
+ * _stripDisallowedAttributes
2087
+ *
2088
+ * Removes every attribute the active configuration does not allow from a
2089
+ * single element, using the same allowlist as the main attribute pass (so
2090
+ * `on*` handlers go, but no `/^on/` blocklist is introduced). Used only to
2091
+ * neutralise nodes that are being discarded from an in-place tree.
2092
+ *
2093
+ * @param element the element to strip
2094
+ */
2095
+ const _stripDisallowedAttributes = function _stripDisallowedAttributes(element) {
2096
+ const attributes = getAttributes(element);
2097
+ if (!attributes) {
2098
+ return;
2099
+ }
2100
+ for (let i = attributes.length - 1; i >= 0; --i) {
2101
+ const attribute = attributes[i];
2102
+ const name = attribute && attribute.name;
2103
+ if (typeof name !== 'string' || ALLOWED_ATTR[transformCaseFunc(name)]) {
2104
+ continue;
2105
+ }
2106
+ _stripAttributeNode(element, attribute, name);
2107
+ }
2108
+ };
2109
+ /**
2110
+ * _neutralizeSubtree
2111
+ *
2112
+ * Completes the audit-5 F1 fix across every removal path. The KEEP_CONTENT
2113
+ * move-hoist neutralises only disallowed-tag removals; clobber, mXSS-canary,
2114
+ * namespace, comment, processing-instruction and KEEP_CONTENT:false removals
2115
+ * all drop their subtree wholesale via `_forceRemove`. On the IN_PLACE path
2116
+ * those dropped nodes are detached from the caller's LIVE tree but a
2117
+ * handler-bearing original among them (an `<img onerror>`/`<video>` that was
2118
+ * loading) keeps its queued resource event, which fires in page scope after
2119
+ * sanitize returns. This walks a removed subtree and strips every attribute
2120
+ * the active configuration does not allow — so `on*` handlers are cancelled
2121
+ * through the SAME allowlist that governs kept nodes, not a separate `/^on/`
2122
+ * blocklist. Run synchronously before sanitize returns, i.e. before any
2123
+ * queued event can fire. Hook-free by design: these nodes leave the output,
2124
+ * so firing attribute hooks for them would be surprising. Clobber-safe reads;
2125
+ * a doomed clobbered node may shadow `removeAttribute` (its own attributes are
2126
+ * irrelevant — it is discarded — while its non-clobbered descendants, e.g.
2127
+ * the `<img>`, are reached and scrubbed).
2128
+ *
2129
+ * @param root the root of a removed subtree to neutralise
2130
+ */
2131
+ const _neutralizeSubtree = function _neutralizeSubtree(root) {
2132
+ const stack = [root];
2133
+ while (stack.length > 0) {
2134
+ const node = stack.pop();
2135
+ const nodeType = _readNodeType(node);
2136
+ if (nodeType === NODE_TYPE.element) {
2137
+ _stripDisallowedAttributes(node);
2138
+ }
2139
+ const childNodes = getChildNodes(node);
2140
+ if (childNodes) {
2141
+ for (let i = childNodes.length - 1; i >= 0; --i) {
2142
+ stack.push(childNodes[i]);
2143
+ }
2144
+ }
2145
+ }
2146
+ };
2147
+ /**
2148
+ * _neutralizePatchLinkage
2149
+ *
2150
+ * IN_PLACE entry pre-pass (declarative-partial-updates / streaming
2151
+ * hardening, https://github.com/WICG/declarative-partial-updates).
2152
+ *
2153
+ * The main walk strips patch linkage (`for`/`patchsrc`) and removes range
2154
+ * markers (PIs / markup comments) node-by-node, in document order, AS it
2155
+ * reaches each node. On a live in-place root that leaves a window: from the
2156
+ * moment the root is connected until the walk arrives at a given node, that
2157
+ * node's linkage is live. A patch applied on connection/stream can fire as
2158
+ * a microtask during the walk and inject or teleport an unsanitized DOM
2159
+ * range into a region the iterator has already passed and will not revisit,
2160
+ * so the post-return "tree is sanitized" contract is violated. Sweep the
2161
+ * whole tree once up front and sever every linkage before the walk begins,
2162
+ * closing that window.
2163
+ *
2164
+ * This CANNOT undo a patch that already fired before sanitize ran — that is
2165
+ * the irreducible "do not IN_PLACE a live-connected attacker tree" caveat —
2166
+ * but it closes everything from sanitize-start onward. Gated on SAFE_FOR_XML
2167
+ * to group with the rest of the declarative-partial-updates handling and
2168
+ * stay overridable, consistent with the codebase.
2169
+ *
2170
+ * Clobber-safe traversal (cached childNodes getter); per-node try/catch so a
2171
+ * clobbered root cannot defeat the sweep of its non-clobbered descendants.
2172
+ *
2173
+ * NOTE (pending real-Chrome confirmation, see test/declarative-patch-probe
2174
+ * .html Q1): this mirrors the existing policy of keeping `for` on
2175
+ * <label>/<output>. If the shipping feature can drive a patch through a
2176
+ * surviving `for`-on-label/output + `id` pair, this pre-pass and the
2177
+ * attribute check at _isBasicCustomElement's caller must additionally drop
2178
+ * that pair on the IN_PLACE path. Left as-is until the taxonomy is verified.
2179
+ *
2180
+ * @param root the in-place root to sweep
2181
+ */
2182
+ /**
2183
+ * Central policy for declarative-partial-updates patch-linkage attributes,
2184
+ * shared by the _neutralizePatchLinkage pre-pass and _isValidAttribute so
2185
+ * the two sites cannot drift: `patchsrc` always links, `for` links
2186
+ * everywhere except on <label>/<output>, and the whole policy is gated on
2187
+ * SAFE_FOR_XML (see the rationale block in _isValidAttribute).
2188
+ *
2189
+ * @param lcName the transformCaseFunc'd attribute name
2190
+ * @param lcTag the transformCaseFunc'd tag name of the carrying element
2191
+ * @return true if the attribute is patch linkage and must be dropped
2192
+ */
2193
+ const _isPatchLinkageAttribute = function _isPatchLinkageAttribute(lcName, lcTag) {
2194
+ if (!SAFE_FOR_XML) {
2195
+ return false;
2196
+ }
2197
+ if (lcName === 'patchsrc') {
2198
+ return true;
2199
+ }
2200
+ return lcName === 'for' && lcTag !== 'label' && lcTag !== 'output';
2201
+ };
2202
+ const _neutralizePatchLinkage = function _neutralizePatchLinkage(root) {
2203
+ if (!SAFE_FOR_XML) {
2204
+ return;
2205
+ }
2206
+ const stack = [root];
2207
+ while (stack.length > 0) {
2208
+ const node = stack.pop();
2209
+ const nodeType = _readNodeType(node);
2210
+ /* Remove range markers (the target side of a patch linkage): every
2211
+ processing instruction, and any markup-bearing comment. */
2212
+ if (nodeType === NODE_TYPE.processingInstruction || nodeType === NODE_TYPE.comment && regExpTest(COMMENT_MARKUP_PROBE, node.data)) {
2213
+ try {
2214
+ remove(node);
2215
+ } catch (_) {
2216
+ /* Best-effort */
2217
+ }
2218
+ continue;
2219
+ }
2220
+ /* Strip patch-source attributes (the source side) off elements. */
2221
+ if (nodeType === NODE_TYPE.element) {
2222
+ const element = node;
2223
+ const lcTag = transformCaseFunc(_readNodeName(node));
2224
+ try {
2225
+ if (element.hasAttribute && element.hasAttribute('patchsrc')) {
2226
+ element.removeAttribute('patchsrc');
2227
+ }
2228
+ if (element.hasAttribute && element.hasAttribute('for') && _isPatchLinkageAttribute('for', lcTag)) {
2229
+ element.removeAttribute('for');
2230
+ }
2231
+ } catch (_) {
2232
+ /* Clobbered removeAttribute/hasAttribute on a doomed node — ignore */
2233
+ }
2234
+ }
2235
+ const childNodes = getChildNodes(node);
2236
+ if (childNodes) {
2237
+ for (let i = childNodes.length - 1; i >= 0; --i) {
2238
+ stack.push(childNodes[i]);
2239
+ }
2240
+ }
2241
+ }
2242
+ };
2243
+ /**
2244
+ * _initDocument
2245
+ *
2246
+ * @param dirty - a string of dirty markup
2247
+ * @return a DOM, filled with the dirty markup
2248
+ */
2249
+ const _initDocument = function _initDocument(dirty) {
2250
+ /* Create a HTML document */
2251
+ let doc = null;
2252
+ let leadingWhitespace = null;
2253
+ if (FORCE_BODY) {
2254
+ dirty = '<remove></remove>' + dirty;
2255
+ } else {
2256
+ /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */
2257
+ const matches = stringMatch(dirty, /^[\r\n\t ]+/);
2258
+ leadingWhitespace = matches && matches[0];
2259
+ }
2260
+ if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && NAMESPACE === HTML_NAMESPACE) {
2261
+ // Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)
2262
+ dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + '</body></html>';
2263
+ }
2264
+ const dirtyPayload = trustedTypesPolicy ? _createTrustedHTML(dirty) : dirty;
2265
+ /*
2266
+ * Use the DOMParser API by default, fallback later if needs be
2267
+ * DOMParser not work for svg when has multiple root element.
2268
+ */
2269
+ if (NAMESPACE === HTML_NAMESPACE) {
2270
+ try {
2271
+ doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
2272
+ } catch (_) {}
2273
+ }
2274
+ /* Use createHTMLDocument in case DOMParser is not available */
2275
+ if (!doc || !doc.documentElement) {
2276
+ doc = implementation.createDocument(NAMESPACE, 'template', null);
2277
+ try {
2278
+ doc.documentElement.innerHTML = IS_EMPTY_INPUT ? emptyHTML : dirtyPayload;
2279
+ } catch (_) {
2280
+ // Syntax error if dirtyPayload is invalid xml
2281
+ }
2282
+ }
2283
+ const body = doc.body || doc.documentElement;
2284
+ if (dirty && leadingWhitespace) {
2285
+ body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
2286
+ }
2287
+ /* Work on whole document or just its body */
2288
+ if (NAMESPACE === HTML_NAMESPACE) {
2289
+ return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
2290
+ }
2291
+ return WHOLE_DOCUMENT ? doc.documentElement : body;
2292
+ };
2293
+ /**
2294
+ * Creates a NodeIterator object that you can use to traverse filtered lists of nodes or elements in a document.
2295
+ *
2296
+ * @param root The root element or node to start traversing on.
2297
+ * @return The created NodeIterator
2298
+ */
2299
+ const _createNodeIterator = function _createNodeIterator(root) {
2300
+ /* Read ownerDocument through the cached Node.prototype getter, never the
2301
+ direct property. HTMLFormElement has [LegacyOverrideBuiltIns], so a
2302
+ clobbering child (<input name="ownerDocument"> or a form-associated
2303
+ external input) shadows the prototype getter and makes a direct read
2304
+ return that <input>. createNodeIterator.call(<input>, ...) then throws
2305
+ "Illegal invocation", and on the IN_PLACE path that throw lands before
2306
+ the walk's fail-closed barrier - leaving the caller's live tree, with
2307
+ any already-armed handler in it, un-neutralized. The cached getter
2308
+ returns the real Document regardless of the clobber. */
2309
+ const doc = getOwnerDocument ? getOwnerDocument(root) : root.ownerDocument;
2310
+ return createNodeIterator.call(doc || root, root,
2311
+ // eslint-disable-next-line no-bitwise
2312
+ NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null);
2313
+ };
2314
+ /**
2315
+ * Replace template expression syntax (mustache, ERB, template
2316
+ * literal) with a space; shared by all SAFE_FOR_TEMPLATES scrub
2317
+ * sites. Order matters: mustache, then ERB, then template literal.
2318
+ *
2319
+ * @param value the string to scrub
2320
+ * @returns the scrubbed string
2321
+ */
2322
+ const _stripTemplateExpressions = function _stripTemplateExpressions(value) {
2323
+ value = stringReplace(value, MUSTACHE_EXPR$1, ' ');
2324
+ value = stringReplace(value, ERB_EXPR$1, ' ');
2325
+ value = stringReplace(value, TMPLIT_EXPR$1, ' ');
2326
+ return value;
2327
+ };
2328
+ /**
2329
+ * Strip template-engine expressions ({{...}}, ${...}, <%...%>) from the
2330
+ * character data of an element subtree. Used as the final safety net for
2331
+ * SAFE_FOR_TEMPLATES on every DOM-returning code path so that expressions
2332
+ * which only form after text-node normalization (e.g. fragments split across
2333
+ * stripped elements) cannot survive into a template-evaluating framework.
2334
+ *
2335
+ * Walks text/comment/CDATA/processing-instruction nodes and mutates `.data`
2336
+ * in place rather than round-tripping through innerHTML. This preserves
2337
+ * descendant node references (important for IN_PLACE callers), avoids a
2338
+ * serialize/reparse cycle, and reads literal character data — which means
2339
+ * `<%...%>` in text content matches the ERB regex against its real bytes
2340
+ * instead of the HTML-entity-escaped form innerHTML would produce.
2341
+ *
2342
+ * Attribute values are not visited here; SAFE_FOR_TEMPLATES handling for
2343
+ * attributes is performed during the per-node `_sanitizeAttributes` pass.
2344
+ *
2345
+ * @param node The root element whose character data should be scrubbed.
2346
+ */
2347
+ const _scrubTemplateExpressions2 = function _scrubTemplateExpressions(node) {
2348
+ var _node$querySelectorAl;
2349
+ node.normalize();
2350
+ /* Clobber-safe ownerDocument read, same reasoning as _createNodeIterator:
2351
+ under SAFE_FOR_TEMPLATES this runs on the live IN_PLACE root, which may
2352
+ carry a form-named-getter override of ownerDocument. */
2353
+ const doc = getOwnerDocument ? getOwnerDocument(node) : node.ownerDocument;
2354
+ const walker = createNodeIterator.call(doc || node, node,
2355
+ // eslint-disable-next-line no-bitwise
2356
+ NodeFilter.SHOW_TEXT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_CDATA_SECTION | NodeFilter.SHOW_PROCESSING_INSTRUCTION, null);
2357
+ let currentNode = walker.nextNode();
2358
+ while (currentNode) {
2359
+ currentNode.data = _stripTemplateExpressions(currentNode.data);
2360
+ currentNode = walker.nextNode();
2361
+ }
2362
+ // NodeIterator does not descend into <template>.content per the DOM spec,
2363
+ // so we must explicitly recurse into each template's content fragment,
2364
+ // mirroring the approach used by _sanitizeShadowDOM.
2365
+ const templates = (_node$querySelectorAl = node.querySelectorAll) === null || _node$querySelectorAl === void 0 ? void 0 : _node$querySelectorAl.call(node, 'template');
2366
+ if (templates) {
2367
+ arrayForEach(templates, tmpl => {
2368
+ if (_isDocumentFragment(tmpl.content)) {
2369
+ _scrubTemplateExpressions2(tmpl.content);
2370
+ }
2371
+ });
2372
+ }
2373
+ };
2374
+ /**
2375
+ * _isClobbered
2376
+ *
2377
+ * Detect DOM-clobbering on HTMLFormElement nodes. Form is the only HTML
2378
+ * interface with [LegacyOverrideBuiltIns]; a descendant element with a
2379
+ * `name` attribute matching a prototype property shadows that property
2380
+ * on direct reads. We use this check at the IN_PLACE entry-point and
2381
+ * during attribute sanitization to refuse clobbered forms.
2382
+ *
2383
+ * @param element element to check for clobbering attacks
2384
+ * @return true if clobbered, false if safe
2385
+ */
2386
+ const _isClobbered = function _isClobbered(element) {
2387
+ // Realm-independent tag-name probe. If we can't determine the tag
2388
+ // name at all, we can't reason about clobbering — return false
2389
+ // (the caller's other defences still apply).
2390
+ const realTagName = getNodeName ? getNodeName(element) : null;
2391
+ if (typeof realTagName !== 'string') {
2392
+ return false;
2393
+ }
2394
+ if (transformCaseFunc(realTagName) !== 'form') {
2395
+ return false;
2396
+ }
2397
+ return typeof element.nodeName !== 'string' || typeof element.textContent !== 'string' || typeof element.removeChild !== 'function' ||
2398
+ // Realm-safe NamedNodeMap detection: equality against the cached
2399
+ // prototype getter. Clobbered .attributes (e.g. <input name="attributes">)
2400
+ // makes the direct read diverge from the cached read; a clean form
2401
+ // (same-realm OR foreign-realm) has both reads pointing at the same
2402
+ // canonical NamedNodeMap.
2403
+ element.attributes !== getAttributes(element) || typeof element.removeAttribute !== 'function' || typeof element.setAttribute !== 'function' || typeof element.namespaceURI !== 'string' || typeof element.insertBefore !== 'function' || typeof element.hasChildNodes !== 'function' ||
2404
+ // NodeType clobbering probe. Cached Node.prototype.nodeType getter
2405
+ // returns the integer 1 for any Element regardless of realm; direct
2406
+ // read on a clobbered form (e.g. <input name="nodeType">) returns
2407
+ // the named child element. Cheap addition — nodeType is read from
2408
+ // an internal slot, no serialization cost — and removes a residual
2409
+ // clobbering surface used by several mXSS / PI / comment branches
2410
+ // in _sanitizeElements that compare currentNode.nodeType directly.
2411
+ element.nodeType !== getNodeType(element) ||
2412
+ // HTMLFormElement has [LegacyOverrideBuiltIns]: a descendant named
2413
+ // "childNodes" shadows the prototype getter. Direct reads of
2414
+ // form.childNodes from a clobbered form return the named child
2415
+ // instead of the real NodeList, so any walk that reads it directly
2416
+ // skips the form's real children. Compare the direct read to the
2417
+ // cached Node.prototype getter — when the form's named-property
2418
+ // getter intercepts the read, the two values differ and we flag
2419
+ // the form. This catches every clobbering child type (input,
2420
+ // select, etc.) regardless of whether the named child happens to
2421
+ // carry a numeric .length, which a typeof-based probe would miss
2422
+ // (e.g. HTMLSelectElement.length is a defined unsigned-long).
2423
+ element.childNodes !== getChildNodes(element);
2424
+ };
2425
+ /**
2426
+ * Checks whether the given value is a DocumentFragment from any realm.
2427
+ *
2428
+ * The realm-independent replacement reads `nodeType` through the cached
2429
+ * Node.prototype getter and compares to the DOCUMENT_FRAGMENT_NODE
2430
+ * constant (11). nodeType is a numeric value resolved from the node's
2431
+ * internal slot, identical across realms for the same kind of node.
2432
+ *
2433
+ * @param value object to check
2434
+ * @return true if value is a DocumentFragment-shaped node from any realm
2435
+ */
2436
+ const _isDocumentFragment = function _isDocumentFragment(value) {
2437
+ if (!getNodeType || typeof value !== 'object' || value === null) {
2438
+ return false;
2439
+ }
2440
+ try {
2441
+ return getNodeType(value) === NODE_TYPE.documentFragment;
2442
+ } catch (_) {
2443
+ return false;
2444
+ }
2445
+ };
2446
+ /**
2447
+ * Checks whether the given object is a DOM node, including nodes that
2448
+ * originate from a different window/realm (e.g. an iframe's
2449
+ * contentDocument). The previous `value instanceof Node` check was
2450
+ * realm-bound: nodes from a different window failed it, causing
2451
+ * sanitize() to silently stringify them and reset IN_PLACE to false,
2452
+ * returning the original node unsanitized. See GHSA-4w3q-35jp-p934.
2453
+ *
2454
+ * @param value object to check whether it's a DOM node
2455
+ * @return true if value is a DOM node from any realm
2456
+ */
2457
+ const _isNode = function _isNode(value) {
2458
+ if (!getNodeType || typeof value !== 'object' || value === null) {
2459
+ return false;
2460
+ }
2461
+ try {
2462
+ return typeof getNodeType(value) === 'number';
2463
+ } catch (_) {
2464
+ return false;
2465
+ }
2466
+ };
2467
+ function _executeHooks(hooks, currentNode, data) {
2468
+ if (hooks.length === 0) {
2469
+ return;
2470
+ }
2471
+ arrayForEach(hooks, hook => {
2472
+ hook.call(DOMPurify, currentNode, data, CONFIG);
2473
+ });
2474
+ }
2475
+ /**
2476
+ * Structural-threat checks that condemn a node regardless of the
2477
+ * allowlists: mXSS via namespace confusion, risky CSS construction,
2478
+ * processing instructions, markup-bearing comments. Pure predicate;
2479
+ * the caller removes. Check order is load-bearing.
2480
+ *
2481
+ * @param currentNode the node to inspect
2482
+ * @param tagName the node's transformCaseFunc'd tag name
2483
+ * @return true if the node must be removed
2484
+ */
2485
+ const _isUnsafeNode = function _isUnsafeNode(currentNode, tagName) {
2486
+ /* Detect mXSS attempts abusing namespace confusion */
2487
+ if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(ELEMENT_MARKUP_PROBE, currentNode.textContent) && regExpTest(ELEMENT_MARKUP_PROBE, currentNode.innerHTML)) {
2488
+ return true;
2489
+ }
2490
+ /* Remove rawtext/literal-text elements whose literal serialization
2491
+ re-opens markup on an HTML reparse - shapes (a) and (b) documented at
2492
+ LITERAL_TEXT_ELEMENTS. Both are invisible to rule 1 above (which
2493
+ self-disables once there is an element child, and whose second probe
2494
+ reads the innerHTML an XML/XHTML document serializes escaped), which
2495
+ is why both probes here read textContent instead. Previously only
2496
+ `style`-with-element-child was covered; every element in
2497
+ LITERAL_TEXT_ELEMENTS shares this literal serialization and is
2498
+ equally affected. */
2499
+ if (SAFE_FOR_XML && currentNode.namespaceURI === HTML_NAMESPACE && LITERAL_TEXT_ELEMENTS[tagName] && (_isNode(currentNode.firstElementChild) || typeof currentNode.textContent === 'string' && regExpTest(LITERAL_TEXT_CLOSE[tagName], currentNode.textContent))) {
2500
+ return true;
2501
+ }
2502
+ /* Remove any occurrence of processing instructions */
2503
+ if (currentNode.nodeType === NODE_TYPE.processingInstruction) {
2504
+ return true;
2505
+ }
2506
+ /* Remove any kind of possibly harmful comments */
2507
+ if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(COMMENT_MARKUP_PROBE, currentNode.data)) {
2508
+ return true;
2509
+ }
2510
+ return false;
2511
+ };
2512
+ /**
2513
+ * Evaluate a CUSTOM_ELEMENT_HANDLING check (a RegExp or a predicate
2514
+ * function, per the validation in _parseConfig) against a name.
2515
+ * Additional arguments are forwarded to predicate functions - the
2516
+ * attributeNameCheck predicate receives the tag name as its second
2517
+ * argument. A null/absent check never matches.
2518
+ *
2519
+ * @param check the configured tagNameCheck / attributeNameCheck value
2520
+ * @param name the name to test
2521
+ * @param args extra arguments forwarded to a predicate function
2522
+ * @return true if the check matches the name
2523
+ */
2524
+ const _matchesNameCheck = function _matchesNameCheck(check, name) {
2525
+ if (check instanceof RegExp) {
2526
+ return regExpTest(check, name);
2527
+ }
2528
+ if (check instanceof Function) {
2529
+ for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
2530
+ args[_key - 2] = arguments[_key];
2531
+ }
2532
+ return Boolean(check(name, ...args));
2533
+ }
2534
+ return false;
2535
+ };
2536
+ /**
2537
+ * Handle a node whose tag is forbidden or not allowlisted: keep
2538
+ * allowed custom elements (false return exits _sanitizeElements
2539
+ * early - the namespace and fallback-tag removal checks are
2540
+ * intentionally skipped for kept custom elements), else hoist
2541
+ * content per KEEP_CONTENT and remove.
2542
+ *
2543
+ * A kept custom element is the ONLY case in which this function
2544
+ * returns false, so the caller uses that return value to run the
2545
+ * afterSanitizeElements hook on the kept element and keep the
2546
+ * element-hook lifecycle consistent with normal allowlisted
2547
+ * elements (GHSA-c2j3-45gr-mqc4).
2548
+ *
2549
+ * @param currentNode the disallowed node
2550
+ * @param tagName the node's transformCaseFunc'd tag name
2551
+ * @return true if the node was removed, false if kept
2552
+ */
2553
+ const _sanitizeDisallowedNode = function _sanitizeDisallowedNode(currentNode, tagName, root) {
2554
+ /* Check if we have a custom element to handle */
2555
+ if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName) && _matchesNameCheck(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
2556
+ return false;
2557
+ }
2558
+ /* Keep content except for bad-listed elements.
2559
+ Use the cached prototype getters exclusively — the previous code
2560
+ had `|| currentNode.parentNode` / `|| currentNode.childNodes`
2561
+ fallbacks, but the cached getters always return the canonical
2562
+ value (or null for a real parent-less node), so the fallback
2563
+ path was dead in safe cases and a clobbering surface in unsafe
2564
+ ones. Falsy cached results stay falsy; the `if (childNodes &&
2565
+ parentNode)` check already gates correctly. */
2566
+ if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
2567
+ const parentNode = getParentNode(currentNode);
2568
+ const childNodes = getChildNodes(currentNode);
2569
+ if (childNodes && parentNode) {
2570
+ const childCount = childNodes.length;
2571
+ /* Hoist by moving each child up one level rather than deep-cloning
2572
+ it. Moving transfers every descendant exactly once, so a chain of
2573
+ nested disallowed elements costs O(n) instead of the O(n^2) that
2574
+ re-cloning the shrinking subtree at each level produced; it also
2575
+ empties the removed original, so `DOMPurify.removed` no longer
2576
+ pins whole subtrees. Moving preserves the in-place guarantee too:
2577
+ an original carrying already-queued resource events (`<img
2578
+ onerror>`, `<video>`/`<audio>` error, lazy/`onload`, …) is
2579
+ relocated and sanitised rather than left detached but still armed.
2580
+ The sole case that must clone is removing the walk root itself.
2581
+ The result is serialised from the root's subtree, so a restrictive
2582
+ ALLOWED_TAGS that strips the root (`body` on the string path) must
2583
+ leave the content inside it, which only cloning does. In IN_PLACE
2584
+ the root is pre-validated as an allowed tag and so is never removed
2585
+ here, so that path always takes the move branch.
2586
+ `childNodes` is live; a tail-to-head walk keeps `childNodes[i]`
2587
+ valid whether we move (drops the trailing entry) or clone (leaves
2588
+ the list intact). */
2589
+ for (let i = childCount - 1; i >= 0; --i) {
2590
+ const hoisted = currentNode === root ? cloneNode(childNodes[i], true) : childNodes[i];
2591
+ parentNode.insertBefore(hoisted, getNextSibling(currentNode));
2592
+ }
2593
+ }
2594
+ }
2595
+ _forceRemove(currentNode);
2596
+ return true;
2597
+ };
2598
+ /**
2599
+ * Fork a hook-mutable allowlist off its shared binding the first time a
2600
+ * (possibly lazily-installed) uponSanitize* hook is about to see it, so the
2601
+ * hook cannot widen the per-instance default or the setConfig binding by
2602
+ * reference and leak past the call. Returns the set unchanged once it is
2603
+ * already call-local, so repeated calls across elements are idempotent.
2604
+ *
2605
+ * @param hookList the uponSanitize* hook array for this event
2606
+ * @param set the current ALLOWED_TAGS / ALLOWED_ATTR binding
2607
+ * @param defaultSet the per-instance DEFAULT_ALLOWED_* constant
2608
+ * @param setConfigSet the captured setConfig() binding, or null
2609
+ * @return a call-local clone if a hook is present and set is still shared,
2610
+ * else set unchanged
2611
+ */
2612
+ const _forkSharedAllowlist = function _forkSharedAllowlist(hookList, set, defaultSet, setConfigSet) {
2613
+ if (hookList.length === 0) {
2614
+ return set;
2615
+ }
2616
+ return set === defaultSet || set === setConfigSet ? clone(set) : set;
2617
+ };
2618
+ /**
2619
+ * Shared guard for a node that a hook has detached from the walk tree,
2620
+ * used after each element-hook site in _sanitizeElements. Detaching is a
2621
+ * long-standing user pattern (issue #469; draw.io-style foreignObject
2622
+ * filtering). Per the cached, unclobberable parentNode getter the node is
2623
+ * genuinely out of the tree, so it can reach neither the serialized
2624
+ * output nor an IN_PLACE live tree; treat it as removed and stop
2625
+ * processing it. Without this guard, the unsafe-node / namespace checks
2626
+ * would call _forceRemove on a parentless node and hit the REPORT-3
2627
+ * fail-closed throw — which exists for nodes DOMPurify wants gone but
2628
+ * *cannot* detach (clobbered / parentless roots), the opposite of a node
2629
+ * that is already safely gone. The walk root is exempt: a detached
2630
+ * IN_PLACE root is legitimate input and must still be fully sanitized,
2631
+ * and a kill-decision on it must keep hitting the REPORT-3 throw.
2632
+ *
2633
+ * Nodes detached by hooks stay the hook's responsibility for placement:
2634
+ * they are not recorded in DOMPurify.removed, so the post-walk IN_PLACE
2635
+ * pass (which iterates DOMPurify.removed) does not reach them. But a
2636
+ * hook-detached subtree can still hold a queued resource-event handler -
2637
+ * e.g. an <img onload> that began loading when the caller built the live
2638
+ * tree - which fires in page scope after sanitize returns even though the
2639
+ * handler never reached the returned tree. That is the audit-5 F1 hazard,
2640
+ * and the documented node.remove() hook pattern walks straight into it.
2641
+ * So on the IN_PLACE path we neutralize the detached subtree inline,
2642
+ * stripping its non-allow-listed attributes before returning, exactly as
2643
+ * the post-walk pass does for _forceRemove'd subtrees.
2644
+ *
2645
+ * @param currentNode the node a hook may have detached
2646
+ * @param root the current walk root
2647
+ * @return true if the node is detached and now handled, false otherwise
2648
+ */
2649
+ const _handleHookDetachedNode = function _handleHookDetachedNode(currentNode, root) {
2650
+ if (currentNode === root || getParentNode(currentNode) !== null) {
2651
+ return false;
2652
+ }
2653
+ if (IN_PLACE) {
2654
+ _neutralizeSubtree(currentNode);
2655
+ }
2656
+ return true;
2657
+ };
2658
+ /**
2659
+ * _sanitizeElements
2660
+ *
2661
+ * @protect nodeName
2662
+ * @protect textContent
2663
+ * @protect removeChild
2664
+ * @param currentNode to check for permission to exist
2665
+ * @return true if node was killed, false if left alive
2666
+ */
2667
+ const _sanitizeElements = function _sanitizeElements(currentNode, root) {
2668
+ /* Execute a hook if present */
2669
+ _executeHooks(hooks.beforeSanitizeElements, currentNode, null);
2670
+ /* A hook may have detached the node - treat it as removed (see
2671
+ _handleHookDetachedNode for the full rationale). */
2672
+ if (_handleHookDetachedNode(currentNode, root)) {
2673
+ return true;
2674
+ }
2675
+ /* Check if element is clobbered or can clobber */
2676
+ if (_isClobbered(currentNode)) {
2677
+ _forceRemove(currentNode);
2678
+ return true;
2679
+ }
2680
+ /* Now let's check the element's type and name */
2681
+ const tagName = transformCaseFunc(_readNodeName(currentNode));
2682
+ /* Close the pre-walk clone-guard's timing gap: an uponSanitizeElement
2683
+ hook may have been installed after that guard sampled the hook arrays
2684
+ (e.g. lazily from beforeSanitizeElements), leaving ALLOWED_TAGS still
2685
+ aliasing a shared binding that a widening hook would mutate by
2686
+ reference. Fork it before exposing it to the hook. */
2687
+ ALLOWED_TAGS = _forkSharedAllowlist(hooks.uponSanitizeElement, ALLOWED_TAGS, DEFAULT_ALLOWED_TAGS, SET_CONFIG_ALLOWED_TAGS);
2688
+ /* Execute a hook if present */
2689
+ _executeHooks(hooks.uponSanitizeElement, currentNode, {
2690
+ tagName,
2691
+ allowedTags: ALLOWED_TAGS
2692
+ });
2693
+ /* The uponSanitizeElement hook may have detached the node, exactly as
2694
+ above (see _handleHookDetachedNode for the full rationale). */
2695
+ if (_handleHookDetachedNode(currentNode, root)) {
2696
+ return true;
2697
+ }
2698
+ /* Remove mXSS vectors, processing instructions and risky comments */
2699
+ if (_isUnsafeNode(currentNode, tagName)) {
2700
+ _forceRemove(currentNode);
2701
+ return true;
2702
+ }
2703
+ /* Remove element if anything forbids its presence */
2704
+ if (FORBID_TAGS[tagName] || !(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && !ALLOWED_TAGS[tagName]) {
2705
+ const removed = _sanitizeDisallowedNode(currentNode, tagName, root);
2706
+ /* A false return means the node is a custom element kept via
2707
+ CUSTOM_ELEMENT_HANDLING - the only keep path through
2708
+ _sanitizeDisallowedNode. Run afterSanitizeElements on it so the
2709
+ element-hook lifecycle matches normal allowlisted elements: a
2710
+ security policy applied in this hook (e.g. stripping an attribute
2711
+ from every surviving element) must not silently skip kept custom
2712
+ elements (GHSA-c2j3-45gr-mqc4). This mirrors the normal-element
2713
+ tail below - the hook runs, then the walker's subsequent
2714
+ _sanitizeAttributes pass sanitizes the element's attributes. The
2715
+ deliberately skipped namespace and fallback-tag removal checks stay
2716
+ skipped; they are removal decisions, not the hook contract. */
2717
+ if (removed === false) {
2718
+ _executeHooks(hooks.afterSanitizeElements, currentNode, null);
2719
+ }
2720
+ return removed;
2721
+ }
2722
+ /* Check whether element has a valid namespace.
2723
+ Realm-safe check (GHSA-hpcv-96wg-7vj8): use the cached Node.prototype
2724
+ nodeType getter rather than `instanceof Element`, which is realm-
2725
+ bound and short-circuits to false for any node minted in a different
2726
+ realm — letting a foreign-realm element with a forbidden namespace
2727
+ slip past the namespace check entirely. */
2728
+ const nt = _readNodeType(currentNode);
2729
+ if (nt === NODE_TYPE.element && !_checkValidNamespace(currentNode)) {
2730
+ _forceRemove(currentNode);
2731
+ return true;
2732
+ }
2733
+ /* Make sure that older browsers don't get fallback-tag mXSS */
2734
+ if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(FALLBACK_TAG_CLOSE, currentNode.innerHTML)) {
2735
+ _forceRemove(currentNode);
2736
+ return true;
2737
+ }
2738
+ /* Sanitize element content to be template-safe */
2739
+ if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
2740
+ /* Get the element's text content */
2741
+ const content = _stripTemplateExpressions(currentNode.textContent);
2742
+ if (currentNode.textContent !== content) {
2743
+ arrayPush(DOMPurify.removed, {
2744
+ element: currentNode.cloneNode()
2745
+ });
2746
+ currentNode.textContent = content;
2747
+ }
2748
+ }
2749
+ /* Execute a hook if present */
2750
+ _executeHooks(hooks.afterSanitizeElements, currentNode, null);
2751
+ return false;
2752
+ };
2753
+ /**
2754
+ * _isValidAttribute
2755
+ *
2756
+ * @param lcTag Lowercase tag name of containing element.
2757
+ * @param lcName Lowercase attribute name.
2758
+ * @param value Attribute value.
2759
+ * @return Returns true if `value` is valid, otherwise false.
2760
+ */
2761
+ // eslint-disable-next-line complexity
2762
+ const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
2763
+ /* FORBID_ATTR must always win, even if ADD_ATTR predicate would allow it */
2764
+ if (FORBID_ATTR[lcName]) {
2765
+ return false;
2766
+ }
2767
+ /* Reject declarative-partial-updates patch-linkage attributes
2768
+ (https://github.com/WICG/declarative-partial-updates).
2769
+ Empirical note (Chrome 150, verified — see
2770
+ test/declarative-patch-probe-v3.html): expansion is NOT applied after
2771
+ sanitization. For the string path it fires during sanitize()'s own
2772
+ parse, so the walk sees and sanitizes the fully materialized expanded
2773
+ tree — teleports into MathML/SVG integration points included; a
2774
+ weaponized `<template for>`->`<img onerror>` comes back with the handler
2775
+ stripped. For the IN_PLACE path it fires on connection, before the walk.
2776
+ Either way DOMPurify is NOT blind to the patch.
2777
+ This removal is therefore defense-in-depth rather than the sole barrier:
2778
+ it prevents live linkage from surviving into the OUTPUT and re-expanding
2779
+ in the caller's context, and keeps behaviour deterministic if a future
2780
+ engine defers expansion. `for` is legitimate only on <label>/<output>;
2781
+ anywhere else (notably <template for>) it links the element to a patch
2782
+ target and teleports or removes an arbitrary DOM range by id/marker name.
2783
+ `patchsrc` fetches remote markup and is treated as a script-loading
2784
+ mechanism (CSP). Gated on SAFE_FOR_XML so the removal groups with the
2785
+ other structural-threat checks and stays overridable, consistent with
2786
+ the rest of the codebase. PI range markers are already removed by
2787
+ _isUnsafeNode. */
2788
+ if (_isPatchLinkageAttribute(lcName, lcTag)) {
2789
+ return false;
2790
+ }
2791
+ /* Make sure attribute cannot clobber */
2792
+ if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
2793
+ return false;
2794
+ }
2795
+ const nameIsPermitted = ALLOWED_ATTR[lcName] || EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag);
2796
+ /* Allow valid data-* attributes: At least one character after "-"
2797
+ (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
2798
+ XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
2799
+ We don't need to check the value; it's always URI safe. */
2800
+ if (ALLOW_DATA_ATTR && regExpTest(DATA_ATTR$1, lcName)) {
2801
+ return true;
2802
+ }
2803
+ /* Allow valid aria-* attributes, the value is always URI safe */
2804
+ if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$1, lcName)) {
2805
+ return true;
2806
+ }
2807
+ /* A name outside the allowlist is acceptable on custom-element terms
2808
+ only. The value checks below are intentionally skipped in that case:
2809
+ if the user supplied a tagNameCheck we also allow derived custom
2810
+ elements using the same test, and attributes passing the configured
2811
+ attributeNameCheck are allowed as custom elements define these at
2812
+ their own discretion. */
2813
+ if (!nameIsPermitted) {
2814
+ return (
2815
+ // Condition a) covers a basically valid custom element tag name whose
2816
+ // tag passes the configured tagNameCheck and whose attribute name
2817
+ // passes the configured attributeNameCheck ...
2818
+ _isBasicCustomElement(lcTag) && _matchesNameCheck(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) && _matchesNameCheck(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName, lcTag) ||
2819
+ // Condition b) covers an `is` attribute whose value passes the
2820
+ // configured tagNameCheck while customized built-in elements are
2821
+ // allowed.
2822
+ lcName === 'is' && CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements && _matchesNameCheck(CUSTOM_ELEMENT_HANDLING.tagNameCheck, value)
2823
+ );
2824
+ }
2825
+ /* Check value is safe. First, is attr inert? If so, is safe */
2826
+ if (URI_SAFE_ATTRIBUTES[lcName]) {
2827
+ return true;
2828
+ }
2829
+ /* Check no script, data or unknown possibly unsafe URI
2830
+ unless we know URI values are safe for that attribute */
2831
+ if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE$1, ''))) {
2832
+ return true;
2833
+ }
2834
+ /* Keep image data URIs alive if src/xlink:href is allowed */
2835
+ /* Further prevent gadget XSS for dynamically built script tags */
2836
+ if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) {
2837
+ return true;
2838
+ }
2839
+ /* Allow unknown protocols: This provides support for links that
2840
+ are handled by protocol handlers which may be unknown ahead of
2841
+ time, e.g. fb:, spotify: */
2842
+ if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA$1, stringReplace(value, ATTR_WHITESPACE$1, ''))) {
2843
+ return true;
2844
+ }
2845
+ /* Only an empty (binary) value remains safe at this point;
2846
+ anything else is presumed unsafe, do not add it back */
2847
+ return !value;
2848
+ };
2849
+ /* Names the HTML spec reserves from valid-custom-element-name; these must
2850
+ * never be treated as basic custom elements even when a permissive
2851
+ * CUSTOM_ELEMENT_HANDLING.tagNameCheck is configured. */
2852
+ const RESERVED_CUSTOM_ELEMENT_NAMES = addToSet({}, ['annotation-xml', 'color-profile', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'missing-glyph']);
2853
+ /**
2854
+ * _isBasicCustomElement
2855
+ * checks if at least one dash is included in tagName, and it's not the first char
2856
+ * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
2857
+ *
2858
+ * @param tagName name of the tag of the node to sanitize
2859
+ * @returns Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
2860
+ */
2861
+ const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
2862
+ return !RESERVED_CUSTOM_ELEMENT_NAMES[stringToLowerCase(tagName)] && regExpTest(CUSTOM_ELEMENT$1, tagName);
2863
+ };
2864
+ /**
2865
+ * Wrap an attribute value in the matching Trusted Types object when
2866
+ * the active policy requires it. Namespaced attributes pass through
2867
+ * unchanged (no TT support yet, see
2868
+ * https://bugs.chromium.org/p/chromium/issues/detail?id=1305293).
2869
+ *
2870
+ * @param lcTag lowercase tag name of the containing element
2871
+ * @param lcName lowercase attribute name
2872
+ * @param namespaceURI the attribute's namespace, if any
2873
+ * @param value the attribute value to wrap
2874
+ * @return the value, wrapped when Trusted Types demand it
2875
+ */
2876
+ const _applyTrustedTypesToAttribute = function _applyTrustedTypesToAttribute(lcTag, lcName, namespaceURI, value) {
2877
+ if (trustedTypesPolicy && typeof trustedTypes === 'object' && typeof trustedTypes.getAttributeType === 'function' && !namespaceURI) {
2878
+ switch (trustedTypes.getAttributeType(lcTag, lcName)) {
2879
+ case 'TrustedHTML':
2880
+ {
2881
+ return _createTrustedHTML(value);
2882
+ }
2883
+ case 'TrustedScriptURL':
2884
+ {
2885
+ return _createTrustedScriptURL(value);
2886
+ }
2887
+ }
2888
+ }
2889
+ return value;
2890
+ };
2891
+ /**
2892
+ * Write a modified attribute value back onto the element. On
2893
+ * success, re-probe for clobbering introduced by the new value and
2894
+ * remove the element when found; otherwise pop the removal entry
2895
+ * recorded by the earlier _removeAttribute (long-standing pairing
2896
+ * with the SANITIZE_NAMED_PROPS path - do not "fix" casually). On
2897
+ * failure, remove the attribute instead.
2898
+ *
2899
+ * @param currentNode the element carrying the attribute
2900
+ * @param name the attribute name as present on the element
2901
+ * @param namespaceURI the attribute's namespace, if any
2902
+ * @param value the new attribute value
2903
+ */
2904
+ const _setAttributeValue = function _setAttributeValue(currentNode, name, namespaceURI, value) {
2905
+ try {
2906
+ if (namespaceURI) {
2907
+ currentNode.setAttributeNS(namespaceURI, name, value);
2908
+ } else {
2909
+ /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
2910
+ currentNode.setAttribute(name, value);
2911
+ }
2912
+ if (_isClobbered(currentNode)) {
2913
+ _forceRemove(currentNode);
2914
+ } else {
2915
+ arrayPop(DOMPurify.removed);
2916
+ }
2917
+ } catch (_) {
2918
+ _removeAttribute(name, currentNode);
2919
+ }
2920
+ };
2921
+ /**
2922
+ * _sanitizeAttributes
2923
+ *
2924
+ * @protect attributes
2925
+ * @protect nodeName
2926
+ * @protect removeAttribute
2927
+ * @protect setAttribute
2928
+ *
2929
+ * @param currentNode to sanitize
2930
+ */
2931
+ const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
2932
+ /* Execute a hook if present */
2933
+ _executeHooks(hooks.beforeSanitizeAttributes, currentNode, null);
2934
+ const attributes = currentNode.attributes;
2935
+ /* Check if we have attributes; if not we might have a text node */
2936
+ if (!attributes || _isClobbered(currentNode)) {
2937
+ return;
2938
+ }
2939
+ /* Same lazy-install guard as uponSanitizeElement (see there): fork the
2940
+ attribute allowlist off its shared binding before a hook can see it. */
2941
+ ALLOWED_ATTR = _forkSharedAllowlist(hooks.uponSanitizeAttribute, ALLOWED_ATTR, DEFAULT_ALLOWED_ATTR, SET_CONFIG_ALLOWED_ATTR);
2942
+ const hookEvent = {
2943
+ attrName: '',
2944
+ attrValue: '',
2945
+ keepAttr: true,
2946
+ allowedAttributes: ALLOWED_ATTR,
2947
+ forceKeepAttr: undefined
2948
+ };
2949
+ let l = attributes.length;
2950
+ const lcTag = transformCaseFunc(currentNode.nodeName);
2951
+ /* Go backwards over all attributes; safely remove bad ones */
2952
+ while (l--) {
2953
+ const attr = attributes[l];
2954
+ const name = attr.name,
2955
+ namespaceURI = attr.namespaceURI,
2956
+ attrValue = attr.value;
2957
+ const lcName = transformCaseFunc(name);
2958
+ const initValue = attrValue;
2959
+ let value = name === 'value' ? initValue : stringTrim(initValue);
2960
+ /* Execute a hook if present */
2961
+ hookEvent.attrName = lcName;
2962
+ hookEvent.attrValue = value;
2963
+ hookEvent.keepAttr = true;
2964
+ hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
2965
+ _executeHooks(hooks.uponSanitizeAttribute, currentNode, hookEvent);
2966
+ value = hookEvent.attrValue;
2967
+ /* Full DOM Clobbering protection via namespace isolation,
2968
+ * Prefix id and name attributes with `user-content-`
2969
+ */
2970
+ if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name') && stringIndexOf(value, SANITIZE_NAMED_PROPS_PREFIX) !== 0) {
2971
+ // Remove the attribute with this value
2972
+ _removeAttribute(name, currentNode, attr);
2973
+ // Prefix the value and later re-create the attribute with the sanitized value
2974
+ value = SANITIZE_NAMED_PROPS_PREFIX + value;
2975
+ }
2976
+ // Else: already prefixed, leave the attribute alone — the prefix is
2977
+ // itself the clobbering protection, and re-applying it is incorrect.
2978
+ /* Work around a security issue with comments inside attributes */
2979
+ if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i, value)) {
2980
+ _removeAttribute(name, currentNode, attr);
2981
+ continue;
2982
+ }
2983
+ /* Make sure we cannot easily use animated hrefs, even if animations are allowed */
2984
+ if (lcName === 'attributename' && stringMatch(value, 'href')) {
2985
+ _removeAttribute(name, currentNode, attr);
2986
+ continue;
2987
+ }
2988
+ /* Did the hooks force-keep the attribute? */
2989
+ if (hookEvent.forceKeepAttr) {
2990
+ continue;
2991
+ }
2992
+ /* Did the hooks approve of the attribute? */
2993
+ if (!hookEvent.keepAttr) {
2994
+ _removeAttribute(name, currentNode, attr);
2995
+ continue;
2996
+ }
2997
+ /* Work around a security issue in jQuery 3.0 */
2998
+ if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(SELF_CLOSING_TAG, value)) {
2999
+ _removeAttribute(name, currentNode, attr);
3000
+ continue;
3001
+ }
3002
+ /* Sanitize attribute content to be template-safe */
3003
+ if (SAFE_FOR_TEMPLATES) {
3004
+ value = _stripTemplateExpressions(value);
3005
+ }
3006
+ /* Is `value` valid for this attribute? */
3007
+ if (!_isValidAttribute(lcTag, lcName, value)) {
3008
+ _removeAttribute(name, currentNode, attr);
3009
+ continue;
3010
+ }
3011
+ /* Handle attributes that require Trusted Types */
3012
+ value = _applyTrustedTypesToAttribute(lcTag, lcName, namespaceURI, value);
3013
+ /* Handle invalid data-* attribute set by try-catching it */
3014
+ if (value !== initValue) {
3015
+ _setAttributeValue(currentNode, name, namespaceURI, value);
3016
+ }
3017
+ }
3018
+ /* Execute a hook if present */
3019
+ _executeHooks(hooks.afterSanitizeAttributes, currentNode, null);
3020
+ };
3021
+ /**
3022
+ * _sanitizeShadowDOM
3023
+ *
3024
+ * @param fragment to iterate over recursively
3025
+ */
3026
+ const _sanitizeShadowDOM2 = function _sanitizeShadowDOM(fragment) {
3027
+ let shadowNode = null;
3028
+ const shadowIterator = _createNodeIterator(fragment);
3029
+ /* Execute a hook if present */
3030
+ _executeHooks(hooks.beforeSanitizeShadowDOM, fragment, null);
3031
+ while (shadowNode = shadowIterator.nextNode()) {
3032
+ /* Execute a hook if present */
3033
+ _executeHooks(hooks.uponSanitizeShadowNode, shadowNode, null);
3034
+ /* Sanitize tags and elements */
3035
+ _sanitizeElements(shadowNode, fragment);
3036
+ /* Check attributes next */
3037
+ _sanitizeAttributes(shadowNode);
3038
+ /* Deep shadow DOM detected.
3039
+ Realm-safe check (GHSA-hpcv-96wg-7vj8): use nodeType against the
3040
+ DOCUMENT_FRAGMENT_NODE constant rather than instanceof, so we
3041
+ recurse into <template>.content from foreign realms too. */
3042
+ if (_isDocumentFragment(shadowNode.content)) {
3043
+ _sanitizeShadowDOM2(shadowNode.content);
3044
+ }
3045
+ /* An element iterated here may itself host an attached
3046
+ shadow root. The default NodeIterator does not enter shadow
3047
+ trees, so a shadow root nested inside template.content was
3048
+ previously reached by no walk at all (the pre-pass at
3049
+ _sanitizeAttachedShadowRoots descends via childNodes, which
3050
+ doesn't enter template.content; the template-content recursion
3051
+ above iterates the content but never inspected shadowRoot).
3052
+ Walk it explicitly. The nodeType guard avoids reading
3053
+ shadowRoot off text / comment / CDATA / PI nodes that the
3054
+ iterator also surfaces. */
3055
+ if (_readNodeType(shadowNode) === NODE_TYPE.element) {
3056
+ const innerSr = getShadowRoot(shadowNode);
3057
+ if (_isDocumentFragment(innerSr)) {
3058
+ _sanitizeAttachedShadowRoots(innerSr);
3059
+ _sanitizeShadowDOM2(innerSr);
3060
+ }
3061
+ }
3062
+ }
3063
+ /* Execute a hook if present */
3064
+ _executeHooks(hooks.afterSanitizeShadowDOM, fragment, null);
3065
+ };
3066
+ /**
3067
+ * _sanitizeAttachedShadowRoots
3068
+ *
3069
+ * Walks `root` and feeds every attached shadow root we encounter into
3070
+ * the existing _sanitizeShadowDOM pipeline. The default node iterator
3071
+ * does not descend into shadow trees, so nodes inside an attached
3072
+ * shadow root would otherwise be skipped entirely.
3073
+ *
3074
+ * Two real input paths put attached shadow roots in front of us:
3075
+ * 1. IN_PLACE on a DOM node that already has shadow roots attached.
3076
+ * 2. DOM-node input where importNode(dirty, true) deep-clones the
3077
+ * shadow root because it was created with `clonable: true`.
3078
+ *
3079
+ * This pass runs once, up front, so the main iteration loop (and the
3080
+ * existing _sanitizeShadowDOM template-content recursion) stay
3081
+ * untouched — string-input paths are not affected.
3082
+ *
3083
+ * @param root the subtree root to walk for attached shadow roots
3084
+ */
3085
+ const _sanitizeAttachedShadowRoots = function _sanitizeAttachedShadowRoots(root) {
3086
+ /* Iterative (explicit stack) rather than per-child recursion. DOM APIs
3087
+ impose no depth cap, so an attacker-shaped tree (JSON/CRDT/editor data
3088
+ built straight into the DOM — the IN_PLACE surface) deeper than the JS
3089
+ call-stack budget would otherwise overflow native recursion here and
3090
+ throw at the IN_PLACE entry pre-pass, before a single node is
3091
+ sanitized, leaving the caller's live tree untouched (fail-open). See
3092
+ campaign-3 F4. A heap stack keeps depth off the call stack.
3093
+ Each work item is either a node to descend into, or a deferred
3094
+ `_sanitizeShadowDOM` for an already-walked shadow root. The deferred
3095
+ form preserves the original post-order discipline: a shadow root's
3096
+ nested shadow roots are discovered before the outer shadow is
3097
+ sanitized (which may remove hosts). Pushes are in reverse of the
3098
+ desired processing order (LIFO): template content, then children, then
3099
+ the shadow-sanitize, then the shadow walk — so the order matches the
3100
+ previous recursion exactly. */
3101
+ const stack = [{
3102
+ node: root,
3103
+ shadow: null
3104
+ }];
3105
+ while (stack.length > 0) {
3106
+ const item = stack.pop();
3107
+ /* Deferred shadow-DOM sanitisation: runs after its subtree was walked. */
3108
+ if (item.shadow) {
3109
+ _sanitizeShadowDOM2(item.shadow);
3110
+ continue;
3111
+ }
3112
+ const node = item.node;
3113
+ const nodeType = _readNodeType(node);
3114
+ const isElement = nodeType === NODE_TYPE.element;
3115
+ /* (pushed last → processed first) Children, snapshotted in reverse so
3116
+ the first child is processed first. Snapshotting matters because a
3117
+ hook may detach siblings mid-walk. */
3118
+ const childNodes = getChildNodes(node);
3119
+ if (childNodes) {
3120
+ for (let i = childNodes.length - 1; i >= 0; --i) {
3121
+ stack.push({
3122
+ node: childNodes[i],
3123
+ shadow: null
3124
+ });
3125
+ }
3126
+ }
3127
+ /* (pushed before children → processed after them, matching the old
3128
+ "template content last" order) When the node is a <template>,
3129
+ descend into its content. */
3130
+ if (isElement) {
3131
+ const rootName = getNodeName ? getNodeName(node) : null;
3132
+ if (typeof rootName === 'string' && transformCaseFunc(rootName) === 'template') {
3133
+ const content = node.content;
3134
+ if (_isDocumentFragment(content)) {
3135
+ stack.push({
3136
+ node: content,
3137
+ shadow: null
3138
+ });
3139
+ }
3140
+ }
3141
+ }
3142
+ /* Shadow root (processed first): walk its subtree, then sanitise it.
3143
+ Realm-safe check (GHSA-hpcv-96wg-7vj8): nodeType-based detection
3144
+ rather than `instanceof DocumentFragment`, which is realm-bound and
3145
+ silently skipped foreign-realm shadow roots (e.g.
3146
+ iframe.contentDocument attachShadow). */
3147
+ if (isElement) {
3148
+ const sr = getShadowRoot(node);
3149
+ if (_isDocumentFragment(sr)) {
3150
+ /* Push the deferred sanitise first so it pops after the shadow
3151
+ walk we push next, i.e. nested shadow roots are discovered
3152
+ before this one is sanitised. */
3153
+ stack.push({
3154
+ node: null,
3155
+ shadow: sr
3156
+ }, {
3157
+ node: sr,
3158
+ shadow: null
3159
+ });
3160
+ }
3161
+ }
3162
+ }
3163
+ };
3164
+ // eslint-disable-next-line complexity
3165
+ DOMPurify.sanitize = function (dirty) {
3166
+ let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
3167
+ let body = null;
3168
+ let importedNode = null;
3169
+ let currentNode = null;
3170
+ let returnNode = null;
3171
+ /* Make sure we have a string to sanitize.
3172
+ DO NOT return early, as this will return the wrong type if
3173
+ the user has requested a DOM object rather than a string */
3174
+ IS_EMPTY_INPUT = !dirty;
3175
+ if (IS_EMPTY_INPUT) {
3176
+ dirty = '<!-->';
3177
+ }
3178
+ /* Stringify, in case dirty is an object */
3179
+ if (typeof dirty !== 'string' && !_isNode(dirty)) {
3180
+ dirty = stringifyValue(dirty);
3181
+ if (typeof dirty !== 'string') {
3182
+ throw typeErrorCreate('dirty is not a string, aborting');
3183
+ }
3184
+ }
3185
+ /* Return dirty HTML if DOMPurify cannot run */
3186
+ if (!DOMPurify.isSupported) {
3187
+ return dirty;
3188
+ }
3189
+ /* Assign config vars */
3190
+ if (SET_CONFIG) {
3191
+ /* Persistent setConfig() path: _parseConfig is skipped, so the sets are
3192
+ * not re-derived per call. Restore them from the pristine bindings
3193
+ * captured at setConfig() time so a previous call's hook clone (mutated
3194
+ * below) does not carry over. */
3195
+ ALLOWED_TAGS = SET_CONFIG_ALLOWED_TAGS;
3196
+ ALLOWED_ATTR = SET_CONFIG_ALLOWED_ATTR;
575
3197
  } else {
576
- setPost(unreadcount);
3198
+ _parseConfig(cfg);
3199
+ }
3200
+ /* Clone the hook-mutable allowlists before the walk whenever an
3201
+ * uponSanitize* hook is registered. The hook event exposes ALLOWED_TAGS
3202
+ * and ALLOWED_ATTR by reference (as allowedTags / allowedAttributes), so
3203
+ * a hook that widens them would otherwise mutate the shared set
3204
+ * permanently: across later calls and across every element. Cloning per
3205
+ * walk keeps documented in-call widening working while scoping it to the
3206
+ * call. A single guard for both config paths - the per-call path rebinds
3207
+ * the sets in _parseConfig each call, the persistent path restores them
3208
+ * from the captured bindings just above - so the two cannot diverge. */
3209
+ if (hooks.uponSanitizeElement.length > 0 || hooks.uponSanitizeAttribute.length > 0) {
3210
+ ALLOWED_TAGS = clone(ALLOWED_TAGS);
3211
+ }
3212
+ if (hooks.uponSanitizeAttribute.length > 0) {
3213
+ ALLOWED_ATTR = clone(ALLOWED_ATTR);
3214
+ }
3215
+ /* Clean up removed elements */
3216
+ DOMPurify.removed = [];
3217
+ /* Resolve IN_PLACE for this call without mutating persistent config.
3218
+ Writing the IN_PLACE closure variable here leaks under setConfig(),
3219
+ where _parseConfig is skipped on later calls: a single string call would
3220
+ disable in-place mode for every subsequent node call, returning a
3221
+ sanitized copy while leaving the caller's node — which in-place callers
3222
+ keep using and whose return value they ignore — unsanitized. REPORT-2. */
3223
+ const inPlace = IN_PLACE && typeof dirty !== 'string' && _isNode(dirty);
3224
+ if (inPlace) {
3225
+ /* Declarative-partial-updates / streaming pre-pass: sever every patch
3226
+ linkage across the live tree BEFORE the walk, so no patch can fire
3227
+ mid-walk and inject into an already-processed region. Runs first, so
3228
+ it also covers the forbidden/clobbered roots that throw below. */
3229
+ _neutralizePatchLinkage(dirty);
3230
+ /* Do some early pre-sanitization to avoid unsafe root nodes.
3231
+ Read nodeName through the cached prototype getter — a clobbering
3232
+ child named "nodeName" on the form root would otherwise shadow
3233
+ the property and let this check skip the root-allowlist
3234
+ validation entirely. */
3235
+ const nn = _readNodeName(dirty);
3236
+ if (typeof nn === 'string') {
3237
+ const tagName = transformCaseFunc(nn);
3238
+ if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
3239
+ /* Fail closed on a live root: neutralize handlers/children before
3240
+ throwing, exactly as the mid-walk abort path does. */
3241
+ _neutralizeRoot(dirty);
3242
+ throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
3243
+ }
3244
+ }
3245
+ /* Pre-flight the root through _isClobbered. The iterator-driven
3246
+ removal path can not detach a parent-less root: _forceRemove
3247
+ falls through to Element.prototype.remove(), which per spec
3248
+ is a no-op on a node with no parent. A clobbered root would
3249
+ then survive the main loop with its attributes uninspected,
3250
+ because _sanitizeAttributes early-returns on _isClobbered. The
3251
+ result would be an attacker-controlled form, complete with any
3252
+ event-handler attributes the caller passed in, handed back to
3253
+ the application unsanitized. Refuse to sanitize such a root
3254
+ the same way we refuse a forbidden tag. GHSA-r47g-fvhr-h676. */
3255
+ if (_isClobbered(dirty)) {
3256
+ /* Fail closed on a live clobbered root before throwing.
3257
+ _neutralizeRoot's reads are clobber-safe (cached getters); the
3258
+ form's non-clobbered descendants, e.g. an armed <img>, are scrubbed. */
3259
+ _neutralizeRoot(dirty);
3260
+ throw typeErrorCreate('root node is clobbered and cannot be sanitized in-place');
3261
+ }
3262
+ /* Sanitize attached shadow roots before the main iterator runs.
3263
+ The iterator does not descend into shadow trees. Same fail-closed
3264
+ barrier as the main walk (campaign-3 F2): a custom-element reaction
3265
+ inside a shadow root could abort this pre-pass before the walk runs,
3266
+ which would otherwise leave the entire live tree unsanitized. */
3267
+ try {
3268
+ _sanitizeAttachedShadowRoots(dirty);
3269
+ } catch (error) {
3270
+ _neutralizeRoot(dirty);
3271
+ throw error;
3272
+ }
3273
+ } else if (_isNode(dirty)) {
3274
+ /* If dirty is a DOM element, append to an empty document to avoid
3275
+ elements being stripped by the parser */
3276
+ body = _initDocument('<!---->');
3277
+ importedNode = body.ownerDocument.importNode(dirty, true);
3278
+ if (importedNode.nodeType === NODE_TYPE.element && importedNode.nodeName === 'BODY') {
3279
+ /* Node is already a body, use as is */
3280
+ body = importedNode;
3281
+ } else if (importedNode.nodeName === 'HTML') {
3282
+ body = importedNode;
3283
+ } else {
3284
+ // eslint-disable-next-line unicorn/prefer-dom-node-append
3285
+ body.appendChild(importedNode);
3286
+ }
3287
+ /* Clonable shadow roots are deep-cloned by importNode(); sanitize
3288
+ them before the main iterator runs, since the iterator does not
3289
+ descend into shadow trees. The walk routes every read through a
3290
+ cached prototype getter so clobbering descendants on a form root
3291
+ cannot hide a shadow host from this pass. */
3292
+ _sanitizeAttachedShadowRoots(importedNode);
3293
+ } else {
3294
+ /* Exit directly if we have nothing to do */
3295
+ if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
3296
+ // eslint-disable-next-line unicorn/prefer-includes
3297
+ dirty.indexOf('<') === -1) {
3298
+ return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? _createTrustedHTML(dirty) : dirty;
3299
+ }
3300
+ /* Initialize the document to work on */
3301
+ body = _initDocument(dirty);
3302
+ /* Check we have a DOM node from the data */
3303
+ if (!body) {
3304
+ return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
3305
+ }
577
3306
  }
578
- logConsole(configLogs, 'data', '', unreadcount);
579
- const favoriteValue = unreadcount && typeof unreadcount === 'object' && 'data' in unreadcount && !Array.isArray(unreadcount) ? unreadcount.data : unreadcount;
580
- if (type === 'get' && favoriteValue === true) {
581
- setColor('#ffcd38');
582
- } else if (type === 'set') {
583
- setColor('#ffcd38');
584
- localStorage.setItem('favoritessynch01', 'true');
585
- localStorage.setItem('favoritessynch02', 'true');
586
- window.dispatchEvent(new Event('favorites'));
587
- } else if (type === 'del') {
588
- setColor('');
589
- localStorage.setItem('favoritessynch01', 'true');
590
- localStorage.setItem('favoritessynch02', 'true');
591
- window.dispatchEvent(new Event('favorites'));
3307
+ /* Remove first element node (ours) if FORCE_BODY is set */
3308
+ if (body && FORCE_BODY) {
3309
+ _forceRemove(body.firstChild);
3310
+ }
3311
+ /* Get node iterator */
3312
+ const walkRoot = inPlace ? dirty : body;
3313
+ /* Now start iterating over the created document.
3314
+ The walk runs inside an exception barrier (campaign-3 F2): a re-entrant
3315
+ engine/custom-element mutation can detach a node mid-walk so
3316
+ `_forceRemove`'s parentless guard throws, aborting the loop. Without the
3317
+ barrier the caller's in-place tree would be left half-sanitized with the
3318
+ unvisited tail still armed. _createNodeIterator itself is inside the
3319
+ barrier too: constructing the iterator dereferences the root's document,
3320
+ and any failure there (e.g. an exotic/clobbered root) must still fail
3321
+ closed rather than skip the neutralize. On any throw we fail closed -
3322
+ strip the in-place root bare - then rethrow so the existing throw
3323
+ contract is preserved. (String/DOM-copy paths never return the partial
3324
+ body, so the propagating throw is already fail-closed there.) */
3325
+ try {
3326
+ const nodeIterator = _createNodeIterator(walkRoot);
3327
+ while (currentNode = nodeIterator.nextNode()) {
3328
+ /* Sanitize tags and elements */
3329
+ _sanitizeElements(currentNode, walkRoot);
3330
+ /* Check attributes next */
3331
+ _sanitizeAttributes(currentNode);
3332
+ /* Shadow DOM detected, sanitize it.
3333
+ Realm-safe check (GHSA-hpcv-96wg-7vj8): nodeType-based detection
3334
+ instead of instanceof, so foreign-realm <template>.content is
3335
+ walked correctly. */
3336
+ if (_isDocumentFragment(currentNode.content)) {
3337
+ _sanitizeShadowDOM2(currentNode.content);
3338
+ }
3339
+ }
3340
+ } catch (error) {
3341
+ if (inPlace) {
3342
+ _neutralizeRoot(dirty);
3343
+ /* Nodes _forceRemove'd earlier in the aborted walk are already
3344
+ detached from the root, so _neutralizeRoot's subtree pass does not
3345
+ reach them. Defuse them too, mirroring the success-path loop below. */
3346
+ arrayForEach(DOMPurify.removed, entry => {
3347
+ if (entry.element) {
3348
+ _neutralizeSubtree(entry.element);
3349
+ }
3350
+ });
3351
+ }
3352
+ throw error;
592
3353
  }
593
- setIsError(false);
594
- } catch (err) {
595
- setError(true);
596
- handleError$3(err, t, configLogs, setIsError, setMessageerror);
597
- } finally {
598
- setLoading(false);
599
- }
600
- };
601
- const serviceGetFavorites = async (fpHash = '', sessionID, postURL, cxauthxc, t, setPost, setIsError, setError, setMessageerror, setLoading, configLogs, ...rest) => {
602
- const visitorIdHash = await getOrSetFingerprint(fpHash);
603
- try {
604
- const requestData = {
605
- ...rest[0],
606
- sessionID,
607
- cxauthxc,
608
- postServerURL: postURL,
609
- authorization: true,
610
- fingerprint: visitorIdHash
611
- };
612
- const unreadcount = await getservice(requestData, configLogs);
613
- const responseData = unreadcount && typeof unreadcount === 'object' && 'data' in unreadcount && !Array.isArray(unreadcount) ? unreadcount.data : unreadcount;
614
- setPost(responseData);
615
- localStorage.setItem('favoritessynch03', JSON.stringify(responseData));
616
- logConsole(configLogs, 'data', '', unreadcount);
617
- setIsError(false);
618
- } catch (err) {
619
- setError(true);
620
- handleError$3(err, t, configLogs, setIsError, setMessageerror);
621
- } finally {
622
- setLoading(false);
623
- }
624
- };
625
-
626
- const defaultConfig$3 = {
627
- enableLogs: false,
628
- enableLogsError: false,
629
- enableLogsWarning: false,
630
- enableLogsInfo: false,
631
- enableLogsData: false,
632
- enableLogsDebug: false,
633
- developmentMode: false
634
- };
635
- const ConexysConfigContext = /*#__PURE__*/createContext(defaultConfig$3);
636
- const ConexysConfigProvider = ({
637
- children,
638
- config = {}
639
- }) => {
640
- const mergedConfig = {
641
- ...defaultConfig$3,
642
- ...config
3354
+ /* If we sanitized `dirty` in-place, return it. */
3355
+ if (inPlace) {
3356
+ /* Fail-closed completion of the audit-5 F1 fix: every node removed from
3357
+ the caller's live tree is detached but may still hold a queued
3358
+ resource-event handler that fires in page scope after we return. The
3359
+ move-hoist covers only disallowed-tag KEEP_CONTENT removals; strip the
3360
+ non-allow-listed attributes off every other removed subtree (clobber,
3361
+ mXSS, namespace, comments, KEEP_CONTENT:false, …) so those handlers are
3362
+ cancelled before any event can fire. Runs synchronously, pre-return. */
3363
+ arrayForEach(DOMPurify.removed, entry => {
3364
+ if (entry.element) {
3365
+ _neutralizeSubtree(entry.element);
3366
+ }
3367
+ });
3368
+ if (SAFE_FOR_TEMPLATES) {
3369
+ _scrubTemplateExpressions2(dirty);
3370
+ }
3371
+ return dirty;
3372
+ }
3373
+ /* Return sanitized string or DOM */
3374
+ if (RETURN_DOM) {
3375
+ if (SAFE_FOR_TEMPLATES) {
3376
+ _scrubTemplateExpressions2(body);
3377
+ }
3378
+ if (RETURN_DOM_FRAGMENT) {
3379
+ returnNode = createDocumentFragment.call(body.ownerDocument);
3380
+ while (body.firstChild) {
3381
+ // eslint-disable-next-line unicorn/prefer-dom-node-append
3382
+ returnNode.appendChild(body.firstChild);
3383
+ }
3384
+ } else {
3385
+ returnNode = body;
3386
+ }
3387
+ if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmode) {
3388
+ /*
3389
+ AdoptNode() is not used because internal state is not reset
3390
+ (e.g. the past names map of a HTMLFormElement), this is safe
3391
+ in theory but we would rather not risk another attack vector.
3392
+ The state that is cloned by importNode() is explicitly defined
3393
+ by the specs.
3394
+ */
3395
+ returnNode = importNode.call(originalDocument, returnNode, true);
3396
+ }
3397
+ return returnNode;
3398
+ }
3399
+ let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
3400
+ /* Serialize doctype if allowed */
3401
+ if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
3402
+ serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML;
3403
+ }
3404
+ /* Sanitize final string template-safe */
3405
+ if (SAFE_FOR_TEMPLATES) {
3406
+ serializedHTML = _stripTemplateExpressions(serializedHTML);
3407
+ }
3408
+ return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? _createTrustedHTML(serializedHTML) : serializedHTML;
3409
+ };
3410
+ DOMPurify.setConfig = function () {
3411
+ let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
3412
+ _parseConfig(cfg);
3413
+ SET_CONFIG = true;
3414
+ SET_CONFIG_ALLOWED_TAGS = ALLOWED_TAGS;
3415
+ SET_CONFIG_ALLOWED_ATTR = ALLOWED_ATTR;
3416
+ };
3417
+ DOMPurify.clearConfig = function () {
3418
+ CONFIG = null;
3419
+ SET_CONFIG = false;
3420
+ SET_CONFIG_ALLOWED_TAGS = null;
3421
+ SET_CONFIG_ALLOWED_ATTR = null;
3422
+ // Drop any caller-supplied Trusted Types policy so it cannot poison later
3423
+ // `RETURN_TRUSTED_TYPE` output. The internal default policy (cached, and
3424
+ // never recreated — Trusted Types throws on duplicate names) is restored by
3425
+ // the next `_parseConfig`. See GHSA-vxr8-fq34-vvx9.
3426
+ trustedTypesPolicy = defaultTrustedTypesPolicy;
3427
+ emptyHTML = '';
3428
+ };
3429
+ DOMPurify.isValidAttribute = function (tag, attr, value) {
3430
+ /* Initialize shared config vars if necessary. */
3431
+ if (!CONFIG) {
3432
+ _parseConfig({});
3433
+ }
3434
+ const lcTag = transformCaseFunc(tag);
3435
+ const lcName = transformCaseFunc(attr);
3436
+ return _isValidAttribute(lcTag, lcName, value);
3437
+ };
3438
+ DOMPurify.addHook = function (entryPoint, hookFunction) {
3439
+ if (typeof hookFunction !== 'function') {
3440
+ return;
3441
+ }
3442
+ /* Reject unknown entry points. Without this, a non-hook key (e.g.
3443
+ * '__proto__') indexes off the prototype chain rather than a real
3444
+ * hook array, and arrayPush then writes to Object.prototype. Guard
3445
+ * with an own-property check against the known hook names. */
3446
+ if (!objectHasOwnProperty(hooks, entryPoint)) {
3447
+ return;
3448
+ }
3449
+ arrayPush(hooks[entryPoint], hookFunction);
643
3450
  };
644
- return /*#__PURE__*/React__default.createElement(ConexysConfigContext.Provider, {
645
- value: mergedConfig
646
- }, children);
647
- };
648
- const useConexysConfig = () => {
649
- return useContext(ConexysConfigContext);
650
- };
651
- const isDevelopmentMode = config => {
652
- return config.developmentMode;
653
- };
654
-
655
- const config$3 = {
656
- headers: {
657
- 'Content-Type': 'application/json'
658
- }
659
- };
660
- function AppDataSettings({
661
- keys
662
- }) {
663
- const configLogs = useConexysConfig();
664
- const postURL = Url + 'getsettings';
665
- const [post, setPost] = useState(null);
666
- const [error, setError] = useState(false);
667
- const hasFetched = useRef(false);
668
- const fetchData = useCallback(async () => {
669
- if (hasFetched.current) return;
670
- hasFetched.current = true;
671
- try {
672
- await serviceData(postURL, {
673
- keys: keys
674
- }, config$3, setPost, configLogs);
675
- } catch (err) {
676
- setError(true);
677
- logConsole(configLogs, 'error', 'Error fetching data:', err);
678
- console.error('Error fetching data:', err);
3451
+ DOMPurify.removeHook = function (entryPoint, hookFunction) {
3452
+ if (!objectHasOwnProperty(hooks, entryPoint)) {
3453
+ return undefined;
679
3454
  }
680
- }, []);
681
- useEffect(() => {
682
- fetchData();
683
- }, [fetchData]);
684
- if (!post) return null;
685
- return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, error ? /*#__PURE__*/React__default.createElement("div", null, "Error loading data") : post);
3455
+ if (hookFunction !== undefined) {
3456
+ const index = arrayLastIndexOf(hooks[entryPoint], hookFunction);
3457
+ return index === -1 ? undefined : arraySplice(hooks[entryPoint], index, 1)[0];
3458
+ }
3459
+ return arrayPop(hooks[entryPoint]);
3460
+ };
3461
+ DOMPurify.removeHooks = function (entryPoint) {
3462
+ if (!objectHasOwnProperty(hooks, entryPoint)) {
3463
+ return;
3464
+ }
3465
+ hooks[entryPoint] = [];
3466
+ };
3467
+ DOMPurify.removeAllHooks = function () {
3468
+ hooks = _createHooksMap();
3469
+ };
3470
+ return DOMPurify;
3471
+ }
3472
+ var purify = createDOMPurify();
3473
+
3474
+ function sanitizeHtml(html) {
3475
+ if (!html) return '';
3476
+ return purify.sanitize(html);
686
3477
  }
687
3478
 
688
3479
  const config$2 = {
@@ -717,7 +3508,7 @@ function AppDataSettingsHTML({
717
3508
  if (!htmlString) return null;
718
3509
  return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, error ? (/*#__PURE__*/React__default.createElement("div", null, "Error loading data")) : (/*#__PURE__*/React__default.createElement("div", {
719
3510
  dangerouslySetInnerHTML: {
720
- __html: htmlString
3511
+ __html: sanitizeHtml(htmlString)
721
3512
  }
722
3513
  })));
723
3514
  }
@@ -811,14 +3602,18 @@ const postFormService$1 = async (datauser, configLogs) => {
811
3602
  }
812
3603
  }
813
3604
  const dataload = [restData, valueToPush];
3605
+ const xSessionType = authStorage.getSessionTypeValue(configLogs);
814
3606
  let config;
815
3607
  let datasend;
816
3608
  if (authorization === true) {
817
3609
  config = {
3610
+ withCredentials: true,
818
3611
  headers: {
819
3612
  Authorization: `Bearer ${datauser.cxauthxc}`,
3613
+ 'X-CSRF-Token': authStorage.getCsrfToken() || '',
820
3614
  'X-Session-ID': datauser.sessionID || '',
821
3615
  'X-Fingerprint': datauser.fingerprint || '',
3616
+ 'X-Session-Type': xSessionType,
822
3617
  'Content-Type': 'application/json'
823
3618
  }
824
3619
  };
@@ -838,7 +3633,9 @@ const postFormService$1 = async (datauser, configLogs) => {
838
3633
  }
839
3634
  } else {
840
3635
  config = {
3636
+ withCredentials: true,
841
3637
  headers: {
3638
+ 'X-CSRF-Token': authStorage.getCsrfToken() || '',
842
3639
  'Content-Type': 'application/json'
843
3640
  }
844
3641
  };
@@ -892,14 +3689,18 @@ const patchFormService$1 = async (datauser, configLogs) => {
892
3689
  }
893
3690
  }
894
3691
  const dataload = [restData, valueToPush];
3692
+ const xSessionType = authStorage.getSessionTypeValue(configLogs);
895
3693
  let config;
896
3694
  let datasend;
897
3695
  if (authorization === true) {
898
3696
  config = {
3697
+ withCredentials: true,
899
3698
  headers: {
900
3699
  Authorization: `Bearer ${datauser.cxauthxc}`,
3700
+ 'X-CSRF-Token': authStorage.getCsrfToken() || '',
901
3701
  'X-Session-ID': datauser.sessionID || '',
902
3702
  'X-Fingerprint': datauser.fingerprint || '',
3703
+ 'X-Session-Type': xSessionType,
903
3704
  'Content-Type': 'application/json'
904
3705
  }
905
3706
  };
@@ -919,7 +3720,9 @@ const patchFormService$1 = async (datauser, configLogs) => {
919
3720
  }
920
3721
  } else {
921
3722
  config = {
3723
+ withCredentials: true,
922
3724
  headers: {
3725
+ 'X-CSRF-Token': authStorage.getCsrfToken() || '',
923
3726
  'Content-Type': 'application/json'
924
3727
  }
925
3728
  };
@@ -956,11 +3759,15 @@ const deleteFormService$1 = async (datauser, configLogs) => {
956
3759
  ...datasend
957
3760
  } = datauser;
958
3761
  const logs = configLogs || defaultConfig$2;
3762
+ const xSessionType = authStorage.getSessionTypeValue(logs);
959
3763
  const config = {
3764
+ withCredentials: true,
960
3765
  headers: {
961
3766
  Authorization: `Bearer ${cxauthxc}`,
3767
+ 'X-CSRF-Token': authStorage.getCsrfToken() || '',
962
3768
  'X-Session-ID': datauser.sessionID || '',
963
3769
  'X-Fingerprint': datauser.fingerprint || '',
3770
+ 'X-Session-Type': xSessionType,
964
3771
  'Content-Type': 'application/json'
965
3772
  }
966
3773
  };
@@ -999,11 +3806,15 @@ const restoreFormService$1 = async (datauser, configLogs) => {
999
3806
  ...datasend
1000
3807
  } = datauser;
1001
3808
  const logs = configLogs || defaultConfig$1;
3809
+ const xSessionType = authStorage.getSessionTypeValue(logs);
1002
3810
  const config = {
3811
+ withCredentials: true,
1003
3812
  headers: {
1004
3813
  Authorization: `Bearer ${cxauthxc}`,
3814
+ 'X-CSRF-Token': authStorage.getCsrfToken() || '',
1005
3815
  'X-Session-ID': datauser.sessionID || '',
1006
3816
  'X-Fingerprint': datauser.fingerprint || '',
3817
+ 'X-Session-Type': xSessionType,
1007
3818
  'Content-Type': 'application/json'
1008
3819
  }
1009
3820
  };
@@ -1022,7 +3833,7 @@ var restoreFormService = {
1022
3833
  restoreFormService: restoreFormService$1
1023
3834
  };
1024
3835
 
1025
- const useCustomForm$3 = (fpHash, postServerURL, getServerURL, deleteServerURL, id, restoreServerURL, feedback, method = 'post', restoreMethod = 'post', fetchMethod = 'post') => {
3836
+ const useCustomForm$3 = (fpHash, postServerURL, getServerURL, deleteServerURL, id, restoreServerURL, feedback, method = 'post', restoreMethod = 'post', fetchMethod = 'post', allowEmptyIdFetch = false) => {
1026
3837
  const configLogs = useConexysConfig();
1027
3838
  const [t] = useTranslation('global');
1028
3839
  const MySwal = withReactContent(SweetAlert2);
@@ -1034,16 +3845,19 @@ const useCustomForm$3 = (fpHash, postServerURL, getServerURL, deleteServerURL, i
1034
3845
  const [error, setError] = useState(false);
1035
3846
  const cxauthxc = authStorage.getAuthToken(configLogs) || '';
1036
3847
  const sessionID = authStorage.getSessionId(configLogs) || '';
3848
+ const xSessionType = authStorage.getSessionTypeValue(configLogs);
1037
3849
  const iditem = id;
1038
3850
  const authorization = true;
1039
3851
  const updateDataUser = async visitorIdHash => {
1040
3852
  const getProfile = Url + 'getprofile';
1041
3853
  try {
1042
3854
  const response = await axios.get(getProfile, {
3855
+ withCredentials: true,
1043
3856
  headers: {
1044
3857
  Authorization: `Bearer ${cxauthxc}`,
1045
3858
  'X-Session-ID': sessionID,
1046
3859
  'X-Fingerprint': visitorIdHash,
3860
+ 'X-Session-Type': xSessionType,
1047
3861
  'Content-Type': 'application/json'
1048
3862
  }
1049
3863
  });
@@ -1189,6 +4003,7 @@ const useCustomForm$3 = (fpHash, postServerURL, getServerURL, deleteServerURL, i
1189
4003
  Authorization: `Bearer ${cxauthxc}`,
1190
4004
  'X-Session-ID': sessionID,
1191
4005
  'X-Fingerprint': fpHash,
4006
+ 'X-Session-Type': xSessionType,
1192
4007
  'Content-Type': 'application/json'
1193
4008
  }
1194
4009
  }), [cxauthxc, sessionID, fpHash]);
@@ -1201,7 +4016,7 @@ const useCustomForm$3 = (fpHash, postServerURL, getServerURL, deleteServerURL, i
1201
4016
  useEffect(() => {
1202
4017
  const setFp = async () => {
1203
4018
  setCheckdata(false);
1204
- if (id !== '') {
4019
+ if (id !== '' || allowEmptyIdFetch) {
1205
4020
  const visitorIdHash = await getOrSetFingerprint(fpHash, configLogs);
1206
4021
  const key = {
1207
4022
  sessionID: sessionID,
@@ -1211,11 +4026,21 @@ const useCustomForm$3 = (fpHash, postServerURL, getServerURL, deleteServerURL, i
1211
4026
  try {
1212
4027
  let response;
1213
4028
  if (fetchMethod === 'get') {
1214
- const url = getServerURL + id;
1215
- response = await axios.get(url, config);
4029
+ const url = id !== '' ? getServerURL + id : getServerURL;
4030
+ response = await axios.get(url, {
4031
+ ...config,
4032
+ withCredentials: true
4033
+ });
1216
4034
  logConsole(configLogs, 'info', '[Request] ', url);
1217
4035
  } else {
1218
- response = await axios.post(getServerURL, key, config);
4036
+ response = await axios.post(getServerURL, key, {
4037
+ ...config,
4038
+ withCredentials: true,
4039
+ headers: {
4040
+ ...(config?.headers || {}),
4041
+ 'X-CSRF-Token': authStorage.getCsrfToken() || ''
4042
+ }
4043
+ });
1219
4044
  logConsole(configLogs, 'info', '[Request] ', getServerURL);
1220
4045
  }
1221
4046
  const responseData = response.data;
@@ -1231,7 +4056,7 @@ const useCustomForm$3 = (fpHash, postServerURL, getServerURL, deleteServerURL, i
1231
4056
  }
1232
4057
  };
1233
4058
  setFp();
1234
- }, [id]);
4059
+ }, [id, allowEmptyIdFetch]);
1235
4060
  return {
1236
4061
  handleFormSubmit,
1237
4062
  handleInputChange,
@@ -1260,17 +4085,20 @@ function AppFormFields({
1260
4085
  permission,
1261
4086
  show,
1262
4087
  feedback,
1263
- method = 'post'
4088
+ method = 'post',
4089
+ allowEmptyIdFetch = false
1264
4090
  }) {
1265
4091
  const configLogs = useConexysConfig();
1266
4092
  const [t] = useTranslation('global');
1267
4093
  const authTokens = authStorage.getAuthToken(configLogs) || '';
1268
4094
  const authTokensSes = authStorage.getSessionId(configLogs) || '';
4095
+ const xSessionType = authStorage.getSessionTypeValue(configLogs);
1269
4096
  const config = useMemo(() => ({
1270
4097
  headers: {
1271
4098
  Authorization: `Bearer ${authTokens}`,
1272
4099
  'X-Session-ID': authTokensSes,
1273
4100
  'X-Fingerprint': fpHash,
4101
+ 'X-Session-Type': xSessionType,
1274
4102
  'Content-Type': 'application/json'
1275
4103
  }
1276
4104
  }), [authTokens, authTokensSes, fpHash]);
@@ -1297,7 +4125,7 @@ function AppFormFields({
1297
4125
  formInputs,
1298
4126
  handleInputChange,
1299
4127
  handleFormSubmit
1300
- } = useCustomForm$3(fpHash, postServerURL1, getServerURL1, deleteServerURL1, id1, '', feedback, method, 'post', 'get');
4128
+ } = useCustomForm$3(fpHash, postServerURL1, getServerURL1, deleteServerURL1, id1, '', feedback, method, 'post', 'get', allowEmptyIdFetch);
1301
4129
  if (!post) return null;
1302
4130
  const dataform = JSON.parse(post.data);
1303
4131
  let permissionstatus;
@@ -1343,6 +4171,7 @@ function AppFormFields({
1343
4171
  maxLength: block.maxlength,
1344
4172
  pattern: block.pattern
1345
4173
  },
4174
+ showPasswordToggle: block.showPasswordToggle,
1346
4175
  items: block.items
1347
4176
  }, 'form')), additionalfields1.content.body.map(block => RenderForm({
1348
4177
  component: block.component,
@@ -1366,6 +4195,7 @@ function AppFormFields({
1366
4195
  maxLength: block.maxlength,
1367
4196
  pattern: block.pattern
1368
4197
  },
4198
+ showPasswordToggle: block.showPasswordToggle,
1369
4199
  items: block.items
1370
4200
  }, 'form')), children);
1371
4201
  }
@@ -1440,6 +4270,7 @@ function AppFormFieldsFromConfig({
1440
4270
  maxLength: block.maxlength || block.validate && block.validate.maxLength,
1441
4271
  pattern: block.pattern || block.validate && block.validate.pattern
1442
4272
  },
4273
+ showPasswordToggle: block.showPasswordToggle,
1443
4274
  items: block.items || []
1444
4275
  }, nouser || 'user')));
1445
4276
  }
@@ -1460,6 +4291,7 @@ const useCustomForm$2 = (postServerURL, setAuth, message, dependencies = {}) =>
1460
4291
  const {
1461
4292
  setAuthTokens
1462
4293
  } = typeof externalUseAuth === 'function' ? externalUseAuth() : externalUseAuth || useAuth();
4294
+ const xSessionType = authStorage.getSessionTypeValue(configLogs);
1463
4295
  const [t] = useTranslation('global');
1464
4296
  const MySwal = withReactContent(SweetAlert2);
1465
4297
  const [formInputs, setFormInputs] = useState({});
@@ -1516,11 +4348,14 @@ const useCustomForm$2 = (postServerURL, setAuth, message, dependencies = {}) =>
1516
4348
  try {
1517
4349
  const postServerURL2 = Url + 'authTwostep';
1518
4350
  const config = {
4351
+ withCredentials: true,
1519
4352
  headers: {
1520
4353
  Authorization: `Bearer ${dataform.auth}`,
1521
4354
  'X-Session-ID': dataform.sessionid,
1522
4355
  'X-Fingerprint': dataform.fingerprint || visitorId,
1523
- 'Content-Type': 'application/json'
4356
+ 'X-Session-Type': xSessionType,
4357
+ 'Content-Type': 'application/json',
4358
+ 'X-CSRF-Token': authStorage.getCsrfToken() || ''
1524
4359
  }
1525
4360
  };
1526
4361
  const dataload = [dataform, visitorId, login];
@@ -1636,11 +4471,17 @@ const getServiceBasic = async (fpHash = '', sessionID, baseURL, config, setData,
1636
4471
  sessionID,
1637
4472
  fingerprint: visitorIdHash
1638
4473
  };
4474
+ const xSessionType = authStorage.getSessionTypeValue(configLogs);
1639
4475
  logConsole(configLogs, 'debug', '[Params] ', key);
1640
4476
  try {
1641
4477
  const response = await axios.get(baseURL, {
1642
4478
  ...config,
1643
- params: key
4479
+ withCredentials: true,
4480
+ params: key,
4481
+ headers: {
4482
+ ...(config?.headers || {}),
4483
+ 'X-Session-Type': xSessionType
4484
+ }
1644
4485
  });
1645
4486
  logConsole(configLogs, 'info', '[Request] ', baseURL);
1646
4487
  logConsole(configLogs, 'data', '', response.data);
@@ -1680,7 +4521,12 @@ const getServiceData = async (baseURL, params, config, setData, configLogs) => {
1680
4521
  try {
1681
4522
  const response = await axios.get(baseURL, {
1682
4523
  ...config,
1683
- params
4524
+ withCredentials: true,
4525
+ params,
4526
+ headers: {
4527
+ ...(config?.headers || {}),
4528
+ 'X-Session-Type': authStorage.getSessionTypeValue(configLogs)
4529
+ }
1684
4530
  });
1685
4531
  logConsole(configLogs, 'debug', '[Params] ', params);
1686
4532
  logConsole(configLogs, 'info', '[Request] ', baseURL);
@@ -1710,11 +4556,13 @@ function AppFormFieldsNoUser({
1710
4556
  const [fingerprint, setFingerprint] = useState('');
1711
4557
  const authTokens = authStorage.getAuthToken(configLogs) || '';
1712
4558
  const authTokensSes = authStorage.getSessionId(configLogs) || '';
4559
+ const xSessionType = authStorage.getSessionTypeValue(configLogs);
1713
4560
  const config = useMemo(() => ({
1714
4561
  headers: {
1715
4562
  Authorization: `Bearer ${authTokens}`,
1716
4563
  'X-Session-ID': authTokensSes,
1717
4564
  'X-Fingerprint': fingerprint,
4565
+ 'X-Session-Type': xSessionType,
1718
4566
  'Content-Type': 'application/json'
1719
4567
  }
1720
4568
  }), [authTokens, authTokensSes, fingerprint]);
@@ -1782,6 +4630,7 @@ function AppFormFieldsNoUser({
1782
4630
  maxLength: block.maxlength,
1783
4631
  pattern: block.pattern
1784
4632
  },
4633
+ showPasswordToggle: block.showPasswordToggle,
1785
4634
  items: block.items
1786
4635
  }, 'nouser')), additionalfields1.content.body.map(block => RenderForm({
1787
4636
  component: block.component,
@@ -1805,6 +4654,7 @@ function AppFormFieldsNoUser({
1805
4654
  maxLength: block.maxlength,
1806
4655
  pattern: block.pattern
1807
4656
  },
4657
+ showPasswordToggle: block.showPasswordToggle,
1808
4658
  items: block.items
1809
4659
  }, 'nouser')), children);
1810
4660
  }
@@ -1828,11 +4678,13 @@ function AppFormFieldsTable({
1828
4678
  const [t] = useTranslation('global');
1829
4679
  const authTokens = authStorage.getAuthToken(configLogs) || '';
1830
4680
  const authTokensSes = authStorage.getSessionId(configLogs) || '';
4681
+ const xSessionType = authStorage.getSessionTypeValue(configLogs);
1831
4682
  useMemo(() => ({
1832
4683
  headers: {
1833
4684
  Authorization: `Bearer ${authTokens}`,
1834
4685
  'X-Session-ID': authTokensSes,
1835
4686
  'X-Fingerprint': fpHash,
4687
+ 'X-Session-Type': xSessionType,
1836
4688
  'Content-Type': 'application/json'
1837
4689
  }
1838
4690
  }), [authTokens, authTokensSes, fpHash]);
@@ -1956,7 +4808,10 @@ function AppFormFieldsTable({
1956
4808
  maxLength: block.maxlength || block.validate && block.validate.maxLength,
1957
4809
  pattern: block.pattern || block.validate && block.validate.pattern
1958
4810
  },
1959
- items: block.items || []
4811
+ showPasswordToggle: block.showPasswordToggle,
4812
+ items: block.items || [],
4813
+ itemID: id1,
4814
+ adminCheck: show === 'admin'
1960
4815
  }, nouser || '')), additionalfields1.content.body.map(block => RenderForm({
1961
4816
  component: block.component,
1962
4817
  type: block.type,
@@ -1985,7 +4840,10 @@ function AppFormFieldsTable({
1985
4840
  maxLength: block.maxlength || block.validate && block.validate.maxLength,
1986
4841
  pattern: block.pattern || block.validate && block.validate.pattern
1987
4842
  },
1988
- items: block.items || []
4843
+ showPasswordToggle: block.showPasswordToggle,
4844
+ items: block.items || [],
4845
+ itemID: id1,
4846
+ adminCheck: show === 'admin'
1989
4847
  }, nouser || '')));
1990
4848
  }
1991
4849
 
@@ -5804,11 +8662,9 @@ function GenIcon(data) {
5804
8662
  }
5805
8663
  function IconBase(props) {
5806
8664
  var elem = conf => {
5807
- var {
5808
- attr,
5809
- size,
5810
- title
5811
- } = props,
8665
+ var attr = props.attr,
8666
+ size = props.size,
8667
+ title = props.title,
5812
8668
  svgProps = _objectWithoutProperties(props, _excluded);
5813
8669
  var computedSize = size || conf.size || "1em";
5814
8670
  var className;
@@ -6152,6 +9008,9 @@ function Mailbox({
6152
9008
  theObj,
6153
9009
  t
6154
9010
  }) {
9011
+ const safeObj = {
9012
+ __html: sanitizeHtml(theObj.__html)
9013
+ };
6155
9014
  return /*#__PURE__*/React__default.createElement("div", {
6156
9015
  className: "mailbox-email",
6157
9016
  style: {
@@ -6178,7 +9037,7 @@ function Mailbox({
6178
9037
  }), ' ', msgTarget)), /*#__PURE__*/React__default.createElement("div", {
6179
9038
  className: "card-body"
6180
9039
  }, /*#__PURE__*/React__default.createElement("div", {
6181
- dangerouslySetInnerHTML: theObj
9040
+ dangerouslySetInnerHTML: safeObj
6182
9041
  })), /*#__PURE__*/React__default.createElement("div", {
6183
9042
  className: "card-footer"
6184
9043
  }, /*#__PURE__*/React__default.createElement("p", {
@@ -6230,7 +9089,7 @@ function MailTemplateWidget({
6230
9089
  setIsError(false);
6231
9090
  }, []);
6232
9091
  const theObj = {
6233
- __html: getthemedata.html || ''
9092
+ __html: sanitizeHtml(getthemedata.html) || ''
6234
9093
  };
6235
9094
  logConsole(configLogs, 'data', '', getthemedata);
6236
9095
  return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement(Loading, {
@@ -6769,7 +9628,7 @@ function UserWidget({
6769
9628
  setIsError(false);
6770
9629
  };
6771
9630
  const theObj = {
6772
- __html: datauserdata.biography || ''
9631
+ __html: sanitizeHtml(datauserdata.biography) || ''
6773
9632
  };
6774
9633
  return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement(Loading, {
6775
9634
  error: error,
@@ -6786,7 +9645,7 @@ function UserWidget({
6786
9645
  className: "solid short"
6787
9646
  }), /*#__PURE__*/React__default.createElement("ul", {
6788
9647
  className: "simple-todo-list mt-3"
6789
- }, /*#__PURE__*/React__default.createElement("li", null, /*#__PURE__*/React__default.createElement("b", null, t('login.username')), ": ", datauserdata.username), /*#__PURE__*/React__default.createElement("li", null, /*#__PURE__*/React__default.createElement("b", null, t('login.email')), ": ", datauserdata.email), /*#__PURE__*/React__default.createElement("li", null, /*#__PURE__*/React__default.createElement("b", null, t('login.name')), ": ", datauserdata.name), /*#__PURE__*/React__default.createElement("li", null, /*#__PURE__*/React__default.createElement("b", null, t('login.lastname')), ": ", datauserdata.lastname), /*#__PURE__*/React__default.createElement("li", null, /*#__PURE__*/React__default.createElement("b", null, t('User.type_of_user')), ": ", datausertype)))), /*#__PURE__*/React__default.createElement(Snackbar, {
9648
+ }, /*#__PURE__*/React__default.createElement("li", null, /*#__PURE__*/React__default.createElement("b", null, t('login.username')), ": ", datauserdata.username), /*#__PURE__*/React__default.createElement("li", null, /*#__PURE__*/React__default.createElement("b", null, t('login.name')), ": ", datauserdata.name), /*#__PURE__*/React__default.createElement("li", null, /*#__PURE__*/React__default.createElement("b", null, t('login.lastname')), ": ", datauserdata.lastname), /*#__PURE__*/React__default.createElement("li", null, /*#__PURE__*/React__default.createElement("b", null, t('User.type_of_user')), ": ", datausertype)))), /*#__PURE__*/React__default.createElement(Snackbar, {
6790
9649
  open: isError,
6791
9650
  autoHideDuration: 6000,
6792
9651
  onClose: handleClose,
@@ -6821,16 +9680,32 @@ const AppHeaderPage = ({
6821
9680
  const [color, setColor] = useState('');
6822
9681
  const [getfavorites, setFavoritesList] = useState('');
6823
9682
  const hasFetched = useRef(false);
6824
- const cxauthxc = authStorage.getAuthToken(configLogs) || '';
6825
- const sessionID = authStorage.getSessionId(configLogs) || '';
9683
+ const [token, setToken] = useState('');
9684
+ const [sessionId, setSessionId] = useState('');
9685
+ const [authReady, setAuthReady] = useState(false);
9686
+ useEffect(() => {
9687
+ let cancelled = false;
9688
+ authStorage.initialize(configLogs).then(() => {
9689
+ if (cancelled) return;
9690
+ setToken(authStorage.getAuthToken(configLogs) || '');
9691
+ setSessionId(authStorage.getSessionId(configLogs) || '');
9692
+ setAuthReady(true);
9693
+ });
9694
+ return () => {
9695
+ cancelled = true;
9696
+ };
9697
+ }, []);
9698
+ const cxauthxc = token;
9699
+ const sessionID = sessionId;
6826
9700
  const hash = window.location.pathname;
6827
9701
  useEffect(() => {
9702
+ if (!authReady) return;
6828
9703
  if (hasFetched.current) return;
6829
9704
  hasFetched.current = true;
6830
9705
  serviceFavorites('get', fpHash, sessionID, 'getfavorites', cxauthxc, t, setFavoritesList, setIsError, setError, setMessageerror, setLoading, setColor, configLogs, {
6831
9706
  url: hash
6832
9707
  });
6833
- }, []);
9708
+ }, [authReady, fpHash, sessionID, cxauthxc, t, hash, configLogs]);
6834
9709
  const handleClickMenuRigth = useCallback(() => {
6835
9710
  setOpenMenuRigth(prev => !prev);
6836
9711
  const themeElement = document.getElementById('themeclass');
@@ -7427,6 +10302,11 @@ const FormLogin = ({
7427
10302
  const [trigger1, setTrigger1] = useState(0);
7428
10303
  const [trigger2, setTrigger2] = useState(0);
7429
10304
  const [isChecked, setIsChecked] = useState(false);
10305
+ const showToggle = block.showPasswordToggle === true;
10306
+ const [showPassword, setShowPassword] = useState(false);
10307
+ const [showConfirmation, setShowConfirmation] = useState(false);
10308
+ const handleTogglePassword = () => setShowPassword(v => !v);
10309
+ const handleToggleConfirmation = () => setShowConfirmation(v => !v);
7430
10310
  const handleChange = () => {
7431
10311
  setIsChecked(current => !current);
7432
10312
  };
@@ -7475,7 +10355,7 @@ const FormLogin = ({
7475
10355
  }, block.label)), /*#__PURE__*/React__default.createElement("div", {
7476
10356
  className: "input-group"
7477
10357
  }, /*#__PURE__*/React__default.createElement("input", {
7478
- type: block.type,
10358
+ type: showToggle && showPassword ? 'text' : block.type,
7479
10359
  value: block.value || '',
7480
10360
  name: block.name,
7481
10361
  onChange: block.onChange,
@@ -7485,7 +10365,13 @@ const FormLogin = ({
7485
10365
  className: "input-group-text"
7486
10366
  }, /*#__PURE__*/React__default.createElement("i", {
7487
10367
  className: "bx bx-lock text-4"
7488
- }))), /*#__PURE__*/React__default.createElement(NavLink, {
10368
+ })), showToggle && (/*#__PURE__*/React__default.createElement("button", {
10369
+ type: "button",
10370
+ className: "input-group-text password-toggle-btn",
10371
+ onClick: handleTogglePassword,
10372
+ "aria-label": t('System.show_password'),
10373
+ tabIndex: -1
10374
+ }, showPassword ? /*#__PURE__*/React__default.createElement(VisibilityOff, null) : /*#__PURE__*/React__default.createElement(Visibility, null)))), /*#__PURE__*/React__default.createElement(NavLink, {
7489
10375
  to: forgot_passworddir,
7490
10376
  className: "float-end"
7491
10377
  }, t('login.recover_password')));
@@ -7589,15 +10475,23 @@ const FormLogin = ({
7589
10475
  className: "col-sm-6 mb-3"
7590
10476
  }, /*#__PURE__*/React__default.createElement("label", null, labels[1]), /*#__PURE__*/React__default.createElement("span", {
7591
10477
  className: "required"
7592
- }, "*"), /*#__PURE__*/React__default.createElement("input", {
10478
+ }, "*"), /*#__PURE__*/React__default.createElement("div", {
10479
+ className: "input-group"
10480
+ }, /*#__PURE__*/React__default.createElement("input", {
7593
10481
  id: ids[1],
7594
- type: "password",
10482
+ type: showToggle && showPassword ? 'text' : 'password',
7595
10483
  className: errors.password ? 'form-control form-control-lg is-invalid' : 'form-control form-control-lg',
7596
10484
  ...register('password', {
7597
10485
  required: true,
7598
10486
  minLength: 5
7599
10487
  })
7600
- }), errors.password?.type === 'required' && (/*#__PURE__*/React__default.createElement("em", {
10488
+ }), showToggle && (/*#__PURE__*/React__default.createElement("button", {
10489
+ type: "button",
10490
+ className: "input-group-text password-toggle-btn",
10491
+ onClick: handleTogglePassword,
10492
+ "aria-label": t('System.show_password'),
10493
+ tabIndex: -1
10494
+ }, showPassword ? /*#__PURE__*/React__default.createElement(VisibilityOff, null) : /*#__PURE__*/React__default.createElement(Visibility, null)))), errors.password?.type === 'required' && (/*#__PURE__*/React__default.createElement("em", {
7601
10495
  className: "error invalid-feedback"
7602
10496
  }, t('error.please_enter_password'))), errors.password?.type === 'minLength' && (/*#__PURE__*/React__default.createElement("em", {
7603
10497
  className: "error invalid-feedback"
@@ -7605,16 +10499,24 @@ const FormLogin = ({
7605
10499
  className: "col-sm-6 mb-3"
7606
10500
  }, /*#__PURE__*/React__default.createElement("label", null, labels[2]), /*#__PURE__*/React__default.createElement("span", {
7607
10501
  className: "required"
7608
- }, "*"), /*#__PURE__*/React__default.createElement("input", {
10502
+ }, "*"), /*#__PURE__*/React__default.createElement("div", {
10503
+ className: "input-group"
10504
+ }, /*#__PURE__*/React__default.createElement("input", {
7609
10505
  id: ids[2],
7610
- type: "password",
10506
+ type: showToggle && showConfirmation ? 'text' : 'password',
7611
10507
  className: errors.password_confirmation ? 'form-control form-control-lg is-invalid' : 'form-control form-control-lg',
7612
10508
  ...register('password_confirmation', {
7613
10509
  required: true,
7614
10510
  minLength: 5,
7615
10511
  validate: value => value === password.current || t('error.passwords_do_not_match')
7616
10512
  })
7617
- }), errors.password_confirmation?.type === 'required' && (/*#__PURE__*/React__default.createElement("em", {
10513
+ }), showToggle && (/*#__PURE__*/React__default.createElement("button", {
10514
+ type: "button",
10515
+ className: "input-group-text password-toggle-btn",
10516
+ onClick: handleToggleConfirmation,
10517
+ "aria-label": t('System.show_password'),
10518
+ tabIndex: -1
10519
+ }, showConfirmation ? /*#__PURE__*/React__default.createElement(VisibilityOff, null) : /*#__PURE__*/React__default.createElement(Visibility, null)))), errors.password_confirmation?.type === 'required' && (/*#__PURE__*/React__default.createElement("em", {
7618
10520
  className: "error invalid-feedback"
7619
10521
  }, t('error.repeat_password'))), errors.password_confirmation?.type === 'minLength' && (/*#__PURE__*/React__default.createElement("em", {
7620
10522
  className: "error invalid-feedback"
@@ -7687,7 +10589,7 @@ const Info = ({
7687
10589
  texthtml
7688
10590
  } = block;
7689
10591
  const theObj = {
7690
- __html: texthtml
10592
+ __html: sanitizeHtml(texthtml)
7691
10593
  };
7692
10594
  return /*#__PURE__*/React__default.createElement("div", {
7693
10595
  className: ref
@@ -7703,9 +10605,9 @@ const Info = ({
7703
10605
 
7704
10606
  // THIS FILE IS AUTO GENERATED
7705
10607
  function MdCloudUpload (props) {
7706
- return GenIcon({"attr":{"viewBox":"0 0 24 24"},"child":[{"tag":"path","attr":{"fill":"none","d":"M0 0h24v24H0z"},"child":[]},{"tag":"path","attr":{"d":"M19.35 10.04A7.49 7.49 0 0 0 12 4C9.11 4 6.6 5.64 5.35 8.04A5.994 5.994 0 0 0 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM14 13v4h-4v-4H7l5-5 5 5h-3z"},"child":[]}]})(props);
10608
+ return GenIcon({"attr":{"viewBox":"0 0 24 24"},"child":[{"tag":"path","attr":{"fill":"none","d":"M0 0h24v24H0z"},"child":[]},{"tag":"path","attr":{"d":"M19.35 10.04A7.49 7.49 0 0 0 12 4C9.11 4 6.6 5.64 5.35 8.04A5.994 5.994 0 0 0 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96M14 13v4h-4v-4H7l5-5 5 5z"},"child":[]}]})(props);
7707
10609
  }function MdDelete (props) {
7708
- return GenIcon({"attr":{"viewBox":"0 0 24 24"},"child":[{"tag":"path","attr":{"fill":"none","d":"M0 0h24v24H0z"},"child":[]},{"tag":"path","attr":{"d":"M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"},"child":[]}]})(props);
10610
+ return GenIcon({"attr":{"viewBox":"0 0 24 24"},"child":[{"tag":"path","attr":{"fill":"none","d":"M0 0h24v24H0z"},"child":[]},{"tag":"path","attr":{"d":"M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6zM19 4h-3.5l-1-1h-5l-1 1H5v2h14z"},"child":[]}]})(props);
7709
10611
  }
7710
10612
 
7711
10613
  // THIS FILE IS AUTO GENERATED
@@ -7898,6 +10800,11 @@ const InputPassword = ({
7898
10800
  inParagraph = false
7899
10801
  }) => {
7900
10802
  const [t] = useTranslation('global');
10803
+ const showToggle = block.showPasswordToggle === true;
10804
+ const [showPassword, setShowPassword] = useState(false);
10805
+ const [showConfirmation, setShowConfirmation] = useState(false);
10806
+ const handleTogglePassword = () => setShowPassword(v => !v);
10807
+ const handleToggleConfirmation = () => setShowConfirmation(v => !v);
7901
10808
  const {
7902
10809
  register,
7903
10810
  formState: {
@@ -7931,7 +10838,7 @@ const InputPassword = ({
7931
10838
  }, block.label, block.validate.required && /*#__PURE__*/React__default.createElement("span", {
7932
10839
  className: "required"
7933
10840
  }, "*")), /*#__PURE__*/React__default.createElement("input", {
7934
- type: "password",
10841
+ type: showToggle && showPassword ? 'text' : 'password',
7935
10842
  id: block.id,
7936
10843
  className: errors.password ? `${block.className} is-invalid` : block.className,
7937
10844
  ...register(block.name, {
@@ -7940,7 +10847,13 @@ const InputPassword = ({
7940
10847
  maxLength: block.validate.maxLength
7941
10848
  }),
7942
10849
  required: true
7943
- }), errors.password && (/*#__PURE__*/React__default.createElement("span", {
10850
+ }), showToggle && (/*#__PURE__*/React__default.createElement("button", {
10851
+ type: "button",
10852
+ className: "password-toggle-btn",
10853
+ onClick: handleTogglePassword,
10854
+ "aria-label": t('System.show_password'),
10855
+ tabIndex: -1
10856
+ }, showPassword ? /*#__PURE__*/React__default.createElement(VisibilityOff, null) : /*#__PURE__*/React__default.createElement(Visibility, null))), errors.password && (/*#__PURE__*/React__default.createElement("span", {
7944
10857
  className: "error invalid-feedback"
7945
10858
  }, errors.password.type === 'required' && t('error.please_enter_password'), errors.password.type === 'minLength' && `${t('error.password_minimum')} ${block.validate.minLength} ${t('characters')}`, errors.password.type === 'maxLength' && `${t('error.password_maximum')} ${block.validate.maxLength} ${t('characters')}`)))) : (/*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement(InputLabel, {
7946
10859
  htmlFor: block.id
@@ -7950,7 +10863,7 @@ const InputPassword = ({
7950
10863
  hiddenLabel: true,
7951
10864
  size: "small",
7952
10865
  id: block.id,
7953
- type: "password",
10866
+ type: showToggle && showPassword ? 'text' : 'password',
7954
10867
  value: resetvalue,
7955
10868
  className: errors.password ? `${block.className} is-invalid` : block.className,
7956
10869
  ...register(block.name, {
@@ -7958,7 +10871,19 @@ const InputPassword = ({
7958
10871
  minLength: block.validate.minLength,
7959
10872
  maxLength: block.validate.maxLength
7960
10873
  }),
7961
- required: true
10874
+ required: true,
10875
+ slotProps: showToggle ? {
10876
+ input: {
10877
+ endAdornment: (/*#__PURE__*/React__default.createElement(InputAdornment, {
10878
+ position: "end"
10879
+ }, /*#__PURE__*/React__default.createElement(IconButton, {
10880
+ "aria-label": t('System.show_password'),
10881
+ onClick: handleTogglePassword,
10882
+ edge: "end",
10883
+ tabIndex: -1
10884
+ }, showPassword ? /*#__PURE__*/React__default.createElement(VisibilityOff, null) : /*#__PURE__*/React__default.createElement(Visibility, null))))
10885
+ }
10886
+ } : undefined
7962
10887
  }), errors.password?.type === 'required' && (/*#__PURE__*/React__default.createElement("em", {
7963
10888
  className: "error invalid-feedback"
7964
10889
  }, t('error.please_enter_password'))), errors.password?.type === 'minLength' && (/*#__PURE__*/React__default.createElement("em", {
@@ -7972,7 +10897,7 @@ const InputPassword = ({
7972
10897
  }, t('System.repeat'), " ", block.label, block.validate.required && /*#__PURE__*/React__default.createElement("span", {
7973
10898
  className: "required"
7974
10899
  }, "*")), /*#__PURE__*/React__default.createElement("input", {
7975
- type: "password",
10900
+ type: showToggle && showConfirmation ? 'text' : 'password',
7976
10901
  id: block.id + 100,
7977
10902
  className: errors.password_confirmation ? `${block.className} is-invalid` : block.className,
7978
10903
  ...register(`${block.name}_confirmation`, {
@@ -7982,7 +10907,13 @@ const InputPassword = ({
7982
10907
  validate: value => value === password.current || t('error.passwords_do_not_match')
7983
10908
  }),
7984
10909
  required: true
7985
- }), errors.password_confirmation && (/*#__PURE__*/React__default.createElement("span", {
10910
+ }), showToggle && (/*#__PURE__*/React__default.createElement("button", {
10911
+ type: "button",
10912
+ className: "password-toggle-btn",
10913
+ onClick: handleToggleConfirmation,
10914
+ "aria-label": t('System.show_password'),
10915
+ tabIndex: -1
10916
+ }, showConfirmation ? /*#__PURE__*/React__default.createElement(VisibilityOff, null) : /*#__PURE__*/React__default.createElement(Visibility, null))), errors.password_confirmation && (/*#__PURE__*/React__default.createElement("span", {
7986
10917
  className: "error invalid-feedback"
7987
10918
  }, errors.password_confirmation.type === 'required' && t('error.repeat_password'), errors.password_confirmation.type === 'minLength' && `${t('error.password_minimum')} ${block.validate.minLength} ${t('characters')}`, errors.password_confirmation.type === 'maxLength' && `${t('error.password_maximum')} ${block.validate.maxLength} ${t('characters')}`, errors.password_confirmation.type === 'validate' && t('error.password_not_match'))))) : (/*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement(InputLabel, {
7988
10919
  htmlFor: block.id + 100
@@ -7992,7 +10923,7 @@ const InputPassword = ({
7992
10923
  hiddenLabel: true,
7993
10924
  size: "small",
7994
10925
  id: block.id + 100,
7995
- type: "password",
10926
+ type: showToggle && showConfirmation ? 'text' : 'password',
7996
10927
  value: resetvalue,
7997
10928
  className: errors.password_confirmation ? `${block.className} is-invalid` : block.className,
7998
10929
  ...register(`${block.name}_confirmation`, {
@@ -8001,7 +10932,19 @@ const InputPassword = ({
8001
10932
  maxLength: block.validate.maxLength,
8002
10933
  validate: value => value === password.current || t('error.passwords_do_not_match')
8003
10934
  }),
8004
- required: true
10935
+ required: true,
10936
+ slotProps: showToggle ? {
10937
+ input: {
10938
+ endAdornment: (/*#__PURE__*/React__default.createElement(InputAdornment, {
10939
+ position: "end"
10940
+ }, /*#__PURE__*/React__default.createElement(IconButton, {
10941
+ "aria-label": t('System.show_password'),
10942
+ onClick: handleToggleConfirmation,
10943
+ edge: "end",
10944
+ tabIndex: -1
10945
+ }, showConfirmation ? /*#__PURE__*/React__default.createElement(VisibilityOff, null) : /*#__PURE__*/React__default.createElement(Visibility, null))))
10946
+ }
10947
+ } : undefined
8005
10948
  }), errors.password_confirmation?.type === 'required' && (/*#__PURE__*/React__default.createElement("em", {
8006
10949
  className: "error invalid-feedback"
8007
10950
  }, t('error.repeat_password'))), errors.password_confirmation?.type === 'minLength' && (/*#__PURE__*/React__default.createElement("em", {
@@ -8017,6 +10960,8 @@ const InputPassword = ({
8017
10960
 
8018
10961
  const postURLusername = Url + 'checkusername';
8019
10962
  const postURLmail = Url + 'checkmail';
10963
+ const postURLAdminUsername = Url + 'admincheckusername';
10964
+ const postURLAdminMail = Url + 'admincheckmail';
8020
10965
  const config$1 = {
8021
10966
  headers: {
8022
10967
  'Content-Type': 'application/json'
@@ -8034,7 +10979,14 @@ const checkExistsUsername = async ({
8034
10979
  try {
8035
10980
  const {
8036
10981
  data
8037
- } = await axios.post(postURLusername, requestData, config$1);
10982
+ } = await axios.post(postURLusername, requestData, {
10983
+ ...config$1,
10984
+ withCredentials: true,
10985
+ headers: {
10986
+ ...(config$1?.headers || {}),
10987
+ 'X-CSRF-Token': authStorage.getCsrfToken() || ''
10988
+ }
10989
+ });
8038
10990
  logConsole(configLogs, 'info', '[Request] ', postURLusername);
8039
10991
  logConsole(configLogs, 'data', '', data.result);
8040
10992
  return data.result === 'Valid';
@@ -8056,7 +11008,14 @@ const checkExistsMail = async ({
8056
11008
  try {
8057
11009
  const {
8058
11010
  data
8059
- } = await axios.post(postURLmail, requestData, config$1);
11011
+ } = await axios.post(postURLmail, requestData, {
11012
+ ...config$1,
11013
+ withCredentials: true,
11014
+ headers: {
11015
+ ...(config$1?.headers || {}),
11016
+ 'X-CSRF-Token': authStorage.getCsrfToken() || ''
11017
+ }
11018
+ });
8060
11019
  logConsole(configLogs, 'info', '[Request] ', postURLmail);
8061
11020
  logConsole(configLogs, 'data', '', data.result);
8062
11021
  return data.result === 'Valid';
@@ -8066,6 +11025,70 @@ const checkExistsMail = async ({
8066
11025
  return false;
8067
11026
  }
8068
11027
  };
11028
+ const checkExistsUsernameAdmin = async ({
11029
+ username,
11030
+ itemID
11031
+ }, configLogs) => {
11032
+ const requestData = {
11033
+ username,
11034
+ itemID
11035
+ };
11036
+ logConsole(configLogs, 'debug', '[Payload admincheckusername] ', requestData);
11037
+ try {
11038
+ const {
11039
+ data
11040
+ } = await axios.post(postURLAdminUsername, requestData, {
11041
+ ...config$1,
11042
+ withCredentials: true,
11043
+ headers: {
11044
+ ...(config$1?.headers || {}),
11045
+ 'X-CSRF-Token': authStorage.getCsrfToken() || ''
11046
+ }
11047
+ });
11048
+ logConsole(configLogs, 'info', '[Request] ', postURLAdminUsername);
11049
+ logConsole(configLogs, 'data', '', data);
11050
+ return data;
11051
+ } catch (err) {
11052
+ logConsole(configLogs, 'error', '', err);
11053
+ console.error(err);
11054
+ return {
11055
+ available: false,
11056
+ isCurrent: false
11057
+ };
11058
+ }
11059
+ };
11060
+ const checkExistsMailAdmin = async ({
11061
+ email,
11062
+ itemID
11063
+ }, configLogs) => {
11064
+ const requestData = {
11065
+ email,
11066
+ itemID
11067
+ };
11068
+ logConsole(configLogs, 'debug', '[Payload admincheckmail] ', requestData);
11069
+ try {
11070
+ const {
11071
+ data
11072
+ } = await axios.post(postURLAdminMail, requestData, {
11073
+ ...config$1,
11074
+ withCredentials: true,
11075
+ headers: {
11076
+ ...(config$1?.headers || {}),
11077
+ 'X-CSRF-Token': authStorage.getCsrfToken() || ''
11078
+ }
11079
+ });
11080
+ logConsole(configLogs, 'info', '[Request] ', postURLAdminMail);
11081
+ logConsole(configLogs, 'data', '', data);
11082
+ return data;
11083
+ } catch (err) {
11084
+ logConsole(configLogs, 'error', '', err);
11085
+ console.error(err);
11086
+ return {
11087
+ available: false,
11088
+ isCurrent: false
11089
+ };
11090
+ }
11091
+ };
8069
11092
 
8070
11093
  const InputText = /*#__PURE__*/React__default.memo(({
8071
11094
  block
@@ -8074,8 +11097,11 @@ const InputText = /*#__PURE__*/React__default.memo(({
8074
11097
  const [t] = useTranslation('global');
8075
11098
  const [errors, setError] = useState(false);
8076
11099
  const [texterror, setTextError] = useState('');
11100
+ const showToggle = block.showPasswordToggle === true;
11101
+ const [showPassword, setShowPassword] = useState(false);
11102
+ const handleTogglePassword = () => setShowPassword(v => !v);
8077
11103
  const handleInputBlur = useCallback(async event => {
8078
- const session = localStorage.getItem('cx_session') || '';
11104
+ const session = authStorage.getSessionId(configLogs) || '';
8079
11105
  const value = event.target.value;
8080
11106
  const setErrorState = (error, message) => {
8081
11107
  setError(error);
@@ -8084,22 +11110,44 @@ const InputText = /*#__PURE__*/React__default.memo(({
8084
11110
  };
8085
11111
  try {
8086
11112
  if (block.validate.check === 'email' && value) {
8087
- const mailexist = await checkExistsMail({
8088
- email: value,
8089
- sessionID: session
8090
- }, configLogs);
8091
- if (!mailexist) {
8092
- setErrorState(true, t('error.email_already_exists'));
8093
- return;
11113
+ if (block.adminCheck) {
11114
+ const mailAdmin = await checkExistsMailAdmin({
11115
+ email: value,
11116
+ itemID: block.itemID
11117
+ }, configLogs);
11118
+ if (!mailAdmin.available && !mailAdmin.isCurrent) {
11119
+ setErrorState(true, t('error.email_already_exists'));
11120
+ return;
11121
+ }
11122
+ } else {
11123
+ const mailexist = await checkExistsMail({
11124
+ email: value,
11125
+ sessionID: session
11126
+ }, configLogs);
11127
+ if (!mailexist) {
11128
+ setErrorState(true, t('error.email_already_exists'));
11129
+ return;
11130
+ }
8094
11131
  }
8095
11132
  } else if (block.validate.check === 'username' && value) {
8096
- const usernameExist = await checkExistsUsername({
8097
- username: value,
8098
- sessionID: session
8099
- }, configLogs);
8100
- if (!usernameExist) {
8101
- setErrorState(true, t('error.username_is_not_valid'));
8102
- return;
11133
+ if (block.adminCheck) {
11134
+ const usernameAdmin = await checkExistsUsernameAdmin({
11135
+ username: value,
11136
+ itemID: block.itemID
11137
+ }, configLogs);
11138
+ if (!usernameAdmin.available && !usernameAdmin.isCurrent) {
11139
+ setErrorState(true, t('error.username_already_exists'));
11140
+ return;
11141
+ }
11142
+ } else {
11143
+ const usernameExist = await checkExistsUsername({
11144
+ username: value,
11145
+ sessionID: session
11146
+ }, configLogs);
11147
+ if (!usernameExist) {
11148
+ setErrorState(true, t('error.username_is_not_valid'));
11149
+ return;
11150
+ }
8103
11151
  }
8104
11152
  }
8105
11153
  if (block.validate.minLength && value.length < block.validate.minLength) {
@@ -8143,14 +11191,26 @@ const InputText = /*#__PURE__*/React__default.memo(({
8143
11191
  size: "small",
8144
11192
  name: block.name,
8145
11193
  id: block.id,
8146
- type: block.type,
11194
+ type: showToggle && showPassword ? 'text' : block.type,
8147
11195
  placeholder: block.placeholder,
8148
11196
  value: block.value || '',
8149
11197
  onChange: block.onChange,
8150
11198
  onBlur: handleInputBlur,
8151
11199
  autoComplete: block.autocomplete,
8152
11200
  className: errors ? `${block.className} is-invalid` : block.className,
8153
- required: block.validate.required
11201
+ required: block.validate.required,
11202
+ slotProps: showToggle ? {
11203
+ input: {
11204
+ endAdornment: (/*#__PURE__*/React__default.createElement(InputAdornment, {
11205
+ position: "end"
11206
+ }, /*#__PURE__*/React__default.createElement(IconButton, {
11207
+ "aria-label": t('System.show_password'),
11208
+ onClick: handleTogglePassword,
11209
+ edge: "end",
11210
+ tabIndex: -1
11211
+ }, showPassword ? /*#__PURE__*/React__default.createElement(VisibilityOff, null) : /*#__PURE__*/React__default.createElement(Visibility, null))))
11212
+ }
11213
+ } : undefined
8154
11214
  }));
8155
11215
  if (block.style === 'login') {
8156
11216
  return /*#__PURE__*/React__default.createElement("div", {
@@ -8198,6 +11258,8 @@ const InputWYSIWYG = /*#__PURE__*/React__default.memo(({
8198
11258
  const editor = useRef(null);
8199
11259
  const config = useMemo(() => ({
8200
11260
  readonly: false,
11261
+ toolbarAdaptive: false,
11262
+ allowTags: ['p', 'br', 'b', 'strong', 'i', 'em', 'u', 's', 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'code', 'pre', 'a', 'img', 'hr', 'table', 'thead', 'tbody', 'tr', 'th', 'td', 'div', 'span'],
8201
11263
  placeholder: textEditor && textEditor !== '<p><br></p>' || block.value && block.value !== '<p><br></p>' ? '' : t('general.start_typings') || 'Start typings...',
8202
11264
  language: t('language.name')
8203
11265
  }), [t, textEditor, block.value]);
@@ -8631,7 +11693,10 @@ const LanguageSet = () => {
8631
11693
  hasFetched.current = true;
8632
11694
  const fetchLanguages = async () => {
8633
11695
  try {
8634
- const response = await axios.get(postURL, config);
11696
+ const response = await axios.get(postURL, {
11697
+ ...config,
11698
+ withCredentials: true
11699
+ });
8635
11700
  logConsole(configLogs, 'info', '[Request] ', postURL);
8636
11701
  const langData = Array.isArray(response.data) ? {
8637
11702
  data: response.data
@@ -9694,19 +12759,25 @@ const postFormServiceGetPost$1 = async (datauser, configLogs) => {
9694
12759
  formData.append('iditem', iditem);
9695
12760
  formData.append('sessionID', sessionID);
9696
12761
  formData.append('fingerprint', fingerprint);
12762
+ const xSessionType = authStorage.getSessionTypeValue(configLogs || defaultConfig);
9697
12763
  let config;
9698
12764
  if (authorization === true) {
9699
12765
  config = {
12766
+ withCredentials: true,
9700
12767
  headers: {
9701
12768
  Authorization: `Bearer ${cxauthxc}`,
12769
+ 'X-CSRF-Token': authStorage.getCsrfToken() || '',
9702
12770
  'X-Session-ID': sessionID,
9703
12771
  'X-Fingerprint': fingerprint,
12772
+ 'X-Session-Type': xSessionType,
9704
12773
  'Content-Type': 'multipart/form-data'
9705
12774
  }
9706
12775
  };
9707
12776
  } else {
9708
12777
  config = {
12778
+ withCredentials: true,
9709
12779
  headers: {
12780
+ 'X-CSRF-Token': authStorage.getCsrfToken() || '',
9710
12781
  'Content-Type': 'multipart/form-data'
9711
12782
  }
9712
12783
  };
@@ -9742,6 +12813,7 @@ const useCustomForm$1 = (fpHash, postServerURL, getServerURL, deleteServerURL, i
9742
12813
  const [error, setError] = useState(false);
9743
12814
  const cxauthxc = authStorage.getAuthToken(configLogs) || '';
9744
12815
  const sessionID = authStorage.getSessionId(configLogs) || '';
12816
+ const xSessionType = authStorage.getSessionTypeValue(configLogs);
9745
12817
  const iditem = id;
9746
12818
  const authorization = true;
9747
12819
  const handleFormSubmit = async event => {
@@ -9863,6 +12935,7 @@ const useCustomForm$1 = (fpHash, postServerURL, getServerURL, deleteServerURL, i
9863
12935
  Authorization: `Bearer ${cxauthxc}`,
9864
12936
  'X-Session-ID': sessionID,
9865
12937
  'X-Fingerprint': fpHash,
12938
+ 'X-Session-Type': xSessionType,
9866
12939
  'Content-Type': 'application/json'
9867
12940
  }
9868
12941
  }), [cxauthxc, sessionID, fpHash]);
@@ -9879,6 +12952,7 @@ const useCustomForm$1 = (fpHash, postServerURL, getServerURL, deleteServerURL, i
9879
12952
  await getOrSetFingerprint(fpHash, configLogs);
9880
12953
  try {
9881
12954
  const response = await axios.get(getServerURL, {
12955
+ withCredentials: true,
9882
12956
  headers: config.headers,
9883
12957
  params: {
9884
12958
  itemID: id
@@ -10171,19 +13245,25 @@ const patchservice = async (datauser, configLogs) => {
10171
13245
  cxauthxc,
10172
13246
  ...requestData
10173
13247
  } = datauser;
13248
+ const xSessionType = authStorage.getSessionTypeValue(configLogs);
10174
13249
  let config;
10175
13250
  if (authorization === true || authorization === 'true') {
10176
13251
  config = {
13252
+ withCredentials: true,
10177
13253
  headers: {
10178
13254
  Authorization: `Bearer ${cxauthxc}`,
13255
+ 'X-CSRF-Token': authStorage.getCsrfToken() || '',
10179
13256
  'X-Session-ID': datauser.sessionID || '',
10180
13257
  'X-Fingerprint': datauser.fingerprint || '',
13258
+ 'X-Session-Type': xSessionType,
10181
13259
  'Content-Type': 'application/json'
10182
13260
  }
10183
13261
  };
10184
13262
  } else {
10185
13263
  config = {
13264
+ withCredentials: true,
10186
13265
  headers: {
13266
+ 'X-CSRF-Token': authStorage.getCsrfToken() || '',
10187
13267
  'Content-Type': 'application/json'
10188
13268
  }
10189
13269
  };
@@ -10294,9 +13374,8 @@ const serviceDelete2 = async (fpHash = '', sessionID, deleteURL, cxauthxc, t, se
10294
13374
  };
10295
13375
 
10296
13376
  const checkAuth = configLogs => {
10297
- const cxauthxc = authStorage.getAuthToken(configLogs) || '';
10298
- const sessionID = authStorage.getSessionId(configLogs) || '';
10299
- if (cxauthxc === '' || sessionID === '' || cxauthxc === null || sessionID === null) {
13377
+ const hasSession = authStorage.hasActiveSession(configLogs);
13378
+ if (!hasSession) {
10300
13379
  localStorage.removeItem('datauser');
10301
13380
  localStorage.removeItem('sidebarleft');
10302
13381
  localStorage.removeItem('cxl0k2mw');
@@ -10337,6 +13416,7 @@ const useCustomForm = (fpHash, getServerURL, postServerURL, feedback, id, reset
10337
13416
  const [error, setError] = useState(false);
10338
13417
  const cxauthxc = authStorage.getAuthToken(configLogs) || '';
10339
13418
  const sessionID = authStorage.getSessionId(configLogs) || '';
13419
+ const xSessionType = authStorage.getSessionTypeValue(configLogs);
10340
13420
  const authorization = true;
10341
13421
  const handleFormSubmit = async event => {
10342
13422
  const visitorIdHash = await getOrSetFingerprint(fpHash, configLogs);
@@ -10433,6 +13513,7 @@ const useCustomForm = (fpHash, getServerURL, postServerURL, feedback, id, reset
10433
13513
  Authorization: `Bearer ${cxauthxc}`,
10434
13514
  'X-Session-ID': sessionID,
10435
13515
  'X-Fingerprint': fpHash,
13516
+ 'X-Session-Type': xSessionType,
10436
13517
  'Content-Type': 'application/json'
10437
13518
  }
10438
13519
  }), [cxauthxc, sessionID, fpHash]);
@@ -10445,7 +13526,14 @@ const useCustomForm = (fpHash, getServerURL, postServerURL, feedback, id, reset
10445
13526
  fingerprint: visitorIdHash
10446
13527
  };
10447
13528
  try {
10448
- const response = await axios.post(getServerURL, key, config);
13529
+ const response = await axios.post(getServerURL, key, {
13530
+ ...config,
13531
+ withCredentials: true,
13532
+ headers: {
13533
+ ...(config?.headers || {}),
13534
+ 'X-CSRF-Token': authStorage.getCsrfToken() || ''
13535
+ }
13536
+ });
10449
13537
  logConsole(configLogs, 'info', '[Request] ', getServerURL);
10450
13538
  const responseData = response.data;
10451
13539
  const formData = Array.isArray(responseData) ? responseData[0] : typeof responseData?.data === 'object' && responseData.data !== null ? Array.isArray(responseData.data) ? responseData.data[0] : responseData.data : responseData;
@@ -10480,6 +13568,7 @@ const useCustomFormPatch = (fpHash, getServerURL, patchServerURL, feedback, id,
10480
13568
  const [error, setError] = useState(false);
10481
13569
  const cxauthxc = authStorage.getAuthToken(configLogs) || '';
10482
13570
  const sessionID = authStorage.getSessionId(configLogs) || '';
13571
+ const xSessionType = authStorage.getSessionTypeValue(configLogs);
10483
13572
  const authorization = true;
10484
13573
  const handleFormSubmit = async event => {
10485
13574
  const visitorIdHash = await getOrSetFingerprint(fpHash, configLogs);
@@ -10576,6 +13665,7 @@ const useCustomFormPatch = (fpHash, getServerURL, patchServerURL, feedback, id,
10576
13665
  Authorization: `Bearer ${cxauthxc}`,
10577
13666
  'X-Session-ID': sessionID,
10578
13667
  'X-Fingerprint': fpHash,
13668
+ 'X-Session-Type': xSessionType,
10579
13669
  'Content-Type': 'application/json'
10580
13670
  }
10581
13671
  }), [cxauthxc, sessionID, fpHash]);
@@ -10588,7 +13678,14 @@ const useCustomFormPatch = (fpHash, getServerURL, patchServerURL, feedback, id,
10588
13678
  fingerprint: visitorIdHash
10589
13679
  };
10590
13680
  try {
10591
- const response = await axios.post(getServerURL, key, config);
13681
+ const response = await axios.post(getServerURL, key, {
13682
+ ...config,
13683
+ withCredentials: true,
13684
+ headers: {
13685
+ ...(config?.headers || {}),
13686
+ 'X-CSRF-Token': authStorage.getCsrfToken() || ''
13687
+ }
13688
+ });
10592
13689
  logConsole(configLogs, 'info', '[Request] ', getServerURL);
10593
13690
  const responseData = response.data;
10594
13691
  const formData = Array.isArray(responseData) ? responseData[0] : typeof responseData?.data === 'object' && responseData.data !== null ? Array.isArray(responseData.data) ? responseData.data[0] : responseData.data : responseData;
@@ -10696,7 +13793,7 @@ const useZoomPrevention = () => {
10696
13793
  };
10697
13794
 
10698
13795
  const getWsBaseUrl = () => {
10699
- const restApi = window.restAPI || 'http://localhost:3001/restapi/';
13796
+ const restApi = Url || 'http://localhost:3001/restapi/';
10700
13797
  return restApi.replace(/\/restapi\/?$/, '');
10701
13798
  };
10702
13799
  const useSocket = () => {
@@ -10713,12 +13810,16 @@ const useSocket = () => {
10713
13810
  logConsole(configLogs, 'warn', '[WS] ', 'No hay token, no se conecta WebSocket');
10714
13811
  return;
10715
13812
  }
13813
+ const isCookieMode = authStorage.getSessionTypeValue(configLogs) === 'cookie';
10716
13814
  const wsUrl = getWsBaseUrl();
10717
13815
  logConsole(configLogs, 'info', '[WS] ', `Conectando a ${wsUrl}...`);
10718
13816
  const socket = io(wsUrl, {
10719
- auth: {
10720
- token
10721
- },
13817
+ ...(isCookieMode ? {} : {
13818
+ auth: {
13819
+ token
13820
+ }
13821
+ }),
13822
+ withCredentials: true,
10722
13823
  transports: ['websocket', 'polling'],
10723
13824
  reconnection: true,
10724
13825
  reconnectionAttempts: Infinity,
@@ -10914,5 +14015,5 @@ function usePermissionContext() {
10914
14015
  return ctx;
10915
14016
  }
10916
14017
 
10917
- export { ActionButtons, AppDataSettings, AppDataSettingsHTML, AppDialogModal, AppError, AppFormFields, AppFormFieldsFromConfig, AppFormFieldsNoUser, AppFormFieldsTable, AppHeaderPage, AppHeaderPage404, AppSetHeaderTitle, AuthContext, BackendOfflinePage, BackendStatusProvider, Button, Card, CardDashboard, CardUser, Checkbox, ConexysConfigProvider, ErrorBoundary, GenerateRandomPassword, Heading, Image, Info, InputFile, InputPassword, InputText, InputWYSIWYG, LanguageSet, Loading, MRT_Localization, MailTemplateWidget, Mailbox, MessagesBlock, NotFound, PermissionGate, PermissionProvider, Radiobutton, RenderForm, Select, Switch, Table, Text, ThemeContextProvider, Timeline, Url, UserWidget, Uservalidationerror, authStorage, checkAuth, deleteFormService, deleteservice, getOrSetFingerprint, getServiceBasic, getServiceData, getservice, getservice2, initializeSharedComponents, initializeSharedServices, isDevelopmentMode, logConsole, patchFormService, patchservice, postFormService, postservice, serviceData, serviceDelete2, serviceFavorites, serviceGetFavorites, serviceLockscreen, serviceLogout, servicePatch2, servicePost, servicePost2, servicePostBasic, servicePostData, useAuth, useBackendStatus, useConexysConfig, useCustomForm$1 as useCustomFormGetPost, useCustomFormMulti, useCustomForm$3 as useCustomFormNormal, useCustomForm$2 as useCustomFormNormalNoUser, useCustomForm as useCustomFormUpload, useCustomFormPatch as useCustomFormUploadPatch, useFingerprintJS, useLanguageSync, usePermission, usePermissionContext, useSocket, useTableData, useTableDataInstall, useZoomPrevention };
14018
+ export { ActionButtons, AppDataSettings, AppDataSettingsHTML, AppDialogModal, AppError, AppFormFields, AppFormFieldsFromConfig, AppFormFieldsNoUser, AppFormFieldsTable, AppHeaderPage, AppHeaderPage404, AppSetHeaderTitle, AuthContext, BackendOfflinePage, BackendStatusProvider, Button, Card, CardDashboard, CardUser, Checkbox, ConexysConfigProvider, ErrorBoundary, GenerateRandomPassword, Heading, Image, Info, InputFile, InputPassword, InputText, InputWYSIWYG, LanguageSet, Loading, MRT_Localization, MailTemplateWidget, Mailbox, MessagesBlock, NotFound, PermissionGate, PermissionProvider, Radiobutton, RenderForm, Select, Switch, Table, Text, ThemeContextProvider, Timeline, Url, UserWidget, Uservalidationerror, authStorage, checkAuth, deleteFormService, deleteservice, getOrSetFingerprint, getServiceBasic, getServiceData, getservice, getservice2, initializeSharedComponents, initializeSharedServices, isDevelopmentMode, logConsole, patchFormService, patchservice, postFormService, postservice, sanitizeHtml, serviceData, serviceDelete2, serviceFavorites, serviceGetFavorites, serviceLockscreen, serviceLogout, servicePatch2, servicePost, servicePost2, servicePostBasic, servicePostData, useAuth, useBackendStatus, useConexysConfig, useCustomForm$1 as useCustomFormGetPost, useCustomFormMulti, useCustomForm$3 as useCustomFormNormal, useCustomForm$2 as useCustomFormNormalNoUser, useCustomForm as useCustomFormUpload, useCustomFormPatch as useCustomFormUploadPatch, useFingerprintJS, useLanguageSync, usePermission, usePermissionContext, useSocket, useTableData, useTableDataInstall, useZoomPrevention };
10918
14019
  //# sourceMappingURL=index.esm.js.map