From 73732c3430af9586fd7e407683039de9e5e6a712 Mon Sep 17 00:00:00 2001 From: hkong-mitre <122547078+hkong-mitre@users.noreply.github.com> Date: Fri, 19 Sep 2025 20:57:51 -0400 Subject: [PATCH 01/11] update lib due to 1 high vulnerability --- package-lock.json | 638 ++++++++++++++++++++++++---------------------- 1 file changed, 338 insertions(+), 300 deletions(-) diff --git a/package-lock.json b/package-lock.json index d3e596f..3409c56 100644 --- a/package-lock.json +++ b/package-lock.json @@ -61,20 +61,6 @@ "node": ">= 16.20.2 < 22" } }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/code-frame": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", @@ -91,9 +77,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", - "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", + "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", "dev": true, "license": "MIT", "engines": { @@ -101,22 +87,22 @@ } }, "node_modules/@babel/core": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz", - "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", + "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", "dev": true, "license": "MIT", "dependencies": { - "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.3", - "@babel/parser": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.3", - "@babel/types": "^7.28.2", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", + "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -275,27 +261,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz", - "integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.27.2", - "@babel/types": "^7.28.2" + "@babel/types": "^7.28.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", - "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.2" + "@babel/types": "^7.28.4" }, "bin": { "parser": "bin/babel-parser.js" @@ -528,9 +514,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz", - "integrity": "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -552,18 +538,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz", - "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", + "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.3", + "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", - "@babel/types": "^7.28.2", + "@babel/types": "^7.28.4", "debug": "^4.3.1" }, "engines": { @@ -571,9 +557,9 @@ } }, "node_modules/@babel/types": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", - "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", "dev": true, "license": "MIT", "dependencies": { @@ -601,9 +587,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", - "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz", + "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==", "cpu": [ "ppc64" ], @@ -618,9 +604,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", - "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.10.tgz", + "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==", "cpu": [ "arm" ], @@ -635,9 +621,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", - "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz", + "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==", "cpu": [ "arm64" ], @@ -652,9 +638,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", - "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.10.tgz", + "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==", "cpu": [ "x64" ], @@ -669,9 +655,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", - "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz", + "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==", "cpu": [ "arm64" ], @@ -686,9 +672,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", - "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz", + "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==", "cpu": [ "x64" ], @@ -703,9 +689,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", - "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz", + "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==", "cpu": [ "arm64" ], @@ -720,9 +706,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", - "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz", + "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==", "cpu": [ "x64" ], @@ -737,9 +723,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", - "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz", + "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==", "cpu": [ "arm" ], @@ -754,9 +740,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", - "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz", + "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==", "cpu": [ "arm64" ], @@ -771,9 +757,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", - "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz", + "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==", "cpu": [ "ia32" ], @@ -788,9 +774,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", - "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz", + "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==", "cpu": [ "loong64" ], @@ -805,9 +791,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", - "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz", + "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==", "cpu": [ "mips64el" ], @@ -822,9 +808,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", - "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz", + "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==", "cpu": [ "ppc64" ], @@ -839,9 +825,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", - "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz", + "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==", "cpu": [ "riscv64" ], @@ -856,9 +842,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", - "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz", + "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==", "cpu": [ "s390x" ], @@ -873,9 +859,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", - "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz", + "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", "cpu": [ "x64" ], @@ -890,9 +876,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", - "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz", + "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==", "cpu": [ "arm64" ], @@ -907,9 +893,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", - "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz", + "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==", "cpu": [ "x64" ], @@ -924,9 +910,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", - "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz", + "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==", "cpu": [ "arm64" ], @@ -941,9 +927,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", - "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz", + "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==", "cpu": [ "x64" ], @@ -958,9 +944,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", - "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz", + "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==", "cpu": [ "arm64" ], @@ -975,9 +961,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", - "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz", + "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==", "cpu": [ "x64" ], @@ -992,9 +978,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", - "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz", + "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==", "cpu": [ "arm64" ], @@ -1009,9 +995,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", - "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz", + "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==", "cpu": [ "ia32" ], @@ -1026,9 +1012,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", - "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz", + "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==", "cpu": [ "x64" ], @@ -1129,9 +1115,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", - "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "license": "MIT", "engines": { "node": ">=12" @@ -1141,9 +1127,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" @@ -1578,6 +1564,17 @@ "@jridgewell/trace-mapping": "^0.3.24" } }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -1596,9 +1593,9 @@ "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.30", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", - "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, "license": "MIT", "dependencies": { @@ -1689,9 +1686,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.48.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.48.1.tgz", - "integrity": "sha512-rGmb8qoG/zdmKoYELCBwu7vt+9HxZ7Koos3pD0+sH5fR3u3Wb/jGcpnqxcnWsPEKDUyzeLSqksN8LJtgXjqBYw==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.0.tgz", + "integrity": "sha512-VxDYCDqOaR7NXzAtvRx7G1u54d2kEHopb28YH/pKzY6y0qmogP3gG7CSiWsq9WvDFxOQMpNEyjVAHZFXfH3o/A==", "cpu": [ "arm" ], @@ -1703,9 +1700,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.48.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.48.1.tgz", - "integrity": "sha512-4e9WtTxrk3gu1DFE+imNJr4WsL13nWbD/Y6wQcyku5qadlKHY3OQ3LJ/INrrjngv2BJIHnIzbqMk1GTAC2P8yQ==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.0.tgz", + "integrity": "sha512-pqDirm8koABIKvzL59YI9W9DWbRlTX7RWhN+auR8HXJxo89m4mjqbah7nJZjeKNTNYopqL+yGg+0mhCpf3xZtQ==", "cpu": [ "arm64" ], @@ -1717,9 +1714,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.48.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.48.1.tgz", - "integrity": "sha512-+XjmyChHfc4TSs6WUQGmVf7Hkg8ferMAE2aNYYWjiLzAS/T62uOsdfnqv+GHRjq7rKRnYh4mwWb4Hz7h/alp8A==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.0.tgz", + "integrity": "sha512-YCdWlY/8ltN6H78HnMsRHYlPiKvqKagBP1r+D7SSylxX+HnsgXGCmLiV3Y4nSyY9hW8qr8U9LDUx/Lo7M6MfmQ==", "cpu": [ "arm64" ], @@ -1731,9 +1728,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.48.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.48.1.tgz", - "integrity": "sha512-upGEY7Ftw8M6BAJyGwnwMw91rSqXTcOKZnnveKrVWsMTF8/k5mleKSuh7D4v4IV1pLxKAk3Tbs0Lo9qYmii5mQ==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.0.tgz", + "integrity": "sha512-z4nw6y1j+OOSGzuVbSWdIp1IUks9qNw4dc7z7lWuWDKojY38VMWBlEN7F9jk5UXOkUcp97vA1N213DF+Lz8BRg==", "cpu": [ "x64" ], @@ -1745,9 +1742,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.48.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.48.1.tgz", - "integrity": "sha512-P9ViWakdoynYFUOZhqq97vBrhuvRLAbN/p2tAVJvhLb8SvN7rbBnJQcBu8e/rQts42pXGLVhfsAP0k9KXWa3nQ==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.0.tgz", + "integrity": "sha512-Q/dv9Yvyr5rKlK8WQJZVrp5g2SOYeZUs9u/t2f9cQ2E0gJjYB/BWoedXfUT0EcDJefi2zzVfhcOj8drWCzTviw==", "cpu": [ "arm64" ], @@ -1759,9 +1756,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.48.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.48.1.tgz", - "integrity": "sha512-VLKIwIpnBya5/saccM8JshpbxfyJt0Dsli0PjXozHwbSVaHTvWXJH1bbCwPXxnMzU4zVEfgD1HpW3VQHomi2AQ==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.0.tgz", + "integrity": "sha512-kdBsLs4Uile/fbjZVvCRcKB4q64R+1mUq0Yd7oU1CMm1Av336ajIFqNFovByipciuUQjBCPMxwJhCgfG2re3rg==", "cpu": [ "x64" ], @@ -1773,9 +1770,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.48.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.48.1.tgz", - "integrity": "sha512-3zEuZsXfKaw8n/yF7t8N6NNdhyFw3s8xJTqjbTDXlipwrEHo4GtIKcMJr5Ed29leLpB9AugtAQpAHW0jvtKKaQ==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.0.tgz", + "integrity": "sha512-aL6hRwu0k7MTUESgkg7QHY6CoqPgr6gdQXRJI1/VbFlUMwsSzPGSR7sG5d+MCbYnJmJwThc2ol3nixj1fvI/zQ==", "cpu": [ "arm" ], @@ -1787,9 +1784,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.48.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.48.1.tgz", - "integrity": "sha512-leo9tOIlKrcBmmEypzunV/2w946JeLbTdDlwEZ7OnnsUyelZ72NMnT4B2vsikSgwQifjnJUbdXzuW4ToN1wV+Q==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.0.tgz", + "integrity": "sha512-BTs0M5s1EJejgIBJhCeiFo7GZZ2IXWkFGcyZhxX4+8usnIo5Mti57108vjXFIQmmJaRyDwmV59Tw64Ap1dkwMw==", "cpu": [ "arm" ], @@ -1801,9 +1798,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.48.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.48.1.tgz", - "integrity": "sha512-Vy/WS4z4jEyvnJm+CnPfExIv5sSKqZrUr98h03hpAMbE2aI0aD2wvK6GiSe8Gx2wGp3eD81cYDpLLBqNb2ydwQ==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.0.tgz", + "integrity": "sha512-uj672IVOU9m08DBGvoPKPi/J8jlVgjh12C9GmjjBxCTQc3XtVmRkRKyeHSmIKQpvJ7fIm1EJieBUcnGSzDVFyw==", "cpu": [ "arm64" ], @@ -1815,9 +1812,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.48.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.48.1.tgz", - "integrity": "sha512-x5Kzn7XTwIssU9UYqWDB9VpLpfHYuXw5c6bJr4Mzv9kIv242vmJHbI5PJJEnmBYitUIfoMCODDhR7KoZLot2VQ==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.0.tgz", + "integrity": "sha512-/+IVbeDMDCtB/HP/wiWsSzduD10SEGzIZX2945KSgZRNi4TSkjHqRJtNTVtVb8IRwhJ65ssI56krlLik+zFWkw==", "cpu": [ "arm64" ], @@ -1828,10 +1825,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.48.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.48.1.tgz", - "integrity": "sha512-yzCaBbwkkWt/EcgJOKDUdUpMHjhiZT/eDktOPWvSRpqrVE04p0Nd6EGV4/g7MARXXeOqstflqsKuXVM3H9wOIQ==", + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.0.tgz", + "integrity": "sha512-U1vVzvSWtSMWKKrGoROPBXMh3Vwn93TA9V35PldokHGqiUbF6erSzox/5qrSMKp6SzakvyjcPiVF8yB1xKr9Pg==", "cpu": [ "loong64" ], @@ -1843,9 +1840,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.48.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.48.1.tgz", - "integrity": "sha512-UK0WzWUjMAJccHIeOpPhPcKBqax7QFg47hwZTp6kiMhQHeOYJeaMwzeRZe1q5IiTKsaLnHu9s6toSYVUlZ2QtQ==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.0.tgz", + "integrity": "sha512-X/4WfuBAdQRH8cK3DYl8zC00XEE6aM472W+QCycpQJeLWVnHfkv7RyBFVaTqNUMsTgIX8ihMjCvFF9OUgeABzw==", "cpu": [ "ppc64" ], @@ -1857,9 +1854,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.48.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.48.1.tgz", - "integrity": "sha512-3NADEIlt+aCdCbWVZ7D3tBjBX1lHpXxcvrLt/kdXTiBrOds8APTdtk2yRL2GgmnSVeX4YS1JIf0imFujg78vpw==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.0.tgz", + "integrity": "sha512-xIRYc58HfWDBZoLmWfWXg2Sq8VCa2iJ32B7mqfWnkx5mekekl0tMe7FHpY8I72RXEcUkaWawRvl3qA55og+cwQ==", "cpu": [ "riscv64" ], @@ -1871,9 +1868,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.48.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.48.1.tgz", - "integrity": "sha512-euuwm/QTXAMOcyiFCcrx0/S2jGvFlKJ2Iro8rsmYL53dlblp3LkUQVFzEidHhvIPPvcIsxDhl2wkBE+I6YVGzA==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.0.tgz", + "integrity": "sha512-mbsoUey05WJIOz8U1WzNdf+6UMYGwE3fZZnQqsM22FZ3wh1N887HT6jAOjXs6CNEK3Ntu2OBsyQDXfIjouI4dw==", "cpu": [ "riscv64" ], @@ -1885,9 +1882,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.48.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.48.1.tgz", - "integrity": "sha512-w8mULUjmPdWLJgmTYJx/W6Qhln1a+yqvgwmGXcQl2vFBkWsKGUBRbtLRuKJUln8Uaimf07zgJNxOhHOvjSQmBQ==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.0.tgz", + "integrity": "sha512-qP6aP970bucEi5KKKR4AuPFd8aTx9EF6BvutvYxmZuWLJHmnq4LvBfp0U+yFDMGwJ+AIJEH5sIP+SNypauMWzg==", "cpu": [ "s390x" ], @@ -1899,9 +1896,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.48.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.48.1.tgz", - "integrity": "sha512-90taWXCWxTbClWuMZD0DKYohY1EovA+W5iytpE89oUPmT5O1HFdf8cuuVIylE6vCbrGdIGv85lVRzTcpTRZ+kA==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.0.tgz", + "integrity": "sha512-nmSVN+F2i1yKZ7rJNKO3G7ZzmxJgoQBQZ/6c4MuS553Grmr7WqR7LLDcYG53Z2m9409z3JLt4sCOhLdbKQ3HmA==", "cpu": [ "x64" ], @@ -1913,9 +1910,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.48.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.48.1.tgz", - "integrity": "sha512-2Gu29SkFh1FfTRuN1GR1afMuND2GKzlORQUP3mNMJbqdndOg7gNsa81JnORctazHRokiDzQ5+MLE5XYmZW5VWg==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.0.tgz", + "integrity": "sha512-2d0qRo33G6TfQVjaMR71P+yJVGODrt5V6+T0BDYH4EMfGgdC/2HWDVjSSFw888GSzAZUwuska3+zxNUCDco6rQ==", "cpu": [ "x64" ], @@ -1926,10 +1923,24 @@ "linux" ] }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.0.tgz", + "integrity": "sha512-A1JalX4MOaFAAyGgpO7XP5khquv/7xKzLIyLmhNrbiCxWpMlnsTYr8dnsWM7sEeotNmxvSOEL7F65j0HXFcFsw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.48.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.48.1.tgz", - "integrity": "sha512-6kQFR1WuAO50bxkIlAVeIYsz3RUx+xymwhTo9j94dJ+kmHe9ly7muH23sdfWduD0BA8pD9/yhonUvAjxGh34jQ==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.0.tgz", + "integrity": "sha512-YQugafP/rH0eOOHGjmNgDURrpYHrIX0yuojOI8bwCyXwxC9ZdTd3vYkmddPX0oHONLXu9Rb1dDmT0VNpjkzGGw==", "cpu": [ "arm64" ], @@ -1941,9 +1952,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.48.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.48.1.tgz", - "integrity": "sha512-RUyZZ/mga88lMI3RlXFs4WQ7n3VyU07sPXmMG7/C1NOi8qisUg57Y7LRarqoGoAiopmGmChUhSwfpvQ3H5iGSQ==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.0.tgz", + "integrity": "sha512-zYdUYhi3Qe2fndujBqL5FjAFzvNeLxtIqfzNEVKD1I7C37/chv1VxhscWSQHTNfjPCrBFQMnynwA3kpZpZ8w4A==", "cpu": [ "ia32" ], @@ -1954,10 +1965,24 @@ "win32" ] }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.0.tgz", + "integrity": "sha512-fGk03kQylNaCOQ96HDMeT7E2n91EqvCDd3RwvT5k+xNdFCeMGnj5b5hEgTGrQuyidqSsD3zJDQ21QIaxXqTBJw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.48.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.48.1.tgz", - "integrity": "sha512-8a/caCUN4vkTChxkaIJcMtwIVcBhi4X2PQRoT+yCK3qRYaZ7cURrmJFL5Ux9H9RaMIXj9RuihckdmkBX3zZsgg==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.0.tgz", + "integrity": "sha512-6iKDCVSIUQ8jPMoIV0OytRKniaYyy5EbY/RRydmLW8ZR3cEBhxbWl5ro0rkUNe0ef6sScvhbY79HrjRm8i3vDQ==", "cpu": [ "x64" ], @@ -2103,9 +2128,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "18.19.123", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.123.tgz", - "integrity": "sha512-K7DIaHnh0mzVxreCR9qwgNxp3MH9dltPNIEddW9MYUlcKAzm+3grKNSTe2vCJHI1FaLpvpL5JGJrz1UZDKYvDg==", + "version": "18.19.127", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.127.tgz", + "integrity": "sha512-gSjxjrnKXML/yo0BO099uPixMqfpJU0TKYjpfLU7TrtA2WWDki412Np/RSTPRil1saKBhvVVKzVx/p/6p94nVA==", "dev": true, "license": "MIT", "dependencies": { @@ -2127,9 +2152,9 @@ "license": "MIT" }, "node_modules/@types/validator": { - "version": "13.15.2", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.2.tgz", - "integrity": "sha512-y7pa/oEJJ4iGYBxOpfAKn5b9+xuihvzDVnC/OSvlVnGxVg0pOqmjiMafiJ1KVNQEaPZf9HsEp5icEwGg8uIe5Q==", + "version": "13.15.3", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.3.tgz", + "integrity": "sha512-7bcUmDyS6PN3EuD9SlGGOxM77F8WLVsrwkxyWxKnxzmXoequ6c7741QBrANq6htVRGOITJ7z72mTP6Z4XyuG+Q==", "dev": true, "license": "MIT" }, @@ -2505,9 +2530,9 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", - "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", + "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -2633,6 +2658,16 @@ "dev": true, "license": "MIT" }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.6.tgz", + "integrity": "sha512-wrH5NNqren/QMtKUEEJf7z86YjfqW/2uw3IL3/xpqZUC95SSVIFXYQeeGjL6FT/X68IROu6RMehZQS5foy2BXw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -2658,9 +2693,9 @@ } }, "node_modules/browserslist": { - "version": "4.25.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.3.tgz", - "integrity": "sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ==", + "version": "4.26.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.2.tgz", + "integrity": "sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==", "dev": true, "funding": [ { @@ -2678,9 +2713,10 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001735", - "electron-to-chromium": "^1.5.204", - "node-releases": "^2.0.19", + "baseline-browser-mapping": "^2.8.3", + "caniuse-lite": "^1.0.30001741", + "electron-to-chromium": "^1.5.218", + "node-releases": "^2.0.21", "update-browserslist-db": "^1.1.3" }, "bin": { @@ -2780,9 +2816,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001737", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001737.tgz", - "integrity": "sha512-BiloLiXtQNrY5UyF0+1nSJLXUENuhka2pzy2Fx5pGxqavdrxSCW4U6Pn/PoG3Efspi2frRbHpBV2XsrPE6EDlw==", + "version": "1.0.30001743", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001743.tgz", + "integrity": "sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==", "dev": true, "funding": [ { @@ -3061,9 +3097,9 @@ } }, "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -3189,9 +3225,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.209", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.209.tgz", - "integrity": "sha512-Xoz0uMrim9ZETCQt8UgM5FxQF9+imA7PBpokoGcZloA1uw2LeHzTlip5cb5KOAsXZLjh/moN2vReN3ZjJmjI9A==", + "version": "1.5.222", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.222.tgz", + "integrity": "sha512-gA7psSwSwQRE60CEoLz6JBCQPIxNeuzB2nL8vE03GK/OHxlvykbLyeiumQy1iH5C2f3YbRAZpGCMT12a/9ih9w==", "dev": true, "license": "ISC" }, @@ -3215,9 +3251,9 @@ "license": "MIT" }, "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3270,9 +3306,9 @@ } }, "node_modules/esbuild": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", - "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", + "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -3283,32 +3319,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.9", - "@esbuild/android-arm": "0.25.9", - "@esbuild/android-arm64": "0.25.9", - "@esbuild/android-x64": "0.25.9", - "@esbuild/darwin-arm64": "0.25.9", - "@esbuild/darwin-x64": "0.25.9", - "@esbuild/freebsd-arm64": "0.25.9", - "@esbuild/freebsd-x64": "0.25.9", - "@esbuild/linux-arm": "0.25.9", - "@esbuild/linux-arm64": "0.25.9", - "@esbuild/linux-ia32": "0.25.9", - "@esbuild/linux-loong64": "0.25.9", - "@esbuild/linux-mips64el": "0.25.9", - "@esbuild/linux-ppc64": "0.25.9", - "@esbuild/linux-riscv64": "0.25.9", - "@esbuild/linux-s390x": "0.25.9", - "@esbuild/linux-x64": "0.25.9", - "@esbuild/netbsd-arm64": "0.25.9", - "@esbuild/netbsd-x64": "0.25.9", - "@esbuild/openbsd-arm64": "0.25.9", - "@esbuild/openbsd-x64": "0.25.9", - "@esbuild/openharmony-arm64": "0.25.9", - "@esbuild/sunos-x64": "0.25.9", - "@esbuild/win32-arm64": "0.25.9", - "@esbuild/win32-ia32": "0.25.9", - "@esbuild/win32-x64": "0.25.9" + "@esbuild/aix-ppc64": "0.25.10", + "@esbuild/android-arm": "0.25.10", + "@esbuild/android-arm64": "0.25.10", + "@esbuild/android-x64": "0.25.10", + "@esbuild/darwin-arm64": "0.25.10", + "@esbuild/darwin-x64": "0.25.10", + "@esbuild/freebsd-arm64": "0.25.10", + "@esbuild/freebsd-x64": "0.25.10", + "@esbuild/linux-arm": "0.25.10", + "@esbuild/linux-arm64": "0.25.10", + "@esbuild/linux-ia32": "0.25.10", + "@esbuild/linux-loong64": "0.25.10", + "@esbuild/linux-mips64el": "0.25.10", + "@esbuild/linux-ppc64": "0.25.10", + "@esbuild/linux-riscv64": "0.25.10", + "@esbuild/linux-s390x": "0.25.10", + "@esbuild/linux-x64": "0.25.10", + "@esbuild/netbsd-arm64": "0.25.10", + "@esbuild/netbsd-x64": "0.25.10", + "@esbuild/openbsd-arm64": "0.25.10", + "@esbuild/openbsd-x64": "0.25.10", + "@esbuild/openharmony-arm64": "0.25.10", + "@esbuild/sunos-x64": "0.25.10", + "@esbuild/win32-arm64": "0.25.10", + "@esbuild/win32-ia32": "0.25.10", + "@esbuild/win32-x64": "0.25.10" } }, "node_modules/escalade": { @@ -5776,9 +5812,9 @@ } }, "node_modules/magic-string": { - "version": "0.30.18", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz", - "integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==", + "version": "0.30.19", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", "dev": true, "license": "MIT", "dependencies": { @@ -5957,9 +5993,9 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", + "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", "dev": true, "license": "MIT" }, @@ -6186,9 +6222,9 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", - "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.1.tgz", + "integrity": "sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ==", "license": "ISC", "engines": { "node": "20 || >=22" @@ -6573,9 +6609,9 @@ } }, "node_modules/rollup": { - "version": "4.48.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.48.1.tgz", - "integrity": "sha512-jVG20NvbhTYDkGAty2/Yh7HK6/q3DGSRH4o8ALKGArmMuaauM9kLfoMZ+WliPwA5+JHr2lTn3g557FxBV87ifg==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.0.tgz", + "integrity": "sha512-+IuescNkTJQgX7AkIDtITipZdIGcWF0pnVvZTWStiazUmcGA2ag8dfg0urest2XlXUi9kuhfQ+qmdc5Stc3z7g==", "dev": true, "license": "MIT", "dependencies": { @@ -6589,26 +6625,28 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.48.1", - "@rollup/rollup-android-arm64": "4.48.1", - "@rollup/rollup-darwin-arm64": "4.48.1", - "@rollup/rollup-darwin-x64": "4.48.1", - "@rollup/rollup-freebsd-arm64": "4.48.1", - "@rollup/rollup-freebsd-x64": "4.48.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.48.1", - "@rollup/rollup-linux-arm-musleabihf": "4.48.1", - "@rollup/rollup-linux-arm64-gnu": "4.48.1", - "@rollup/rollup-linux-arm64-musl": "4.48.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.48.1", - "@rollup/rollup-linux-ppc64-gnu": "4.48.1", - "@rollup/rollup-linux-riscv64-gnu": "4.48.1", - "@rollup/rollup-linux-riscv64-musl": "4.48.1", - "@rollup/rollup-linux-s390x-gnu": "4.48.1", - "@rollup/rollup-linux-x64-gnu": "4.48.1", - "@rollup/rollup-linux-x64-musl": "4.48.1", - "@rollup/rollup-win32-arm64-msvc": "4.48.1", - "@rollup/rollup-win32-ia32-msvc": "4.48.1", - "@rollup/rollup-win32-x64-msvc": "4.48.1", + "@rollup/rollup-android-arm-eabi": "4.52.0", + "@rollup/rollup-android-arm64": "4.52.0", + "@rollup/rollup-darwin-arm64": "4.52.0", + "@rollup/rollup-darwin-x64": "4.52.0", + "@rollup/rollup-freebsd-arm64": "4.52.0", + "@rollup/rollup-freebsd-x64": "4.52.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.0", + "@rollup/rollup-linux-arm-musleabihf": "4.52.0", + "@rollup/rollup-linux-arm64-gnu": "4.52.0", + "@rollup/rollup-linux-arm64-musl": "4.52.0", + "@rollup/rollup-linux-loong64-gnu": "4.52.0", + "@rollup/rollup-linux-ppc64-gnu": "4.52.0", + "@rollup/rollup-linux-riscv64-gnu": "4.52.0", + "@rollup/rollup-linux-riscv64-musl": "4.52.0", + "@rollup/rollup-linux-s390x-gnu": "4.52.0", + "@rollup/rollup-linux-x64-gnu": "4.52.0", + "@rollup/rollup-linux-x64-musl": "4.52.0", + "@rollup/rollup-openharmony-arm64": "4.52.0", + "@rollup/rollup-win32-arm64-msvc": "4.52.0", + "@rollup/rollup-win32-ia32-msvc": "4.52.0", + "@rollup/rollup-win32-x64-gnu": "4.52.0", + "@rollup/rollup-win32-x64-msvc": "4.52.0", "fsevents": "~2.3.2" } }, @@ -6823,9 +6861,9 @@ "license": "MIT" }, "node_modules/string-width/node_modules/ansi-regex": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", - "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "license": "MIT", "engines": { "node": ">=12" @@ -6835,9 +6873,9 @@ } }, "node_modules/string-width/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" @@ -7159,14 +7197,14 @@ "license": "MIT" }, "node_modules/tinyglobby": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", - "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "license": "MIT", "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { "node": ">=12.0.0" @@ -7662,9 +7700,9 @@ } }, "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", - "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "license": "MIT", "engines": { "node": ">=12" @@ -7674,9 +7712,9 @@ } }, "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "license": "MIT", "engines": { "node": ">=12" @@ -7686,9 +7724,9 @@ } }, "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" From 9fbbb4461aad744b71afa9260348f979d1736e64 Mon Sep 17 00:00:00 2001 From: hkong-mitre <122547078+hkong-mitre@users.noreply.github.com> Date: Fri, 19 Sep 2025 21:00:23 -0400 Subject: [PATCH 02/11] initial searchToken class in preparation for managing dates and date ranges, which require very different query methods than strings --- src/search/SearchRequest.ts | 62 +++++++++++------- src/search/SearchToken.ts | 30 +++++++++ .../test_cases/search_dates.test.unit.ts | 65 +++++++++++++++++++ 3 files changed, 134 insertions(+), 23 deletions(-) create mode 100644 src/search/SearchToken.ts create mode 100644 src/search/test_cases/search_dates.test.unit.ts diff --git a/src/search/SearchRequest.ts b/src/search/SearchRequest.ts index c78982c..75f9f63 100644 --- a/src/search/SearchRequest.ts +++ b/src/search/SearchRequest.ts @@ -4,6 +4,7 @@ import validator from 'validator'; // import { CveId } from "../CveId.js" import { CveResult, CveErrorId } from "../result/CveResult.js"; import { SearchOptions } from "./BasicSearchManager.js" +import { SearchToken } from './SearchToken.js'; export const SearchRequestType = { @@ -33,6 +34,10 @@ export const SearchRequestType = { 'SEARCH_AS_FILESPEC': `search text is a version string`, 'SEARCH_PHRASE': `search text is a phrase (surrounded by double quotes)`, + // dates and date ranges + 'SEARCH_DATE': `search text is a date (ISO 8601)`, + 'SEARCH_DATE_RANGE': `search text is a date range (ISO 8601)`, + // multiple types 'SEARCH_STRING_MULTIPLE_TYPES': `search text is made up of multiple request types`, } as const @@ -45,7 +50,7 @@ export type SearchRequestTypeId = Extract) { this._searchText = searchText - // this._searchOptions = { - // useCache: options?.useCache ?? true, - // track_total_hits: options?.track_total_hits ?? true, - // default_operator: options?.default_operator ?? "AND", - // metadataOnly: options?.metadataOnly ?? false, - // fields: options?.fields ?? [], - // sort: options?.sort ?? [{ - // "cveMetadata.cveId.keyword": { "order": "desc" } - // }], - // from: options?.from ?? 0, - // size: options?.size ?? 25, - // } - // this._query = {} } @@ -128,15 +120,15 @@ export class SearchRequest { } else { // general search text processing - this._searchText?.trim(); + this._searchText = this._searchText?.trim(); const cleanedSearchText = SearchRequest.replaceRepeatingSymbols(this._searchText); const text: string[] = SearchRequest.tokenizeSearchText(cleanedSearchText); - let type; let overallType: SearchRequestTypeId = 'SEARCH_STRING_UNKNOWN_TYPE'; let errorIds: CveErrorId[] = [] let newSearchText = '' + let searchTokens: SearchToken[] = [] text.forEach(token => { - type = SearchRequest.findSearchRequestType(token) + let type = SearchRequest.findSearchRequestType(token); // post-processing based on type switch (type) { case 'SEARCH_AS_CVE_ID': @@ -170,10 +162,14 @@ export class SearchRequest { result = CveResult.ok({}, [SearchRequestType[type]]); break; } + // token at this point has been properly decorated ready to be used + // in opensearch + let searchToken = new SearchToken(token, type); newSearchText = `${newSearchText} ${token}` if (overallType === 'SEARCH_STRING_UNKNOWN_TYPE') { - overallType = type - } else if (overallType !== type) { + overallType = type; + } + else if (overallType !== type) { overallType = 'SEARCH_STRING_MULTIPLE_TYPES' } }) @@ -197,7 +193,7 @@ export class SearchRequest { // matches repeats of anything except language characters and numbers and ... and --- static repeatingSymbolsRegex = /([^\p{L}0-9.\-])\1{2,}/gu; // not greedy version /(.+?)\1+/ - static repeatingPatternsRegex = /(.+)\1+/gu + // static repeatingPatternsRegex = /(.+)\1+/gu /** checks for repeating symbols and optionally removes them @@ -205,7 +201,11 @@ export class SearchRequest { * @returns true iff there are repeating symbols */ static hasRepeatingSymbols(searchText: string): boolean { - return this.repeatingSymbolsRegex.test(searchText); + // return this.repeatingSymbolsRegex.test(searchText); + // creating a new regex of repeatingSymbolsRegex each time because + // it has a global flag + const regex = new RegExp(SearchRequest.repeatingSymbolsRegex); + return regex.test(searchText); } @@ -214,7 +214,23 @@ export class SearchRequest { * @returns string after repeating symbols have been removed */ static replaceRepeatingSymbols(searchText: string): string { - return searchText.replaceAll(SearchRequest.repeatingSymbolsRegex, ""); + // return searchText.replaceAll(SearchRequest.repeatingSymbolsRegex, ""); + // creating a new regex of repeatingSymbolsRegex each time because + // it has a global flag + const regex = new RegExp(SearchRequest.repeatingSymbolsRegex); + return searchText.replaceAll(regex, ""); + } + + + static isDateString(searchText: string): boolean { + // performance-optimized and safer version of CVE Schema 5.1 regex for dates generated by gemini 2.5 pro + const regex = /^((?:(?:2000|2400|2800)|(?:(?:19|2\d)(?:0[48]|[2468][048]|[13579][26])))-02-29|(?:(?:19|2\d)\d{2})-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|02-(?:0[1-9]|1\d|2[0-8])))$/; + if (!searchText || typeof searchText !== 'string' || searchText.length <= 0) { + return false; + } + else { + return regex.test(searchText); + } } diff --git a/src/search/SearchToken.ts b/src/search/SearchToken.ts new file mode 100644 index 0000000..b2c934b --- /dev/null +++ b/src/search/SearchToken.ts @@ -0,0 +1,30 @@ +import { SearchRequestTypeId } from './SearchRequest.js'; + +/** SearchToken is the user's search string that has been tokenized, analyzed, cateogrized, and decorated + */ +export class SearchToken { + + /** the processed text string */ + private _text: string; + private set text(value: string) { + this._text = value; + } + public get text(): string { + return this._text; + } + + /** the token type */ + private _typeId: SearchRequestTypeId; + public get typeId(): SearchRequestTypeId { + return this._typeId; + } + public set typeId(value: SearchRequestTypeId) { + this._typeId = value; + } + + + constructor(text: string, typeId: SearchRequestTypeId) { + this._text = text + this._typeId = typeId + } +} \ No newline at end of file diff --git a/src/search/test_cases/search_dates.test.unit.ts b/src/search/test_cases/search_dates.test.unit.ts new file mode 100644 index 0000000..04126bf --- /dev/null +++ b/src/search/test_cases/search_dates.test.unit.ts @@ -0,0 +1,65 @@ +import { SearchRequest, SearchRequestTypeId } from '../SearchRequest.js'; + +describe('Date Search (unit tests)', () => { + // ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- + + describe('SearchRequest.tokenizeSearchText()', () => { + // expected defaults to false in this series! + const testCases: Array<{ input: string; expected?: boolean; }> = [ + // valid dates + { input: `2025-09-18`, expected: true }, + { input: `2024-02-29`, expected: true }, // leap year + // invalid dates (expected defaults to false in this series, so we can leave expect out) + { input: null, expected: false }, + { input: undefined }, + { input: `` }, // empty string + { input: ` ` }, // whitespace + { input: `2025-09-01/2025-09-18` }, // date range + { input: `2025-09-18T12:00:00:00.000Z`}, // datetime Z + { input: `2025-09-18T12:00:00:00.000+05:00` }, // datetime offset + { input: `2025-02-29` }, // not a leap year + { input: `1899-12-30` }, // out of range date + { input: `T12:00:00:00.000Z` }, // ISO 8601 time + + // malformed dates + { input: '2023-4-01'}, // month not zero‑padded + { input: '23-04-01'}, // year not 4‑digit + { input: '2023-13-01'}, // month > 12 + { input: '2023-00-10'}, // month 00 + { input: '2023-02-30'}, // day > 28/29 + { input: '2023-01-00'}, // day 00 + + // non‑ISO strings + { input: 'April 1, 2023'}, + { input: '2023/04/01' }, + { input: '19/01/2023' }, + { input: '01/19/2023' }, + + // non-dates that can look like dates + + // // malformed times + // '2023-04-01T24:00', // hour 24 not allowed in our pattern + // '2023-04-01T14:60', // minute 60 + // '2023-04-01T14:30:60',// second 60 + // '2023-04-01T14', // incomplete time + // // wrong timezone + // '2023-04-01T14:30+25:00', // offset hour > 23 + // '2023-04-01T14:30+02:60', // offset minute > 59 + // '2023-04-01T14:30+02', // missing :mm + // // missing separator in interval + // '2023-04-01 2023-04-10', + // // extra characters + // '2023-04-01T14:30Zextra', + ]; + + testCases.forEach(({ input, expected }) => { + it(`isDateString() should match proper dates and recognize non-dates --> '${input}': ${expected??false}`, () => { + const result = SearchRequest.isDateString(input); + expected = expected ?? false + expect(result).toEqual(expected); + }); + }); + }); + + +}); \ No newline at end of file From 953250df475b9c3bb93ee89ce54729da90eae721 Mon Sep 17 00:00:00 2001 From: hkong-mitre <122547078+hkong-mitre@users.noreply.github.com> Date: Wed, 24 Sep 2025 21:01:13 -0400 Subject: [PATCH 03/11] working search for simple dates (local date regex) --- src/search/SearchQueryBuilder.test.unit.ts | 1 + src/search/SearchQueryBuilder.ts | 86 +++++++++++---- src/search/SearchRequest.ts | 5 +- .../SearchQueryBuilder.test.unit.ts.snap | 103 ++++++++++++++++++ .../test_cases/search_dates.test.unit.ts | 24 ++-- 5 files changed, 187 insertions(+), 32 deletions(-) diff --git a/src/search/SearchQueryBuilder.test.unit.ts b/src/search/SearchQueryBuilder.test.unit.ts index 6da0ecd..d09aeac 100644 --- a/src/search/SearchQueryBuilder.test.unit.ts +++ b/src/search/SearchQueryBuilder.test.unit.ts @@ -110,6 +110,7 @@ describe(`SearchQueryBuilder`, () => { metadataOnly: true }], [`CAPEC-64`, { default_operator: 'OR' }], + [`2023-12-21`, { track_total_hits: true }], ] testCases.forEach((test: [string, Partial]) => { it(`(${test[0]},${JSON.stringify(test[1])})..buildQuery() correctly returns the expected query`, async () => { diff --git a/src/search/SearchQueryBuilder.ts b/src/search/SearchQueryBuilder.ts index a9549cc..5c44b90 100644 --- a/src/search/SearchQueryBuilder.ts +++ b/src/search/SearchQueryBuilder.ts @@ -2,6 +2,7 @@ import { CveResult } from '../result/CveResult.js'; import { SearchOptions } from './BasicSearchManager.js'; import { SearchRequest } from './SearchRequest.js'; + /** * a search query builder that analyzes a user's search text and builds a proper search query * for OpenSearch @@ -9,13 +10,27 @@ import { SearchRequest } from './SearchRequest.js'; export class SearchQueryBuilder { /** default number of results to return when not specified */ - static kDefaultNumResults = 25 + static kDefaultNumResults = 25; + + /** the JSON paths to CVE fields that are of the date type */ + static kDateFieldPaths = [ + 'cveMetadata.datePublished', + 'cveMetadata.dateRejected', + 'cveMetadata.dateReserved', + 'cveMetadata.dateUpdated', + 'containers.cna.datePublic', + 'containers.cna.providerMetadata.dateUpdated', + 'containers.cna.timeline.time', + 'containers.adp.metrics.other.content.dateAdded', + 'containers.adp.metrics.other.content.timestamp', + 'containers.adp.providerMetadata.dateUpdated' + ]; /** the user entered text */ _searchText: string; /** search options when validating input and building query string */ - _searchOptions: SearchOptions + _searchOptions: SearchOptions; /** the searchRequest based on the search term(s) from the user */ _searchRequest: SearchRequest; @@ -44,9 +59,9 @@ export class SearchQueryBuilder { if (this._searchOptions.size < this._searchOptions.from) { this._searchOptions.size = this._searchOptions.from + 1; } - this._searchRequest = new SearchRequest(searchText) + this._searchRequest = new SearchRequest(searchText); } - + /** builds the proper query for openSearch */ buildQuery(): CveResult { @@ -61,25 +76,56 @@ export class SearchQueryBuilder { let q = { query: {} }; - // ----- query_string - q.query['query_string'] = { - query: `${this._searchText}`, - default_operator: this._searchOptions.default_operator - }; - // ----- _source, which specifies which CVE fields are to be returned - const source: string[] = []; - if (this._searchOptions.metadataOnly) { - source.push("cveMetadata", "containers.cna.descriptions.value"); - } - if (source.length > 0) { - q['_source'] = source; + // right now, we only handle 2 types of queries: + // 1. date/date ranges + // 2. query_string for everything else + const isDate = SearchRequest.isDateString(this._searchText); + if (isDate) { + // assemble all the date fields into an array + let dateFields = []; + SearchQueryBuilder.kDateFieldPaths.map(path => { + let field = `{ + "range": { + "${path}": { + "gte": "${this._searchText}" + } + } + }`; + dateFields.push(JSON.parse(field)); + }); + console.log(`dateFields: ${JSON.stringify(dateFields, null, 2)}`); + q = { + query: { + bool: { + should: dateFields, + minimum_should_match: 1 + } + } + }; } - // ----- search only in fields - if (this._searchOptions.fields) { - source.push(...this._searchOptions.fields); + else { + q = { + query: { + query_string: { + query: this._searchText, + default_operator: this._searchOptions.default_operator + } + } + }; + // ----- _source, which specifies which CVE fields are to be returned + const source: string[] = []; + if (this._searchOptions.metadataOnly) { + source.push("cveMetadata", "containers.cna.descriptions.value"); + } + if (source.length > 0) { + q['_source'] = source; + } + // ----- search only in fields + if (this._searchOptions.fields) { + source.push(...this._searchOptions.fields); + } } - // console.log(`***${JSON.stringify(q, null, 2)}`) // ----- track_total_hits if (this._searchOptions.track_total_hits) { diff --git a/src/search/SearchRequest.ts b/src/search/SearchRequest.ts index 75f9f63..11099fe 100644 --- a/src/search/SearchRequest.ts +++ b/src/search/SearchRequest.ts @@ -50,7 +50,7 @@ export type SearchRequestTypeId = Extract { describe('SearchRequest.tokenizeSearchText()', () => { // expected defaults to false in this series! - const testCases: Array<{ input: string; expected?: boolean; }> = [ + const testCases: Array<{ input?: string | null; expected?: boolean; }> = [ // valid dates { input: `2025-09-18`, expected: true }, { input: `2024-02-29`, expected: true }, // leap year @@ -14,23 +14,25 @@ describe('Date Search (unit tests)', () => { { input: undefined }, { input: `` }, // empty string { input: ` ` }, // whitespace + { input: `2025` }, // year only + { input: `2025-01` }, // year and month only { input: `2025-09-01/2025-09-18` }, // date range - { input: `2025-09-18T12:00:00:00.000Z`}, // datetime Z + { input: `2025-09-18T12:00:00:00.000Z` }, // datetime Z { input: `2025-09-18T12:00:00:00.000+05:00` }, // datetime offset { input: `2025-02-29` }, // not a leap year { input: `1899-12-30` }, // out of range date { input: `T12:00:00:00.000Z` }, // ISO 8601 time // malformed dates - { input: '2023-4-01'}, // month not zero‑padded - { input: '23-04-01'}, // year not 4‑digit - { input: '2023-13-01'}, // month > 12 - { input: '2023-00-10'}, // month 00 - { input: '2023-02-30'}, // day > 28/29 - { input: '2023-01-00'}, // day 00 + { input: '2023-4-01' }, // month not zero‑padded + { input: '23-04-01' }, // year not 4‑digit + { input: '2023-13-01' }, // month > 12 + { input: '2023-00-10' }, // month 00 + { input: '2023-02-30' }, // day > 28/29 + { input: '2023-01-00' }, // day 00 // non‑ISO strings - { input: 'April 1, 2023'}, + { input: 'April 1, 2023' }, { input: '2023/04/01' }, { input: '19/01/2023' }, { input: '01/19/2023' }, @@ -53,9 +55,9 @@ describe('Date Search (unit tests)', () => { ]; testCases.forEach(({ input, expected }) => { - it(`isDateString() should match proper dates and recognize non-dates --> '${input}': ${expected??false}`, () => { + it(`isDateString() should match proper dates and recognize non-dates --> '${input}': ${expected ?? false}`, () => { const result = SearchRequest.isDateString(input); - expected = expected ?? false + expected = expected ?? false; expect(result).toEqual(expected); }); }); From ddf780a88469c3b9f3b0568d46e130320356cfe1 Mon Sep 17 00:00:00 2001 From: hkong-mitre <122547078+hkong-mitre@users.noreply.github.com> Date: Thu, 25 Sep 2025 20:29:49 -0400 Subject: [PATCH 04/11] add IsoDate and IsoDatetime for improved ISO 8601 functionality needed for date search --- src/common/IsoDate/IsoDate.test.unit.ts | 112 ++++++++++++ src/common/IsoDate/IsoDate.ts | 157 ++++++++++++++++ src/common/IsoDate/IsoDatetime.test.int.ts | 12 ++ src/common/IsoDate/IsoDatetime.test.unit.ts | 150 ++++++++++++++++ src/common/IsoDate/IsoDatetime.ts | 187 ++++++++++++++++++++ 5 files changed, 618 insertions(+) create mode 100644 src/common/IsoDate/IsoDate.test.unit.ts create mode 100644 src/common/IsoDate/IsoDate.ts create mode 100644 src/common/IsoDate/IsoDatetime.test.int.ts create mode 100644 src/common/IsoDate/IsoDatetime.test.unit.ts create mode 100644 src/common/IsoDate/IsoDatetime.ts diff --git a/src/common/IsoDate/IsoDate.test.unit.ts b/src/common/IsoDate/IsoDate.test.unit.ts new file mode 100644 index 0000000..02db008 --- /dev/null +++ b/src/common/IsoDate/IsoDate.test.unit.ts @@ -0,0 +1,112 @@ +/** + * Unit tests for IsoDate + * + * AI Usage: + * - This code was originally generated using + * 1. OpenAI/GPT OSS 120b on Roo Code + * 2. Gemini 2.5 Flash and 2.5 Pro + * then modified to fix incorrect implementations and fit project needs. + * The first commit contains these corrections so that all code committed + * works as designed. + */ + +import { describe, test, expect } from '@jest/globals'; +import { IsoDate, isValidIsoDate } from './IsoDate'; + +describe('IsoDate.parse – valid inputs', () => { + + test('full date with hyphens', () => { + const str = '2025-01-01'; + expect(isValidIsoDate(str)).toBeTruthy(); + const d = IsoDate.parse(str); + expect(d.year).toBe(2025); + expect(d.month).toBe(1); + expect(d.day).toBe(1); + expect(d.toString()).toBe('2025-01-01'); + }); + + test('year‑month', () => { + const str = '2025-01'; + expect(isValidIsoDate(str)).toBeTruthy(); + const d = IsoDate.parse(str); + expect(d.year).toBe(2025); + expect(d.month).toBe(1); + expect(d.day).toBeUndefined(); + expect(d.toString()).toBe('2025-01'); + }); + + test('year only', () => { + const str = '2025'; + expect(isValidIsoDate(str)).toBeTruthy(); + const d = IsoDate.parse(str); + expect(d.year).toBe(2025); + expect(d.month).toBeUndefined(); + expect(d.day).toBeUndefined(); + expect(d.toString()).toBe('2025'); + }); + + test('leap‑year February 29', () => { + const str = '2024-02-29'; + expect(isValidIsoDate(str)).toBeTruthy(); + const d = IsoDate.parse(str); + expect(d.year).toBe(2024); + expect(d.month).toBe(2); + expect(d.day).toBe(29); + expect(d.toString()).toBe('2024-02-29'); + }); + + test('month‑day boundary', () => { + const str = '2025-01-30'; + expect(isValidIsoDate(str)).toBeTruthy(); + const d = IsoDate.parse(str); + expect(d.year).toBe(2025); + expect(d.month).toBe(1); + expect(d.day).toBe(30); + expect(d.toString()).toBe('2025-01-30'); + }); +}); + +describe('IsoDate.parse – invalid inputs', () => { + const invalid = [ + '202501', // year+month without hyphen + '20250101', // compact full date (properly rejected in this class) + '250101', // two‑digit year + '2025-13-01', // invalid month + '2025-02-30', // invalid day (Feb 30) + '2025-04-31', // invalid day (April 31) + '-2025-04-31', // invalid year + '--01-01', // leading hyphens + '-2025-01', // leading hyphen before year + '2025--01', // double hyphen between year and month + '2025-01--01', // double hyphen before day + '2025-02-29', // illegal leap year + '2025-01-01T014:00:00:00Z', // datetime does not match in this class + ]; + + invalid.forEach((value) => { + test(`throws for "${value}"`, () => { + expect(() => IsoDate.parse(value)).toThrow(Error); + }); + }); + + invalid.forEach((value) => { + test(`"${value}" is not an IsoDate`, () => { + expect(isValidIsoDate(value)).toBeFalsy(); + }); + }); +}); + +describe('IsoDate.toString', () => { + const tests: Array<{ input: string; expected: string; }> = [ + { input: '2025-01-01', expected: '2025-01-01' }, + { input: '2025-01', expected: '2025-01' }, + { input: '2025', expected: '2025' } + ]; + + tests.forEach(({input, expected}) => { + test(`properly prints out '${input}' as '${expected}'`, () => { + const isoDate = IsoDate.parse(input) + expect(isoDate.toString()).toBe(expected) + }); + }); +}) \ No newline at end of file diff --git a/src/common/IsoDate/IsoDate.ts b/src/common/IsoDate/IsoDate.ts new file mode 100644 index 0000000..c3c489f --- /dev/null +++ b/src/common/IsoDate/IsoDate.ts @@ -0,0 +1,157 @@ +/** + * IsoDate – a lightweight class for representing calendar dates. +* + * Supported input formats: + * - YYYY-MM-DD (e.g., 2025-01-01) + * - YYYY-MM (e.g., 2025-01) + * - YYYY (e.g., 2025) + * + * Note we do not support (even though it is allowed by ISO 8601) + * - "compact date" (i.e., YYYYMMDD) + * - years previous to 1 AD (i.e., zero and negative years) + * - years after 2500 + * + * The class validates monthly day rules and leap‑year rules. + * It provides a normalized `toString()` output: + * - full date → YYYY-MM-DD + * - year‑month → YYYY-MM + * - year only → YYYY + * + * Example: + * const d = IsoDate.parse('2025-01-01'); + * console.log(d.year, d.month, d.day); // 2025 1 1 + * console.log(d.toString()); // "2025-01-01" + * + * AI Usage: + * - This code was originally generated using + * 1. OpenAI/GPT OSS 120b on Roo Code + * 2. Gemini 2.5 Flash and 2.5 Pro + * then modified to fix incorrect implementations and fit project needs. + * The first commit contains these corrections so that all code committed + * works as designed. + */ + +export class IsoDate { + + /** Full year (e.g., 2025) */ + public readonly year: number; + /** Month number 1‑12 (optional) */ + public readonly month?: number; + /** Day number 1‑31 (optional, requires month) */ + public readonly day?: number; + + protected constructor(year: number, month?: number, day?: number) { + this.year = year; + if (month !== undefined) this.month = month; + if (day !== undefined) this.day = day; + } + + /** + * Parse a string into a IsoDate. + * Throws an Error if the string does not match any supported format + * or if the date components are out of range. + */ + public static parse(value: string): IsoDate { + // Regex with named capture groups for clarity. + // 1. YYYY‑MM‑DD + // 2. we do not allow YYYYMMDD anymore + // 3. YYYY‑MM + // 4. YYYY + const regex = + // GPT OSS 120b generated regex + // /^(?\d{4})(?:[-]?(?\d{2})(?:[-]?(?\d{2})?)?)?$/; + /^(?\d{4})(?:[-](?\d{2})(?:[-](?\d{2})?)?)?$/; + + const match = regex.exec(value); + if (!match || !match.groups) { + throw new Error(`Invalid calendar date format: "${value}": must be one of YYYY-MM-DD, YYYY-MM, or YYYY`); + } + + const year = Number(match.groups.year); + const monthStr = match.groups.month; + const dayStr = match.groups.day; + + // Validate year range (reasonable limits) + if (year < 1 || year > 2500) { + throw new Error(`Year out of range: ${year}`); + } + + // If month is present, validate it. + if (monthStr !== undefined) { + const month = Number(monthStr); + if (month < 1 || month > 12) { + throw new Error(`Month out of range: ${monthStr}`); + } + + // If day is present, validate day according to month & leap year. + if (dayStr !== undefined) { + const day = Number(dayStr); + const maxDay = IsoDate.daysInMonth(year, month); + if (day < 1 || day > maxDay) { + throw new Error( + `Day out of range for ${year}-${String(month).padStart( + 2, + '0' + )}: ${dayStr}` + ); + } + return new IsoDate(year, month, day); + } + + // Month only (no day) + return new IsoDate(year, month); + } + + // Year only + return new IsoDate(year); + } + + /** Return true if the stored year is a leap year. */ + public isLeapYear(): boolean { + return IsoDate.isLeapYear(this.year); + } + + /** Normalized string representation. */ + public toString(): string { + const y = String(this.year).padStart(4, '0'); + if (this.month !== undefined) { + const m = String(this.month).padStart(2, '0'); + if (this.day !== undefined) { + const d = String(this.day).padStart(2, '0'); + return `${y}-${m}-${d}`; + } + return `${y}-${m}`; + } + return y; + } + + /** Static helper – leap‑year check for any year. */ + public static isLeapYear(year: number): boolean { + return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; + } + + /** Static helper – number of days in a given month/year. */ + public static daysInMonth(year: number, month: number): number { + switch (month) { + case 2: + return IsoDate.isLeapYear(year) ? 29 : 28; + case 4: + case 6: + case 9: + case 11: + return 30; + default: + return 31; + } + } +} + +// Utility function to check if a string is a valid ISO date according to IsoDate parsing rules. +export function isValidIsoDate(value: string): boolean { + try { + IsoDate.parse(value); + return true; + } catch { + return false; + } +} diff --git a/src/common/IsoDate/IsoDatetime.test.int.ts b/src/common/IsoDate/IsoDatetime.test.int.ts new file mode 100644 index 0000000..cb3069d --- /dev/null +++ b/src/common/IsoDate/IsoDatetime.test.int.ts @@ -0,0 +1,12 @@ +import { describe, test, expect } from '@jest/globals'; +import { IsoDate } from './IsoDate.js'; +import { IsoDatetime } from './IsoDatetime.js'; + +describe('IsoDatetime.fromIsoDate', () => { + test('converts an IsoDate to midnight UTC IsoDatetime', () => { + const date = IsoDate.parse('2025-03-15'); + const datetime = IsoDatetime.fromIsoDate(date); + expect(datetime).toBeInstanceOf(IsoDatetime); + expect(datetime.toString()).toBe('2025-03-15T00:00:00Z'); + }); +}); \ No newline at end of file diff --git a/src/common/IsoDate/IsoDatetime.test.unit.ts b/src/common/IsoDate/IsoDatetime.test.unit.ts new file mode 100644 index 0000000..2477554 --- /dev/null +++ b/src/common/IsoDate/IsoDatetime.test.unit.ts @@ -0,0 +1,150 @@ +import { describe, test, expect } from '@jest/globals'; +import { IsoDatetime } from './IsoDatetime.js'; + +describe('IsoDatetime.parse – valid inputs', () => { + test('basic UTC datetime without fractional seconds', () => { + const str = '2025-03-01T12:34:56Z'; + const dt = IsoDatetime.parse(str); + expect(dt.year).toBe(2025); + expect(dt.month).toBe(3); + expect(dt.day).toBe(1); + expect(dt.hour).toBe(12); + expect(dt.minute).toBe(34); + expect(dt.second).toBe(56); + expect(dt.millisecond).toBe(0); + expect(dt.toString()).toBe('2025-03-01T12:34:56Z'); + }); + + test('datetime with fractional seconds', () => { + const str = '2025-03-01T12:34:56.789Z'; + const dt = IsoDatetime.parse(str); + expect(dt.millisecond).toBe(789); + expect(dt.toString()).toBe('2025-03-01T12:34:56.789Z'); + }); + + test('datetime with positive offset', () => { + const str = '2025-03-01T12:34:56+02:00'; + const dt = IsoDatetime.parse(str); + // 12:34:56+02:00 => 10:34:56Z + expect(dt.toString()).toBe('2025-03-01T10:34:56Z'); + }); + + test('datetime with negative offset', () => { + const str = '2025-03-01T12:34:56-04:30'; + const dt = IsoDatetime.parse(str); + // 12:34:56-04:30 => 17:04:56Z + expect(dt.toString()).toBe('2025-03-01T17:04:56Z'); + }); + + test('datetime crossing day boundary with positive offset', () => { + const str = '2025-03-01T01:15:00+05:00'; + const dt = IsoDatetime.parse(str); + // 01:15 +05:00 => previous day 20:15Z + expect(dt.toString()).toBe('2025-02-28T20:15:00Z'); + }); + + test('datetime crossing month boundary with negative offset', () => { + const str = '2025-03-01T00:30:00-02:00'; + const dt = IsoDatetime.parse(str); + // 00:30 -02:00 => next day 02:30Z + expect(dt.toString()).toBe('2025-03-01T02:30:00Z'); + }); + + test('leap year date with offset', () => { + const str = '2024-02-29T23:00:00+01:00'; + const dt = IsoDatetime.parse(str); + // 23:00 +01:00 => 22:00Z on leap day + expect(dt.toString()).toBe('2024-02-29T22:00:00Z'); + }); +}); + +describe('IsoDatetime.parse – invalid inputs', () => { + const invalid = [ + '2025-03-01 12:34:56Z', // no 'T' separator + '2025-03-01T12:34Z', // missing seconds + '2025-03-01T12:34:56.789', // missing Z + '2025-13-01T12:34:56Z', // bad month + '2025-00-01T12:34:56Z', // bad month + '2025-02-30T12:34:56Z', // bad number of days in February + '2025-03-01T24:00:00Z', // bad minute + '2025-03-01T12:60:00Z', // bad minute + '2025-03-01T12:34:60Z', // bad second + // '2025-03-01T12:34:56.1000Z', + // '2025-03-01T12:34:56+25:00', + // '2025-03-01T12:34:56+02:60', + ]; + + invalid.forEach((value) => { + test(`throws for "${value}"`, () => { + expect(() => IsoDatetime.parse(value)).toThrow(Error); + }); + }); +}); + +describe('IsoDatetime.toString – formatting', () => { + const tests = [ + { input: '2025-03-01T12:34:56Z', expected: '2025-03-01T12:34:56Z' }, + { input: '2025-03-01T12:34:56.001Z', expected: '2025-03-01T12:34:56.001Z' }, + { input: '2025-03-01T12:34:56+02:00', expected: '2025-03-01T10:34:56Z' }, + { input: '2025-03-01T12:34:56-04:30', expected: '2025-03-01T17:04:56Z' }, + ]; + + tests.forEach(({ input, expected }) => { + test(`parses "${input}" and formats as "${expected}"`, () => { + const dt = IsoDatetime.parse(input); + expect(dt.toString()).toBe(expected); + }); + }); +}); + +describe('IsoDatetime.getNextDay – day increments and decrements', () => { + test('leap year: Feb 28 +1 day => Feb 29', () => { + const dt = IsoDatetime.parse('2024-02-28T00:00:00Z'); + expect(dt.getNextDay()).toBe('2024-02-29T00:00:00Z'); + }); + + test('non‑leap year: Feb 28 +1 day => Mar 01', () => { + const dt = IsoDatetime.parse('2025-02-28T00:00:00Z'); + expect(dt.getNextDay(1)).toBe('2025-03-01T00:00:00Z'); + }); + + test('leap day: Feb 29 +1 day => Mar 01', () => { + const dt = IsoDatetime.parse('2024-02-29T00:00:00Z'); + expect(dt.getNextDay(1)).toBe('2024-03-01T00:00:00Z'); + }); + + test('month boundary: Jan 31 +1 day => Feb 01', () => { + const dt = IsoDatetime.parse('2025-01-31T00:00:00Z'); + expect(dt.getNextDay(1)).toBe('2025-02-01T00:00:00Z'); + }); + + test('year boundary: Dec 31 +1 day => Jan 01 of next year', () => { + const dt = IsoDatetime.parse('2025-12-31T00:00:00Z'); + expect(dt.getNextDay(1)).toBe('2026-01-01T00:00:00Z'); + }); + + test('century non‑leap year (1900): Feb 28 +1 day => Mar 01', () => { + const dt = IsoDatetime.parse('1900-02-28T00:00:00Z'); + expect(dt.getNextDay(1)).toBe('1900-03-01T00:00:00Z'); + }); + + test('century leap year (2000): Feb 28 +1 day => Feb 29', () => { + const dt = IsoDatetime.parse('2000-02-28T00:00:00Z'); + expect(dt.getNextDay(1)).toBe('2000-02-29T00:00:00Z'); + }); + + test('negative increment: Mar 01 -1 day => Feb 28 (non‑leap year)', () => { + const dt = IsoDatetime.parse('2025-03-01T00:00:00Z'); + expect(dt.getNextDay(-1)).toBe('2025-02-28T00:00:00Z'); + }); + + test('negative increment: Mar 01 -1 day => Feb 28 (leap year)', () => { + const dt = IsoDatetime.parse('2024-03-01T00:00:00Z'); + expect(dt.getNextDay(-1)).toBe('2024-02-29T00:00:00Z'); + }); + + test('negative crossing year: Jan 01 -1 day => Dec 31 of previous year', () => { + const dt = IsoDatetime.parse('2025-01-01T00:00:00Z'); + expect(dt.getNextDay(-1)).toBe('2024-12-31T00:00:00Z'); + }); +}); \ No newline at end of file diff --git a/src/common/IsoDate/IsoDatetime.ts b/src/common/IsoDate/IsoDatetime.ts new file mode 100644 index 0000000..7acbeec --- /dev/null +++ b/src/common/IsoDate/IsoDatetime.ts @@ -0,0 +1,187 @@ +/** + * IsoDatetime – extends IsoDate to include time components and full ISO‑8601 datetime parsing. + * + * Supported input format (required ‘T’ separator): + * YYYY‑MM‑DD'T'HH:MM:SS[.sss][Z|±hh:mm] + * + * • Fractional seconds (.sss) are optional. + * • ‘Z’ denotes UTC. + * • A signed offset (e.g., +02:00 or -04:30) is interpreted and the resulting + * datetime is normalized to UTC. + * + * The parser validates component ranges and leap‑year rules, then stores the + * normalized UTC components (year, month, day, hour, minute, second, millisecond). + * + * Example: + * const dt = IsoDatetime.parse('2025-03-01T12:34:56.789+02:00'); + * console.log(dt.toString()); // "2025-03-01T10:34:56.789Z" + */ +import { IsoDate } from './IsoDate.js'; + +export class IsoDatetime extends IsoDate { + /** Hour (0‑23) */ + public readonly hour: number; + /** Minute (0‑59) */ + public readonly minute: number; + /** Second (0‑59) */ + public readonly second: number; + /** Millisecond (0‑999) – defaults to 0 when not provided */ + public readonly millisecond: number; + + private constructor( + year: number, + month: number, + day: number, + hour: number, + minute: number, + second: number, + millisecond: number, + ) { + super(year, month, day); + this.hour = hour; + this.minute = minute; + this.second = second; + this.millisecond = millisecond; + } + + /** + * Parse a string into an IsoDatetime. + * Throws an Error if the string does not match the supported format + * or if any component is out of range. + */ + public static parse(value: string): IsoDatetime { + // Regex with named capture groups for clarity. + // 1. YYYY‑MM‑DD + // 2. T separator (required) + // 3. HH:MM:SS + // 4. optional .sss (fractional seconds) + // 5. timezone: Z or ±hh:mm + const regex = + /^(?\d{4})-(?\d{2})-(?\d{2})T(?\d{2}):(?\d{2}):(?\d{2})(?:\.(?\d+))?(?Z|[+-]\d{2}:\d{2})$/; + + const match = regex.exec(value); + if (!match || !match.groups) { + throw new Error( + `Invalid ISO‑8601 datetime format: "${value}". Expected YYYY-MM-DDTHH:MM:SS[.sss][Z|±hh:mm]`, + ); + } + + const year = Number(match.groups.year); + const month = Number(match.groups.month); + const day = Number(match.groups.day); + const hour = Number(match.groups.hour); + const minute = Number(match.groups.minute); + const second = Number(match.groups.second); + const msStr = match.groups.ms ?? '0'; + const millisecond = Number(msStr.padEnd(3, '0').substring(0, 3)); // keep three digits + const tz = match.groups.tz; + + // Validate date components using IsoDate's helpers. + if (year < 1 || year > 2500) { + throw new Error(`Year out of range: ${year}`); + } + if (month < 1 || month > 12) { + throw new Error(`Month out of range: ${month}`); + } + const maxDay = IsoDate.daysInMonth(year, month); + if (day < 1 || day > maxDay) { + throw new Error(`Day out of range for ${year}-${String(month).padStart(2, '0')}: ${day}`); + } + + // Validate time components. + if (hour < 0 || hour > 23) { + throw new Error(`Hour out of range: ${hour}`); + } + if (minute < 0 || minute > 59) { + throw new Error(`Minute out of range: ${minute}`); + } + if (second < 0 || second > 59) { + throw new Error(`Second out of range: ${second}`); + } + if (millisecond < 0 || millisecond > 999) { + throw new Error(`Millisecond out of range: ${millisecond}`); + } + + // Compute UTC timestamp. + // Date.UTC creates a timestamp as if the supplied components are UTC. + let utcMs = Date.UTC(year, month - 1, day, hour, minute, second, millisecond); + + if (tz !== 'Z') { + // tz format: ±hh:mm + const sign = tz[0] === '+' ? -1 : 1; // offset must be subtracted to get UTC + const [offHour, offMin] = tz.slice(1).split(':').map(Number); + const offsetMinutes = sign * (offHour * 60 + offMin); + utcMs += offsetMinutes * 60 * 1000; + } + + const utcDate = new Date(utcMs); + const normYear = utcDate.getUTCFullYear(); + const normMonth = utcDate.getUTCMonth() + 1; + const normDay = utcDate.getUTCDate(); + const normHour = utcDate.getUTCHours(); + const normMinute = utcDate.getUTCMinutes(); + const normSecond = utcDate.getUTCSeconds(); + const normMs = utcDate.getUTCMilliseconds(); + + return new IsoDatetime( + normYear, + normMonth, + normDay, + normHour, + normMinute, + normSecond, + normMs, + ); + } + + /** Convert this IsoDate to an IsoDatetime at midnight UTC. */ + public static fromIsoDate(date: IsoDate): IsoDatetime { + const isoString = `${date.toString()}T00:00:00.000Z`; + return IsoDatetime.parse(isoString); + } + + /** Normalized ISO‑8601 string in UTC (always ends with ‘Z’). */ + public toString(): string { + const y = String(this.year).padStart(4, '0'); + const m = String(this.month).padStart(2, '0'); + const d = String(this.day).padStart(2, '0'); + const h = String(this.hour).padStart(2, '0'); + const min = String(this.minute).padStart(2, '0'); + const s = String(this.second).padStart(2, '0'); + const msPart = this.millisecond > 0 ? `.${String(this.millisecond).padStart(3, '0')}` : ''; + return `${y}-${m}-${d}T${h}:${min}:${s}${msPart}Z`; + } + /** + * Returns an ISO‑8601 string representing this datetime shifted by the given + * number of days (positive or negative). The shift respects month lengths, + * leap‑year rules, and century leap‑year exceptions. + * + * @param increment Number of days to shift; may be negative. + */ + public getNextDay(increment: number = 1): string { + // Compute the UTC timestamp for the current instance. + const utcMs = Date.UTC( + this.year, + this.month - 1, + this.day, + this.hour, + this.minute, + this.second, + this.millisecond, + ); + // Add the day offset (24 h = 86 400 000 ms). + const newMs = utcMs + increment * 86_400_000; + const d = new Date(newMs); + const next = new IsoDatetime( + d.getUTCFullYear(), + d.getUTCMonth() + 1, + d.getUTCDate(), + d.getUTCHours(), + d.getUTCMinutes(), + d.getUTCSeconds(), + d.getUTCMilliseconds(), + ); + return next.toString(); + } + +} \ No newline at end of file From 5e8c6e42411e3533e9a4c3064e8e67b6cd9a3919 Mon Sep 17 00:00:00 2001 From: hkong-mitre <122547078+hkong-mitre@users.noreply.github.com> Date: Thu, 25 Sep 2025 20:30:53 -0400 Subject: [PATCH 05/11] clean up --- src/common/IsoDate/IsoDateString.test.ts | 1 + src/date/CveDate.ts | 1 - src/search/SearchRequest.ts | 2 +- src/search/test_cases/search_dates.test.unit.ts | 1 + 4 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/common/IsoDate/IsoDateString.test.ts b/src/common/IsoDate/IsoDateString.test.ts index 41f8871..79e06dd 100644 --- a/src/common/IsoDate/IsoDateString.test.ts +++ b/src/common/IsoDate/IsoDateString.test.ts @@ -1,3 +1,4 @@ +import { describe, it, expect } from '@jest/globals'; import { IsoDateStringRegEx, IsoDateString } from './IsoDateString.js'; describe(`IsoDateString`, () => { diff --git a/src/date/CveDate.ts b/src/date/CveDate.ts index 8d658a0..df501b5 100644 --- a/src/date/CveDate.ts +++ b/src/date/CveDate.ts @@ -2,7 +2,6 @@ * Date utility and class to * - facilitate using dates in CveRecords and Javascript, standardizing all dates to * ISO format: 2023-03-29T00:00:00.000Z - * - provide timer functions inside instances * * This is necessary because the Javascript Date object, while tracking UTC time * internally (that is, the number of milliseconds since 1970-01-01T00:00:00.000Z) diff --git a/src/search/SearchRequest.ts b/src/search/SearchRequest.ts index 11099fe..4403a6b 100644 --- a/src/search/SearchRequest.ts +++ b/src/search/SearchRequest.ts @@ -222,7 +222,7 @@ export class SearchRequest { } - static isDateString(searchText: string): boolean { + static isDateString(searchText: string | null): boolean { // performance-optimized and safer version of CVE Schema 5.1 regex for dates generated by gemini 2.5 pro const regex = /^((?:(?:2000|2400|2800)|(?:(?:19|2\d)(?:0[48]|[2468][048]|[13579][26])))-02-29|(?:(?:19|2\d)\d{2})-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|02-(?:0[1-9]|1\d|2[0-8])))$/; if (!searchText || typeof searchText !== 'string' || searchText.length <= 0) { diff --git a/src/search/test_cases/search_dates.test.unit.ts b/src/search/test_cases/search_dates.test.unit.ts index af462d2..7d2dbdc 100644 --- a/src/search/test_cases/search_dates.test.unit.ts +++ b/src/search/test_cases/search_dates.test.unit.ts @@ -1,3 +1,4 @@ +import { describe, it, expect } from '@jest/globals'; import { SearchRequest, SearchRequestTypeId } from '../SearchRequest.js'; describe('Date Search (unit tests)', () => { From 25a52d4156480ebc782cbd9774a01a216ce519c5 Mon Sep 17 00:00:00 2001 From: hkong-mitre <122547078+hkong-mitre@users.noreply.github.com> Date: Thu, 25 Sep 2025 20:36:50 -0400 Subject: [PATCH 06/11] make CveDate and IsoDateString deprecated. Code using it will be updated in a later sprint --- src/common/IsoDate/IsoDateString.ts | 2 ++ src/date/CveDate.ts | 23 ++++++++++++----------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/common/IsoDate/IsoDateString.ts b/src/common/IsoDate/IsoDateString.ts index e566918..9dd2045 100644 --- a/src/common/IsoDate/IsoDateString.ts +++ b/src/common/IsoDate/IsoDateString.ts @@ -5,6 +5,8 @@ * * Note that in the future, if necessary, we can extend what this class covers, but for now * this strict and opinionated set is very useful for processing ISO Date+Time+TZ strings + * + * @deprecated Use IsoDatetime or IsoDate instead for safer and more efficient datetime and date functions */ /** a regular expression to represent an ISO Date+Time+TZ string diff --git a/src/date/CveDate.ts b/src/date/CveDate.ts index df501b5..3fd3ab9 100644 --- a/src/date/CveDate.ts +++ b/src/date/CveDate.ts @@ -1,3 +1,13 @@ +import { + differenceInSeconds, + // endOfYesterday, + // startOfToday, + // startOfYesterday, + sub +} from 'date-fns'; +import { formatInTimeZone } from 'date-fns-tz'; +import { IsoDateString } from '../common/IsoDate/IsoDateString.js'; + /** * Date utility and class to * - facilitate using dates in CveRecords and Javascript, standardizing all dates to @@ -16,19 +26,10 @@ * Throughout this class, we will use * - jsDate to represent a standard JS Date object * - isoDateStr to represent an ISO/UTC/Z date string (e.g. 2023-03-29T00:00:00.000Z) + * + * @deprecated Use IsoDatetime or IsoDate instead for safer and more efficient datetime and date functions */ - -import { - differenceInSeconds, - // endOfYesterday, - // startOfToday, - // startOfYesterday, - sub -} from 'date-fns'; -import { formatInTimeZone } from 'date-fns-tz'; -import { IsoDateString } from '../common/IsoDate/IsoDateString.js'; - export class CveDate { /** the Date object this CveDate instance wraps */ From 1f3fa8a905e9a88e9b302cf8c2f1f77e9aeec390 Mon Sep 17 00:00:00 2001 From: hkong-mitre <122547078+hkong-mitre@users.noreply.github.com> Date: Fri, 26 Sep 2025 15:21:27 -0400 Subject: [PATCH 07/11] working single date-only query (i.e., no multi search term searches, only either date or string). It has 1 problem in that the results are larger than what the developer console returns, and also that it is only searching containers.cna.providerMetadata.dateUpdated and containers.cna.timeline.time. Note change in devel.jsonc search index --- config/devel.jsonc | 4 +- src/search/BasicSearchManager.ts | 2 +- src/search/SearchQueryBuilder.test.unit.ts | 1 + src/search/SearchQueryBuilder.ts | 21 +- src/search/SearchRequest.test.unit.ts | 2 +- .../SearchQueryBuilder.test.unit.ts.snap | 56 ------ .../search_dates.test.e2e.ts.snap | 190 ++++++++++++++++++ .../test_cases/search_dates.test.e2e.ts | 64 ++++++ 8 files changed, 270 insertions(+), 70 deletions(-) create mode 100644 src/search/test_cases/__snapshots__/search_dates.test.e2e.ts.snap create mode 100644 src/search/test_cases/search_dates.test.e2e.ts diff --git a/config/devel.jsonc b/config/devel.jsonc index c2d14ba..2f9e340 100644 --- a/config/devel.jsonc +++ b/config/devel.jsonc @@ -31,8 +31,8 @@ "fixtures": { // @todo these constants needs to be in sync in cve-fixtures // so that testing snapshots are consistent and valid - "name": "fixtures-search-baseline-1086", // release tag - "numCves": "1086" // possible identifier assuming we always add cves to a new release + "name": "fixtures-search-baseline-1008", // release tag + "numCves": "1008" // possible identifier assuming we always add cves to a new release } }, // constants for testing node-config diff --git a/src/search/BasicSearchManager.ts b/src/search/BasicSearchManager.ts index 94d06ed..fbb3251 100644 --- a/src/search/BasicSearchManager.ts +++ b/src/search/BasicSearchManager.ts @@ -54,7 +54,7 @@ export class BasicSearchManager { let response = undefined; const builder = new SearchQueryBuilder(searchText, options); const result: CveResult = builder.buildQuery() - // console.log(`result=${JSON.stringify(result, null, 2)}`) + // console.log(`query body (q)=${JSON.stringify(result.data['q'], null, 2)}`) if (result.isOk()) { // console.log(`q: ${JSON.stringify(result.data['q'], null, 2)}`) response = await this._searchReader._client.search({ diff --git a/src/search/SearchQueryBuilder.test.unit.ts b/src/search/SearchQueryBuilder.test.unit.ts index d09aeac..5f1f456 100644 --- a/src/search/SearchQueryBuilder.test.unit.ts +++ b/src/search/SearchQueryBuilder.test.unit.ts @@ -1,5 +1,6 @@ // For a more comprehensive set of test cases, see the tests // in test_cases/search_* +import { describe, it, test, expect } from '@jest/globals'; import { SearchOptions } from "./BasicSearchManager.js" import { SearchRequestType, SearchRequest, SearchRequestTypeId } from "./SearchRequest.js"; diff --git a/src/search/SearchQueryBuilder.ts b/src/search/SearchQueryBuilder.ts index 5c44b90..0c2d220 100644 --- a/src/search/SearchQueryBuilder.ts +++ b/src/search/SearchQueryBuilder.ts @@ -14,16 +14,16 @@ export class SearchQueryBuilder { /** the JSON paths to CVE fields that are of the date type */ static kDateFieldPaths = [ - 'cveMetadata.datePublished', - 'cveMetadata.dateRejected', - 'cveMetadata.dateReserved', - 'cveMetadata.dateUpdated', - 'containers.cna.datePublic', + // 'cveMetadata.datePublished', + // 'cveMetadata.dateRejected', + // 'cveMetadata.dateReserved', + // 'cveMetadata.dateUpdated', + // 'containers.cna.datePublic', 'containers.cna.providerMetadata.dateUpdated', 'containers.cna.timeline.time', - 'containers.adp.metrics.other.content.dateAdded', - 'containers.adp.metrics.other.content.timestamp', - 'containers.adp.providerMetadata.dateUpdated' + // 'containers.adp.metrics.other.content.dateAdded', + // 'containers.adp.metrics.other.content.timestamp', + // 'containers.adp.providerMetadata.dateUpdated' ]; /** the user entered text */ @@ -81,6 +81,7 @@ export class SearchQueryBuilder { // 1. date/date ranges // 2. query_string for everything else const isDate = SearchRequest.isDateString(this._searchText); + // console.log(`isDate(): ${isDate}`) if (isDate) { // assemble all the date fields into an array let dateFields = []; @@ -94,7 +95,7 @@ export class SearchQueryBuilder { }`; dateFields.push(JSON.parse(field)); }); - console.log(`dateFields: ${JSON.stringify(dateFields, null, 2)}`); + // console.log(`dateFields: ${JSON.stringify(dateFields, null, 2)}`); q = { query: { bool: { @@ -147,7 +148,7 @@ export class SearchQueryBuilder { q['size'] = this._searchOptions.size; // ----- q result.data['q'] = q; - + // console.log(`q: ${JSON.stringify(q, null, 2)}`) return result; } diff --git a/src/search/SearchRequest.test.unit.ts b/src/search/SearchRequest.test.unit.ts index 80859cc..f4d1fac 100644 --- a/src/search/SearchRequest.test.unit.ts +++ b/src/search/SearchRequest.test.unit.ts @@ -1,6 +1,6 @@ // For a more comprehensive set of test cases, see the tests // in test_cases/search_* - +import { describe, it, expect } from '@jest/globals'; import { SearchOptions } from "./BasicSearchManager.js" import { SearchRequestType, SearchRequest, SearchRequestTypeId } from "./SearchRequest.js"; diff --git a/src/search/__snapshots__/SearchQueryBuilder.test.unit.ts.snap b/src/search/__snapshots__/SearchQueryBuilder.test.unit.ts.snap index 7272aab..f4f0c72 100644 --- a/src/search/__snapshots__/SearchQueryBuilder.test.unit.ts.snap +++ b/src/search/__snapshots__/SearchQueryBuilder.test.unit.ts.snap @@ -105,41 +105,6 @@ CveResult { "bool": Object { "minimum_should_match": 1, "should": Array [ - Object { - "range": Object { - "cveMetadata.datePublished": Object { - "gte": "2023-12-21", - }, - }, - }, - Object { - "range": Object { - "cveMetadata.dateRejected": Object { - "gte": "2023-12-21", - }, - }, - }, - Object { - "range": Object { - "cveMetadata.dateReserved": Object { - "gte": "2023-12-21", - }, - }, - }, - Object { - "range": Object { - "cveMetadata.dateUpdated": Object { - "gte": "2023-12-21", - }, - }, - }, - Object { - "range": Object { - "containers.cna.datePublic": Object { - "gte": "2023-12-21", - }, - }, - }, Object { "range": Object { "containers.cna.providerMetadata.dateUpdated": Object { @@ -154,27 +119,6 @@ CveResult { }, }, }, - Object { - "range": Object { - "containers.adp.metrics.other.content.dateAdded": Object { - "gte": "2023-12-21", - }, - }, - }, - Object { - "range": Object { - "containers.adp.metrics.other.content.timestamp": Object { - "gte": "2023-12-21", - }, - }, - }, - Object { - "range": Object { - "containers.adp.providerMetadata.dateUpdated": Object { - "gte": "2023-12-21", - }, - }, - }, ], }, }, diff --git a/src/search/test_cases/__snapshots__/search_dates.test.e2e.ts.snap b/src/search/test_cases/__snapshots__/search_dates.test.e2e.ts.snap new file mode 100644 index 0000000..78b32c1 --- /dev/null +++ b/src/search/test_cases/__snapshots__/search_dates.test.e2e.ts.snap @@ -0,0 +1,190 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Date Search (e2e tests) BasicSearchManager.search() search('2024-06-14') should return cves including CVE-2024-6006 1`] = ` +Array [ + "CVE-2017-9711", + "CVE-2018-4301", + "CVE-2018-9367", + "CVE-2022-21833", + "CVE-2022-21834", + "CVE-2022-21836", + "CVE-2022-21843", + "CVE-2022-21862", + "CVE-2022-21864", + "CVE-2022-21867", + "CVE-2022-21880", + "CVE-2022-21881", + "CVE-2022-21883", + "CVE-2022-21894", + "CVE-2022-21900", + "CVE-2022-21903", + "CVE-2022-21904", + "CVE-2022-21908", + "CVE-2022-21916", + "CVE-2022-21924", + "CVE-2022-21958", + "CVE-2022-21960", + "CVE-2022-21961", + "CVE-2022-21962", + "CVE-2022-21963", + "CVE-2022-21983", + "CVE-2022-21989", + "CVE-2022-21990", + "CVE-2022-25622", + "CVE-2022-30133", + "CVE-2022-30146", + "CVE-2022-30190", + "CVE-2022-30194", + "CVE-2022-30202", + "CVE-2022-30213", + "CVE-2022-34714", + "CVE-2022-45186", + "CVE-2023-32041", + "CVE-2023-40708", + "CVE-2023-47039", + "CVE-2023-50781", + "CVE-2024-10277", + "CVE-2024-10451", + "CVE-2024-10773", + "CVE-2024-10785", + "CVE-2024-10881", + "CVE-2024-10941", + "CVE-2024-11385", + "CVE-2024-11388", + "CVE-2024-11396", + "CVE-2024-11424", + "CVE-2024-11921", + "CVE-2024-12012", + "CVE-2024-12110", + "CVE-2024-20151", + "CVE-2024-20152", + "CVE-2024-20153", + "CVE-2024-20441", + "CVE-2024-26118", + "CVE-2024-26817", + "CVE-2024-31162", + "CVE-2024-31315", + "CVE-2024-31318", + "CVE-2024-31880", + "CVE-2024-35532", + "CVE-2024-36155", + "CVE-2024-36156", + "CVE-2024-36157", + "CVE-2024-36158", + "CVE-2024-36159", + "CVE-2024-36160", + "CVE-2024-36176", + "CVE-2024-36177", + "CVE-2024-38633", + "CVE-2024-38634", + "CVE-2024-38635", + "CVE-2024-38649", + "CVE-2024-40750", + "CVE-2024-41030", + "CVE-2024-41875", + "CVE-2024-41877", + "CVE-2024-42169", + "CVE-2024-43624", + "CVE-2024-43639", + "CVE-2024-44025", + "CVE-2024-44042", + "CVE-2024-44043", + "CVE-2024-44045", + "CVE-2024-44299", + "CVE-2024-45251", + "CVE-2024-46685", + "CVE-2024-46689", + "CVE-2024-46692", + "CVE-2024-46704", + "CVE-2024-46705", + "CVE-2024-46706", + "CVE-2024-46707", + "CVE-2024-46835", + "CVE-2024-46847", + "CVE-2024-46866", + "CVE-2024-47333", + "CVE-2024-47360", + "CVE-2024-47517", + "CVE-2024-47518", + "CVE-2024-47519", + "CVE-2024-47642", + "CVE-2024-47863", + "CVE-2024-48245", + "CVE-2024-48510", + "CVE-2024-51757", + "CVE-2024-52940", + "CVE-2024-53476", + "CVE-2024-54014", + "CVE-2024-54763", + "CVE-2024-54818", + "CVE-2024-54849", + "CVE-2024-55408", + "CVE-2024-56521", + "CVE-2024-56527", + "CVE-2024-57224", + "CVE-2024-57650", + "CVE-2024-5938", + "CVE-2024-5965", + "CVE-2024-5981", + "CVE-2024-6006", + "CVE-2024-6073", + "CVE-2024-6136", + "CVE-2024-6615", + "CVE-2024-7234", + "CVE-2024-7382", + "CVE-2024-7658", + "CVE-2024-8608", + "CVE-2024-8848", + "CVE-2024-9134", + "CVE-2024-9313", + "CVE-2024-9420", + "CVE-2024-9442", + "CVE-2025-0240", + "CVE-2025-0241", + "CVE-2025-23022", + "CVE-2025-23036", + "CVE-2025-23037", + "CVE-2025-23091", + "CVE-2025-23114", + "CVE-2025-24339", + "CVE-2025-25305", + "CVE-2025-25728", + "CVE-2025-27594", + "CVE-2025-27722", + "CVE-2025-28169", + "CVE-2025-30140", + "CVE-2025-30474", + "CVE-2025-30676", + "CVE-2025-32053", + "CVE-2025-37730", +] +`; + +exports[`Date Search (e2e tests) BasicSearchManager.search() search('2025-01-11') should return cves including CVE-2025-37730 1`] = ` +Array [ + "CVE-2022-25622", + "CVE-2024-11396", + "CVE-2024-12012", + "CVE-2024-26817", + "CVE-2024-42169", + "CVE-2024-57650", + "CVE-2025-0240", + "CVE-2025-0241", + "CVE-2025-23022", + "CVE-2025-23036", + "CVE-2025-23037", + "CVE-2025-23091", + "CVE-2025-23114", + "CVE-2025-24339", + "CVE-2025-25305", + "CVE-2025-25728", + "CVE-2025-27594", + "CVE-2025-27722", + "CVE-2025-28169", + "CVE-2025-30140", + "CVE-2025-30474", + "CVE-2025-30676", + "CVE-2025-32053", + "CVE-2025-37730", +] +`; diff --git a/src/search/test_cases/search_dates.test.e2e.ts b/src/search/test_cases/search_dates.test.e2e.ts new file mode 100644 index 0000000..784f5ea --- /dev/null +++ b/src/search/test_cases/search_dates.test.e2e.ts @@ -0,0 +1,64 @@ +import { describe, it, expect } from '@jest/globals'; + +import { BasicSearchManager } from "../BasicSearchManager.js"; +import { SearchResultData } from "../SearchResultData.js"; +import { SearchProviderSpec } from '../../adapters/search/SearchAdapter.js'; +import { SearchRequest, SearchRequestTypeId } from '../SearchRequest.js'; + +describe('Date Search (e2e tests)', () => { + const searchProviderSpec = SearchProviderSpec.getDefaultSearchProviderSpec(); + + // ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- + + describe.only('BasicSearchManager.search()', () => { + // expected defaults to false in this series! + const testCases: Array<{ input: string; expected?: number; first?: string; includes: string; snapshot?: boolean}> = [ + // valid dates + { input: `2022-01-11`, expected: 892, includes: "CVE-2022-31279", snapshot: false }, // developer tools returns 861 instead + { input: `2024-06-14`, expected: 155, includes: "CVE-2024-6006", snapshot: true }, // developer tools returns 138 instead + { input: `2025-01-11`, expected: 24, includes: "CVE-2025-37730", snapshot: true } // developer tools reads 9 instead of 24 + ]; + + testCases.forEach(({ input, expected, first, includes, snapshot }) => { + it(`search('${input}') should return cves including ${includes}`, async () => { + const searchManager = new BasicSearchManager(searchProviderSpec); + const resp = await searchManager.search(input, { + track_total_hits: true, + metadataOnly: true, +  from: 0, + size: 2000 + }); + if (!resp.isOk()) { + console.log(`resp: ${JSON.stringify(resp, null, 2)}`); + } + expect(resp.isOk()).toBeTruthy(); + if (resp['data']) { + const dat = resp['data']['hits']['hits']; + let retlist: {}[] = dat + let cves: string[] = [] + const cveIdMap = retlist.map(cve => { + cves.push(cve["_id"]) + }) + if (snapshot) { + expect(cves.sort()).toMatchSnapshot(); + } + if ( expected ) { + expect(resp['data']['hits']['total']['value']).toBe(expected); + } + if (first) { + expect(dat[0]["_id"]).toBe(first); + } + if ( includes ) { + // let found = retlist.find( cve => { + // return cve["_id"] === includes + // }) + let found = retlist.some(item => item["_id"] === includes) + expect( found ).toBeTruthy() + } + } + }); + }); + }); + + +}); \ No newline at end of file From d5cd6a6ce37426e2d83d441251f90af21e96b41b Mon Sep 17 00:00:00 2001 From: hkong-mitre <122547078+hkong-mitre@users.noreply.github.com> Date: Tue, 30 Sep 2025 09:32:10 -0400 Subject: [PATCH 08/11] improve search parameter when working with dates --- .../OpensearchDatetimeUtils.test.unit.ts | 24 ++++++++ .../search/OpensearchDatetimeUtils.ts | 25 ++++++++ src/common/IsoDate/IsoDate.test.unit.ts | 9 +++ src/common/IsoDate/IsoDate.ts | 26 +++++++-- src/common/IsoDate/IsoDatetime.test.unit.ts | 58 +++++++++++++++---- src/common/IsoDate/IsoDatetime.ts | 28 ++++++++- src/search/SearchQueryBuilder.test.unit.ts | 2 +- src/search/SearchQueryBuilder.ts | 7 ++- .../SearchQueryBuilder.test.unit.ts.snap | 4 +- 9 files changed, 162 insertions(+), 21 deletions(-) create mode 100644 src/adapters/search/OpensearchDatetimeUtils.test.unit.ts create mode 100644 src/adapters/search/OpensearchDatetimeUtils.ts diff --git a/src/adapters/search/OpensearchDatetimeUtils.test.unit.ts b/src/adapters/search/OpensearchDatetimeUtils.test.unit.ts new file mode 100644 index 0000000..f6a6b01 --- /dev/null +++ b/src/adapters/search/OpensearchDatetimeUtils.test.unit.ts @@ -0,0 +1,24 @@ +import { describe, test, expect } from '@jest/globals'; +import { toSearchDateDslString } from './OpensearchDatetimeUtils'; +import { IsoDate, IsoDatetime } from '../../common/IsoDate/IsoDatetime'; + + +describe('OpensearchDatetimeUtils.toSearchDateDslString()', () => { + const testcases = [ + { input: '2025-03-01T12:34:56Z', expected: '2025-03-01T12:34:56Z' }, + { input: '2025-03-01', expected: '2025-03-01||/d' }, + { input: '2025-03', expected: '2025-03||/M' }, + { input: '2025', expected: '2025||/y' }, + ]; + + testcases.forEach(({ input, expected }) => { + test(`throws for "${input}"`, () => { + let iso = (input.length > 10) ? IsoDatetime.parse(input) : IsoDate.parse(input); + expect(toSearchDateDslString(iso)).toBe(expected); + }); + }); + + test('throws on invalid date string', () => { + expect(() => IsoDate.parse('2025-13')).toThrow(); + }); +}); \ No newline at end of file diff --git a/src/adapters/search/OpensearchDatetimeUtils.ts b/src/adapters/search/OpensearchDatetimeUtils.ts new file mode 100644 index 0000000..16a3bbd --- /dev/null +++ b/src/adapters/search/OpensearchDatetimeUtils.ts @@ -0,0 +1,25 @@ +/** + * OpenSearch and ElasticSearch both use additional symbols in the Search DSL to make ISO 8601 representations + * even more precise for years, months, days. This utility class simplifies that + */ + +import { IsoDate, IsoDatetime } from '../../common/IsoDate/IsoDatetime.js'; + +export const toSearchDateDslString = (iso: IsoDate | IsoDatetime): string => { + if (iso.isMonth()) { + // isoDate is a month + return `${iso.toString()}||/M`; + } + else if (iso.isYear()) { + // isoDate is a year + return `${iso.toString()}||/y`; + } + else if (iso.isDate()) { + // isoDate is a year + return `${iso.toString()}||/d`; + } + else { + // all other instances will be the original string + return iso.toString(); + } +}; \ No newline at end of file diff --git a/src/common/IsoDate/IsoDate.test.unit.ts b/src/common/IsoDate/IsoDate.test.unit.ts index 02db008..dce7657 100644 --- a/src/common/IsoDate/IsoDate.test.unit.ts +++ b/src/common/IsoDate/IsoDate.test.unit.ts @@ -23,6 +23,9 @@ describe('IsoDate.parse – valid inputs', () => { expect(d.month).toBe(1); expect(d.day).toBe(1); expect(d.toString()).toBe('2025-01-01'); + expect(d.isDate()).toBeTruthy(); + expect(d.isMonth()).toBeFalsy(); + expect(d.isYear()).toBeFalsy() }); test('year‑month', () => { @@ -33,6 +36,9 @@ describe('IsoDate.parse – valid inputs', () => { expect(d.month).toBe(1); expect(d.day).toBeUndefined(); expect(d.toString()).toBe('2025-01'); + expect(d.isDate()).toBeFalsy(); + expect(d.isMonth()).toBeTruthy(); + expect(d.isYear()).toBeFalsy() }); test('year only', () => { @@ -43,6 +49,9 @@ describe('IsoDate.parse – valid inputs', () => { expect(d.month).toBeUndefined(); expect(d.day).toBeUndefined(); expect(d.toString()).toBe('2025'); + expect(d.isDate()).toBeFalsy(); + expect(d.isMonth()).toBeFalsy(); + expect(d.isYear()).toBeTruthy() }); test('leap‑year February 29', () => { diff --git a/src/common/IsoDate/IsoDate.ts b/src/common/IsoDate/IsoDate.ts index c3c489f..00613db 100644 --- a/src/common/IsoDate/IsoDate.ts +++ b/src/common/IsoDate/IsoDate.ts @@ -1,10 +1,12 @@ /** * IsoDate – a lightweight class for representing calendar dates. -* + * * Supported input formats: - * - YYYY-MM-DD (e.g., 2025-01-01) - * - YYYY-MM (e.g., 2025-01) - * - YYYY (e.g., 2025) + * - YYYY-MM-DD (e.g., 2025-01-01, equivalent to 2025-01-01T00:00:00.000Z/2025-01-01T23:59:59.999Z) + * - YYYY-MM (e.g., 2025-01, equivalent to 2025-01-01T00:00:00.000Z/2025-01-31T23:59:59.999Z) + * - YYYY (e.g., 2025, equivalent to 2025-01-01T00:00:00.000Z/2025-12-31T23:59:59.999Z) + * + * As shown in the example above, all IsoDate is assumed to be UTC (i.e., timezone = 'Z') * * Note we do not support (even though it is allowed by ISO 8601) * - "compact date" (i.e., YYYYMMDD) @@ -111,6 +113,22 @@ export class IsoDate { return IsoDate.isLeapYear(this.year); } + /** Return true if the stored year is a leap year. */ + public isYear(): boolean { + return this.toString().length === 4; + } + + /** Return true if the stored year is a leap year. */ + public isMonth(): boolean { + return this.toString().length === 7; + } + + /** Return true if the stored year is a leap year. */ + public isDate(): boolean { + return this.toString().length === 10; + } + + /** Normalized string representation. */ public toString(): string { const y = String(this.year).padStart(4, '0'); diff --git a/src/common/IsoDate/IsoDatetime.test.unit.ts b/src/common/IsoDate/IsoDatetime.test.unit.ts index 2477554..ac360fb 100644 --- a/src/common/IsoDate/IsoDatetime.test.unit.ts +++ b/src/common/IsoDate/IsoDatetime.test.unit.ts @@ -1,3 +1,14 @@ +/** + * Unit tests for IsoDatetime + * + * AI Usage: + * - This code was originally generated using + * 1. OpenAI/GPT OSS 120b on Roo Code + * 2. Gemini 2.5 Flash and 2.5 Pro + * then modified to fix incorrect implementations and fit project needs. + * The first commit contains these corrections so that all code committed + * works as designed. + */ import { describe, test, expect } from '@jest/globals'; import { IsoDatetime } from './IsoDatetime.js'; @@ -60,6 +71,9 @@ describe('IsoDatetime.parse – valid inputs', () => { describe('IsoDatetime.parse – invalid inputs', () => { const invalid = [ + '2025-03-01', // date only + '2025-03', // month only + '2025', // year only '2025-03-01 12:34:56Z', // no 'T' separator '2025-03-01T12:34Z', // missing seconds '2025-03-01T12:34:56.789', // missing Z @@ -97,54 +111,76 @@ describe('IsoDatetime.toString – formatting', () => { }); }); + +describe('IsoDatetime.toIsoDate', () => { + const tests = [ + { input: '2025-03-01T12:34:56Z', expected: '2025-03-01' }, + { input: '2025-03-01T12:34:56.001Z', expected: '2025-03-01' }, + { input: '2025-03-01T12:34:56+02:00', expected: '2025-03-01' }, + { input: '2025-03-01T12:34:56-04:30', expected: '2025-03-01' }, + ]; + + tests.forEach(({ input, expected }) => { + test(`converts an IsoDatetime(${input}) to its IsoDate equivalent`, () => { + const isoDatetime = IsoDatetime.parse(input); + const isoDate = isoDatetime.toIsoDate(); + expect(isoDate.toString()).toBe(expected); + }); + }); +}); + describe('IsoDatetime.getNextDay – day increments and decrements', () => { + test('regular date: March 4 +1 day => March 5', () => { + const dt = IsoDatetime.parse('2024-03-04T00:00:00Z'); + expect(dt.getNextDay()).toEqual(IsoDatetime.parse('2024-03-05T00:00:00Z')); + }); test('leap year: Feb 28 +1 day => Feb 29', () => { const dt = IsoDatetime.parse('2024-02-28T00:00:00Z'); - expect(dt.getNextDay()).toBe('2024-02-29T00:00:00Z'); + expect(dt.getNextDay()).toEqual(IsoDatetime.parse('2024-02-29T00:00:00Z')); }); test('non‑leap year: Feb 28 +1 day => Mar 01', () => { const dt = IsoDatetime.parse('2025-02-28T00:00:00Z'); - expect(dt.getNextDay(1)).toBe('2025-03-01T00:00:00Z'); + expect(dt.getNextDay(1)).toEqual(IsoDatetime.parse('2025-03-01T00:00:00Z')); }); test('leap day: Feb 29 +1 day => Mar 01', () => { const dt = IsoDatetime.parse('2024-02-29T00:00:00Z'); - expect(dt.getNextDay(1)).toBe('2024-03-01T00:00:00Z'); + expect(dt.getNextDay(1)).toEqual(IsoDatetime.parse('2024-03-01T00:00:00Z')); }); test('month boundary: Jan 31 +1 day => Feb 01', () => { const dt = IsoDatetime.parse('2025-01-31T00:00:00Z'); - expect(dt.getNextDay(1)).toBe('2025-02-01T00:00:00Z'); + expect(dt.getNextDay(1)).toEqual(IsoDatetime.parse('2025-02-01T00:00:00Z')); }); test('year boundary: Dec 31 +1 day => Jan 01 of next year', () => { const dt = IsoDatetime.parse('2025-12-31T00:00:00Z'); - expect(dt.getNextDay(1)).toBe('2026-01-01T00:00:00Z'); + expect(dt.getNextDay(1)).toEqual(IsoDatetime.parse('2026-01-01T00:00:00Z')); }); test('century non‑leap year (1900): Feb 28 +1 day => Mar 01', () => { const dt = IsoDatetime.parse('1900-02-28T00:00:00Z'); - expect(dt.getNextDay(1)).toBe('1900-03-01T00:00:00Z'); + expect(dt.getNextDay(1)).toEqual(IsoDatetime.parse('1900-03-01T00:00:00Z')); }); test('century leap year (2000): Feb 28 +1 day => Feb 29', () => { const dt = IsoDatetime.parse('2000-02-28T00:00:00Z'); - expect(dt.getNextDay(1)).toBe('2000-02-29T00:00:00Z'); + expect(dt.getNextDay(1)).toEqual(IsoDatetime.parse('2000-02-29T00:00:00Z')); }); test('negative increment: Mar 01 -1 day => Feb 28 (non‑leap year)', () => { const dt = IsoDatetime.parse('2025-03-01T00:00:00Z'); - expect(dt.getNextDay(-1)).toBe('2025-02-28T00:00:00Z'); + expect(dt.getNextDay(-1)).toEqual(IsoDatetime.parse('2025-02-28T00:00:00Z')); }); test('negative increment: Mar 01 -1 day => Feb 28 (leap year)', () => { const dt = IsoDatetime.parse('2024-03-01T00:00:00Z'); - expect(dt.getNextDay(-1)).toBe('2024-02-29T00:00:00Z'); + expect(dt.getNextDay(-1)).toEqual(IsoDatetime.parse('2024-02-29T00:00:00Z')); }); test('negative crossing year: Jan 01 -1 day => Dec 31 of previous year', () => { const dt = IsoDatetime.parse('2025-01-01T00:00:00Z'); - expect(dt.getNextDay(-1)).toBe('2024-12-31T00:00:00Z'); + expect(dt.getNextDay(-1)).toEqual(IsoDatetime.parse('2024-12-31T00:00:00Z')); }); -}); \ No newline at end of file +}); diff --git a/src/common/IsoDate/IsoDatetime.ts b/src/common/IsoDate/IsoDatetime.ts index 7acbeec..8864341 100644 --- a/src/common/IsoDate/IsoDatetime.ts +++ b/src/common/IsoDate/IsoDatetime.ts @@ -15,9 +15,19 @@ * Example: * const dt = IsoDatetime.parse('2025-03-01T12:34:56.789+02:00'); * console.log(dt.toString()); // "2025-03-01T10:34:56.789Z" + * + * AI Usage: + * - This code was originally generated using + * 1. OpenAI/GPT OSS 120b on Roo Code + * 2. Gemini 2.5 Flash and 2.5 Pro + * then modified to fix incorrect implementations and fit project needs. + * The first commit contains these corrections so that all code committed + * works as designed. */ import { IsoDate } from './IsoDate.js'; +export * from './IsoDate.js' + export class IsoDatetime extends IsoDate { /** Hour (0‑23) */ public readonly hour: number; @@ -151,6 +161,20 @@ export class IsoDatetime extends IsoDate { const msPart = this.millisecond > 0 ? `.${String(this.millisecond).padStart(3, '0')}` : ''; return `${y}-${m}-${d}T${h}:${min}:${s}${msPart}Z`; } + + /** returns an IsoDate + * Note: lossy precision + * Even though this reduces the precision of a Datetime, it is useful for some purposes + */ + public toIsoDate(): IsoDate { + const y = String(this.year).padStart(4, '0'); + const m = String(this.month).padStart(2, '0'); + const d = String(this.day).padStart(2, '0'); + const dateString = `${y}-${m}-${d}`; + return IsoDate.parse(dateString); + } + + /** * Returns an ISO‑8601 string representing this datetime shifted by the given * number of days (positive or negative). The shift respects month lengths, @@ -158,7 +182,7 @@ export class IsoDatetime extends IsoDate { * * @param increment Number of days to shift; may be negative. */ - public getNextDay(increment: number = 1): string { + public getNextDay(increment: number = 1): IsoDatetime { // Compute the UTC timestamp for the current instance. const utcMs = Date.UTC( this.year, @@ -181,7 +205,7 @@ export class IsoDatetime extends IsoDate { d.getUTCSeconds(), d.getUTCMilliseconds(), ); - return next.toString(); + return next; } } \ No newline at end of file diff --git a/src/search/SearchQueryBuilder.test.unit.ts b/src/search/SearchQueryBuilder.test.unit.ts index 5f1f456..03f289e 100644 --- a/src/search/SearchQueryBuilder.test.unit.ts +++ b/src/search/SearchQueryBuilder.test.unit.ts @@ -1,6 +1,6 @@ // For a more comprehensive set of test cases, see the tests // in test_cases/search_* -import { describe, it, test, expect } from '@jest/globals'; +import { describe, it, expect } from '@jest/globals'; import { SearchOptions } from "./BasicSearchManager.js" import { SearchRequestType, SearchRequest, SearchRequestTypeId } from "./SearchRequest.js"; diff --git a/src/search/SearchQueryBuilder.ts b/src/search/SearchQueryBuilder.ts index 0c2d220..2cc7230 100644 --- a/src/search/SearchQueryBuilder.ts +++ b/src/search/SearchQueryBuilder.ts @@ -1,4 +1,7 @@ import { CveResult } from '../result/CveResult.js'; +import { IsoDate } from '../common/IsoDate/IsoDate.js'; +import { IsoDatetime } from '../common/IsoDate/IsoDatetime.js'; +import { toSearchDateDslString } from '../adapters/search/OpensearchDatetimeUtils.js'; import { SearchOptions } from './BasicSearchManager.js'; import { SearchRequest } from './SearchRequest.js'; @@ -85,11 +88,13 @@ export class SearchQueryBuilder { if (isDate) { // assemble all the date fields into an array let dateFields = []; + const startDate = (this._searchText.length > 10) ? IsoDatetime.parse(this._searchText) : IsoDate.parse(this._searchText); + const startDateStr = toSearchDateDslString(startDate) SearchQueryBuilder.kDateFieldPaths.map(path => { let field = `{ "range": { "${path}": { - "gte": "${this._searchText}" + "gte": "${startDateStr}" } } }`; diff --git a/src/search/__snapshots__/SearchQueryBuilder.test.unit.ts.snap b/src/search/__snapshots__/SearchQueryBuilder.test.unit.ts.snap index f4f0c72..bcea759 100644 --- a/src/search/__snapshots__/SearchQueryBuilder.test.unit.ts.snap +++ b/src/search/__snapshots__/SearchQueryBuilder.test.unit.ts.snap @@ -108,14 +108,14 @@ CveResult { Object { "range": Object { "containers.cna.providerMetadata.dateUpdated": Object { - "gte": "2023-12-21", + "gte": "2023-12-21||/d", }, }, }, Object { "range": Object { "containers.cna.timeline.time": Object { - "gte": "2023-12-21", + "gte": "2023-12-21||/d", }, }, }, From 42592188e3f40c8433acd7d8151169118d447d01 Mon Sep 17 00:00:00 2001 From: hkong-mitre <122547078+hkong-mitre@users.noreply.github.com> Date: Tue, 30 Sep 2025 13:02:33 -0400 Subject: [PATCH 09/11] date search when specifying just a date now searches for any time during that day instead of defaulting to a date range of that date to current time --- src/common/IsoDate/IsoDate.ts | 7 +- src/common/IsoDate/IsoDatetime.test.int.ts | 19 +- src/common/IsoDate/IsoDatetime.test.unit.ts | 41 +++- src/common/IsoDate/IsoDatetime.ts | 43 ++++- src/search/SearchQueryBuilder.ts | 7 +- .../search_dates.test.e2e.ts.snap | 177 +----------------- .../test_cases/search_dates.test.e2e.ts | 6 +- 7 files changed, 100 insertions(+), 200 deletions(-) diff --git a/src/common/IsoDate/IsoDate.ts b/src/common/IsoDate/IsoDate.ts index 00613db..496d093 100644 --- a/src/common/IsoDate/IsoDate.ts +++ b/src/common/IsoDate/IsoDate.ts @@ -143,12 +143,12 @@ export class IsoDate { return y; } - /** Static helper – leap‑year check for any year. */ + /** Static function that returns true iff year is a leap‑year */ public static isLeapYear(year: number): boolean { return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; } - /** Static helper – number of days in a given month/year. */ + /** Static function that returns number of days in a given month/year. */ public static daysInMonth(year: number, month: number): number { switch (month) { case 2: @@ -164,7 +164,8 @@ export class IsoDate { } } -// Utility function to check if a string is a valid ISO date according to IsoDate parsing rules. +/* Utility function to check if a string is a valid ISO date according to IsoDate parsing rules + */ export function isValidIsoDate(value: string): boolean { try { IsoDate.parse(value); diff --git a/src/common/IsoDate/IsoDatetime.test.int.ts b/src/common/IsoDate/IsoDatetime.test.int.ts index cb3069d..40c4a15 100644 --- a/src/common/IsoDate/IsoDatetime.test.int.ts +++ b/src/common/IsoDate/IsoDatetime.test.int.ts @@ -1,6 +1,6 @@ import { describe, test, expect } from '@jest/globals'; import { IsoDate } from './IsoDate.js'; -import { IsoDatetime } from './IsoDatetime.js'; +import { IsoDatetime, toIsoDatetime } from './IsoDatetime.js'; describe('IsoDatetime.fromIsoDate', () => { test('converts an IsoDate to midnight UTC IsoDatetime', () => { @@ -9,4 +9,21 @@ describe('IsoDatetime.fromIsoDate', () => { expect(datetime).toBeInstanceOf(IsoDatetime); expect(datetime.toString()).toBe('2025-03-15T00:00:00Z'); }); +}); + + +describe('IsoDatetime.toIsoDatetime', () => { + test('converts an IsoDate to midnight UTC IsoDatetime', () => { + const date = IsoDate.parse('2025-03-15'); + const datetime = toIsoDatetime(date); + expect(datetime).toBeInstanceOf(IsoDatetime); + expect(datetime.toString()).toBe('2025-03-15T00:00:00Z'); + }); + + test('converts an IsoDatetime to midnight UTC IsoDatetime', () => { + const date = IsoDatetime.parse('2025-03-15T00:00:00Z'); + const datetime = toIsoDatetime(date); + expect(datetime).toBeInstanceOf(IsoDatetime); + expect(datetime.toString()).toBe('2025-03-15T00:00:00Z'); + }); }); \ No newline at end of file diff --git a/src/common/IsoDate/IsoDatetime.test.unit.ts b/src/common/IsoDate/IsoDatetime.test.unit.ts index ac360fb..fedf9ad 100644 --- a/src/common/IsoDate/IsoDatetime.test.unit.ts +++ b/src/common/IsoDate/IsoDatetime.test.unit.ts @@ -10,6 +10,7 @@ * works as designed. */ import { describe, test, expect } from '@jest/globals'; +import { IsoDate } from './IsoDatetime.js'; import { IsoDatetime } from './IsoDatetime.js'; describe('IsoDatetime.parse – valid inputs', () => { @@ -61,22 +62,30 @@ describe('IsoDatetime.parse – valid inputs', () => { expect(dt.toString()).toBe('2025-03-01T02:30:00Z'); }); - test('leap year date with offset', () => { - const str = '2024-02-29T23:00:00+01:00'; - const dt = IsoDatetime.parse(str); - // 23:00 +01:00 => 22:00Z on leap day - expect(dt.toString()).toBe('2024-02-29T22:00:00Z'); + const valid = [ + { input: '2024-02-29T23:00:00+01:00', expected: '2024-02-29T22:00:00Z' }, // leap year date with offset + { input: '2025-03-01', expected: '2025-03-01T00:00:00Z' }, // date only + { input: '2025-03-01T12:34:56.789', expected: '2025-03-01T12:34:56.789Z' }, // missing Z + // currently does not allow the following even though it is valid in ISO 8601 + // { input: '2025-03', expected: '2024-03' }, // month only + // { input: '2025', expected: '2024' }, // year only + ]; + + valid.forEach(({ input, expected }) => { + test(`IsoDatetime.parse(${input})`, () => { + const datetime = IsoDatetime.parse(input); + expect(datetime.toString()).toBe(expected); + }); }); }); + describe('IsoDatetime.parse – invalid inputs', () => { const invalid = [ - '2025-03-01', // date only '2025-03', // month only '2025', // year only '2025-03-01 12:34:56Z', // no 'T' separator '2025-03-01T12:34Z', // missing seconds - '2025-03-01T12:34:56.789', // missing Z '2025-13-01T12:34:56Z', // bad month '2025-00-01T12:34:56Z', // bad month '2025-02-30T12:34:56Z', // bad number of days in February @@ -95,6 +104,24 @@ describe('IsoDatetime.parse – invalid inputs', () => { }); }); + +describe('IsoDatetime.fromIsoDate()', () => { + const valid = [ + { input: '2025-03-02', expected: '2025-03-02T00:00:00Z' }, // date only + { input: '2025-03', expected: '2025-03-01T00:00:00Z' }, // month only + { input: '2025', expected: '2025-01-01T00:00:00Z' }, // year only + ]; + + valid.forEach(({ input, expected }) => { + test(`IsoDatetime.fromIsoDate(${input})`, () => { + const date = IsoDate.parse(input); + const datetime = IsoDatetime.fromIsoDate(date); + expect(datetime.toString()).toBe(expected); + }); + }); +}); + + describe('IsoDatetime.toString – formatting', () => { const tests = [ { input: '2025-03-01T12:34:56Z', expected: '2025-03-01T12:34:56Z' }, diff --git a/src/common/IsoDate/IsoDatetime.ts b/src/common/IsoDate/IsoDatetime.ts index 8864341..413c144 100644 --- a/src/common/IsoDate/IsoDatetime.ts +++ b/src/common/IsoDate/IsoDatetime.ts @@ -67,7 +67,7 @@ export class IsoDatetime extends IsoDate { // 4. optional .sss (fractional seconds) // 5. timezone: Z or ±hh:mm const regex = - /^(?\d{4})-(?\d{2})-(?\d{2})T(?\d{2}):(?\d{2}):(?\d{2})(?:\.(?\d+))?(?Z|[+-]\d{2}:\d{2})$/; + /^(?\d{4})-(?\d{2})-(?\d{2})(?:T(?\d{2}):(?\d{2}):(?\d{2})(?:\.(?\d+))?(?Z|[+-]\d{2}:\d{2})?)?$/; const match = regex.exec(value); if (!match || !match.groups) { @@ -79,12 +79,12 @@ export class IsoDatetime extends IsoDate { const year = Number(match.groups.year); const month = Number(match.groups.month); const day = Number(match.groups.day); - const hour = Number(match.groups.hour); - const minute = Number(match.groups.minute); - const second = Number(match.groups.second); + const hour = Number(match.groups.hour ?? '0'); + const minute = Number(match.groups.minute ?? '0'); + const second = Number(match.groups.second ?? '0'); const msStr = match.groups.ms ?? '0'; const millisecond = Number(msStr.padEnd(3, '0').substring(0, 3)); // keep three digits - const tz = match.groups.tz; + const tz = match.groups.tz ?? 'Z'; // Validate date components using IsoDate's helpers. if (year < 1 || year > 2500) { @@ -144,9 +144,22 @@ export class IsoDatetime extends IsoDate { ); } - /** Convert this IsoDate to an IsoDatetime at midnight UTC. */ + /** Convert this IsoDate to an IsoDatetime + * if date -> at midnight UTC. + * if month -> 1st of month at midnight UTC. + * if year -> 1st of the year at midnight UTC. + */ public static fromIsoDate(date: IsoDate): IsoDatetime { - const isoString = `${date.toString()}T00:00:00.000Z`; + let isoString; + if (date.isDate()) { + isoString = `${date.toString()}T00:00:00.000Z`; + } + else if (date.isMonth()) { + isoString = `${date.toString()}-01T00:00:00.000Z`; + } + else if (date.isYear()) { + isoString = `${date.toString()}-01-01T00:00:00.000Z`; + } return IsoDatetime.parse(isoString); } @@ -208,4 +221,18 @@ export class IsoDatetime extends IsoDate { return next; } -} \ No newline at end of file +} + + +/* Utility function that always returns a IsoDatetime version + * of a IsoDate or IsoDatetime object, very useful + * when the type of the IsoDate object can be either + */ +export function toIsoDatetime(o: IsoDate | IsoDatetime): IsoDatetime { + if (o instanceof IsoDatetime) { + return o; + } + else { // if (o instanceof IsoDate) { + return IsoDatetime.fromIsoDate(o); + } +} diff --git a/src/search/SearchQueryBuilder.ts b/src/search/SearchQueryBuilder.ts index 2cc7230..52aadb1 100644 --- a/src/search/SearchQueryBuilder.ts +++ b/src/search/SearchQueryBuilder.ts @@ -88,13 +88,16 @@ export class SearchQueryBuilder { if (isDate) { // assemble all the date fields into an array let dateFields = []; + // if the user had requested an IsoDate, it should stay as an IsoDate and not be cast to an IsoDatetime + // otherwise YYYY or YYYY-MM requests would not work const startDate = (this._searchText.length > 10) ? IsoDatetime.parse(this._searchText) : IsoDate.parse(this._searchText); - const startDateStr = toSearchDateDslString(startDate) + const stopDate = IsoDatetime.fromIsoDate(startDate).getNextDay() SearchQueryBuilder.kDateFieldPaths.map(path => { let field = `{ "range": { "${path}": { - "gte": "${startDateStr}" + "gte": "${toSearchDateDslString(startDate)}", + "lt": "${toSearchDateDslString(stopDate)}" } } }`; diff --git a/src/search/test_cases/__snapshots__/search_dates.test.e2e.ts.snap b/src/search/test_cases/__snapshots__/search_dates.test.e2e.ts.snap index 78b32c1..5188a58 100644 --- a/src/search/test_cases/__snapshots__/search_dates.test.e2e.ts.snap +++ b/src/search/test_cases/__snapshots__/search_dates.test.e2e.ts.snap @@ -2,189 +2,14 @@ exports[`Date Search (e2e tests) BasicSearchManager.search() search('2024-06-14') should return cves including CVE-2024-6006 1`] = ` Array [ - "CVE-2017-9711", - "CVE-2018-4301", - "CVE-2018-9367", - "CVE-2022-21833", - "CVE-2022-21834", - "CVE-2022-21836", - "CVE-2022-21843", - "CVE-2022-21862", - "CVE-2022-21864", - "CVE-2022-21867", - "CVE-2022-21880", - "CVE-2022-21881", - "CVE-2022-21883", - "CVE-2022-21894", - "CVE-2022-21900", - "CVE-2022-21903", - "CVE-2022-21904", - "CVE-2022-21908", - "CVE-2022-21916", - "CVE-2022-21924", - "CVE-2022-21958", - "CVE-2022-21960", - "CVE-2022-21961", - "CVE-2022-21962", - "CVE-2022-21963", - "CVE-2022-21983", - "CVE-2022-21989", - "CVE-2022-21990", - "CVE-2022-25622", - "CVE-2022-30133", - "CVE-2022-30146", - "CVE-2022-30190", - "CVE-2022-30194", - "CVE-2022-30202", - "CVE-2022-30213", - "CVE-2022-34714", - "CVE-2022-45186", - "CVE-2023-32041", - "CVE-2023-40708", - "CVE-2023-47039", - "CVE-2023-50781", - "CVE-2024-10277", - "CVE-2024-10451", - "CVE-2024-10773", - "CVE-2024-10785", - "CVE-2024-10881", - "CVE-2024-10941", - "CVE-2024-11385", - "CVE-2024-11388", - "CVE-2024-11396", - "CVE-2024-11424", - "CVE-2024-11921", - "CVE-2024-12012", - "CVE-2024-12110", - "CVE-2024-20151", - "CVE-2024-20152", - "CVE-2024-20153", - "CVE-2024-20441", - "CVE-2024-26118", - "CVE-2024-26817", "CVE-2024-31162", - "CVE-2024-31315", - "CVE-2024-31318", - "CVE-2024-31880", - "CVE-2024-35532", - "CVE-2024-36155", - "CVE-2024-36156", - "CVE-2024-36157", - "CVE-2024-36158", - "CVE-2024-36159", - "CVE-2024-36160", - "CVE-2024-36176", - "CVE-2024-36177", - "CVE-2024-38633", - "CVE-2024-38634", - "CVE-2024-38635", - "CVE-2024-38649", - "CVE-2024-40750", - "CVE-2024-41030", - "CVE-2024-41875", - "CVE-2024-41877", - "CVE-2024-42169", - "CVE-2024-43624", - "CVE-2024-43639", - "CVE-2024-44025", - "CVE-2024-44042", - "CVE-2024-44043", - "CVE-2024-44045", - "CVE-2024-44299", - "CVE-2024-45251", - "CVE-2024-46685", - "CVE-2024-46689", - "CVE-2024-46692", - "CVE-2024-46704", - "CVE-2024-46705", - "CVE-2024-46706", - "CVE-2024-46707", - "CVE-2024-46835", - "CVE-2024-46847", - "CVE-2024-46866", - "CVE-2024-47333", - "CVE-2024-47360", - "CVE-2024-47517", - "CVE-2024-47518", - "CVE-2024-47519", - "CVE-2024-47642", - "CVE-2024-47863", - "CVE-2024-48245", - "CVE-2024-48510", - "CVE-2024-51757", - "CVE-2024-52940", - "CVE-2024-53476", - "CVE-2024-54014", - "CVE-2024-54763", - "CVE-2024-54818", - "CVE-2024-54849", - "CVE-2024-55408", - "CVE-2024-56521", - "CVE-2024-56527", - "CVE-2024-57224", - "CVE-2024-57650", - "CVE-2024-5938", - "CVE-2024-5965", "CVE-2024-5981", "CVE-2024-6006", - "CVE-2024-6073", - "CVE-2024-6136", - "CVE-2024-6615", - "CVE-2024-7234", - "CVE-2024-7382", - "CVE-2024-7658", - "CVE-2024-8608", - "CVE-2024-8848", - "CVE-2024-9134", - "CVE-2024-9313", - "CVE-2024-9420", - "CVE-2024-9442", - "CVE-2025-0240", - "CVE-2025-0241", - "CVE-2025-23022", - "CVE-2025-23036", - "CVE-2025-23037", - "CVE-2025-23091", - "CVE-2025-23114", - "CVE-2025-24339", - "CVE-2025-25305", - "CVE-2025-25728", - "CVE-2025-27594", - "CVE-2025-27722", - "CVE-2025-28169", - "CVE-2025-30140", - "CVE-2025-30474", - "CVE-2025-30676", - "CVE-2025-32053", - "CVE-2025-37730", ] `; -exports[`Date Search (e2e tests) BasicSearchManager.search() search('2025-01-11') should return cves including CVE-2025-37730 1`] = ` +exports[`Date Search (e2e tests) BasicSearchManager.search() search('2025-01-11') should return cves including CVE-2024-42169 1`] = ` Array [ - "CVE-2022-25622", - "CVE-2024-11396", - "CVE-2024-12012", - "CVE-2024-26817", "CVE-2024-42169", - "CVE-2024-57650", - "CVE-2025-0240", - "CVE-2025-0241", - "CVE-2025-23022", - "CVE-2025-23036", - "CVE-2025-23037", - "CVE-2025-23091", - "CVE-2025-23114", - "CVE-2025-24339", - "CVE-2025-25305", - "CVE-2025-25728", - "CVE-2025-27594", - "CVE-2025-27722", - "CVE-2025-28169", - "CVE-2025-30140", - "CVE-2025-30474", - "CVE-2025-30676", - "CVE-2025-32053", - "CVE-2025-37730", ] `; diff --git a/src/search/test_cases/search_dates.test.e2e.ts b/src/search/test_cases/search_dates.test.e2e.ts index 784f5ea..1dc891a 100644 --- a/src/search/test_cases/search_dates.test.e2e.ts +++ b/src/search/test_cases/search_dates.test.e2e.ts @@ -14,9 +14,9 @@ describe('Date Search (e2e tests)', () => { // expected defaults to false in this series! const testCases: Array<{ input: string; expected?: number; first?: string; includes: string; snapshot?: boolean}> = [ // valid dates - { input: `2022-01-11`, expected: 892, includes: "CVE-2022-31279", snapshot: false }, // developer tools returns 861 instead - { input: `2024-06-14`, expected: 155, includes: "CVE-2024-6006", snapshot: true }, // developer tools returns 138 instead - { input: `2025-01-11`, expected: 24, includes: "CVE-2025-37730", snapshot: true } // developer tools reads 9 instead of 24 + // { input: `2022-01-11`, expected: 892, includes: "CVE-2022-31279", snapshot: false }, // developer tools returns 861 instead + { input: `2024-06-14`, expected: 3, includes: "CVE-2024-6006", snapshot: true }, // developer tools returns 138 instead + { input: `2025-01-11`, expected: 1, includes: "CVE-2024-42169", snapshot: true } // developer tools reads 9 instead of 24 ]; testCases.forEach(({ input, expected, first, includes, snapshot }) => { From 4c3434e5e5d0ce89cc47c93620747488a621f516 Mon Sep 17 00:00:00 2001 From: hkong-mitre <122547078+hkong-mitre@users.noreply.github.com> Date: Tue, 30 Sep 2025 13:04:35 -0400 Subject: [PATCH 10/11] enable all date fields --- src/search/SearchQueryBuilder.ts | 16 ++++++++-------- .../__snapshots__/search_dates.test.e2e.ts.snap | 2 ++ src/search/test_cases/search_dates.test.e2e.ts | 4 ++-- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/search/SearchQueryBuilder.ts b/src/search/SearchQueryBuilder.ts index 52aadb1..87f98e9 100644 --- a/src/search/SearchQueryBuilder.ts +++ b/src/search/SearchQueryBuilder.ts @@ -17,16 +17,16 @@ export class SearchQueryBuilder { /** the JSON paths to CVE fields that are of the date type */ static kDateFieldPaths = [ - // 'cveMetadata.datePublished', - // 'cveMetadata.dateRejected', - // 'cveMetadata.dateReserved', - // 'cveMetadata.dateUpdated', - // 'containers.cna.datePublic', + 'cveMetadata.datePublished', + 'cveMetadata.dateRejected', + 'cveMetadata.dateReserved', + 'cveMetadata.dateUpdated', + 'containers.cna.datePublic', 'containers.cna.providerMetadata.dateUpdated', 'containers.cna.timeline.time', - // 'containers.adp.metrics.other.content.dateAdded', - // 'containers.adp.metrics.other.content.timestamp', - // 'containers.adp.providerMetadata.dateUpdated' + 'containers.adp.metrics.other.content.dateAdded', + 'containers.adp.metrics.other.content.timestamp', + 'containers.adp.providerMetadata.dateUpdated' ]; /** the user entered text */ diff --git a/src/search/test_cases/__snapshots__/search_dates.test.e2e.ts.snap b/src/search/test_cases/__snapshots__/search_dates.test.e2e.ts.snap index 5188a58..357e990 100644 --- a/src/search/test_cases/__snapshots__/search_dates.test.e2e.ts.snap +++ b/src/search/test_cases/__snapshots__/search_dates.test.e2e.ts.snap @@ -3,6 +3,7 @@ exports[`Date Search (e2e tests) BasicSearchManager.search() search('2024-06-14') should return cves including CVE-2024-6006 1`] = ` Array [ "CVE-2024-31162", + "CVE-2024-36155", "CVE-2024-5981", "CVE-2024-6006", ] @@ -11,5 +12,6 @@ Array [ exports[`Date Search (e2e tests) BasicSearchManager.search() search('2025-01-11') should return cves including CVE-2024-42169 1`] = ` Array [ "CVE-2024-42169", + "CVE-2025-23114", ] `; diff --git a/src/search/test_cases/search_dates.test.e2e.ts b/src/search/test_cases/search_dates.test.e2e.ts index 1dc891a..747bf18 100644 --- a/src/search/test_cases/search_dates.test.e2e.ts +++ b/src/search/test_cases/search_dates.test.e2e.ts @@ -15,8 +15,8 @@ describe('Date Search (e2e tests)', () => { const testCases: Array<{ input: string; expected?: number; first?: string; includes: string; snapshot?: boolean}> = [ // valid dates // { input: `2022-01-11`, expected: 892, includes: "CVE-2022-31279", snapshot: false }, // developer tools returns 861 instead - { input: `2024-06-14`, expected: 3, includes: "CVE-2024-6006", snapshot: true }, // developer tools returns 138 instead - { input: `2025-01-11`, expected: 1, includes: "CVE-2024-42169", snapshot: true } // developer tools reads 9 instead of 24 + { input: `2024-06-14`, expected: 4, includes: "CVE-2024-6006", snapshot: true }, // developer tools returns 138 instead + { input: `2025-01-11`, expected: 2, includes: "CVE-2024-42169", snapshot: true } // developer tools reads 9 instead of 24 ]; testCases.forEach(({ input, expected, first, includes, snapshot }) => { From 0beb37e0d37921e45b8648fffc82a498563d9f1f Mon Sep 17 00:00:00 2001 From: hkong-mitre <122547078+hkong-mitre@users.noreply.github.com> Date: Tue, 30 Sep 2025 13:34:17 -0400 Subject: [PATCH 11/11] add more tests --- .../search_dates.test.e2e.ts.snap | 34 +++++++++++++++++++ .../test_cases/search_dates.test.e2e.ts | 15 +++++--- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/search/test_cases/__snapshots__/search_dates.test.e2e.ts.snap b/src/search/test_cases/__snapshots__/search_dates.test.e2e.ts.snap index 357e990..cdb0619 100644 --- a/src/search/test_cases/__snapshots__/search_dates.test.e2e.ts.snap +++ b/src/search/test_cases/__snapshots__/search_dates.test.e2e.ts.snap @@ -1,5 +1,39 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Date Search (e2e tests) BasicSearchManager.search() search('1999') should return cves including undefined 1`] = ` +Array [ + "CVE-1999-0529", +] +`; + +exports[`Date Search (e2e tests) BasicSearchManager.search() search('2022-01-11') should return cves including CVE-2022-21963 1`] = ` +Array [ + "CVE-2021-45052", + "CVE-2022-21833", + "CVE-2022-21834", + "CVE-2022-21836", + "CVE-2022-21843", + "CVE-2022-21862", + "CVE-2022-21864", + "CVE-2022-21867", + "CVE-2022-21880", + "CVE-2022-21881", + "CVE-2022-21883", + "CVE-2022-21894", + "CVE-2022-21900", + "CVE-2022-21903", + "CVE-2022-21904", + "CVE-2022-21908", + "CVE-2022-21916", + "CVE-2022-21924", + "CVE-2022-21958", + "CVE-2022-21960", + "CVE-2022-21961", + "CVE-2022-21962", + "CVE-2022-21963", +] +`; + exports[`Date Search (e2e tests) BasicSearchManager.search() search('2024-06-14') should return cves including CVE-2024-6006 1`] = ` Array [ "CVE-2024-31162", diff --git a/src/search/test_cases/search_dates.test.e2e.ts b/src/search/test_cases/search_dates.test.e2e.ts index 747bf18..f69522d 100644 --- a/src/search/test_cases/search_dates.test.e2e.ts +++ b/src/search/test_cases/search_dates.test.e2e.ts @@ -12,11 +12,17 @@ describe('Date Search (e2e tests)', () => { describe.only('BasicSearchManager.search()', () => { // expected defaults to false in this series! - const testCases: Array<{ input: string; expected?: number; first?: string; includes: string; snapshot?: boolean}> = [ + const testCases: Array<{ input: string; expected?: number; first?: string; includes?: string; snapshot?: boolean; }> = [ // valid dates - // { input: `2022-01-11`, expected: 892, includes: "CVE-2022-31279", snapshot: false }, // developer tools returns 861 instead - { input: `2024-06-14`, expected: 4, includes: "CVE-2024-6006", snapshot: true }, // developer tools returns 138 instead - { input: `2025-01-11`, expected: 2, includes: "CVE-2024-42169", snapshot: true } // developer tools reads 9 instead of 24 + { input: `2022-01-11`, expected: 23, includes: "CVE-2022-21963", snapshot: true }, + // { input: `2025-01-11T02:31:22.611Z`, expected: 1, includes: "CVE-2024-42169", snapshot: true }, + { input: `2024-06-14`, expected: 4, includes: "CVE-2024-6006", snapshot: true }, + { input: `2025-01-11`, expected: 2, includes: "CVE-2024-42169", snapshot: true }, + { input: `1999`, expected: 0, snapshot: true }, + // { input: `2022`, expected: 0, snapshot: true }, + // { input: `2022-01`, expected: 12, includes: "CVE-2022-40621", snapshot: true }, // why search by month results in less than search by date in same month + // invalid dates + ]; testCases.forEach(({ input, expected, first, includes, snapshot }) => { @@ -39,6 +45,7 @@ describe('Date Search (e2e tests)', () => { const cveIdMap = retlist.map(cve => { cves.push(cve["_id"]) }) + console.log(`cves: ${JSON.stringify(cves, null, 2)}`) if (snapshot) { expect(cves.sort()).toMatchSnapshot(); }