wsemi 1.9.21 → 1.9.22

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 (518) hide show
  1. package/README.md +1 -1
  2. package/dist/wsemi.umd.js +6 -6
  3. package/dist/wsemi.umd.js.map +1 -1
  4. package/docs/-_class.mjs.html +1 -1
  5. package/docs/-_const.mjs.html +1 -1
  6. package/docs/-_detector.mjs.html +1 -1
  7. package/docs/-_errToStr.mjs.html +1 -1
  8. package/docs/-_execThrownMsg.mjs.html +1 -1
  9. package/docs/-_isRealError.mjs.html +1 -1
  10. package/docs/-_optNum.mjs.html +1 -1
  11. package/docs/-_pmMarkHandled.mjs.html +1 -1
  12. package/docs/-_pmReadItems.mjs.html +1 -1
  13. package/docs/-_pmSettleByState.mjs.html +1 -1
  14. package/docs/-_realErrorToStr.mjs.html +1 -1
  15. package/docs/-_toResultError.mjs.html +1 -1
  16. package/docs/ab2blob.mjs.html +1 -1
  17. package/docs/ab2u8arr.mjs.html +1 -1
  18. package/docs/addExcelWorksheetFromData.mjs.html +1 -1
  19. package/docs/addMonth.mjs.html +1 -1
  20. package/docs/aes2str.mjs.html +1 -1
  21. package/docs/alive.mjs.html +1 -1
  22. package/docs/arr2dt.mjs.html +1 -1
  23. package/docs/arrAccum.mjs.html +1 -1
  24. package/docs/arrAdd.mjs.html +1 -1
  25. package/docs/arrAt.mjs.html +1 -1
  26. package/docs/arrDiff.mjs.html +1 -1
  27. package/docs/arrFilterByKeywords.mjs.html +1 -1
  28. package/docs/arrFilterByNum.mjs.html +1 -1
  29. package/docs/arrFilterByPnum.mjs.html +1 -1
  30. package/docs/arrFilterByPnumAndToLog.mjs.html +1 -1
  31. package/docs/arrFind.mjs.html +1 -1
  32. package/docs/arrGroupByMaxmin.mjs.html +1 -1
  33. package/docs/arrHas.mjs.html +1 -1
  34. package/docs/arrInsert.mjs.html +1 -1
  35. package/docs/arrLookupByMaxmin.mjs.html +1 -1
  36. package/docs/arrMax.mjs.html +1 -1
  37. package/docs/arrMin.mjs.html +1 -1
  38. package/docs/arrMulti.mjs.html +1 -1
  39. package/docs/arrNorm.mjs.html +1 -1
  40. package/docs/arrProduct.mjs.html +1 -1
  41. package/docs/arrPull.mjs.html +1 -1
  42. package/docs/arrPullAt.mjs.html +1 -1
  43. package/docs/arrReduce.mjs.html +1 -1
  44. package/docs/arrReverse.mjs.html +1 -1
  45. package/docs/arrSep.mjs.html +1 -1
  46. package/docs/arrSort.mjs.html +1 -1
  47. package/docs/asind.mjs.html +1 -1
  48. package/docs/atan2d.mjs.html +1 -1
  49. package/docs/atand.mjs.html +1 -1
  50. package/docs/attstr.mjs.html +1 -1
  51. package/docs/b642obj.mjs.html +1 -1
  52. package/docs/b642str.mjs.html +1 -1
  53. package/docs/b642u16arr.mjs.html +1 -1
  54. package/docs/b642u8arr.mjs.html +1 -1
  55. package/docs/binstr.mjs.html +1 -1
  56. package/docs/blob2ab.mjs.html +1 -1
  57. package/docs/blob2b64.mjs.html +1 -1
  58. package/docs/blob2str.mjs.html +1 -1
  59. package/docs/blob2u8arr.mjs.html +1 -1
  60. package/docs/blobs2b64s.mjs.html +1 -1
  61. package/docs/blobs2u8arrs.mjs.html +1 -1
  62. package/docs/bodyLog.mjs.html +1 -1
  63. package/docs/browserView.mjs.html +1 -1
  64. package/docs/browserViewFromBlob.mjs.html +1 -1
  65. package/docs/browserViewFromText.mjs.html +1 -1
  66. package/docs/browserViewFromU8arr.mjs.html +1 -1
  67. package/docs/bs2u8arr.mjs.html +1 -1
  68. package/docs/bufRead.mjs.html +1 -1
  69. package/docs/bufReadDbl.mjs.html +1 -1
  70. package/docs/bufWrite.mjs.html +1 -1
  71. package/docs/bufWriteDbl.mjs.html +1 -1
  72. package/docs/cache.mjs.html +1 -1
  73. package/docs/cacheBd.mjs.html +1 -1
  74. package/docs/cacheSt.mjs.html +1 -1
  75. package/docs/cbol.mjs.html +1 -1
  76. package/docs/cdbl.mjs.html +1 -1
  77. package/docs/ceil.mjs.html +1 -1
  78. package/docs/cfilesize.mjs.html +1 -1
  79. package/docs/cfinancial.mjs.html +1 -1
  80. package/docs/cint.mjs.html +1 -1
  81. package/docs/clearXSS.mjs.html +263 -25
  82. package/docs/clearXSSDyn.mjs.html +1 -1
  83. package/docs/color.mjs.html +1 -1
  84. package/docs/composeToTree.mjs.html +1 -1
  85. package/docs/convertToTree.mjs.html +1 -1
  86. package/docs/copyStrToClipboard.mjs.html +1 -1
  87. package/docs/cosd.mjs.html +1 -1
  88. package/docs/cotd.mjs.html +1 -1
  89. package/docs/createExcelWorkbook.mjs.html +1 -1
  90. package/docs/cscd.mjs.html +1 -1
  91. package/docs/cstr.mjs.html +1 -1
  92. package/docs/cvOrmFind.mjs.html +1 -1
  93. package/docs/day2cht.mjs.html +1 -1
  94. package/docs/debounce.mjs.html +1 -1
  95. package/docs/delay.mjs.html +1 -1
  96. package/docs/detectXSS.mjs.html +1 -1
  97. package/docs/detectXSSDyn.mjs.html +1 -1
  98. package/docs/dig.mjs.html +1 -1
  99. package/docs/digExp.mjs.html +1 -1
  100. package/docs/domAlert.mjs.html +1 -1
  101. package/docs/domAppend.mjs.html +1 -1
  102. package/docs/domCancelEvent.mjs.html +1 -1
  103. package/docs/domCompareText.mjs.html +1 -1
  104. package/docs/domConvertToPic.mjs.html +1 -1
  105. package/docs/domConvertToPicDyn.mjs.html +1 -1
  106. package/docs/domDetect.mjs.html +1 -1
  107. package/docs/domDrag.mjs.html +1 -1
  108. package/docs/domDragBarAndScroll.mjs.html +1 -1
  109. package/docs/domDragPointer.mjs.html +1 -1
  110. package/docs/domDragPreview.mjs.html +1 -1
  111. package/docs/domDropFiles.mjs.html +1 -1
  112. package/docs/domElementsFromPoint.mjs.html +1 -1
  113. package/docs/domFadeIn.mjs.html +1 -1
  114. package/docs/domFadeOut.mjs.html +1 -1
  115. package/docs/domFind.mjs.html +1 -1
  116. package/docs/domFinds.mjs.html +1 -1
  117. package/docs/domGetAttr.mjs.html +1 -1
  118. package/docs/domGetBoudRect.mjs.html +1 -1
  119. package/docs/domGetBoudRectRefSelf.mjs.html +1 -1
  120. package/docs/domGetFileAccept.mjs.html +1 -1
  121. package/docs/domGetFiles.mjs.html +1 -1
  122. package/docs/domGetParents.mjs.html +1 -1
  123. package/docs/domGetPointFromEvent.mjs.html +1 -1
  124. package/docs/domGetScrollableParent.mjs.html +1 -1
  125. package/docs/domGetWindowSize.mjs.html +1 -1
  126. package/docs/domIsClientXYIn.mjs.html +1 -1
  127. package/docs/domIsFocused.mjs.html +1 -1
  128. package/docs/domIsRendered.mjs.html +1 -1
  129. package/docs/domIsStable.mjs.html +1 -1
  130. package/docs/domIsVisible.mjs.html +1 -1
  131. package/docs/domPrepend.mjs.html +1 -1
  132. package/docs/domRemove.mjs.html +1 -1
  133. package/docs/domRenderMarkdownMind.mjs.html +1 -1
  134. package/docs/domRenderMarkdownMindDyn.mjs.html +1 -1
  135. package/docs/domRipple.mjs.html +1 -1
  136. package/docs/domScrollIntoView.mjs.html +1 -1
  137. package/docs/domScrollTo.mjs.html +1 -1
  138. package/docs/domSetAttr.mjs.html +1 -1
  139. package/docs/domShowImages.mjs.html +1 -1
  140. package/docs/domShowImagesDyn.mjs.html +1 -1
  141. package/docs/domShowInputAndGetFiles.mjs.html +1 -1
  142. package/docs/domShowInputAndGetFilesU8Arrs.mjs.html +1 -1
  143. package/docs/domShowInputDatatime.mjs.html +1 -1
  144. package/docs/domTooltip.mjs.html +1 -1
  145. package/docs/domTriggerEvent.mjs.html +1 -1
  146. package/docs/domVirtualCreate.mjs.html +1 -1
  147. package/docs/domVirtualCreateQueue.mjs.html +1 -1
  148. package/docs/downloadExcelFileFromData.mjs.html +1 -1
  149. package/docs/downloadExcelFileFromDataDyn.mjs.html +1 -1
  150. package/docs/downloadFileFromB64.mjs.html +1 -1
  151. package/docs/downloadFileFromBlob.mjs.html +1 -1
  152. package/docs/downloadFileFromText.mjs.html +1 -1
  153. package/docs/downloadFileFromU8Arr.mjs.html +1 -1
  154. package/docs/dtmapping.mjs.html +1 -1
  155. package/docs/dtpick.mjs.html +1 -1
  156. package/docs/estimateTicks.mjs.html +1 -1
  157. package/docs/evEmit.mjs.html +1 -1
  158. package/docs/evEmitDelay.mjs.html +1 -1
  159. package/docs/evem.mjs.html +1 -1
  160. package/docs/examples/ex-blob2b64.html +1 -1
  161. package/docs/examples/ex-clearXSS.html +1 -1
  162. package/docs/examples/ex-clearXSSDyn.html +1 -1
  163. package/docs/examples/ex-colorConverter.html +1 -1
  164. package/docs/examples/ex-detectXSS.html +1 -1
  165. package/docs/examples/ex-detectXSSDyn.html +1 -1
  166. package/docs/examples/ex-domCompareText.html +1 -1
  167. package/docs/examples/ex-domConvertToPic.html +1 -1
  168. package/docs/examples/ex-domDrag.html +1 -1
  169. package/docs/examples/ex-domDropFiles.html +1 -1
  170. package/docs/examples/ex-domIsVisible.html +1 -1
  171. package/docs/examples/ex-domRenderMarkdownMind.html +1 -1
  172. package/docs/examples/ex-domRenderMarkdownMindDyn.html +1 -1
  173. package/docs/examples/ex-domRipple.html +1 -1
  174. package/docs/examples/ex-domScrollIntoView.html +1 -1
  175. package/docs/examples/ex-domScrollTo.html +1 -1
  176. package/docs/examples/ex-domShowImages.html +1 -1
  177. package/docs/examples/ex-domShowImagesDyn.html +1 -1
  178. package/docs/examples/ex-domShowInputAndGetFiles.html +1 -1
  179. package/docs/examples/ex-domTooltip.html +1 -1
  180. package/docs/examples/ex-getFileHash.html +1 -1
  181. package/docs/examples/ex-getUserAgent.html +1 -1
  182. package/docs/examples/ex-getUserAgentDyn.html +1 -1
  183. package/docs/examples/ex-html2str.html +1 -1
  184. package/docs/examples/ex-obj2u8arr-u8arr2obj.html +1 -1
  185. package/docs/examples/ex-openLink.html +1 -1
  186. package/docs/examples/ex-str2aes-aes2str.html +1 -1
  187. package/docs/examples/ex-str2b64-b642str.html +1 -1
  188. package/docs/examples/ex-strFindFuzz.html +1 -1
  189. package/docs/examples/ex-timeTZ2expire-timeTZ2past.html +1 -1
  190. package/docs/examples/ex-xlsx.html +1 -1
  191. package/docs/execCli.mjs.html +1 -1
  192. package/docs/execCliCore.mjs.html +1 -1
  193. package/docs/execCliJsonRpc.mjs.html +1 -1
  194. package/docs/execCliSession.mjs.html +1 -1
  195. package/docs/execJsCode.mjs.html +1 -1
  196. package/docs/execProcess.mjs.html +1 -1
  197. package/docs/execProcessKillPid.mjs.html +1 -1
  198. package/docs/filepathToTree.mjs.html +1 -1
  199. package/docs/files2data.mjs.html +1 -1
  200. package/docs/flattenToConn.mjs.html +12 -8
  201. package/docs/flattenTree.mjs.html +9 -9
  202. package/docs/floor.mjs.html +1 -1
  203. package/docs/fsBuildReadStreamText.mjs.html +1 -1
  204. package/docs/fsBuildWriteStreamText.mjs.html +1 -1
  205. package/docs/fsCleanFolder.mjs.html +1 -1
  206. package/docs/fsCleanFolderCore.mjs.html +1 -1
  207. package/docs/fsCopyFile.mjs.html +1 -1
  208. package/docs/fsCopyFileCore.mjs.html +1 -1
  209. package/docs/fsCopyFolder.mjs.html +1 -1
  210. package/docs/fsCopyFolderCore.mjs.html +1 -1
  211. package/docs/fsCreateFile.mjs.html +1 -1
  212. package/docs/fsCreateFileCore.mjs.html +1 -1
  213. package/docs/fsCreateFolder.mjs.html +1 -1
  214. package/docs/fsCreateFolderCore.mjs.html +1 -1
  215. package/docs/fsDeleteFile.mjs.html +1 -1
  216. package/docs/fsDeleteFileCore.mjs.html +1 -1
  217. package/docs/fsDeleteFileSafe.mjs.html +1 -1
  218. package/docs/fsDeleteFileSafeCore.mjs.html +1 -1
  219. package/docs/fsDeleteFolder.mjs.html +1 -1
  220. package/docs/fsDeleteFolderCore.mjs.html +1 -1
  221. package/docs/fsDeleteFolderSafe.mjs.html +1 -1
  222. package/docs/fsDeleteFolderSafeCore.mjs.html +1 -1
  223. package/docs/fsDownloadFile.mjs.html +1 -1
  224. package/docs/fsDownloadFileCore.mjs.html +1 -1
  225. package/docs/fsEvem.mjs.html +1 -1
  226. package/docs/fsExists.mjs.html +1 -1
  227. package/docs/fsExistsCore.mjs.html +1 -1
  228. package/docs/fsGetFileBasicHash.mjs.html +1 -1
  229. package/docs/fsGetFileBasicHashCore.mjs.html +1 -1
  230. package/docs/fsGetFileHash.mjs.html +1 -1
  231. package/docs/fsGetFileHashCore.mjs.html +1 -1
  232. package/docs/fsGetFileXxHash.mjs.html +1 -1
  233. package/docs/fsGetFileXxHashCore.mjs.html +1 -1
  234. package/docs/fsGetFilesInFolder.mjs.html +1 -1
  235. package/docs/fsGetFilesInFolderCore.mjs.html +1 -1
  236. package/docs/fsGetFilesWithHashInFolder.mjs.html +1 -1
  237. package/docs/fsGetFilesWithHashInFolderCore.mjs.html +1 -1
  238. package/docs/fsGetFolderBasicHash.mjs.html +1 -1
  239. package/docs/fsGetFolderBasicHashCore.mjs.html +1 -1
  240. package/docs/fsGetFoldersInFolder.mjs.html +1 -1
  241. package/docs/fsGetFoldersInFolderCore.mjs.html +1 -1
  242. package/docs/fsIsFile.mjs.html +1 -1
  243. package/docs/fsIsFileCore.mjs.html +1 -1
  244. package/docs/fsIsFolder.mjs.html +1 -1
  245. package/docs/fsIsFolderCore.mjs.html +1 -1
  246. package/docs/fsMergeFiles.mjs.html +1 -1
  247. package/docs/fsMergeFilesCore.mjs.html +1 -1
  248. package/docs/fsReadJson.mjs.html +1 -1
  249. package/docs/fsReadText.mjs.html +1 -1
  250. package/docs/fsReadTextCore.mjs.html +1 -1
  251. package/docs/fsRenameFile.mjs.html +1 -1
  252. package/docs/fsRenameFileCore.mjs.html +1 -1
  253. package/docs/fsRenameFolder.mjs.html +1 -1
  254. package/docs/fsRenameFolderCore.mjs.html +1 -1
  255. package/docs/fsSrlog.mjs.html +1 -1
  256. package/docs/fsSyncFolder.mjs.html +1 -1
  257. package/docs/fsSyncFolderCore.mjs.html +1 -1
  258. package/docs/fsTask.mjs.html +14 -13
  259. package/docs/fsTaskCp.mjs.html +1 -1
  260. package/docs/fsTreeFolder.mjs.html +1 -1
  261. package/docs/fsTreeFolderCore.mjs.html +1 -1
  262. package/docs/fsTreeFolderWithHash.mjs.html +1 -1
  263. package/docs/fsTreeFolderWithHashCore.mjs.html +1 -1
  264. package/docs/fsWatchFile.mjs.html +1 -1
  265. package/docs/fsWatchFolder.mjs.html +1 -1
  266. package/docs/fsWriteJson.mjs.html +1 -1
  267. package/docs/fsWriteText.mjs.html +1 -1
  268. package/docs/fsWriteTextCore.mjs.html +1 -1
  269. package/docs/genGradientColor.mjs.html +1 -1
  270. package/docs/genID.mjs.html +1 -1
  271. package/docs/genIDSeq.mjs.html +1 -1
  272. package/docs/genPm.mjs.html +1 -1
  273. package/docs/getBufferSize.mjs.html +1 -1
  274. package/docs/getClearPathName.mjs.html +1 -1
  275. package/docs/getCsvStrFromData.mjs.html +1 -1
  276. package/docs/getDataFromExcelFileU8Arr.mjs.html +1 -1
  277. package/docs/getDataFromExcelFileU8ArrDyn.mjs.html +1 -1
  278. package/docs/getEnv.mjs.html +1 -1
  279. package/docs/getErrorMessage.mjs.html +1 -1
  280. package/docs/getExcelU8ArrFromData.mjs.html +1 -1
  281. package/docs/getExcelU8ArrFromWorkbook.mjs.html +1 -1
  282. package/docs/getExcelWorkbookFromData.mjs.html +1 -1
  283. package/docs/getExcelWorkbookFromWorksheet.mjs.html +1 -1
  284. package/docs/getExcelWorksheetFromData.mjs.html +1 -1
  285. package/docs/getFileAccept.mjs.html +1 -1
  286. package/docs/getFileBasicHash.mjs.html +1 -1
  287. package/docs/getFileHash.mjs.html +1 -1
  288. package/docs/getFileName.mjs.html +1 -1
  289. package/docs/getFileNameExt.mjs.html +1 -1
  290. package/docs/getFileTrueName.mjs.html +1 -1
  291. package/docs/getFileType.mjs.html +1 -1
  292. package/docs/getFileXxHash.mjs.html +1 -1
  293. package/docs/getGlobal.mjs.html +1 -1
  294. package/docs/getObjSize.mjs.html +1 -1
  295. package/docs/getPathParent.mjs.html +1 -1
  296. package/docs/getTimeBetween.mjs.html +1 -1
  297. package/docs/getTimeFromUnit.mjs.html +1 -1
  298. package/docs/getTimeHp.mjs.html +1 -1
  299. package/docs/getTimeObject.mjs.html +1 -1
  300. package/docs/getTimeRange.mjs.html +1 -1
  301. package/docs/getUserAgent.mjs.html +1 -1
  302. package/docs/getUserAgentDyn.mjs.html +1 -1
  303. package/docs/getltdtkeys.mjs.html +7 -6
  304. package/docs/global.html +1 -1
  305. package/docs/haskey.mjs.html +1 -1
  306. package/docs/html2str.mjs.html +1 -1
  307. package/docs/htmlDecode.mjs.html +1 -1
  308. package/docs/htmlEncode.mjs.html +1 -1
  309. package/docs/importResources.mjs.html +1 -1
  310. package/docs/index.html +1 -1
  311. package/docs/interp1.mjs.html +1 -1
  312. package/docs/isDate.mjs.html +1 -1
  313. package/docs/isDev.mjs.html +1 -1
  314. package/docs/isEle.mjs.html +1 -1
  315. package/docs/isEleExistByID.mjs.html +1 -1
  316. package/docs/isEmail.mjs.html +1 -1
  317. package/docs/isErr.mjs.html +1 -1
  318. package/docs/isIE.mjs.html +1 -1
  319. package/docs/isNarrow.mjs.html +1 -1
  320. package/docs/isStrHasCapital.mjs.html +1 -1
  321. package/docs/isStrHasLetter.mjs.html +1 -1
  322. package/docs/isStrHasLowerCase.mjs.html +1 -1
  323. package/docs/isStrHasNumber.mjs.html +1 -1
  324. package/docs/isStrHasSpace.mjs.html +1 -1
  325. package/docs/isStrHasSymbol.mjs.html +1 -1
  326. package/docs/isUserIdentify.mjs.html +1 -1
  327. package/docs/isUserName.mjs.html +1 -1
  328. package/docs/isUserPw.mjs.html +1 -1
  329. package/docs/isWindow.mjs.html +1 -1
  330. package/docs/isab.mjs.html +1 -1
  331. package/docs/isarr.mjs.html +1 -1
  332. package/docs/isarr0.mjs.html +1 -1
  333. package/docs/isarr1.mjs.html +1 -1
  334. package/docs/isbigint.mjs.html +1 -1
  335. package/docs/isblob.mjs.html +1 -1
  336. package/docs/isbol.mjs.html +1 -1
  337. package/docs/isday.mjs.html +1 -1
  338. package/docs/isearr.mjs.html +1 -1
  339. package/docs/iseobj.mjs.html +1 -1
  340. package/docs/iser.mjs.html +1 -1
  341. package/docs/isernot.mjs.html +1 -1
  342. package/docs/isestr.mjs.html +1 -1
  343. package/docs/isfile.mjs.html +1 -1
  344. package/docs/isfun.mjs.html +1 -1
  345. package/docs/isint.mjs.html +1 -1
  346. package/docs/ismonth.mjs.html +1 -1
  347. package/docs/isn0int.mjs.html +1 -1
  348. package/docs/isn0num.mjs.html +1 -1
  349. package/docs/isnan.mjs.html +1 -1
  350. package/docs/isnbr.mjs.html +1 -1
  351. package/docs/isnint.mjs.html +1 -1
  352. package/docs/isnnum.mjs.html +1 -1
  353. package/docs/isnull.mjs.html +1 -1
  354. package/docs/isnum.mjs.html +1 -1
  355. package/docs/isobj.mjs.html +1 -1
  356. package/docs/isobj0.mjs.html +1 -1
  357. package/docs/isp0int.mjs.html +1 -1
  358. package/docs/isp0num.mjs.html +1 -1
  359. package/docs/ispint.mjs.html +1 -1
  360. package/docs/ispm.mjs.html +1 -1
  361. package/docs/ispnum.mjs.html +1 -1
  362. package/docs/isstr.mjs.html +1 -1
  363. package/docs/isstr0.mjs.html +1 -1
  364. package/docs/istime.mjs.html +1 -1
  365. package/docs/istimeTZ.mjs.html +1 -1
  366. package/docs/istimems.mjs.html +1 -1
  367. package/docs/istimemsTZ.mjs.html +1 -1
  368. package/docs/isu16arr.mjs.html +1 -1
  369. package/docs/isu8arr.mjs.html +1 -1
  370. package/docs/isundefined.mjs.html +1 -1
  371. package/docs/j2o.mjs.html +1 -1
  372. package/docs/judge.mjs.html +1 -1
  373. package/docs/keysmat2ltdt.mjs.html +1 -1
  374. package/docs/ltdt2vrtsable.mjs.html +1 -1
  375. package/docs/ltdtDiff.mjs.html +1 -1
  376. package/docs/ltdtDiffByKey.mjs.html +1 -1
  377. package/docs/ltdtkeys2mat.mjs.html +1 -1
  378. package/docs/ltdtkeysheads2mat.mjs.html +1 -1
  379. package/docs/ltdtmapping.mjs.html +1 -1
  380. package/docs/ltdtmerge.mjs.html +1 -1
  381. package/docs/ltdtpick.mjs.html +1 -1
  382. package/docs/ltdtpull.mjs.html +1 -1
  383. package/docs/mat2ltdt.mjs.html +1 -1
  384. package/docs/matConcat.mjs.html +1 -1
  385. package/docs/matDiff.mjs.html +1 -1
  386. package/docs/matat.mjs.html +1 -1
  387. package/docs/now2str.mjs.html +1 -1
  388. package/docs/now2strp.mjs.html +1 -1
  389. package/docs/nowDay2str.mjs.html +1 -1
  390. package/docs/nowms2str.mjs.html +1 -1
  391. package/docs/nowms2strp.mjs.html +1 -1
  392. package/docs/o2j.mjs.html +1 -1
  393. package/docs/obj2b64.mjs.html +1 -1
  394. package/docs/obj2pb64.mjs.html +1 -1
  395. package/docs/obj2str.mjs.html +1 -1
  396. package/docs/obj2stru8arr.mjs.html +1 -1
  397. package/docs/obj2u8arr.mjs.html +1 -1
  398. package/docs/objSortBy.mjs.html +1 -1
  399. package/docs/objSortByKeys.mjs.html +1 -1
  400. package/docs/oo.mjs.html +1 -1
  401. package/docs/openLink.mjs.html +1 -1
  402. package/docs/pb642obj.mjs.html +1 -1
  403. package/docs/pmChain.mjs.html +1 -1
  404. package/docs/pmConvertResolve.mjs.html +1 -1
  405. package/docs/pmDebounce.mjs.html +1 -1
  406. package/docs/pmHook.mjs.html +1 -1
  407. package/docs/pmHookReject.mjs.html +1 -1
  408. package/docs/pmIni.mjs.html +1 -1
  409. package/docs/pmInvResolve.mjs.html +1 -1
  410. package/docs/pmKeyMutex.mjs.html +1 -1
  411. package/docs/pmMap.mjs.html +1 -1
  412. package/docs/pmQueue.mjs.html +1 -1
  413. package/docs/pmSeries.mjs.html +1 -1
  414. package/docs/pmThrottle.mjs.html +1 -1
  415. package/docs/pmTimeout.mjs.html +1 -1
  416. package/docs/preciseNum.mjs.html +1 -1
  417. package/docs/pseudoRandom.mjs.html +1 -1
  418. package/docs/pseudoRandomIntRange.mjs.html +1 -1
  419. package/docs/pseudoRandomIntsNdpRange.mjs.html +1 -1
  420. package/docs/pseudoRandomIntsRange.mjs.html +1 -1
  421. package/docs/pseudoRandomRange.mjs.html +1 -1
  422. package/docs/pseudoRandomsRange.mjs.html +1 -1
  423. package/docs/queue.mjs.html +1 -1
  424. package/docs/random.mjs.html +1 -1
  425. package/docs/randomIntRange.mjs.html +1 -1
  426. package/docs/randomIntsNdpRange.mjs.html +1 -1
  427. package/docs/randomIntsRange.mjs.html +1 -1
  428. package/docs/randomRange.mjs.html +1 -1
  429. package/docs/randomsRange.mjs.html +1 -1
  430. package/docs/rang.mjs.html +1 -1
  431. package/docs/rep.mjs.html +1 -1
  432. package/docs/repObj.mjs.html +1 -1
  433. package/docs/replace.mjs.html +1 -1
  434. package/docs/replaceObj.mjs.html +1 -1
  435. package/docs/replacePlus.mjs.html +1 -1
  436. package/docs/round.mjs.html +1 -1
  437. package/docs/secd.mjs.html +1 -1
  438. package/docs/sep.mjs.html +1 -1
  439. package/docs/sepInt.mjs.html +1 -1
  440. package/docs/setIntervalHp.mjs.html +1 -1
  441. package/docs/setTimeoutHp.mjs.html +1 -1
  442. package/docs/sind.mjs.html +1 -1
  443. package/docs/split.mjs.html +1 -1
  444. package/docs/str2aes.mjs.html +1 -1
  445. package/docs/str2b64.mjs.html +1 -1
  446. package/docs/str2hint.mjs.html +1 -1
  447. package/docs/str2md5.mjs.html +1 -1
  448. package/docs/str2obj.mjs.html +1 -1
  449. package/docs/str2sha.mjs.html +1 -1
  450. package/docs/str2sha512.mjs.html +1 -1
  451. package/docs/str2u8arr.mjs.html +1 -1
  452. package/docs/strCompare.mjs.html +1 -1
  453. package/docs/strCompareForVersion.mjs.html +1 -1
  454. package/docs/strDiff.mjs.html +1 -1
  455. package/docs/strDiffToHtml.mjs.html +1 -1
  456. package/docs/strFindFuzz.mjs.html +1 -1
  457. package/docs/strFindSimilar.mjs.html +1 -1
  458. package/docs/strTruncate.mjs.html +1 -1
  459. package/docs/strdelleft.mjs.html +1 -1
  460. package/docs/strdelright.mjs.html +1 -1
  461. package/docs/strleft.mjs.html +1 -1
  462. package/docs/strmid.mjs.html +1 -1
  463. package/docs/strright.mjs.html +1 -1
  464. package/docs/stru8arr2obj.mjs.html +1 -1
  465. package/docs/tand.mjs.html +1 -1
  466. package/docs/time2day.mjs.html +1 -1
  467. package/docs/time2hour.mjs.html +1 -1
  468. package/docs/time2min.mjs.html +1 -1
  469. package/docs/timeTZ2day.mjs.html +1 -1
  470. package/docs/timeTZ2expire.mjs.html +1 -1
  471. package/docs/timeTZ2hour.mjs.html +1 -1
  472. package/docs/timeTZ2min.mjs.html +1 -1
  473. package/docs/timeTZ2past.mjs.html +1 -1
  474. package/docs/timems2day.mjs.html +1 -1
  475. package/docs/timems2hour.mjs.html +1 -1
  476. package/docs/timems2min.mjs.html +1 -1
  477. package/docs/timems2time.mjs.html +1 -1
  478. package/docs/timemsTZ2day.mjs.html +1 -1
  479. package/docs/timemsTZ2expire.mjs.html +1 -1
  480. package/docs/timemsTZ2hour.mjs.html +1 -1
  481. package/docs/timemsTZ2min.mjs.html +1 -1
  482. package/docs/timemsTZ2past.mjs.html +1 -1
  483. package/docs/timemsTZ2time.mjs.html +1 -1
  484. package/docs/treeObj.mjs.html +7 -7
  485. package/docs/treeToFilepath.mjs.html +1 -1
  486. package/docs/trim.mjs.html +1 -1
  487. package/docs/u16arr2b64.mjs.html +1 -1
  488. package/docs/u16arr2u8arr.mjs.html +1 -1
  489. package/docs/u8arr2ab.mjs.html +1 -1
  490. package/docs/u8arr2b64.mjs.html +1 -1
  491. package/docs/u8arr2blob.mjs.html +1 -1
  492. package/docs/u8arr2bs.mjs.html +1 -1
  493. package/docs/u8arr2obj.mjs.html +17 -3
  494. package/docs/u8arr2str.mjs.html +1 -1
  495. package/docs/u8arr2u16arr.mjs.html +1 -1
  496. package/docs/urlParse.mjs.html +1 -1
  497. package/docs/verifyValue.mjs.html +1 -1
  498. package/docs/waitFun.mjs.html +1 -1
  499. package/docs/wsemi.html +36 -13
  500. package/package.json +1 -1
  501. package/src/clearXSS.mjs +262 -24
  502. package/src/flattenToConn.mjs +11 -7
  503. package/src/flattenTree.mjs +8 -8
  504. package/src/fsTask.mjs +13 -12
  505. package/src/getltdtkeys.mjs +6 -5
  506. package/src/treeObj.mjs +6 -6
  507. package/src/u8arr2obj.mjs +16 -2
  508. package/test/clearXSS.test.mjs +1276 -1
  509. package/test/flattenToConn.test.mjs +83 -0
  510. package/test/flattenTree.test.mjs +49 -0
  511. package/test/fsTask.test.mjs +144 -0
  512. package/test/getltdtkeys.test.mjs +71 -0
  513. package/test/tools/clearXssWalkRunner.mjs +202 -0
  514. package/test/treeObj.test.mjs +119 -0
  515. package/test/u8arr2obj.test.mjs +107 -2
  516. package/toolfp/dist/wsemip.umd.js +1 -1
  517. package/toolparfor/dist/wpf.umd.js +1 -1
  518. package/toolparfor/dist/wpf_pmSeries.umd.js +1 -1
@@ -2,9 +2,11 @@ import assert from 'assert'
2
2
  import xss from 'xss'
3
3
  import { fileURLToPath } from 'url'
4
4
  import { dirname, join } from 'path'
5
+ import vm from 'vm'
5
6
  import each from 'lodash-es/each.js'
6
7
  import cloneDeep from 'lodash-es/cloneDeep.js'
7
8
  import clearXSS from '../src/clearXSS.mjs'
9
+ import fxss from '../src/_filterXSS.mjs'
8
10
  import isarr from '../src/isarr.mjs'
9
11
  import isobj from '../src/isobj.mjs'
10
12
  import isestr from '../src/isestr.mjs'
@@ -333,6 +335,1279 @@ describe(`clearXSS`, function() {
333
335
 
334
336
  })
335
337
 
338
+ describe(`走訪結構: 深度、共用、循環、容器型別(R-XSS-WALK-01)`, function() {
339
+
340
+ let x = '<script>alert(1)</script>'
341
+ let fx = '&lt;script>alert(1)&lt;/script>'
342
+ //非冪等字串(R-XSS-WALK-01已知限制): 清一次得nfx, 再清一次得nfx2
343
+ let nx = `<b='<<>`
344
+ let nfx = `&lt;b='<<>`
345
+ let nfx2 = `&lt;b='&lt;<>`
346
+
347
+ //以資料屬性設定自有鍵(含__proto__, 一般賦值會改原型)
348
+ let setOwn = (o, k, v) => {
349
+ Object.defineProperty(o, k, { value: v, writable: true, enumerable: true, configurable: true })
350
+ return o
351
+ }
352
+
353
+ //鍵順序與值皆比對(deepStrictEqual不比鍵順序)
354
+ let same = (r, e) => {
355
+ assert.strict.deepStrictEqual(r, e)
356
+ assert.strict.deepStrictEqual(JSON.stringify(r), JSON.stringify(e))
357
+ }
358
+
359
+ //計filterXSS之取得與呼叫次數(fxss為可變單例, clearXSS每次呼叫經fxss.getFilterXSS取得), 僅同步函數
360
+ let spy = (fun) => {
361
+ let n = { gets: 0, calls: 0 }
362
+ let orig = fxss.getFilterXSS
363
+ fxss.getFilterXSS = (...args) => {
364
+ n.gets++
365
+ let f = orig(...args)
366
+ let w = (c, o) => {
367
+ n.calls++
368
+ return f(c, o)
369
+ }
370
+ w.escapeAttrValue = f.escapeAttrValue
371
+ return w
372
+ }
373
+ try {
374
+ fun()
375
+ }
376
+ finally {
377
+ fxss.getFilterXSS = orig
378
+ }
379
+ return n
380
+ }
381
+
382
+ //修前之演算法(HEAD: cloneDeep後遞迴), 僅用於淺樹之差分; 字串路徑經現行clearXSS(字串路徑不受走訪重寫影響)
383
+ let headClearXSS = (inp) => {
384
+ let sc = (o) => {
385
+ if (Array.isArray(o)) {
386
+ for (let i = 0; i < o.length; i++) {
387
+ o[i] = sc(o[i])
388
+ }
389
+ return o
390
+ }
391
+ else if (isobj(o) || isarr(o)) {
392
+ for (let k of Object.keys(o)) {
393
+ o[k] = sc(o[k])
394
+ }
395
+ return o
396
+ }
397
+ else if (isestr(o)) {
398
+ return clearXSS(o)
399
+ }
400
+ return o
401
+ }
402
+ return sc(cloneDeep(inp))
403
+ }
404
+
405
+ //迭代建構與迭代取葉(深物件不可用遞迴或assert.deepStrictEqual: 失敗時差異輸出會逐層展開而溢位)
406
+ let wrap = (kind, i, cur) => {
407
+ if (kind === 'obj') {
408
+ return { a: cur }
409
+ }
410
+ if (kind === 'arr') {
411
+ return [cur]
412
+ }
413
+ if (kind === 'map') {
414
+ return new Map([['k', cur]])
415
+ }
416
+ if (kind === 'set') {
417
+ return new Set([cur])
418
+ }
419
+ return (i % 2 === 0) ? { a: cur } : [cur] //mixed
420
+ }
421
+ let child = (v) => {
422
+ if (Array.isArray(v)) {
423
+ return v[0]
424
+ }
425
+ if (v instanceof Map) {
426
+ return v.get('k')
427
+ }
428
+ if (v instanceof Set) {
429
+ return [...v][0]
430
+ }
431
+ return v.a
432
+ }
433
+ let build = (kind, n) => {
434
+ let cur = x
435
+ for (let i = 0; i < n; i++) {
436
+ cur = wrap(kind, i, cur)
437
+ }
438
+ return cur
439
+ }
440
+ let leafOf = (v) => {
441
+ let d = 0
442
+ while (typeof v === 'object' && v !== null) {
443
+ v = child(v)
444
+ d++
445
+ }
446
+ return { v, d }
447
+ }
448
+
449
+ //收集一棵樹內所有可走訪容器(物件、陣列、Map、Set)之參照, 迭代
450
+ let containersOf = (root) => {
451
+ let seen = new Set()
452
+ let st = [root]
453
+ while (st.length > 0) {
454
+ let v = st.pop()
455
+ if (typeof v !== 'object' || v === null || seen.has(v)) {
456
+ continue
457
+ }
458
+ if (!Array.isArray(v) && !(v instanceof Map) && !(v instanceof Set) && Object.prototype.toString.call(v) !== '[object Object]') {
459
+ continue
460
+ }
461
+ seen.add(v)
462
+ if (v instanceof Map) {
463
+ for (let [, val] of v) {
464
+ st.push(val)
465
+ }
466
+ }
467
+ else if (v instanceof Set) {
468
+ for (let val of v) {
469
+ st.push(val)
470
+ }
471
+ }
472
+ else {
473
+ for (let k of Reflect.ownKeys(v)) {
474
+ let d = Object.getOwnPropertyDescriptor(v, k)
475
+ if (d.enumerable && 'value' in d) {
476
+ st.push(d.value)
477
+ }
478
+ }
479
+ }
480
+ }
481
+ return seen
482
+ }
483
+
484
+ describe(`JSON可產生之輸入: 與修前演算法逐位元相同(行為鎖定)`, function() {
485
+
486
+ let seed = 20261020
487
+ let rnd = () => {
488
+ seed = (seed * 1103515245 + 12345) % 2147483648
489
+ return seed / 2147483648
490
+ }
491
+ let pick = (arr) => arr[Math.floor(rnd() * arr.length)]
492
+ let strs = [x, 'plain', '', ' ', 'a < b', '<img src=x onerror=alert(1)>', '<b>粗體</b>', '孔深 < 500 m', 'x<', '<a href="javascript:alert(1)">x</a>', nx, '<<<i>', '&lt;x&gt;', '<!-- c -->', '<div style="color:red">x</div>']
493
+ let keyNames = ['a', 'b', 'c', '0', '1', '10', 'length', 'constructor', 'toString', '__proto__', 'hasOwnProperty', 'x y', '中文', x]
494
+ let lenVals = [-1, 1.5, '3', null, true, NaN, Infinity, {}, [], x, undefined, 0, 2, -0]
495
+
496
+ let gen = (depth) => {
497
+ let t = rnd()
498
+ if (depth <= 0 || t < 0.3) {
499
+ return pick([...strs, 1, 0, -5, 1.5, true, false, null, -0, 1e21])
500
+ }
501
+ if (t < 0.6) {
502
+ let n = Math.floor(rnd() * 5)
503
+ let a = []
504
+ for (let i = 0; i < n; i++) {
505
+ a.push(gen(depth - 1))
506
+ }
507
+ return a
508
+ }
509
+ let o = {}
510
+ let n = Math.floor(rnd() * 5)
511
+ for (let i = 0; i < n; i++) {
512
+ setOwn(o, pick(keyNames), gen(depth - 1))
513
+ }
514
+ if (rnd() < 0.15) {
515
+ setOwn(o, 'length', pick(lenVals))
516
+ }
517
+ return o
518
+ }
519
+
520
+ //兩個案例共用同一批樹(各自再抽會推進種子而取得不同批)
521
+ let N = 3000
522
+ let trees = Array.from({ length: N }, () => gen(6))
523
+
524
+ it(`should equal the head algorithm on ${N} random JSON-like trees (values, key order, prototypes)`, function() {
525
+ let bad = []
526
+ for (let i = 0; i < N; i++) {
527
+ let t = trees[i]
528
+ try {
529
+ same(clearXSS(t), headClearXSS(t))
530
+ }
531
+ catch (err) {
532
+ bad.push({ i })
533
+ }
534
+ }
535
+ assert.strict.deepStrictEqual(bad, [])
536
+ })
537
+
538
+ it(`should not change the input on the same trees`, function() {
539
+ let bad = []
540
+ for (let i = 0; i < N; i++) {
541
+ let t = trees[i]
542
+ let before = cloneDeep(t)
543
+ clearXSS(t)
544
+ try {
545
+ same(t, before)
546
+ }
547
+ catch (err) {
548
+ bad.push({ i })
549
+ }
550
+ }
551
+ assert.strict.deepStrictEqual(bad, [])
552
+ })
553
+
554
+ it(`should return containers that are not shared with the input`, function() {
555
+ let bad = []
556
+ for (let i = 0; i < 600; i++) {
557
+ let t = trees[i]
558
+ let a = containersOf(t)
559
+ for (let c of containersOf(clearXSS(t))) {
560
+ if (a.has(c)) {
561
+ bad.push({ i })
562
+ break
563
+ }
564
+ }
565
+ }
566
+ assert.strict.deepStrictEqual(bad, [])
567
+ })
568
+
569
+ it(`should clean every string value of nested objects and arrays in order and keep the other values`, function() {
570
+ let inp = { a: x, b: [x, 1, null, true, '', { c: x }], d: { e: { f: [x] } }, g: undefined, h: 0, i: -0, j: 1.5, k: ' ' }
571
+ let r = clearXSS(inp)
572
+ same(r, { a: fx, b: [fx, 1, null, true, '', { c: fx }], d: { e: { f: [fx] } }, g: undefined, h: 0, i: -0, j: 1.5, k: ' ' })
573
+ assert.strict.strictEqual(Object.is(r.i, -0), true)
574
+ assert.strict.strictEqual(Object.getPrototypeOf(r), Object.prototype)
575
+ assert.strict.strictEqual(inp.a, x)
576
+ assert.strict.strictEqual(inp.b[5].c, x)
577
+ })
578
+
579
+ it(`should clean values and keep keys of an object with an own __proto__ key, length key and numeric keys`, function() {
580
+ let t = JSON.parse('{"__proto__":{"a":"<script>alert(1)</script>"},"length":2,"0":"<script>alert(1)</script>","constructor":"<script>alert(1)</script>"}')
581
+ let r = clearXSS(t)
582
+ assert.strict.strictEqual(Object.getPrototypeOf(r), Object.prototype)
583
+ assert.strict.deepStrictEqual(Object.keys(r), ['0', '__proto__', 'length', 'constructor'])
584
+ assert.strict.strictEqual(Object.getOwnPropertyDescriptor(r, '__proto__').value.a, fx)
585
+ assert.strict.strictEqual(r[0], fx)
586
+ assert.strict.strictEqual(r.length, 2)
587
+ assert.strict.strictEqual(r.constructor, fx)
588
+ assert.strict.strictEqual(({}).a, undefined)
589
+ })
590
+
591
+ })
592
+
593
+ describe(`深巢狀: 不受呼叫堆疊限制(修前冷行程約1767層即同步拋RangeError)`, function() {
594
+
595
+ for (let kind of ['obj', 'arr', 'mixed']) {
596
+ for (let n of [10000, 300000]) {
597
+ it(`should clean the leaf of ${n} nested ${kind} levels without throwing, keep the depth and not change the input`, function() {
598
+ let inp = build(kind, n)
599
+ let r = clearXSS(inp)
600
+ let lo = leafOf(r)
601
+ let li = leafOf(inp)
602
+ assert.ok(lo.v === fx, 'leaf not cleaned')
603
+ assert.ok(li.v === x, 'input changed')
604
+ assert.ok(lo.d === n && li.d === n, `depth out=${lo.d} in=${li.d}`)
605
+ assert.ok(r !== inp)
606
+ })
607
+ }
608
+ }
609
+
610
+ for (let kind of ['map', 'set']) {
611
+ it(`should clean the leaf of 100000 nested ${kind} levels without throwing`, function() {
612
+ let inp = build(kind, 100000)
613
+ let r = clearXSS(inp)
614
+ let lo = leafOf(r)
615
+ assert.ok(lo.v === fx, 'leaf not cleaned')
616
+ assert.ok(lo.d === 100000, `depth out=${lo.d}`)
617
+ assert.ok(leafOf(inp).v === x, 'input changed')
618
+ })
619
+ }
620
+
621
+ it(`should clean strings at every level of a deep mixed chain`, function() {
622
+ let n = 20000
623
+ let cur = { s: x }
624
+ for (let i = 0; i < n; i++) {
625
+ cur = { s: x, a: [cur] }
626
+ }
627
+ let r = clearXSS(cur)
628
+ let c = 0
629
+ let v = r
630
+ while (v !== undefined) {
631
+ assert.ok(v.s === fx, `level ${c} not cleaned`)
632
+ v = v.a ? v.a[0] : undefined
633
+ c++
634
+ }
635
+ assert.strict.strictEqual(c, n + 1)
636
+ })
637
+
638
+ })
639
+
640
+ describe(`共用子結構(DAG): 同一來源物件只處理一次, 輸出保持共用`, function() {
641
+
642
+ it(`should call filterXSS once for 20 levels of objects whose two properties refer to the same child (2^20 calls before)`, function() {
643
+ let cur = { v: x }
644
+ for (let i = 0; i < 20; i++) {
645
+ cur = { l: cur, r: cur }
646
+ }
647
+ let r
648
+ let n = spy(() => {
649
+ r = clearXSS(cur)
650
+ })
651
+ assert.strict.strictEqual(n.calls, 1)
652
+ let v = r
653
+ for (let i = 0; i < 20; i++) {
654
+ assert.ok(v.l === v.r, `level ${i} not shared`)
655
+ v = v.l
656
+ }
657
+ assert.strict.strictEqual(v.v, fx)
658
+ })
659
+
660
+ it(`should call filterXSS once for 20 levels of arrays, Maps, Sets and mixed containers sharing the same child`, function() {
661
+ let mk = {
662
+ arr: (cur) => [cur, cur],
663
+ map: (cur) => new Map([['l', cur], ['r', cur]]),
664
+ set: (cur) => new Set([{ p: cur }, { q: cur }]),
665
+ mixed: (cur) => ({ l: [cur, cur], r: cur }),
666
+ }
667
+ for (let [name, f] of Object.entries(mk)) {
668
+ let cur = name === 'map' ? new Map([['v', x]]) : name === 'set' ? new Set([x]) : [x]
669
+ for (let i = 0; i < 20; i++) {
670
+ cur = f(cur)
671
+ }
672
+ let n = spy(() => {
673
+ clearXSS(cur)
674
+ })
675
+ assert.strict.strictEqual(n.calls, 1, name)
676
+ }
677
+ })
678
+
679
+ it(`should keep one output object for an input object referred to by many parents and keep distinct inputs distinct`, function() {
680
+ let o = { t: x }
681
+ let o2 = { t: x } //內容相同但為另一個物件
682
+ let r = clearXSS({ a: o, b: o, c: [o, o], d: o2 })
683
+ assert.ok(r.a === r.b && r.b === r.c[0] && r.c[0] === r.c[1])
684
+ assert.ok(r.d !== r.a)
685
+ assert.strict.strictEqual(r.a.t, fx)
686
+ assert.strict.strictEqual(r.d.t, fx)
687
+ assert.ok(r.a !== o)
688
+ })
689
+
690
+ it(`should keep a shared Date, Map and Set shared in the output as copies of the input`, function() {
691
+ let d = new Date(1234567)
692
+ let m = new Map([['k', x]])
693
+ let s = new Set([x])
694
+ let r = clearXSS({ d1: d, d2: d, m1: m, m2: m, s1: s, s2: s })
695
+ assert.ok(r.d1 === r.d2 && r.d1 !== d && r.d1.getTime() === 1234567)
696
+ assert.ok(r.m1 === r.m2 && r.m1 !== m && r.m1.get('k') === fx)
697
+ assert.ok(r.s1 === r.s2 && r.s1 !== s && r.s1.has(fx))
698
+ })
699
+
700
+ it(`should clean a string in a shared object once: the result equals the one of the same tree without sharing (the cleaning is not idempotent)`, function() {
701
+ //前提: 清兩次與清一次不同
702
+ assert.strict.strictEqual(clearXSS(nx), nfx)
703
+ assert.strict.strictEqual(clearXSS(clearXSS(nx)), nfx2)
704
+ let o = { t: nx }
705
+ let r = clearXSS({ a: o, b: o })
706
+ assert.strict.strictEqual(r.a.t, nfx)
707
+ let r4 = clearXSS([o, o, o, o])
708
+ assert.strict.deepStrictEqual(r4.map((v) => v.t), [nfx, nfx, nfx, nfx])
709
+ let tree = clearXSS({ a: { t: nx }, b: { t: nx } })
710
+ same(r, tree)
711
+ })
712
+
713
+ })
714
+
715
+ describe(`循環參照: 不拋, 保持循環, 字串已清除, 不改輸入`, function() {
716
+
717
+ it(`should handle an object that refers to itself`, function() {
718
+ let o = { s: x }
719
+ o.self = o
720
+ let r = clearXSS(o)
721
+ assert.ok(r !== o && r.self === r)
722
+ assert.strict.strictEqual(r.s, fx)
723
+ assert.ok(o.self === o && o.s === x)
724
+ })
725
+
726
+ it(`should handle an array that contains itself`, function() {
727
+ let a = [x]
728
+ a.push(a)
729
+ let r = clearXSS(a)
730
+ assert.ok(r !== a && r[1] === r && r.length === 2)
731
+ assert.strict.strictEqual(r[0], fx)
732
+ assert.ok(a[1] === a && a[0] === x)
733
+ })
734
+
735
+ it(`should handle two objects that refer to each other and keep both strings cleaned`, function() {
736
+ let a = { s: x }
737
+ let b = { s: x, a }
738
+ a.b = b
739
+ let r = clearXSS({ a, b })
740
+ assert.ok(r.a.b === r.b && r.b.a === r.a)
741
+ assert.strict.strictEqual(r.a.s, fx)
742
+ assert.strict.strictEqual(r.b.s, fx)
743
+ })
744
+
745
+ it(`should handle a Map, a Set and an object that refer to each other`, function() {
746
+ let m = new Map()
747
+ m.set('self', m)
748
+ m.set('s', x)
749
+ let rm = clearXSS(m)
750
+ assert.ok(rm !== m && rm.get('self') === rm && rm.get('s') === fx)
751
+
752
+ let st = new Set()
753
+ st.add(st)
754
+ st.add(x)
755
+ let rs = clearXSS(st)
756
+ assert.ok(rs !== st && rs.has(rs) && rs.has(fx) && rs.size === 2)
757
+
758
+ let o = { s: x }
759
+ let mm = new Map([['o', o]])
760
+ o.m = mm
761
+ let ro = clearXSS(o)
762
+ assert.ok(ro.m.get('o') === ro && ro.s === fx)
763
+ })
764
+
765
+ it(`should handle a cycle through a Symbol-keyed property`, function() {
766
+ let sym = Symbol('c')
767
+ let o = { s: x }
768
+ o[sym] = o
769
+ let r = clearXSS(o)
770
+ assert.ok(r[sym] === r && r.s === fx)
771
+ })
772
+
773
+ it(`should handle a cycle of 200000 nodes`, function() {
774
+ let head = { i: 0, s: x }
775
+ let cur = head
776
+ for (let i = 1; i < 200000; i++) {
777
+ cur.next = { i, s: x }
778
+ cur = cur.next
779
+ }
780
+ cur.next = head
781
+ let r = clearXSS(head)
782
+ let v = r
783
+ for (let i = 0; i < 200000; i++) {
784
+ assert.ok(v.i === i && v.s === fx, `node ${i}`)
785
+ v = v.next
786
+ }
787
+ assert.ok(v === r)
788
+ })
789
+
790
+ })
791
+
792
+ describe(`型別處置表(根/物件內/陣列內)`, function() {
793
+
794
+ //三個位置: 根、物件內、陣列內; chk(out, inp)依型別斷言
795
+ let positions = [
796
+ ['root', (v) => v, (o) => o],
797
+ ['in object', (v) => ({ k: v }), (o) => o.k],
798
+ ['in array', (v) => [v], (o) => o[0]],
799
+ ]
800
+
801
+ describe(`原始值與包裝字串`, function() {
802
+ let prims = {
803
+ 'number': 12.5,
804
+ 'not a number': NaN,
805
+ '-0': -0,
806
+ 'positive infinity': Infinity,
807
+ 'bigint': 10n,
808
+ 'true': true,
809
+ 'false': false,
810
+ 'null': null,
811
+ 'undefined value': undefined,
812
+ 'empty string': '',
813
+ 'blank string': ' ',
814
+ }
815
+ for (let [name, v] of Object.entries(prims)) {
816
+ for (let [pos, put, get] of positions) {
817
+ it(`should return the value as it is: ${name} ${pos}`, function() {
818
+ assert.ok(Object.is(get(clearXSS(put(v))), v))
819
+ })
820
+ }
821
+ }
822
+
823
+ it(`should return the same symbol`, function() {
824
+ let sym = Symbol('s')
825
+ assert.ok(clearXSS(sym) === sym)
826
+ assert.ok(clearXSS({ k: sym }).k === sym)
827
+ })
828
+
829
+ for (let [pos, put, get] of positions) {
830
+ it(`should return the cleaned primitive string for a String object ${pos}`, function() {
831
+ let r = get(clearXSS(put(new String(x)))) // eslint-disable-line no-new-wrappers
832
+ assert.strict.strictEqual(typeof r, 'string')
833
+ assert.strict.strictEqual(r, fx)
834
+ })
835
+ it(`should return a String object copy for an empty String object, which is not processed as before ${pos}`, function() {
836
+ let r = get(clearXSS(put(new String('')))) // eslint-disable-line no-new-wrappers
837
+ assert.strict.strictEqual(r instanceof String, true) //空字串不處理, 原樣(同修前)
838
+ })
839
+ }
840
+ })
841
+
842
+ describe(`複製且無字串者: Date、RegExp、型別陣列、Buffer、ArrayBuffer、包裝Number`, function() {
843
+ let mks = {
844
+ 'Date': [() => new Date(7), (a, b) => a.getTime() === b.getTime()],
845
+ 'RegExp': [() => /a/gi, (a, b) => String(a) === String(b)],
846
+ 'Uint8Array': [() => new Uint8Array([1, 2, 3]), (a, b) => a instanceof Uint8Array && Buffer.compare(Buffer.from(a), Buffer.from(b)) === 0],
847
+ 'Buffer': [() => Buffer.from('ab'), (a, b) => Buffer.isBuffer(a) && a.toString() === b.toString()],
848
+ 'ArrayBuffer': [() => new Uint8Array([4, 5]).buffer, (a, b) => a instanceof ArrayBuffer && a.byteLength === 2 && new Uint8Array(a)[1] === 5],
849
+ 'Number object': [() => new Number(3), (a, b) => a instanceof Number && Number(a) === 3], // eslint-disable-line no-new-wrappers
850
+ }
851
+ for (let [name, [mk, eq]] of Object.entries(mks)) {
852
+ for (let [pos, put, get] of positions) {
853
+ it(`should return a copy with the same content: ${name} ${pos}`, function() {
854
+ let v = mk()
855
+ let r = get(clearXSS(put(v)))
856
+ assert.ok(r !== v, 'same reference')
857
+ assert.ok(eq(r, v), 'content differs')
858
+ })
859
+ }
860
+ }
861
+ })
862
+
863
+ describe(`不可複製者: 巢狀回同一參照且內部不清除, 根沿用cloneDeep頂層語意`, function() {
864
+ it(`should return the same Error object when nested and not clean its message and properties`, function() {
865
+ let e = new Error(x)
866
+ e.extra = x
867
+ let r = clearXSS({ e, a: [e] })
868
+ assert.ok(r.e === e && r.a[0] === e)
869
+ assert.strict.strictEqual(e.message, x)
870
+ assert.strict.strictEqual(e.extra, x)
871
+ })
872
+ it(`should return {} for an Error object at the root`, function() {
873
+ same(clearXSS(new Error(x)), {})
874
+ })
875
+ for (let [name, mk] of Object.entries({ 'WeakMap': () => new WeakMap(), 'Promise': () => Promise.resolve(1), 'URL': () => new URL('http://a/b') })) {
876
+ it(`should return the same ${name} when nested and {} at the root`, function() {
877
+ let v = mk()
878
+ let r = clearXSS({ v, a: [v] })
879
+ assert.ok(r.v === v && r.a[0] === v)
880
+ same(clearXSS(v), {})
881
+ })
882
+ }
883
+ it(`should return the same function when nested and not clean its properties`, function() {
884
+ let f = () => 1
885
+ f.s = x
886
+ let r = clearXSS({ f, a: [f] })
887
+ assert.ok(r.f === f && r.a[0] === f)
888
+ assert.strict.strictEqual(f.s, x)
889
+ })
890
+ it(`should return a plain object copy with cleaned enumerable properties for a function at the root`, function() {
891
+ let f = () => 1
892
+ f.s = x
893
+ let r = clearXSS(f)
894
+ assert.strict.strictEqual(typeof r, 'object')
895
+ assert.strict.strictEqual(r.s, fx)
896
+ assert.strict.strictEqual(f.s, x)
897
+ })
898
+ })
899
+
900
+ describe(`物件形狀`, function() {
901
+ class Foo {
902
+ constructor() {
903
+ this.s = x
904
+ this.n = 1
905
+ }
906
+
907
+ hello() {
908
+ return 'hi'
909
+ }
910
+ }
911
+ for (let [pos, put, get] of positions) {
912
+ it(`should keep the prototype of a class instance and clean its own strings ${pos}`, function() {
913
+ let f = new Foo()
914
+ let r = get(clearXSS(put(f)))
915
+ assert.ok(r !== f && r instanceof Foo && r.hello() === 'hi')
916
+ assert.strict.strictEqual(r.s, fx)
917
+ assert.strict.strictEqual(r.n, 1)
918
+ assert.strict.strictEqual(f.s, x)
919
+ })
920
+ }
921
+ it(`should return an ordinary object for an object that is the prototype of its own constructor, as lodash does`, function() {
922
+ class A {}
923
+
924
+ class B extends A {}
925
+ B.prototype.s = x
926
+ let r = clearXSS({ p: B.prototype }).p
927
+ assert.strict.strictEqual(Object.getPrototypeOf(r), Object.prototype)
928
+ assert.strict.strictEqual(r.s, fx)
929
+ assert.strict.strictEqual(B.prototype.s, x)
930
+ })
931
+ it(`should turn an object without prototype into an ordinary object`, function() {
932
+ let o = Object.create(null)
933
+ o.s = x
934
+ let r = clearXSS({ o }).o
935
+ assert.strict.strictEqual(Object.getPrototypeOf(r), Object.prototype)
936
+ assert.strict.strictEqual(r.s, fx)
937
+ })
938
+ it(`should keep a function-valued own constructor key by reference and an ordinary prototype`, function() {
939
+ let Z = function() {}
940
+ let r = clearXSS({ o: { constructor: Z, a: x } }).o
941
+ assert.ok(r.constructor === Z)
942
+ assert.strict.strictEqual(r.a, fx)
943
+ assert.strict.strictEqual(Object.getPrototypeOf(r), Object.prototype)
944
+ })
945
+ it(`should clean a string-valued own constructor key`, function() {
946
+ let r = clearXSS(JSON.parse('{"constructor":"<script>alert(1)</script>","a":1}'))
947
+ assert.strict.strictEqual(r.constructor, fx)
948
+ assert.strict.strictEqual(Object.getPrototypeOf(r), Object.prototype)
949
+ })
950
+ it(`should clean the values of own enumerable Symbol-keyed properties, strings and nested objects`, function() {
951
+ let sym = Symbol('k')
952
+ let r = clearXSS({ [sym]: x, s: x, t: { [sym]: { u: x } } })
953
+ assert.strict.strictEqual(r[sym], fx)
954
+ assert.strict.strictEqual(r.s, fx)
955
+ assert.strict.strictEqual(r.t[sym].u, fx)
956
+ })
957
+ it(`should not copy a non-enumerable Symbol-keyed or string-keyed property`, function() {
958
+ let sym = Symbol('k')
959
+ let o = { s: x }
960
+ Object.defineProperty(o, sym, { value: x, enumerable: false })
961
+ Object.defineProperty(o, 'h', { value: x, enumerable: false })
962
+ let r = clearXSS(o)
963
+ assert.strict.deepStrictEqual(Reflect.ownKeys(r), ['s'])
964
+ })
965
+ it(`should read an accessor once and return a data property with the cleaned value`, function() {
966
+ let n = 0
967
+ let o = {}
968
+ Object.defineProperty(o, 's', {
969
+ get() {
970
+ n++
971
+ return x
972
+ },
973
+ enumerable: true,
974
+ })
975
+ let r = clearXSS(o)
976
+ let d = Object.getOwnPropertyDescriptor(r, 's')
977
+ assert.strict.strictEqual(d.value, fx)
978
+ assert.strict.strictEqual(d.get, undefined)
979
+ assert.strict.strictEqual(n, 1)
980
+ })
981
+ it(`should keep the prototype but not clean nor copy inherited properties`, function() {
982
+ let p = { s: x }
983
+ let o = Object.create(p)
984
+ o.own = x
985
+ let r = clearXSS(o)
986
+ assert.strict.strictEqual(Object.getPrototypeOf(r), p)
987
+ assert.strict.strictEqual(r.own, fx)
988
+ assert.strict.deepStrictEqual(Object.keys(r), ['own'])
989
+ assert.strict.strictEqual(p.s, x)
990
+ })
991
+ it(`should return an unfrozen copy of a frozen object`, function() {
992
+ let o = Object.freeze({ s: x, a: Object.freeze([x]) })
993
+ let r = clearXSS(o)
994
+ assert.strict.strictEqual(Object.isFrozen(r), false)
995
+ same(r, { s: fx, a: [fx] })
996
+ assert.strict.strictEqual(o.s, x)
997
+ })
998
+ it(`should clean through a Proxy of a plain object`, function() {
999
+ let r = clearXSS({ p: new Proxy({ s: x }, {}) })
1000
+ assert.strict.strictEqual(r.p.s, fx)
1001
+ })
1002
+ it(`should turn an arguments object into an ordinary object`, function() {
1003
+ let args = (function() {
1004
+ return arguments
1005
+ })(x, 7)
1006
+ let r = clearXSS({ a: args }).a
1007
+ assert.strict.strictEqual(Object.getPrototypeOf(r), Object.prototype)
1008
+ same(r, { 0: fx, 1: 7 })
1009
+ })
1010
+ })
1011
+
1012
+ describe(`陣列形狀`, function() {
1013
+ it(`should drop non-index properties and fill holes with undefined`, function() {
1014
+ let a = [x]
1015
+ a.extra = x
1016
+ a[3] = x
1017
+ let r = clearXSS(a)
1018
+ assert.strict.strictEqual(r.length, 4)
1019
+ assert.strict.deepStrictEqual(Object.keys(r), ['0', '1', '2', '3'])
1020
+ assert.strict.strictEqual(r[0], fx)
1021
+ assert.strict.strictEqual(r[1], undefined)
1022
+ assert.strict.strictEqual(r[3], fx)
1023
+ assert.strict.strictEqual(r.extra, undefined)
1024
+ })
1025
+ it(`should keep the Array subclass`, function() {
1026
+ class Arr2 extends Array {}
1027
+ let a = Arr2.from([x, 'b'])
1028
+ let r = clearXSS({ a }).a
1029
+ assert.ok(r instanceof Arr2 && r !== a && r.length === 2)
1030
+ assert.strict.strictEqual(r[0], fx)
1031
+ })
1032
+ })
1033
+
1034
+ describe(`偽稱型別之物件(Symbol.toStringTag)`, function() {
1035
+ it(`should walk an object that fakes Array with a writable tag by its own keys and keep length as an ordinary key`, function() {
1036
+ let o = { a: x, length: 2, [Symbol.toStringTag]: 'Array' }
1037
+ let r = clearXSS({ c: o }).c
1038
+ assert.ok(!Array.isArray(r))
1039
+ assert.strict.strictEqual(r.a, fx)
1040
+ assert.strict.strictEqual(r.length, 2)
1041
+ })
1042
+ it(`should walk an object that fakes Array with a non-writable tag without throwing (TypeError before)`, function() {
1043
+ let o = Object.defineProperty({ a: x, length: 2 }, Symbol.toStringTag, { value: 'Array' })
1044
+ let r = clearXSS({ c: o }).c
1045
+ assert.ok(!Array.isArray(r))
1046
+ assert.strict.strictEqual(r.a, fx)
1047
+ assert.strict.strictEqual(r.length, 2)
1048
+ assert.strict.strictEqual(o.a, x)
1049
+ })
1050
+ it(`should walk an object that fakes Arguments with a non-writable tag by its own keys (lodash keys would give index keys and drop length)`, function() {
1051
+ let o = Object.defineProperty({ a: x, length: 2 }, Symbol.toStringTag, { value: 'Arguments' })
1052
+ let r = clearXSS({ c: o }).c
1053
+ assert.strict.strictEqual(Object.getPrototypeOf(r), Object.prototype)
1054
+ same(r, { a: fx, length: 2 })
1055
+ assert.strict.strictEqual(o.a, x)
1056
+ })
1057
+ it(`should copy an object with an enumerable custom tag; its strings are not guaranteed to be cleaned (known limit, same as before)`, function() {
1058
+ let o = { s: x, [Symbol.toStringTag]: 'Foo' }
1059
+ let r = clearXSS({ c: o }).c
1060
+ assert.ok(r !== o)
1061
+ assert.strict.strictEqual(o.s, x)
1062
+ assert.ok(r.s === x || r.s === fx, 'unexpected value')
1063
+ })
1064
+ })
1065
+
1066
+ describe(`Map與Set`, function() {
1067
+ it(`should clean Map values and Set elements, including nested objects, and keep Map keys as they are`, function() {
1068
+ let m = new Map([['k', x], [x, 1], ['o', { s: x }], ['a', [x]]])
1069
+ let r = clearXSS({ m })
1070
+ assert.ok(r.m instanceof Map && r.m !== m)
1071
+ assert.strict.deepStrictEqual([...r.m.keys()], ['k', x, 'o', 'a'])
1072
+ assert.strict.strictEqual(r.m.get('k'), fx)
1073
+ assert.strict.strictEqual(r.m.get(x), 1)
1074
+ assert.strict.strictEqual(r.m.get('o').s, fx)
1075
+ assert.strict.strictEqual(r.m.get('a')[0], fx)
1076
+ assert.strict.strictEqual(m.get('k'), x)
1077
+ let s = new Set([x, 'p', { s: x }])
1078
+ let rs = clearXSS([s])[0]
1079
+ let es = [...rs]
1080
+ assert.ok(rs instanceof Set && rs !== s && es.length === 3)
1081
+ assert.strict.strictEqual(es[0], fx)
1082
+ assert.strict.strictEqual(es[1], 'p')
1083
+ assert.strict.strictEqual(es[2].s, fx)
1084
+ assert.ok(s.has(x))
1085
+ })
1086
+ it(`should handle a Map and a Set at the root`, function() {
1087
+ assert.strict.strictEqual(clearXSS(new Map([['k', x]])).get('k'), fx)
1088
+ assert.strict.strictEqual(clearXSS(new Set([x])).has(fx), true)
1089
+ })
1090
+ it(`should keep a Map or Set subclass`, function() {
1091
+ class M2 extends Map {}
1092
+ class S2 extends Set {}
1093
+ let r = clearXSS({ m: new M2([['k', x]]), s: new S2([x]) })
1094
+ assert.ok(r.m instanceof M2 && r.m.get('k') === fx)
1095
+ assert.ok(r.s instanceof S2 && r.s.has(fx))
1096
+ })
1097
+ it(`should keep the insertion order of Map entries and Set elements`, function() {
1098
+ let m = new Map([['b', 1], ['a', 2], ['c', x]])
1099
+ assert.strict.deepStrictEqual([...clearXSS(m).keys()], ['b', 'a', 'c'])
1100
+ let s = new Set(['z', x, 'y'])
1101
+ assert.strict.deepStrictEqual([...clearXSS(s)], ['z', fx, 'y'])
1102
+ })
1103
+ })
1104
+
1105
+ describe(`鍵不清除`, function() {
1106
+ it(`should not clean string keys of objects and Maps`, function() {
1107
+ let r = clearXSS({ [x]: x, a: { [x]: x } })
1108
+ assert.strict.deepStrictEqual(Object.keys(r), [x, 'a'])
1109
+ assert.strict.strictEqual(r[x], fx)
1110
+ assert.strict.strictEqual(r.a[x], fx)
1111
+ let m = clearXSS(new Map([[x, x]]))
1112
+ assert.strict.deepStrictEqual([...m.entries()], [[x, fx]])
1113
+ })
1114
+ })
1115
+
1116
+ })
1117
+
1118
+ describe(`lodash複製後之終態、Map/Set補強、跨realm(外審補強)`, function() {
1119
+
1120
+ //三個位置: 根、物件內、陣列內
1121
+ let positions = [
1122
+ ['root', (v) => v, (o) => o],
1123
+ ['in object', (v) => ({ k: v }), (o) => o.k],
1124
+ ['in array', (v) => [v], (o) => o[0]],
1125
+ ]
1126
+
1127
+ for (let tag of ['Foo', 'String', 'Date', 'Map', 'Error', 'Object']) {
1128
+ for (let [pos, put, get] of positions) {
1129
+ it(`should clean an object with a writable, non-enumerable own custom tag ${tag} (lodash unmasks it and treats the object as ordinary) ${pos}`, function() {
1130
+ //修前: cloneDeep揭開可寫標籤並不複製不可列舉之標籤, 複本為一般物件而被清除
1131
+ let o = Object.defineProperty({ s: x, n: { s: x } }, Symbol.toStringTag, { value: tag, writable: true, enumerable: false, configurable: true })
1132
+ let r = get(clearXSS(put(o)))
1133
+ assert.strict.strictEqual(r.s, fx)
1134
+ assert.strict.strictEqual(r.n.s, fx)
1135
+ assert.strict.strictEqual(o.s, x)
1136
+ })
1137
+ }
1138
+ }
1139
+
1140
+ for (let name of ['Array', 'Arguments']) {
1141
+ for (let writable of [true, false]) {
1142
+ for (let enumerable of [true, false]) {
1143
+ for (let [pos, put, get] of positions) {
1144
+ it(`should walk an object faking ${name} (${writable ? 'writable' : 'non-writable'}, ${enumerable ? 'enumerable' : 'non-enumerable'} tag) by its own string keys, keep length as a key ${pos}`, function() {
1145
+ let o = Object.defineProperty({ a: x, length: 2 }, Symbol.toStringTag, { value: name, writable, enumerable, configurable: true })
1146
+ let r = get(clearXSS(put(o)))
1147
+ assert.ok(!Array.isArray(r))
1148
+ assert.strict.strictEqual(r.a, fx)
1149
+ assert.strict.strictEqual(r.length, 2)
1150
+ assert.strict.strictEqual(o.a, x)
1151
+ })
1152
+ }
1153
+ }
1154
+ }
1155
+ }
1156
+
1157
+ for (let [pos, put, get] of positions) {
1158
+ it(`should clean an object faking String (writable, non-enumerable tag, with length) as an ordinary object and not turn it into a string ${pos}`, function() {
1159
+ let o = Object.defineProperty({ s: x, length: 3 }, Symbol.toStringTag, { value: 'String', writable: true, enumerable: false, configurable: true })
1160
+ let r = get(clearXSS(put(o)))
1161
+ assert.strict.strictEqual(typeof r, 'object')
1162
+ assert.strict.strictEqual(r.s, fx)
1163
+ assert.strict.strictEqual(r.length, 3)
1164
+ })
1165
+ }
1166
+
1167
+ it(`should turn an object faking Array with a null prototype or a prototype-shaped object into an ordinary object`, function() {
1168
+ let np = Object.create(null)
1169
+ np.s = x
1170
+ np[Symbol.toStringTag] = 'Array'
1171
+ let r1 = clearXSS({ k: np }).k
1172
+ assert.strict.strictEqual(Object.getPrototypeOf(r1), Object.prototype)
1173
+ assert.strict.strictEqual(r1.s, fx)
1174
+
1175
+ class K {}
1176
+
1177
+ class L extends K {}
1178
+ L.prototype.s = x
1179
+ Object.defineProperty(L.prototype, Symbol.toStringTag, { value: 'Array' })
1180
+ let r2 = clearXSS({ k: L.prototype }).k
1181
+ assert.strict.strictEqual(Object.getPrototypeOf(r2), Object.prototype)
1182
+ assert.strict.strictEqual(r2.s, fx)
1183
+ })
1184
+
1185
+ it(`should keep the prototype of a class instance faking Array and define its own keys as data properties without calling a prototype setter`, function() {
1186
+ let called = 0
1187
+ class S {
1188
+ // eslint-disable-next-line accessor-pairs
1189
+ set s(v) {
1190
+ called++
1191
+ this.t = 'set:' + v
1192
+ }
1193
+ }
1194
+ let o = new S()
1195
+ Object.defineProperty(o, 's', { value: x, enumerable: true, writable: true, configurable: true })
1196
+ o[Symbol.toStringTag] = 'Array'
1197
+ let r = clearXSS({ k: o }).k
1198
+ assert.ok(r instanceof S)
1199
+ assert.strict.strictEqual(called, 0)
1200
+ assert.strict.strictEqual(Object.getOwnPropertyDescriptor(r, 's').value, fx)
1201
+ assert.strict.strictEqual(r.t, undefined)
1202
+ })
1203
+
1204
+ it(`should not clean the strings of an object with a non-writable, non-enumerable custom tag (lodash cannot unmask it, so it is returned as it is when nested)`, function() {
1205
+ let o = Object.defineProperty({ s: x }, Symbol.toStringTag, { value: 'Foo' })
1206
+ let r = clearXSS({ k: o }).k
1207
+ assert.ok(r === o)
1208
+ assert.strict.strictEqual(r.s, x)
1209
+ })
1210
+
1211
+ describe(`Date、RegExp、型別陣列、自訂標籤物件之自有物件值屬性: 與其他路徑共用同一輸出並清除(修前同一複本、已清除)`, function() {
1212
+ let mks = {
1213
+ 'Date': () => new Date(0),
1214
+ 'RegExp': () => /a/g,
1215
+ 'Uint8Array': () => new Uint8Array([1, 2]),
1216
+ 'custom tag': () => ({ [Symbol.toStringTag]: 'Foo' }),
1217
+ }
1218
+ for (let [name, mk] of Object.entries(mks)) {
1219
+ it(`should clean the object a ${name} refers to and share it with the other path to the same object`, function() {
1220
+ let o = { s: x }
1221
+ let d = mk()
1222
+ d.ref = o
1223
+ let r = clearXSS({ o, d })
1224
+ assert.ok(r.d !== d)
1225
+ assert.ok(r.d.ref === r.o, 'not shared')
1226
+ assert.strict.strictEqual(r.o.s, fx)
1227
+ assert.strict.strictEqual(o.s, x)
1228
+ //物件先、foreign後之順序亦同
1229
+ let r2 = clearXSS({ d: mk(), o })
1230
+ assert.ok(r2.o.s === fx)
1231
+ })
1232
+
1233
+ it(`should keep a cycle through a ${name} closed on the output`, function() {
1234
+ let o = { s: x }
1235
+ let d = mk()
1236
+ d.ref = o
1237
+ o.d = d
1238
+ let r = clearXSS(o)
1239
+ assert.ok(r.d.ref === r, 'cycle not closed')
1240
+ assert.ok(r.d !== d)
1241
+ assert.strict.strictEqual(r.s, fx)
1242
+ })
1243
+ }
1244
+
1245
+ it(`should not overflow the call stack for a 20000-level object chain kept as a Date property, and clean its leaf`, function() {
1246
+ let cur = { s: x }
1247
+ for (let i = 0; i < 20000; i++) {
1248
+ cur = { a: cur }
1249
+ }
1250
+ let d = new Date(0)
1251
+ d.child = cur
1252
+ let r = clearXSS({ d })
1253
+ let v = r.d.child
1254
+ let n = 0
1255
+ while (v.a !== undefined) {
1256
+ v = v.a
1257
+ n++
1258
+ }
1259
+ assert.strict.strictEqual(n, 20000)
1260
+ assert.strict.strictEqual(v.s, fx)
1261
+ })
1262
+
1263
+ it(`should convert a child array shared by 100 Dates once (100 distinct outputs before)`, function() {
1264
+ let arr = []
1265
+ for (let i = 0; i < 1000; i++) {
1266
+ arr.push({ s: x })
1267
+ }
1268
+ let ds = []
1269
+ for (let i = 0; i < 100; i++) {
1270
+ let d = new Date(i)
1271
+ d.child = arr
1272
+ ds.push(d)
1273
+ }
1274
+ let r
1275
+ let n = spy(() => {
1276
+ r = clearXSS(ds)
1277
+ })
1278
+ assert.strict.strictEqual(n.calls, 1000)
1279
+ let set = new Set(r.map((v) => v.child))
1280
+ assert.strict.strictEqual(set.size, 1)
1281
+ assert.strict.strictEqual(r[99].child[999].s, fx)
1282
+ })
1283
+ })
1284
+
1285
+ describe(`建構子與原型之效果: 依複本之終態清除(同修前)`, function() {
1286
+ it(`should clean the key a prototype setter creates while the clone is built`, function() {
1287
+ class SetterContainer {
1288
+ // eslint-disable-next-line accessor-pairs
1289
+ set s(v) {
1290
+ this.t = '<script>' + v + '</script>'
1291
+ }
1292
+ }
1293
+ let o = new SetterContainer()
1294
+ Object.defineProperty(o, 's', { value: 'safe', enumerable: true, writable: true, configurable: true })
1295
+ let r = clearXSS({ k: o }).k
1296
+ assert.ok(r instanceof SetterContainer)
1297
+ assert.strict.strictEqual(r.t, '&lt;script>safe&lt;/script>')
1298
+ assert.strict.deepStrictEqual(Object.keys(r), ['t'])
1299
+ assert.strict.strictEqual(o.s, 'safe')
1300
+ })
1301
+
1302
+ it(`should clean an index an Array subclass constructor adds beyond the source length`, function() {
1303
+ class ExtendedArray extends Array {
1304
+ constructor(n) {
1305
+ super(n + 1)
1306
+ this[n] = x
1307
+ }
1308
+ }
1309
+ let a = new ExtendedArray(1)
1310
+ a.length = 1
1311
+ a[0] = 'safe'
1312
+ let r = clearXSS({ k: a }).k
1313
+ assert.strict.strictEqual(r.length, 2)
1314
+ assert.strict.strictEqual(r[0], 'safe')
1315
+ assert.strict.strictEqual(r[1], fx)
1316
+ })
1317
+
1318
+ it(`should clean a hole of an Array subclass whose prototype provides the index`, function() {
1319
+ class A extends Array {}
1320
+ A.prototype[1] = x
1321
+ let a = new A(3)
1322
+ a[0] = 'a'
1323
+ a[2] = 'c'
1324
+ let r = clearXSS({ k: a }).k
1325
+ assert.ok(r instanceof A)
1326
+ assert.strict.deepStrictEqual([r[0], r[1], r[2]], ['a', fx, 'c'])
1327
+ })
1328
+
1329
+ it(`should keep the own enumerable properties of a class instance whose prototype has an accessor of another name`, function() {
1330
+ class B {
1331
+ get v() {
1332
+ return 1
1333
+ }
1334
+ }
1335
+ let b = new B()
1336
+ b.s = x
1337
+ let r = clearXSS({ k: b }).k
1338
+ assert.ok(r instanceof B)
1339
+ assert.strict.strictEqual(r.s, fx)
1340
+ assert.strict.strictEqual(r.v, 1)
1341
+ })
1342
+
1343
+ it(`should clean the input of a RegExp match array and keep index and input`, function() {
1344
+ let m = 'a<script>'.match(/a/)
1345
+ let r = clearXSS({ k: m }).k
1346
+ assert.ok(Array.isArray(r))
1347
+ assert.strict.strictEqual(r.index, 0)
1348
+ assert.strict.strictEqual(r.input, clearXSS('a<script>'))
1349
+ assert.strict.strictEqual(m.input, 'a<script>')
1350
+ })
1351
+
1352
+ it(`should keep the match array input cleaned for an Array subclass match array too`, function() {
1353
+ class A extends Array {}
1354
+ let a = A.from(['a<script>'])
1355
+ a.index = 3
1356
+ a.input = '<script>x'
1357
+ let r = clearXSS({ k: a }).k
1358
+ assert.strict.strictEqual(r.input, clearXSS('<script>x'))
1359
+ })
1360
+ })
1361
+
1362
+ describe(`Map與Set補強`, function() {
1363
+ it(`should keep an object used as a Map key by reference (not cleaned, not copied) while its value is converted`, function() {
1364
+ let key = { s: x }
1365
+ let m = new Map([[key, key]])
1366
+ let r = clearXSS({ m }).m
1367
+ let ks = [...r.keys()]
1368
+ assert.ok(ks.length === 1 && ks[0] === key)
1369
+ assert.ok(r.has(key))
1370
+ assert.strict.strictEqual(key.s, x)
1371
+ assert.ok(r.get(key) !== key)
1372
+ assert.strict.strictEqual(r.get(key).s, fx)
1373
+ })
1374
+
1375
+ it(`should keep and clean the own enumerable properties of a Map or Set`, function() {
1376
+ let m = new Map([['k', x]])
1377
+ m.extra = { s: x }
1378
+ m[Symbol.for('wsemi-test')] = x
1379
+ let s = new Set([x])
1380
+ s.extra = { s: x }
1381
+ let r = clearXSS({ m, s })
1382
+ assert.strict.strictEqual(r.m.get('k'), fx)
1383
+ assert.strict.strictEqual(r.m.extra.s, fx)
1384
+ assert.strict.strictEqual(r.m[Symbol.for('wsemi-test')], fx)
1385
+ assert.ok(r.s.has(fx))
1386
+ assert.strict.strictEqual(r.s.extra.s, fx)
1387
+ assert.strict.strictEqual(m.extra.s, x)
1388
+ })
1389
+
1390
+ it(`should keep the instance fields of a Map subclass`, function() {
1391
+ class M3 extends Map {
1392
+ constructor(...a) {
1393
+ super(...a)
1394
+ this.field = 'v'
1395
+ }
1396
+ }
1397
+ let m = new M3([['k', x]])
1398
+ m.other = x
1399
+ let r = clearXSS({ m }).m
1400
+ assert.ok(r instanceof M3)
1401
+ assert.strict.strictEqual(r.field, 'v')
1402
+ assert.strict.strictEqual(r.other, fx)
1403
+ assert.strict.strictEqual(r.get('k'), fx)
1404
+ })
1405
+
1406
+ it(`should merge Set elements that become equal after cleaning`, function() {
1407
+ let r = clearXSS(new Set([x, fx, 'p']))
1408
+ assert.strict.deepStrictEqual([...r], [fx, 'p'])
1409
+ })
1410
+
1411
+ it(`should keep a Map and a Set with a writable own tag Object as they are`, function() {
1412
+ let m = new Map([['k', x]])
1413
+ Object.defineProperty(m, Symbol.toStringTag, { value: 'Object', writable: true, configurable: true })
1414
+ let s = new Set([x])
1415
+ Object.defineProperty(s, Symbol.toStringTag, { value: 'Object', writable: true, configurable: true })
1416
+ let d = new Date(5)
1417
+ Object.defineProperty(d, Symbol.toStringTag, { value: 'Object', writable: true, configurable: true })
1418
+ let r = clearXSS({ m, s, d })
1419
+ assert.ok(r.m instanceof Map && r.m.get('k') === fx)
1420
+ assert.ok(r.s instanceof Set && r.s.has(fx))
1421
+ assert.strict.strictEqual(r.d.getTime(), 5)
1422
+ })
1423
+
1424
+ it(`should not treat an object that only fakes the Map tag with a writable tag as a Map`, function() {
1425
+ //不可寫之偽標籤(如Object.defineProperty(o, Symbol.toStringTag, { value: 'Set' }))使lodash cloneDeep對其呼叫forEach而拋錯, 修前亦同, 不納入
1426
+ let fakeMap = { s: x, [Symbol.toStringTag]: 'Map' }
1427
+ let r
1428
+ assert.doesNotThrow(() => {
1429
+ r = clearXSS({ a: fakeMap })
1430
+ })
1431
+ assert.ok(!(r.a instanceof Map) && r.a !== fakeMap)
1432
+ assert.strict.strictEqual(fakeMap.s, x)
1433
+ })
1434
+
1435
+ })
1436
+
1437
+ describe(`跨realm(iframe、vm)之物件`, function() {
1438
+ let ctx = (code) => vm.runInNewContext(code, {}, { timeout: 5000 })
1439
+ let src = `'<script>alert(1)</script>'`
1440
+
1441
+ for (let [pos, put, get] of positions) {
1442
+ it(`should clean the values of a Map from another realm ${pos}`, function() {
1443
+ let m = ctx(`new Map([['k', ${src}], ['o', { s: ${src} }]])`)
1444
+ assert.ok(!(m instanceof Map))
1445
+ let r = get(clearXSS(put(m)))
1446
+ assert.strict.strictEqual(r.get('k'), fx)
1447
+ assert.strict.strictEqual(r.get('o').s, fx)
1448
+ assert.strict.strictEqual(m.get('k'), x)
1449
+ })
1450
+
1451
+ it(`should clean the elements of a Set from another realm ${pos}`, function() {
1452
+ let s = ctx(`new Set([${src}, { s: ${src} }])`)
1453
+ assert.ok(!(s instanceof Set))
1454
+ let r = get(clearXSS(put(s)))
1455
+ let es = [...r]
1456
+ assert.strict.strictEqual(es[0], fx)
1457
+ assert.strict.strictEqual(es[1].s, fx)
1458
+ })
1459
+
1460
+ it(`should clean an object and an array from another realm ${pos}`, function() {
1461
+ let o = ctx(`({ s: ${src}, a: [${src}, { t: ${src} }] })`)
1462
+ let r = get(clearXSS(put(o)))
1463
+ assert.strict.strictEqual(r.s, fx)
1464
+ assert.strict.strictEqual(r.a[0], fx)
1465
+ assert.strict.strictEqual(r.a[1].t, fx)
1466
+ let a = ctx(`[${src}, { t: ${src} }]`)
1467
+ let ra = get(clearXSS(put(a)))
1468
+ assert.strict.strictEqual(ra[0], fx)
1469
+ assert.strict.strictEqual(ra[1].t, fx)
1470
+ })
1471
+ }
1472
+ })
1473
+
1474
+ describe(`輸出屬性與共用`, function() {
1475
+ it(`should define an own __proto__ key as a writable, enumerable and configurable data property`, function() {
1476
+ let t = JSON.parse('{"__proto__":{"a":"<script>alert(1)</script>"},"b":1}')
1477
+ let r = clearXSS(t)
1478
+ let d = Object.getOwnPropertyDescriptor(r, '__proto__')
1479
+ assert.strict.strictEqual(d.writable, true)
1480
+ assert.strict.strictEqual(d.enumerable, true)
1481
+ assert.strict.strictEqual(d.configurable, true)
1482
+ assert.strict.strictEqual(d.value.a, fx)
1483
+ r.__proto__ = 5 // eslint-disable-line no-proto -- 可重新設定(自有資料屬性, 不改原型)
1484
+ assert.strict.strictEqual(Object.getOwnPropertyDescriptor(r, '__proto__').value, 5)
1485
+ assert.strict.strictEqual(Object.getPrototypeOf(r), Object.prototype)
1486
+ })
1487
+
1488
+ it(`should clean a String object shared by many properties once, and obtain the options once per call`, function() {
1489
+ let b = new String(x) // eslint-disable-line no-new-wrappers
1490
+ let origOpt = fxss.getOpt
1491
+ let nOpt = 0
1492
+ fxss.getOpt = (...args) => {
1493
+ nOpt++
1494
+ return origOpt(...args)
1495
+ }
1496
+ let r
1497
+ let n
1498
+ try {
1499
+ n = spy(() => {
1500
+ r = clearXSS({ a: b, b, c: [b, b], d: { e: b }, f: x, g: x })
1501
+ })
1502
+ }
1503
+ finally {
1504
+ fxss.getOpt = origOpt
1505
+ }
1506
+ assert.strict.strictEqual(r.a, fx)
1507
+ assert.strict.strictEqual(r.c[1], fx)
1508
+ assert.strict.strictEqual(r.d.e, fx)
1509
+ assert.strict.strictEqual(n.calls, 3) //b一次, 兩個原始字串各一次
1510
+ assert.strict.strictEqual(n.gets, 1)
1511
+ assert.strict.strictEqual(nOpt, 1)
1512
+ })
1513
+ })
1514
+
1515
+ })
1516
+
1517
+ describe(`字串之取得: 有字串須處理時才取得js-xss, 每次呼叫至多取得一次`, function() {
1518
+
1519
+ it(`should not obtain js-xss for inputs without a string to clean`, function() {
1520
+ let inps = [1, null, undefined, true, '', { a: 1, b: [2, null, ''] }, [], new Date(0), new Map([['k', 1]]), new Set([2]), 10n, Symbol('s')]
1521
+ for (let v of inps) {
1522
+ let n = spy(() => {
1523
+ clearXSS(v)
1524
+ })
1525
+ assert.strict.strictEqual(n.gets, 0, String(typeof v))
1526
+ }
1527
+ })
1528
+
1529
+ it(`should obtain js-xss once per call for any number of strings`, function() {
1530
+ let n = spy(() => {
1531
+ clearXSS({ a: x, b: [x, x, x], c: new Map([['k', x]]), d: new Set([x]) })
1532
+ })
1533
+ assert.strict.strictEqual(n.gets, 1)
1534
+ assert.strict.strictEqual(n.calls, 6)
1535
+ let n2 = spy(() => {
1536
+ clearXSS(x)
1537
+ })
1538
+ assert.strict.strictEqual(n2.gets, 1)
1539
+ assert.strict.strictEqual(n2.calls, 1)
1540
+ })
1541
+
1542
+ it(`should call filterXSS in depth-first pre-order of the properties`, function() {
1543
+ let order = []
1544
+ let orig = fxss.getFilterXSS
1545
+ fxss.getFilterXSS = () => {
1546
+ let f = orig()
1547
+ let w = (c, o) => {
1548
+ order.push(c)
1549
+ return f(c, o)
1550
+ }
1551
+ w.escapeAttrValue = f.escapeAttrValue
1552
+ return w
1553
+ }
1554
+ try {
1555
+ clearXSS({ a: 's1', b: { c: 's2', d: ['s3', { e: 's4' }] }, f: 's5' })
1556
+ }
1557
+ finally {
1558
+ fxss.getFilterXSS = orig
1559
+ }
1560
+ assert.strict.deepStrictEqual(order, ['s1', 's2', 's3', 's4', 's5'])
1561
+ })
1562
+
1563
+ })
1564
+
1565
+ describe(`極深、大型共用與大型循環於限額子行程`, function() {
1566
+
1567
+ let runner = join(__dirname, 'tools', 'clearXssWalkRunner.mjs')
1568
+ let execArgv = ['--expose-gc', '--max-old-space-size=512']
1569
+
1570
+ for (let kind of ['obj', 'arr', 'mixed', 'map', 'set']) {
1571
+ let n = (kind === 'map' || kind === 'set') ? 300000 : 1000000 //Map與Set每層配置較大(100萬層Map約444MB)
1572
+ it(`should clean the leaf of ${n} nested ${kind} levels within 5 s and 450 MB of heap`, function() {
1573
+ let rc = runChildCase(runner, `deep:${kind}:${n}`, { execArgv, allowNoResult: true, timeout: 60000 })
1574
+ assert.strict.strictEqual(rc.status, 0, `status=${rc.status} signal=${rc.signal} stderr=${String(rc.stderr).split('\n').find((l) => /FATAL|Error/.test(l)) || ''}`)
1575
+ assert.ok(rc.r !== null, 'no result line')
1576
+ let r = rc.r
1577
+ assert.ok(r.leafCleaned, 'leaf not cleaned')
1578
+ assert.ok(r.inputIntact, 'input changed')
1579
+ assert.ok(r.depthOut === n && r.depthIn === n, `depth out=${r.depthOut} in=${r.depthIn}`)
1580
+ assert.ok(r.calls === 1, `calls=${r.calls}`)
1581
+ assert.ok(r.ms < 5000, `ms=${r.ms}`)
1582
+ assert.ok(r.heapMB < 450, `heapMB=${r.heapMB}`)
1583
+ })
1584
+ }
1585
+
1586
+ for (let kind of ['obj', 'arr', 'map', 'set', 'mixed']) {
1587
+ it(`should clean ${kind} DAG of 1000 levels with one filterXSS call and keep every level shared`, function() {
1588
+ let rc = runChildCase(runner, `dag:${kind}:1000`, { execArgv, allowNoResult: true, timeout: 30000 })
1589
+ assert.strict.strictEqual(rc.status, 0, `status=${rc.status} signal=${rc.signal}`)
1590
+ assert.ok(rc.r !== null, 'no result line')
1591
+ let r = rc.r
1592
+ assert.strict.strictEqual(r.calls, 1)
1593
+ assert.ok(r.shared, 'not shared')
1594
+ assert.ok(r.steps >= 1000, `steps=${r.steps}`)
1595
+ assert.ok(r.ms < 2000, `ms=${r.ms}`)
1596
+ })
1597
+ }
1598
+
1599
+ it(`should clean a cycle of 1000000 nodes and close it`, function() {
1600
+ let rc = runChildCase(runner, `cycle:1000000`, { execArgv, allowNoResult: true, timeout: 60000 })
1601
+ assert.strict.strictEqual(rc.status, 0, `status=${rc.status} signal=${rc.signal}`)
1602
+ assert.ok(rc.r !== null, 'no result line')
1603
+ assert.ok(rc.r.cleaned && rc.r.closed && rc.r.inputIntact)
1604
+ assert.ok(rc.r.ms < 5000, `ms=${rc.r.ms}`)
1605
+ })
1606
+
1607
+ })
1608
+
1609
+ })
1610
+
336
1611
  describe(`使用端物件之迭代(R-OBJ-ITER-01)`, function() {
337
1612
 
338
1613
  let x = '<script>alert(1)</script>'
@@ -557,7 +1832,7 @@ describe(`clearXSS`, function() {
557
1832
  class G {
558
1833
  get length() {
559
1834
  return 1
560
- }
1835
+ }
561
1836
 
562
1837
  constructor() {
563
1838
  this.a = x