From bbd749b7a7d58ef93b9f2e65920561f67fb63ae1 Mon Sep 17 00:00:00 2001 From: Si Hyeong Lee Date: Sun, 11 Oct 2026 19:29:00 -0700 Subject: [PATCH 1/4] fix(deps): upgrade Next and repair lockfile for clean CI --- package-lock.json | 396 +++++++++++++++++++++++----------------------- package.json | 2 +- 2 files changed, 199 insertions(+), 199 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5aeb940..2e3e127 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "fuse.js": "^7.1.0", "js-yaml": "^4.3.1", "lucide-react": "^0.562.0", - "next": "^16.2.6", + "next": "^16.4.0", "next-intl": "^4.9.2", "next-mdx-remote": "^6.0.0", "react": "19.2.8", @@ -1040,9 +1040,9 @@ } }, "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { @@ -1114,9 +1114,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { @@ -1364,9 +1364,9 @@ } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.4.tgz", - "integrity": "sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.5.tgz", + "integrity": "sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg==", "cpu": [ "arm64" ], @@ -1383,13 +1383,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.3.3" + "@img/sharp-libvips-darwin-arm64": "1.3.4" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.4.tgz", - "integrity": "sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.5.tgz", + "integrity": "sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw==", "cpu": [ "x64" ], @@ -1406,13 +1406,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.3.3" + "@img/sharp-libvips-darwin-x64": "1.3.4" } }, "node_modules/@img/sharp-freebsd-wasm32": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.4.tgz", - "integrity": "sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.5.tgz", + "integrity": "sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -1420,7 +1420,7 @@ "freebsd" ], "dependencies": { - "@img/sharp-wasm32": "0.35.4" + "@img/sharp-wasm32": "0.35.5" }, "engines": { "node": ">=20.9.0" @@ -1430,9 +1430,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.3.tgz", - "integrity": "sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.4.tgz", + "integrity": "sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw==", "cpu": [ "arm64" ], @@ -1447,9 +1447,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.3.tgz", - "integrity": "sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.4.tgz", + "integrity": "sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ==", "cpu": [ "x64" ], @@ -1464,9 +1464,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.3.tgz", - "integrity": "sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.4.tgz", + "integrity": "sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw==", "cpu": [ "arm" ], @@ -1481,9 +1481,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.3.tgz", - "integrity": "sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.4.tgz", + "integrity": "sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A==", "cpu": [ "arm64" ], @@ -1498,9 +1498,9 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.3.tgz", - "integrity": "sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.4.tgz", + "integrity": "sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ==", "cpu": [ "ppc64" ], @@ -1515,9 +1515,9 @@ } }, "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.3.tgz", - "integrity": "sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.4.tgz", + "integrity": "sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q==", "cpu": [ "riscv64" ], @@ -1532,9 +1532,9 @@ } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.3.tgz", - "integrity": "sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.4.tgz", + "integrity": "sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ==", "cpu": [ "s390x" ], @@ -1549,9 +1549,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.3.tgz", - "integrity": "sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.4.tgz", + "integrity": "sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw==", "cpu": [ "x64" ], @@ -1566,9 +1566,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.3.tgz", - "integrity": "sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.4.tgz", + "integrity": "sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA==", "cpu": [ "arm64" ], @@ -1583,9 +1583,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.3.tgz", - "integrity": "sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.4.tgz", + "integrity": "sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg==", "cpu": [ "x64" ], @@ -1600,9 +1600,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.4.tgz", - "integrity": "sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.5.tgz", + "integrity": "sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ==", "cpu": [ "arm" ], @@ -1619,13 +1619,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.3.3" + "@img/sharp-libvips-linux-arm": "1.3.4" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.4.tgz", - "integrity": "sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.5.tgz", + "integrity": "sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ==", "cpu": [ "arm64" ], @@ -1642,13 +1642,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.3.3" + "@img/sharp-libvips-linux-arm64": "1.3.4" } }, "node_modules/@img/sharp-linux-ppc64": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.4.tgz", - "integrity": "sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.5.tgz", + "integrity": "sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA==", "cpu": [ "ppc64" ], @@ -1665,13 +1665,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.3.3" + "@img/sharp-libvips-linux-ppc64": "1.3.4" } }, "node_modules/@img/sharp-linux-riscv64": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.4.tgz", - "integrity": "sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.5.tgz", + "integrity": "sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ==", "cpu": [ "riscv64" ], @@ -1688,13 +1688,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.3.3" + "@img/sharp-libvips-linux-riscv64": "1.3.4" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.4.tgz", - "integrity": "sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.5.tgz", + "integrity": "sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA==", "cpu": [ "s390x" ], @@ -1711,13 +1711,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.3.3" + "@img/sharp-libvips-linux-s390x": "1.3.4" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.4.tgz", - "integrity": "sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.5.tgz", + "integrity": "sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog==", "cpu": [ "x64" ], @@ -1734,13 +1734,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.3.3" + "@img/sharp-libvips-linux-x64": "1.3.4" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.4.tgz", - "integrity": "sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.5.tgz", + "integrity": "sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg==", "cpu": [ "arm64" ], @@ -1757,13 +1757,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.3.3" + "@img/sharp-libvips-linuxmusl-arm64": "1.3.4" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.4.tgz", - "integrity": "sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.5.tgz", + "integrity": "sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A==", "cpu": [ "x64" ], @@ -1780,13 +1780,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.3.3" + "@img/sharp-libvips-linuxmusl-x64": "1.3.4" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.4.tgz", - "integrity": "sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.5.tgz", + "integrity": "sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ==", "dev": true, "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, @@ -1801,9 +1801,9 @@ } }, "node_modules/@img/sharp-webcontainers-wasm32": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.4.tgz", - "integrity": "sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.5.tgz", + "integrity": "sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA==", "cpu": [ "wasm32" ], @@ -1811,7 +1811,7 @@ "license": "Apache-2.0", "optional": true, "dependencies": { - "@img/sharp-wasm32": "0.35.4" + "@img/sharp-wasm32": "0.35.5" }, "engines": { "node": ">=20.9.0" @@ -1821,9 +1821,9 @@ } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.4.tgz", - "integrity": "sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.5.tgz", + "integrity": "sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q==", "cpu": [ "arm64" ], @@ -1841,9 +1841,9 @@ } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.4.tgz", - "integrity": "sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.5.tgz", + "integrity": "sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A==", "cpu": [ "ia32" ], @@ -1861,9 +1861,9 @@ } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.4.tgz", - "integrity": "sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.5.tgz", + "integrity": "sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg==", "cpu": [ "x64" ], @@ -2303,15 +2303,15 @@ } }, "node_modules/@next/env": { - "version": "16.3.3", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.3.3.tgz", - "integrity": "sha512-U2eYQRwXj+dsqxV79zFqExDdatnNY/ZWc2nsJU1p/OgT7fd3dXwlF6OjYaFQCfMoeTA19PWq+wVmYgimVA+V+g==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.4.0.tgz", + "integrity": "sha512-pyLlT5oafGlX8X7xjgGNXz5dNIDSxREm+qD6i9VLSZiZFopA/XWl8NM/KPmLx/8EG/++c49iid3gNWSQW1+2dQ==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { - "version": "16.3.3", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.3.3.tgz", - "integrity": "sha512-pbEh30vvjKpDoTAmo1v3q2uM4JUi8QaEBpbmjWvGfoec2jLghy/WNtvzAT0bk+Ik9oz6etjt4YjXEk4BQnicCw==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.4.0.tgz", + "integrity": "sha512-ffeflB/dL8RrvWejQximw01pOpKHN692Y3FjDB82DCd0YVvXvBD6vY7mcZp27yvSVFVM6wA73xVodEKF4spizA==", "dev": true, "license": "MIT", "dependencies": { @@ -2352,9 +2352,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "16.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.3.3.tgz", - "integrity": "sha512-8Hiv32QJPwdV6KYJ8meR9SBA061tQqnIKTJDocvOXlEQqib0xMFpzArosuffFUUc0sslbh7QQ8a3Yey1QV8EIw==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.4.0.tgz", + "integrity": "sha512-yyZ+9MgViAKLLb5l1gPBGjRwnq3teNLb5Zx6JEw8LzNSTjlsnp+baUHFxTgl7PpjCmKf48VQfyipG1eBflb48Q==", "cpu": [ "arm64" ], @@ -2368,9 +2368,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "16.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.3.3.tgz", - "integrity": "sha512-A1lgKgwVchRYmSe467zdwhxT9040dd8lH+o65sL5Jet8fjB4kegw/rDyPIpYVRb6jAqwXFOJpjIXJLxQKLiE3A==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.4.0.tgz", + "integrity": "sha512-RePEEejEj1Auf5s9qEcG0JbcDP3ceNUS/ofNX8AhxzgIjTVlZIHMatlp16YplcSMpsVo/ZMQHJTjbMYJznjGUg==", "cpu": [ "x64" ], @@ -2384,9 +2384,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.3.3.tgz", - "integrity": "sha512-bf0FIssMFueU2dm7vQEWWxk0c8UjKTdW0yzuh0sQsD8pf1+KCLDdaqhYZNMYGmXwEOiHAUzgBKudovIlcvvBjg==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.4.0.tgz", + "integrity": "sha512-HhsyCqd+NpmvDDVY/LqkAN24f+QVvXh3ie+PfcwKinWL2Mu826JymQxJXGjAIfmJ4VlHjp4Pe/nZtXtUoEMAqw==", "cpu": [ "arm64" ], @@ -2400,9 +2400,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.3.3.tgz", - "integrity": "sha512-W7viwCk9JY/cAkdz/A273rd5bb3RgT/IHwR7Upv90tunjBWNtAAhGhoecHh+teRNRSinuAFmE+l7fwZ4YKkrXg==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.4.0.tgz", + "integrity": "sha512-sN9Pf+Zh+RpZAOmbR7d8KAA8T61CVhawtbEJ2Bk6QrRzx6Z/6haaF4UiwBzytM96GRRRWZh9/WALIEO3npMamA==", "cpu": [ "arm64" ], @@ -2416,9 +2416,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.3.3.tgz", - "integrity": "sha512-0W46zw1N3ODpI6n0GeivHvvob1pooozgZVqy65k0mh4/7vr+FbY9+WpHzNVXjHipJf/A3FDheBG19H1s5A25rA==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.4.0.tgz", + "integrity": "sha512-D5M5xLWQVd1CvR+L7BickM1weRfmM41x3L1NWcnKY/oyJJqr1qwaCgmu3dMAhLBOMdZwSuDCRV6DJRRpiUkbDQ==", "cpu": [ "x64" ], @@ -2432,9 +2432,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "16.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.3.3.tgz", - "integrity": "sha512-H4mBso8ZTMBPtdT0PN0pBx2ayTvQuTuvS6qT13d77yVFJXAPCxkyIhLTmdMaGTJs0krQYI/qpzdHijCeihXhbg==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.4.0.tgz", + "integrity": "sha512-8d5WLS6a9YalqpOgXdlbvonFu+sIgjA+CO6tGzetrpPVcUL56foa7B7664h41rAlbLi5fJwK4vWji3zf4QDy+w==", "cpu": [ "x64" ], @@ -2448,9 +2448,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.3.3.tgz", - "integrity": "sha512-cTMUJpcEGmeywofCUfhR+rSsoE33+rVPnPEYNTNdLNlsOeEg/vktOsKUSTb28vUGqD2jkm4Zaskcwn7OCI6FQg==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.4.0.tgz", + "integrity": "sha512-sQuOIiW3snb48UwrBcDMvFA/u03qtw2LeuSIxFIQ9AJuW9UG/4Ddsfo+eV73PnZwBhyncMtsKVh7PrzMol2/fA==", "cpu": [ "arm64" ], @@ -2464,9 +2464,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.3.3.tgz", - "integrity": "sha512-2VR4cTBzHXaBjnGsuH6GyJjENzQOmHeAh11uY1iUhjm3j5dEUrVJuUj+VL78jaGi/Dik8xS76zEj18BsFhlVZQ==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.4.0.tgz", + "integrity": "sha512-6/33lCuxpdHUy27RegyGM/5uTpF7YZd3UR4D0gpkOA/tWY5YR7msbQjqPgCZmSmxrrTLGCXYA4A8vT2H5DPGTQ==", "cpu": [ "x64" ], @@ -5823,9 +5823,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", - "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6900,9 +6900,9 @@ } }, "node_modules/brace-expansion": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", - "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", + "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -7968,21 +7968,21 @@ } }, "node_modules/eslint-config-next": { - "version": "16.3.3", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.3.3.tgz", - "integrity": "sha512-teqtsR26tnlfXFHfVLTM/4tzEzU8DMu6GS1sddZzhfGzgd2f2ofbgDUcsk6cssSCzX6Tk6fmWifJcdANSdPJrw==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.4.0.tgz", + "integrity": "sha512-2RHLYswzc+hNRktWAjIL6XAvwb1cKcjLawJ5KWywAxn/JivNHtG42ZigL1yobVXDqsimuuL2F9jcEcnHFzbppQ==", "dev": true, "license": "MIT", "dependencies": { - "@next/eslint-plugin-next": "16.3.3", + "@next/eslint-plugin-next": "16.4.0", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.32.0", "eslint-plugin-jsx-a11y": "^6.10.0", "eslint-plugin-react": "^7.37.0", - "eslint-plugin-react-hooks": "^7.0.0", + "eslint-plugin-react-hooks": "^7.1.0", "globals": "16.4.0", - "typescript-eslint": "^8.46.0" + "typescript-eslint": "^8.56.0" }, "peerDependencies": { "eslint": ">=9.0.0", @@ -8151,9 +8151,9 @@ } }, "node_modules/eslint-plugin-import/node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { @@ -8215,9 +8215,9 @@ } }, "node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { @@ -8292,9 +8292,9 @@ } }, "node_modules/eslint-plugin-react/node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { @@ -8370,9 +8370,9 @@ } }, "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { @@ -8642,9 +8642,9 @@ "license": "MIT" }, "node_modules/fastq": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", - "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.3.tgz", + "integrity": "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==", "dev": true, "license": "ISC", "dependencies": { @@ -11545,12 +11545,12 @@ } }, "node_modules/next": { - "version": "16.3.3", - "resolved": "https://registry.npmjs.org/next/-/next-16.3.3.tgz", - "integrity": "sha512-tuRTx1nQ/yVw83cwJBo9F+njGUgMn3UHQycreWHB8XsStvvAh1AthbI8/4IpKnFaF58F+iSiHejYOlMQ/eq83g==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/next/-/next-16.4.0.tgz", + "integrity": "sha512-N2C+9q89Z/oLrsepSRS4jRMtwmUZAoZuAHU0e9T6+sAB5DdhADmrnOW+zjYijlrYFmVfmQhGVGy9eMFoB7UgKg==", "license": "MIT", "dependencies": { - "@next/env": "16.3.3", + "@next/env": "16.4.0", "@swc/helpers": "0.5.23", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", @@ -11564,15 +11564,15 @@ "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "16.3.3", - "@next/swc-darwin-x64": "16.3.3", - "@next/swc-linux-arm64-gnu": "16.3.3", - "@next/swc-linux-arm64-musl": "16.3.3", - "@next/swc-linux-x64-gnu": "16.3.3", - "@next/swc-linux-x64-musl": "16.3.3", - "@next/swc-win32-arm64-msvc": "16.3.3", - "@next/swc-win32-x64-msvc": "16.3.3", - "sharp": "^0.35.3" + "@next/swc-darwin-arm64": "16.4.0", + "@next/swc-darwin-x64": "16.4.0", + "@next/swc-linux-arm64-gnu": "16.4.0", + "@next/swc-linux-arm64-musl": "16.4.0", + "@next/swc-linux-x64-gnu": "16.4.0", + "@next/swc-linux-x64-musl": "16.4.0", + "@next/swc-win32-arm64-msvc": "16.4.0", + "@next/swc-win32-x64-msvc": "16.4.0", + "sharp": "^0.35.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -12958,9 +12958,9 @@ } }, "node_modules/sharp": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.4.tgz", - "integrity": "sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.5.tgz", + "integrity": "sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g==", "devOptional": true, "license": "Apache-2.0", "dependencies": { @@ -12975,31 +12975,31 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.35.4", - "@img/sharp-darwin-x64": "0.35.4", - "@img/sharp-freebsd-wasm32": "0.35.4", - "@img/sharp-libvips-darwin-arm64": "1.3.3", - "@img/sharp-libvips-darwin-x64": "1.3.3", - "@img/sharp-libvips-linux-arm": "1.3.3", - "@img/sharp-libvips-linux-arm64": "1.3.3", - "@img/sharp-libvips-linux-ppc64": "1.3.3", - "@img/sharp-libvips-linux-riscv64": "1.3.3", - "@img/sharp-libvips-linux-s390x": "1.3.3", - "@img/sharp-libvips-linux-x64": "1.3.3", - "@img/sharp-libvips-linuxmusl-arm64": "1.3.3", - "@img/sharp-libvips-linuxmusl-x64": "1.3.3", - "@img/sharp-linux-arm": "0.35.4", - "@img/sharp-linux-arm64": "0.35.4", - "@img/sharp-linux-ppc64": "0.35.4", - "@img/sharp-linux-riscv64": "0.35.4", - "@img/sharp-linux-s390x": "0.35.4", - "@img/sharp-linux-x64": "0.35.4", - "@img/sharp-linuxmusl-arm64": "0.35.4", - "@img/sharp-linuxmusl-x64": "0.35.4", - "@img/sharp-webcontainers-wasm32": "0.35.4", - "@img/sharp-win32-arm64": "0.35.4", - "@img/sharp-win32-ia32": "0.35.4", - "@img/sharp-win32-x64": "0.35.4" + "@img/sharp-darwin-arm64": "0.35.5", + "@img/sharp-darwin-x64": "0.35.5", + "@img/sharp-freebsd-wasm32": "0.35.5", + "@img/sharp-libvips-darwin-arm64": "1.3.4", + "@img/sharp-libvips-darwin-x64": "1.3.4", + "@img/sharp-libvips-linux-arm": "1.3.4", + "@img/sharp-libvips-linux-arm64": "1.3.4", + "@img/sharp-libvips-linux-ppc64": "1.3.4", + "@img/sharp-libvips-linux-riscv64": "1.3.4", + "@img/sharp-libvips-linux-s390x": "1.3.4", + "@img/sharp-libvips-linux-x64": "1.3.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.4", + "@img/sharp-libvips-linuxmusl-x64": "1.3.4", + "@img/sharp-linux-arm": "0.35.5", + "@img/sharp-linux-arm64": "0.35.5", + "@img/sharp-linux-ppc64": "0.35.5", + "@img/sharp-linux-riscv64": "0.35.5", + "@img/sharp-linux-s390x": "0.35.5", + "@img/sharp-linux-x64": "0.35.5", + "@img/sharp-linuxmusl-arm64": "0.35.5", + "@img/sharp-linuxmusl-x64": "0.35.5", + "@img/sharp-webcontainers-wasm32": "0.35.5", + "@img/sharp-win32-arm64": "0.35.5", + "@img/sharp-win32-ia32": "0.35.5", + "@img/sharp-win32-x64": "0.35.5" }, "peerDependenciesMeta": { "@types/node": { @@ -13216,9 +13216,9 @@ } }, "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" diff --git a/package.json b/package.json index a1730dc..f30e4b8 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "fuse.js": "^7.1.0", "js-yaml": "^4.3.1", "lucide-react": "^0.562.0", - "next": "^16.2.6", + "next": "^16.4.0", "next-intl": "^4.9.2", "next-mdx-remote": "^6.0.0", "react": "19.2.8", From 2a4973eb91b784b9b146922e9ed27fd977218fd1 Mon Sep 17 00:00:00 2001 From: Si Hyeong Lee Date: Tue, 13 Oct 2026 19:21:29 -0700 Subject: [PATCH 2/4] fix(security): isolate Keystatic watcher from production braces tree --- .github/workflows/ci.yml | 7 ++ ...13-keystatic-chokidar-runtime-isolation.md | 48 ++++++++++ package-lock.json | 87 +++++-------------- package.json | 3 + .../keystatic-dependency-boundary.test.ts | 39 +++++++++ 5 files changed, 117 insertions(+), 67 deletions(-) create mode 100644 docs/reviews/2026-10-13-keystatic-chokidar-runtime-isolation.md create mode 100644 src/lib/__tests__/keystatic-dependency-boundary.test.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c3ba241..b547964 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,5 +47,12 @@ jobs: - name: Build run: npm run build + # Always check production independently so a dev-tool advisory cannot + # conceal any newly introduced runtime vulnerability. + - name: Audit production dependencies + run: npm audit --omit=dev --audit-level=high + + # Keep the original strict all-dependencies gate. No exceptions are + # authorized by this change, so known dev-only advisories still fail. - name: Audit dependencies run: npm audit --audit-level=high diff --git a/docs/reviews/2026-10-13-keystatic-chokidar-runtime-isolation.md b/docs/reviews/2026-10-13-keystatic-chokidar-runtime-isolation.md new file mode 100644 index 0000000..7416622 --- /dev/null +++ b/docs/reviews/2026-10-13-keystatic-chokidar-runtime-isolation.md @@ -0,0 +1,48 @@ +# Security review — Keystatic watcher dependency isolation (2026-10-13 PT) + +Source: isolated 4080 MAIN worktree `portfolio-security-chokidar4-20261013` branched from security PR #33 / `bbd749b`. No production deployment or risk exception is approved. + +## What changed + +Keystatic's latest Next adapter (`@keystatic/next@5.0.5`) declares `chokidar@^3.5.3`, bringing `braces@3.0.3` into the **installed production dependency graph**. npm reports GHSA-vfj7-8cjw-p6xm (high, stack-exhaustion via deeply nested brace patterns), with no patched upstream braces release as of Oct 13. + +An explicitly scoped npm override now resolves **only** `@keystatic/next → chokidar` to Chokidar **4.0.3**. Chokidar 4 does not depend on `braces`. Both package lock and independently run audits confirm `braces@3.0.3` exists *only* in development, pulled by `eslint-config-next → @next/eslint-plugin-next → fast-glob → micromatch`. + +This is a real reduction in production package exposure, **not a vulnerability exception or silent suppression**: +- `npm audit --omit=dev --audit-level=high`: **0 vulnerabilities / exit 0** after the override, previously three High from Keystatic/Chokidar. +- `npm audit --audit-level=high`: **FAIL, five High** in development tools because `braces@3.0.3` is still present for ESLint. No compatible official patched release or replacement was validated. +- GitHub CI now has two independent gates: **Audit production dependencies** and the existing unchanged **Audit dependencies**. The strict full-audit gate remains red until a real fix or separately and explicitly reviewed time-bounded policy. + +## Compatibility observations + +The override crosses a declared semver-major boundary (`^3.5.3` → 4.0.3). It is therefore treated as a reviewed compatibility experiment, **not** claimed to have Keystatic vendor support. + +Source inspection of `@keystatic/next/dist/keystatic-next-api.js` finds its lazy `require('chokidar')` inside a **development-only** legacy reader-refresh watcher path. The legacy call is `watch(directories, {ignored: [/node_modules/]})`, not a glob pattern. The active Next App Router route uses `makeRouteHandler`, not this legacy Node/Pages adapter. + +Observed tests on the isolated worktree with clean `npm ci`: +- npm lockfile assertions for Chokidar 4 and `braces.dev=true`, and production-disabled Keystatic guard: four added regression tests PASS. +- TypeScript, ESLint, Prettier, total **201/201 unit tests** and Next.js 16.4.0 optimized production build: PASS. +- The exact Keystatic-compatible watcher call with a temporary directory and a real new file: `ready` + `add` events PASS; the temporary directory was removed. +- Local **development-mode** `http://localhost:3199/keystatic`: HTTP 200, real dashboard loaded with 10 Korean + 10 English post entries and project/category controls, no JavaScript page errors. +- **Production-mode** `http://localhost:3198/keystatic`, `/api/keystatic/posts`, `/api/keystatic/reader-refresh/test`: all HTTP 404 with no GitHub storage configured. Home and English home HTTP 200, `/api/og?title=QA` HTTP 200 PNG. + +Limitations: a full CMS create/edit/save workflow was **not** exercised (would mutate test content); no authenticated GitHub-storage OAuth mode was tested. These remain separate acceptances if the CMS is to be enabled in production. Do not infer that all independent `braces` uses are absent: it is still in developer tooling. + +## Existing production baseline and release decisions + +GitHub Dependabot `#82` flagged Next.js 16.3.3 Critical ImageResponse RCE; other default branch alerts `#84/#85/#86` concern `brace-expansion`. Draft security PR #33's Next.js 16.4.0 + transitive patches address those older vulnerable versions, but remain unmerged. + +The public site currently uses `ImageResponse` and user-supplied title/description as **div text** under 120/200-character clamps, rather than directly supplying attacker-controlled SVG markup/attributes. The critical advisory specifies that SVG-controlled inputs are affected. This suggests a narrower exposure but is **not** proof of safety across all Next.js advisories or reasons to leave its version outdated. + +Official references: +- https://github.com/advisories/GHSA-vfj7-8cjw-p6xm +- https://github.com/vercel/next.js/security/advisories/GHSA-vcvr-r3jv-pc5j + +## Remaining gates + +1. Keep PR #33 draft until Linux CI and review results are recorded; keep the strict full dependency audit red. +2. Review any separately proposed temporary dev-only GHSA-vfj7-8cjw-p6xm exception with explicit identity, expiry, approver, mitigation and continuous visible audit. **This document is NOT that approval**. +3. If no exception is authorized, investigate a compatible lint dependency replacement or wait for upstream advisory/fix, without global audit bypasses, and do not merge. +4. After security PR passes/gets approved, rebase/merge its changes into staged portfolio PR #34; resolve overlapping three PDF files/CTAs with separate draft PR #37 through explicit editorial approval; verify live Vercel routes, PDF SHA, both locales, canary and rollback in FOLIO-36. + +Plane source of truth: FOLIO-45 security / FOLIO-36 release / FOLIO-51 résumé availability. diff --git a/package-lock.json b/package-lock.json index 2e3e127..e791a96 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6542,19 +6542,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -6887,18 +6874,6 @@ "require-from-string": "^2.0.2" } }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/brace-expansion": { "version": "2.1.7", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", @@ -6912,6 +6887,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -7148,27 +7124,18 @@ } }, "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "^4.0.1" }, "engines": { - "node": ">= 8.10.0" + "node": ">= 14.16.0" }, "funding": { "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" } }, "node_modules/client-only": { @@ -8674,6 +8641,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -8746,6 +8714,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -8906,6 +8875,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -9441,18 +9411,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-boolean-object": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", @@ -9685,6 +9643,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -11703,15 +11662,6 @@ "node": ">=18" } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -12055,6 +12005,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -12478,15 +12429,16 @@ } }, "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, "engines": { - "node": ">=8.10.0" + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/reading-time": { @@ -13692,6 +13644,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" diff --git a/package.json b/package.json index f30e4b8..89ae4d2 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,9 @@ "js-yaml": "^4.3.1", "@toeverything/y-indexeddb": { "nanoid": "^5.1.16" + }, + "@keystatic/next": { + "chokidar": "^4.0.3" } }, "devDependencies": { diff --git a/src/lib/__tests__/keystatic-dependency-boundary.test.ts b/src/lib/__tests__/keystatic-dependency-boundary.test.ts new file mode 100644 index 0000000..b01f9ff --- /dev/null +++ b/src/lib/__tests__/keystatic-dependency-boundary.test.ts @@ -0,0 +1,39 @@ +import fs from 'node:fs' +import path from 'node:path' +import { afterEach, describe, expect, it, vi } from 'vitest' +import { isKeystaticDisabled } from '../keystatic-access' + +const root = process.cwd() +const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')) +const lock = JSON.parse(fs.readFileSync(path.join(root, 'package-lock.json'), 'utf8')).packages + +afterEach(() => vi.unstubAllEnvs()) + +describe('Keystatic runtime dependency isolation', () => { + it('pins Keystatic file watching to the maintained Chokidar 4 package', () => { + expect(pkg.overrides['@keystatic/next'].chokidar).toBe('^4.0.3') + expect(lock['node_modules/@keystatic/next'].version).toBe('5.0.5') + expect(lock['node_modules/chokidar'].version).toMatch(/^4\./) + expect(lock['node_modules/chokidar'].dev).toBeFalsy() + }) + + it('keeps all installed braces packages outside the production dependency tree', () => { + const affected = Object.entries(lock).filter(([name]) => /(^|\/)braces$/.test(name)) + expect(affected.length).toBeGreaterThan(0) + for (const [name, record] of affected) { + expect((record as { dev?: boolean }).dev, name).toBe(true) + } + }) + + it('keeps local-storage CMS routes blocked in production with no GitHub app', () => { + vi.stubEnv('NODE_ENV', 'production') + vi.stubEnv('NEXT_PUBLIC_KEYSTATIC_GITHUB_APP_SLUG', '') + expect(isKeystaticDisabled()).toBe(true) + }) + + it('preserves local development CMS availability', () => { + vi.stubEnv('NODE_ENV', 'development') + vi.stubEnv('NEXT_PUBLIC_KEYSTATIC_GITHUB_APP_SLUG', '') + expect(isKeystaticDisabled()).toBe(false) + }) +}) From 4f355abe977c24a01c6bf5204ee135b9cac21698 Mon Sep 17 00:00:00 2001 From: Si Hyeong Lee <6832586+writingdeveloper@users.noreply.github.com> Date: Thu, 22 Oct 2026 15:46:36 -0700 Subject: [PATCH 3/4] fix(deps): upgrade Next.js to 16.4.2 for the Oct 22 advisories Next.js published three advisories on 2026-10-22 that affect every 16.x release up to 16.4.0, all patched in 16.4.1: - GHSA-2464-fhr4-w9xw (critical): RCE in the Image Optimization API with SVG - GHSA-673q-mjm6-r5xr (critical): RCE in `use cache` returning typed arrays - GHSA-p3m5-59mp-jv5r (high): App Router DoS through Server Actions Production main is still on 16.3.3, which is also below the 16.3.6 fix for GHSA-vcvr-r3jv-pc5j (critical, next/og ImageResponse). Move to 16.4.2, the current patch release, and keep eslint-config-next on the same version. Vitest's nested vite moves from 8.2.2 to 8.3.5 to clear GHSA-587h-7wmj-h5pm and three lower advisories in the dev tree. npm 10.9.2 crashes with "Cannot read properties of null (reading 'edgesOut')" when it rebuilds that peer set, and npm 11 drops vite's optional `yaml` peer, which npm 10's `npm ci` then reports as missing. The lockfile was therefore regenerated with npm 10 `--package-lock-only`, and a clean npm 10 `npm ci` succeeds. Verified locally: - Clean `npm ci` with npm 10.9.2. - Type check, ESLint and Prettier. - 201/201 unit tests, translation parity and 8/8 hero images. - Production build (136 static pages). - `npm audit --omit=dev --audit-level=high`: 0 vulnerabilities. - Production preview on localhost:3100: - 20 core routes return the expected status. - Navigation QA passes in en and ko. - Screenshots of four pages show no visual change. Still open: the strict all-dependency audit fails only on GHSA-vfj7-8cjw-p6xm (braces <= 3.0.3, no patched release). Its sole path is the dev-only eslint-config-next -> @next/eslint-plugin-next -> fast-glob -> micromatch chain, and 16.4.2 and canary still pin fast-glob 3.3.1. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01AfoJbUTPLtMgivSnuxD7jS --- package-lock.json | 254 +++++++++++++++++++++++----------------------- package.json | 4 +- 2 files changed, 129 insertions(+), 129 deletions(-) diff --git a/package-lock.json b/package-lock.json index e791a96..4a435a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "fuse.js": "^7.1.0", "js-yaml": "^4.3.1", "lucide-react": "^0.562.0", - "next": "^16.4.0", + "next": "^16.4.2", "next-intl": "^4.9.2", "next-mdx-remote": "^6.0.0", "react": "19.2.8", @@ -35,7 +35,7 @@ "@types/react-dom": "^19", "@types/three": "^0.183.1", "eslint": "^9", - "eslint-config-next": "^16.2.6", + "eslint-config-next": "^16.4.2", "playwright": "^1.62.0", "prettier": "^3.8.1", "sharp": "^0.35.3", @@ -2303,15 +2303,15 @@ } }, "node_modules/@next/env": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.4.0.tgz", - "integrity": "sha512-pyLlT5oafGlX8X7xjgGNXz5dNIDSxREm+qD6i9VLSZiZFopA/XWl8NM/KPmLx/8EG/++c49iid3gNWSQW1+2dQ==", + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.4.2.tgz", + "integrity": "sha512-nTUTFH7G5/J/fmpkjVLegOkqyKOP0CEsJcMsyO4UCYeN1su+YMW/MfaSErnJskL7XM+UDK6aTvslxsRCyya/sA==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.4.0.tgz", - "integrity": "sha512-ffeflB/dL8RrvWejQximw01pOpKHN692Y3FjDB82DCd0YVvXvBD6vY7mcZp27yvSVFVM6wA73xVodEKF4spizA==", + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.4.2.tgz", + "integrity": "sha512-gCTWlw0e+Y6kajpFyKKgA195jUJCRhlzJE0lV7ufe8a+ghIsANWZ0/FiJwg6DuOG1+OvRbcZDcFBoRh3mNJx0g==", "dev": true, "license": "MIT", "dependencies": { @@ -2352,9 +2352,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.4.0.tgz", - "integrity": "sha512-yyZ+9MgViAKLLb5l1gPBGjRwnq3teNLb5Zx6JEw8LzNSTjlsnp+baUHFxTgl7PpjCmKf48VQfyipG1eBflb48Q==", + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.4.2.tgz", + "integrity": "sha512-ONkhFvZNFWlsEHOM/81AtxdyuJP2KRYKYR0JxPwvcu1AuPMMMgcL+wW2luldJvMtZwKtUxoSkCychW5wIeoYSw==", "cpu": [ "arm64" ], @@ -2368,9 +2368,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.4.0.tgz", - "integrity": "sha512-RePEEejEj1Auf5s9qEcG0JbcDP3ceNUS/ofNX8AhxzgIjTVlZIHMatlp16YplcSMpsVo/ZMQHJTjbMYJznjGUg==", + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.4.2.tgz", + "integrity": "sha512-luPCMbOsCoaF+kXJEUjmjmOZg1lC3BflhmUu+ZmW4tBu2vW/4AXak8JBFvePizGkxVmNiAw/r9ra+6UviCWtWQ==", "cpu": [ "x64" ], @@ -2384,9 +2384,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.4.0.tgz", - "integrity": "sha512-HhsyCqd+NpmvDDVY/LqkAN24f+QVvXh3ie+PfcwKinWL2Mu826JymQxJXGjAIfmJ4VlHjp4Pe/nZtXtUoEMAqw==", + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.4.2.tgz", + "integrity": "sha512-xhwFTAncJyaCNy5KqKKuKaIIdCy1/1ZV1s5S1G0hhT7wGMkDTlfJ6aQbfRFnIG/s3/kDrxQ8aycVe+c5QnP8ow==", "cpu": [ "arm64" ], @@ -2400,9 +2400,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.4.0.tgz", - "integrity": "sha512-sN9Pf+Zh+RpZAOmbR7d8KAA8T61CVhawtbEJ2Bk6QrRzx6Z/6haaF4UiwBzytM96GRRRWZh9/WALIEO3npMamA==", + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.4.2.tgz", + "integrity": "sha512-T1PNmfbPcxr5ndtWUL/3wQtLXsz808lq6sc2GrNT5k2oquF7UyqZddHSuAewHVsWsGXe6nsl9HoN1YNriVjuSg==", "cpu": [ "arm64" ], @@ -2416,9 +2416,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.4.0.tgz", - "integrity": "sha512-D5M5xLWQVd1CvR+L7BickM1weRfmM41x3L1NWcnKY/oyJJqr1qwaCgmu3dMAhLBOMdZwSuDCRV6DJRRpiUkbDQ==", + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.4.2.tgz", + "integrity": "sha512-N9cDUOCGtkJG5PNKDFZUF3xXndrJJcpLp121uAGjIGvO/U2p9tfdNOM9htj7+5f72l0m8rMw/cvYacUJBQoudg==", "cpu": [ "x64" ], @@ -2432,9 +2432,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.4.0.tgz", - "integrity": "sha512-8d5WLS6a9YalqpOgXdlbvonFu+sIgjA+CO6tGzetrpPVcUL56foa7B7664h41rAlbLi5fJwK4vWji3zf4QDy+w==", + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.4.2.tgz", + "integrity": "sha512-yY2uh1YhX6ufuYy2FCbw1dqblbhI57GLYAJfCkqD9auTv6pLrH67Uccjc3QMKBeCYAG6lkgu08ExmX1bbjejjQ==", "cpu": [ "x64" ], @@ -2448,9 +2448,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.4.0.tgz", - "integrity": "sha512-sQuOIiW3snb48UwrBcDMvFA/u03qtw2LeuSIxFIQ9AJuW9UG/4Ddsfo+eV73PnZwBhyncMtsKVh7PrzMol2/fA==", + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.4.2.tgz", + "integrity": "sha512-j/ArWRIHpMMB6wYLMhr2puTguBt70lNU/Cmjl9p6dTDE8DGknYJMP9PdwPk2dqpCNL/KIX9yfngDvIesxJPmsg==", "cpu": [ "arm64" ], @@ -2464,9 +2464,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.4.0.tgz", - "integrity": "sha512-6/33lCuxpdHUy27RegyGM/5uTpF7YZd3UR4D0gpkOA/tWY5YR7msbQjqPgCZmSmxrrTLGCXYA4A8vT2H5DPGTQ==", + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.4.2.tgz", + "integrity": "sha512-AA4edyWHDko3NCSo0x6GomsdgFb0oR1J3MXU1b5xt6FRFg4AucrMPW3S0hCe/9onU0O1BbpGQBsqo1r0Gneu1g==", "cpu": [ "x64" ], @@ -2528,13 +2528,13 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.147.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.147.0.tgz", - "integrity": "sha512-IJ3s6ltHLp45S0bh7phkX+gJO7A1Wuz2EaqpAhb8WjqDwbzMiWKHhyyT42tskaWjEYXtHtVCPpnBJVT9+dcRLg==", + "version": "0.155.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.155.0.tgz", + "integrity": "sha512-SeE8JPwW1xs1tHeNycjUtUBdhorRa8o4jlvjwk49R7/Z291i+4DQa7JJIgOm/TaYQZx+QrPQbVJ/9c8dMIZN8Q==", "dev": true, "license": "MIT", "funding": { - "url": "https://github.com/sponsors/Boshen" + "url": "https://github.com/sponsors/oxc-project" } }, "node_modules/@parcel/watcher": { @@ -4348,9 +4348,9 @@ } }, "node_modules/@rolldown/binding-android-arm-eabi": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.6.tgz", - "integrity": "sha512-b+jTcARdTiFLI6jB4a5XjTm0RWd6KcRfQj/I2356fxUZemiho9zQLxo0RtCuMDAyKcLo6cEltkgbQp6d1+sjjQ==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.15.tgz", + "integrity": "sha512-aLcgK9gIJ3YRvrrtfSBbRa27F5WrpiTaPulnFpsHPxpUHgFlSgnU4854MHwoRGEBhfRJDWCS2WQ7lIjskE9Olw==", "cpu": [ "arm" ], @@ -4365,9 +4365,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.6.tgz", - "integrity": "sha512-lkWU8ZJaRk9q3CIEY1Tc7vIFALp3Xw5NfGJo2hQg5oIqNgxWi1zI+IiDEK3r70BF5Dzol1tcXsnzsRc8NLhG+Q==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.15.tgz", + "integrity": "sha512-xD7DdZCrbThKuxNg7ooQmvp07ys4J/pnSEMTG3/OdOWl6daC67m/Q4fL78sRHMxTH/N4fGUbYhcPM0MF5WFHkg==", "cpu": [ "arm64" ], @@ -4382,9 +4382,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.6.tgz", - "integrity": "sha512-dgR56NYnvAszm7Ob1B2/Vn0e8bUQYZH2UjVaMMtMVOCKFSfjhfLmuA/9+O+F+ajUdG6B/bSssrKW6JJYASa8jA==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.15.tgz", + "integrity": "sha512-m2WuQ6r0B4OTYxyPpNLtXaMRbRDUbG8YQV2Q3KTdSSK8Th6KHCnC8jVCJt8bvYeSB5WivkkNbc85mKadaSdOxA==", "cpu": [ "arm64" ], @@ -4399,9 +4399,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.6.tgz", - "integrity": "sha512-vpVxFvUCFioJqug7OTvqptkc4yb8UX0AwfDmJpaR/0sWz+BUmqSVAf7c8JkUgnN8YLspb4a/N6NhTyMAmdyQ7Q==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.15.tgz", + "integrity": "sha512-5RtnsHM36nodM9WbMy+zip0LGS/G+y5wro5RHrpOlr4ac24+1zSi9kiV2MP1myZeZ/ifEHfulQcWjnAXzc2C2A==", "cpu": [ "x64" ], @@ -4416,9 +4416,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.6.tgz", - "integrity": "sha512-h1wG6Y6K3JlRswxsI64qQJqBAy4vrLuHgRbc8CZMGSWTOFRY6ghMApM1NKzB2I0n5xV1fjkE18SuVl2QpLeNpA==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.15.tgz", + "integrity": "sha512-g3l09pyFqhu+135JKkbkwiKGQmwU5hUMUPJ4VdBADvuqR8d33L6H578rh8m7rtxbdp+VmX/+nzI7nvhSZlzpUQ==", "cpu": [ "x64" ], @@ -4433,9 +4433,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.6.tgz", - "integrity": "sha512-tbCiqub0q2MVWJKgF5PoAlNWCtQydiOYSLIkd8sByqK/6MMYLJRcSXSYodqYtd0O+Fw7QaVmKKlS4oL94YRZ0w==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.15.tgz", + "integrity": "sha512-nIe3VugI97nGQtspPxDqA4sXMUpeiocKIP/CqpY/u/vgbT+alX+cVxu1C/oVBPKg4feNTmYovE91v4DgXvXNwQ==", "cpu": [ "arm" ], @@ -4450,9 +4450,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.6.tgz", - "integrity": "sha512-oxK9+baEBPhZG5HB4URY+uU04zJWeZlH6Tb9rB5DK4DF9XR1uXNLXt5Q5ZsugTKayNCNLhkcwz/ye74hRI98dg==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.15.tgz", + "integrity": "sha512-iTBjXx+B5QSvgI89lJfmqh4wTh8NabR1YZIIN3hNx4zSG0EVfVqkOvLulNpG4251pbd6/IAmZ/LefVk1Y4rPhw==", "cpu": [ "arm64" ], @@ -4467,9 +4467,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.6.tgz", - "integrity": "sha512-muWCk27FVBEZtv0MsK8gnfSmgczA8KQ0uRVJbTABKhkRfQc38aUrcb7fhi3BNiyseFmgcRsoMfQsSNJ+DbZdSw==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.15.tgz", + "integrity": "sha512-uN26XOnVR7rqQupG3V9oAG2EGGatWjcJHOeDOHUFCh4TBWmP2tHTFoVN13aYbwfm0aHqJAEmcwhAu6sooIjfqQ==", "cpu": [ "arm64" ], @@ -4484,9 +4484,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.6.tgz", - "integrity": "sha512-eWDoSfU7Co2qj3vgB3Dt4lj1mG6CoWbcJQkRMP3XJplyCMtuaq3LHvPFjS9QIPvMGWVadJC04Xiy0IdcVPtnwQ==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.15.tgz", + "integrity": "sha512-9nWkcdd+H+XhaA3fQIwpHnKIrfx/PQ6rO0pivr78Og07tXlUlctBBJoEYLPfhTmCR/QxEVdn6uPjHV5SymPhdw==", "cpu": [ "ppc64" ], @@ -4501,9 +4501,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.6.tgz", - "integrity": "sha512-2bWNjRSIayvupRKxXUY2tWG9fYdoUlTqWywHRvE8Eq3GvuQ+f2HeIkve697fIt+IQs/PV8yFsdWuhp1aJ1PdnA==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.15.tgz", + "integrity": "sha512-Ggh0ciFjLwIusLSLz7LZYEaGdxOAUJXI+Qu5JCbhPJJHEt8vP+IjRATTJheiJsdzzTYgUiHUiw3X1Ea3ZpMBdA==", "cpu": [ "s390x" ], @@ -4518,9 +4518,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.6.tgz", - "integrity": "sha512-KekI0gS0wLxe1UBSQSjenBVwou/JkcQPDzBPICGZjxUv9k3RteHDPBQaiOicZUFKRIH2wKEimGwVpnJsbPzu7w==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.15.tgz", + "integrity": "sha512-FHg60rS6DBePAYZ9X9F67x+JObFdmgn6tupA1C/a3B+CufFmTD4mX+e8M75tCiAO3foR/6xwVBI0KQiI7sn/rw==", "cpu": [ "x64" ], @@ -4535,9 +4535,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.6.tgz", - "integrity": "sha512-TvtPnfVr+HtyGiDmPK4VWmlNm7QhNNAcK5Q9A7aOXsI8545yCyaoMaicXrFZ72JzeYjaUVk7yT243zT0jzjFKQ==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.15.tgz", + "integrity": "sha512-/cvV/7v5VGz/moiz0XKRj2dE8DzsKyhIu1KpOvJGPjRywg3yscON889UGvAe8zfiJLHup7VLjBduoGWi3HusWQ==", "cpu": [ "x64" ], @@ -4552,9 +4552,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.6.tgz", - "integrity": "sha512-iOo0VEay2XFhaCcH0sps5XIimkSuOnNaZrf6+ZkoSOQBJPKNU48RkmJv0/lSpipexu5P+ouFgafe5IGr/DiQfg==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.15.tgz", + "integrity": "sha512-MyzC5Hrv/sm+Z7TSGKP/j8001RO2xQm1zOpCNVL4j0cY/VhBZviludA2YFQ7i4lH6ZAXxQm0lcMdBzM+99mE7w==", "cpu": [ "arm64" ], @@ -4569,9 +4569,9 @@ } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.6.tgz", - "integrity": "sha512-y5NTmmasMS455JlOCO4ZM9krIchv3Mvm1crL1iUPGOPgEzSkves9n0SdC5Sjz6+qWDFhd8/JpfWMH8NSWNHe+A==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.15.tgz", + "integrity": "sha512-+PgYiS+AH5omy6L45ZLxybasauJaj3M8buoT83D+nzPmCayXlxUV9hydhciCKVyIg0B/v0BtXPDE1uGqNwCPpg==", "cpu": [ "arm64" ], @@ -4586,9 +4586,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.6.tgz", - "integrity": "sha512-np8iZSLfXlAD4kWhiyq/u0Yt8oZDtRQ8lGhQaCXo2rl37KNjeU0GjJuwr4P3oeZ++ROfofsKNBqR5LTO8aXyWQ==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.15.tgz", + "integrity": "sha512-HFZd2QNQYn1JbIi+Hpd7H+r/lkWxcfIu28nirZ6eCn2LafuvmExMlb/P96Sfx5tf7pPArw39IdhmCF8vmV+NhQ==", "cpu": [ "x64" ], @@ -7935,13 +7935,13 @@ } }, "node_modules/eslint-config-next": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.4.0.tgz", - "integrity": "sha512-2RHLYswzc+hNRktWAjIL6XAvwb1cKcjLawJ5KWywAxn/JivNHtG42ZigL1yobVXDqsimuuL2F9jcEcnHFzbppQ==", + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.4.2.tgz", + "integrity": "sha512-P33vjcETMXc+qBjpF77TeJHZLzk6cPcTy8KjpxLucYB3mCIFIORMcZ+cBXcAiIYjE5RM7jyknEEOwMNdnx/ADQ==", "dev": true, "license": "MIT", "dependencies": { - "@next/eslint-plugin-next": "16.4.0", + "@next/eslint-plugin-next": "16.4.2", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.32.0", @@ -11504,12 +11504,12 @@ } }, "node_modules/next": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/next/-/next-16.4.0.tgz", - "integrity": "sha512-N2C+9q89Z/oLrsepSRS4jRMtwmUZAoZuAHU0e9T6+sAB5DdhADmrnOW+zjYijlrYFmVfmQhGVGy9eMFoB7UgKg==", + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/next/-/next-16.4.2.tgz", + "integrity": "sha512-r+oBJojfefUjOiWkBhXgLS+igxX/7BWyK59uHnyOyplMSLtBZ+QLP3okrdYTbrfrbrmNTVWLBPaHDX9KRaIt3w==", "license": "MIT", "dependencies": { - "@next/env": "16.4.0", + "@next/env": "16.4.2", "@swc/helpers": "0.5.23", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", @@ -11523,15 +11523,15 @@ "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "16.4.0", - "@next/swc-darwin-x64": "16.4.0", - "@next/swc-linux-arm64-gnu": "16.4.0", - "@next/swc-linux-arm64-musl": "16.4.0", - "@next/swc-linux-x64-gnu": "16.4.0", - "@next/swc-linux-x64-musl": "16.4.0", - "@next/swc-win32-arm64-msvc": "16.4.0", - "@next/swc-win32-x64-msvc": "16.4.0", - "sharp": "^0.35.4" + "@next/swc-darwin-arm64": "16.4.2", + "@next/swc-darwin-x64": "16.4.2", + "@next/swc-linux-arm64-gnu": "16.4.2", + "@next/swc-linux-arm64-musl": "16.4.2", + "@next/swc-linux-x64-gnu": "16.4.2", + "@next/swc-linux-x64-musl": "16.4.2", + "@next/swc-win32-arm64-msvc": "16.4.2", + "@next/swc-win32-x64-msvc": "16.4.2", + "sharp": "^0.35.5" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -12711,13 +12711,13 @@ } }, "node_modules/rolldown": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.6.tgz", - "integrity": "sha512-vMM4q3aixf46GiF1Kok8jDPFsEpXgFWGjUHXNkNHNm+Y2adXAG2dbX91jkti3i0ZRsOlcmbuzAz1poObSHCmUA==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.15.tgz", + "integrity": "sha512-6odND+fel5zVw3I4Yc2e69gvguDnX0CAAGiNDZSq4ik5WUcaLrkD2GLuog0XcODbor31hZ5ayEMgVDno+KPV5g==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.147.0", + "@oxc-project/types": "=0.155.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -12727,21 +12727,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm-eabi": "1.2.6", - "@rolldown/binding-android-arm64": "1.2.6", - "@rolldown/binding-darwin-arm64": "1.2.6", - "@rolldown/binding-darwin-x64": "1.2.6", - "@rolldown/binding-freebsd-x64": "1.2.6", - "@rolldown/binding-linux-arm-gnueabihf": "1.2.6", - "@rolldown/binding-linux-arm64-gnu": "1.2.6", - "@rolldown/binding-linux-arm64-musl": "1.2.6", - "@rolldown/binding-linux-ppc64-gnu": "1.2.6", - "@rolldown/binding-linux-s390x-gnu": "1.2.6", - "@rolldown/binding-linux-x64-gnu": "1.2.6", - "@rolldown/binding-linux-x64-musl": "1.2.6", - "@rolldown/binding-openharmony-arm64": "1.2.6", - "@rolldown/binding-win32-arm64-msvc": "1.2.6", - "@rolldown/binding-win32-x64-msvc": "1.2.6" + "@rolldown/binding-android-arm-eabi": "1.2.15", + "@rolldown/binding-android-arm64": "1.2.15", + "@rolldown/binding-darwin-arm64": "1.2.15", + "@rolldown/binding-darwin-x64": "1.2.15", + "@rolldown/binding-freebsd-x64": "1.2.15", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.15", + "@rolldown/binding-linux-arm64-gnu": "1.2.15", + "@rolldown/binding-linux-arm64-musl": "1.2.15", + "@rolldown/binding-linux-ppc64-gnu": "1.2.15", + "@rolldown/binding-linux-s390x-gnu": "1.2.15", + "@rolldown/binding-linux-x64-gnu": "1.2.15", + "@rolldown/binding-linux-x64-musl": "1.2.15", + "@rolldown/binding-openharmony-arm64": "1.2.15", + "@rolldown/binding-win32-arm64-msvc": "1.2.15", + "@rolldown/binding-win32-x64-msvc": "1.2.15" } }, "node_modules/rope-sequence": { @@ -14686,16 +14686,16 @@ } }, "node_modules/vitest/node_modules/vite": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.2.tgz", - "integrity": "sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==", + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.5.tgz", + "integrity": "sha512-z1YpjieJ9g0ebTmQpLBv/oq9E3F9EIlOe+7LahW8SrE3LTiG3udBRUXQOlYXP9b+IC2q2phHeVxj9xGlw34OEQ==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.33.0", - "picomatch": "^4.0.5", - "postcss": "^8.5.26", - "rolldown": "~1.2.4", + "picomatch": "^4.0.7", + "postcss": "^8.5.30", + "rolldown": "~1.2.14", "tinyglobby": "^0.2.17" }, "bin": { @@ -14712,7 +14712,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.4.0 || ^0.5.0", + "@vitejs/devtools": "^0.7.1 || ^0.8.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", @@ -14764,9 +14764,9 @@ } }, "node_modules/vitest/node_modules/yaml": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", - "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz", + "integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==", "dev": true, "license": "ISC", "optional": true, diff --git a/package.json b/package.json index 89ae4d2..3ebfd23 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "fuse.js": "^7.1.0", "js-yaml": "^4.3.1", "lucide-react": "^0.562.0", - "next": "^16.4.0", + "next": "^16.4.2", "next-intl": "^4.9.2", "next-mdx-remote": "^6.0.0", "react": "19.2.8", @@ -57,7 +57,7 @@ "@types/react-dom": "^19", "@types/three": "^0.183.1", "eslint": "^9", - "eslint-config-next": "^16.2.6", + "eslint-config-next": "^16.4.2", "playwright": "^1.62.0", "prettier": "^3.8.1", "sharp": "^0.35.3", From e8a05e8cae2818a1fd5453a6ce7fa5416b321cc9 Mon Sep 17 00:00:00 2001 From: Si Hyeong Lee <6832586+writingdeveloper@users.noreply.github.com> Date: Thu, 22 Oct 2026 18:26:32 -0700 Subject: [PATCH 4/4] ci(security): allow a dated dev-only exception for the braces advisory GHSA-vfj7-8cjw-p6xm (braces <= 3.0.3, high) has no patched release and reaches this repository only through eslint-config-next's @next/eslint-plugin-next -> fast-glob -> micromatch chain. The strict full audit has kept every pull request red while production stays on an affected Next.js release. The full-audit step now runs `npm run audit:deps`, which applies the same high threshold and lets an advisory through only when .github/audit-exceptions.json lists it, the entry is valid, unexpired and no longer than 90 days, and every installed copy is dev-only in package-lock.json. Each excepted advisory is annotated on every run and a stale entry is flagged for removal. The production audit step is unchanged and takes no exceptions. The exception expires 2026-12-31. The approval and risk review are in docs/reviews/2026-10-22-braces-dev-audit-exception.md. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01AfoJbUTPLtMgivSnuxD7jS --- .github/audit-exceptions.json | 12 + .github/workflows/ci.yml | 7 +- README.md | 5 +- .../2026-10-22-braces-dev-audit-exception.md | 51 ++++ package.json | 1 + scripts/audit-deps.ts | 61 +++++ src/lib/__tests__/audit-gate.test.ts | 237 ++++++++++++++++++ src/lib/audit-gate.ts | 146 +++++++++++ 8 files changed, 516 insertions(+), 4 deletions(-) create mode 100644 .github/audit-exceptions.json create mode 100644 docs/reviews/2026-10-22-braces-dev-audit-exception.md create mode 100644 scripts/audit-deps.ts create mode 100644 src/lib/__tests__/audit-gate.test.ts create mode 100644 src/lib/audit-gate.ts diff --git a/.github/audit-exceptions.json b/.github/audit-exceptions.json new file mode 100644 index 0000000..016b34c --- /dev/null +++ b/.github/audit-exceptions.json @@ -0,0 +1,12 @@ +{ + "exceptions": [ + { + "id": "GHSA-vfj7-8cjw-p6xm", + "package": "braces", + "approvedBy": "writingdeveloper", + "approvedOn": "2026-10-22", + "expires": "2026-12-31", + "review": "docs/reviews/2026-10-22-braces-dev-audit-exception.md" + } + ] +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b547964..6e8f0a3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,7 +52,8 @@ jobs: - name: Audit production dependencies run: npm audit --omit=dev --audit-level=high - # Keep the original strict all-dependencies gate. No exceptions are - # authorized by this change, so known dev-only advisories still fail. + # Same threshold over every dependency. An advisory passes only when + # .github/audit-exceptions.json lists it with an expiry and a review, and + # every installed copy is dev-only; each pass is annotated on the run. - name: Audit dependencies - run: npm audit --audit-level=high + run: npm run audit:deps diff --git a/README.md b/README.md index 990f393..c2de491 100644 --- a/README.md +++ b/README.md @@ -46,12 +46,15 @@ npm run format:check npm test npm run untranslated npm run verify:hero -- public/images/posts/*.webp -npm audit --audit-level=high +npm audit --omit=dev --audit-level=high +npm run audit:deps npm run build ``` `npm run format` applies the repository's Prettier rules to TypeScript/TSX source. The formatter accepts either LF or CRLF line endings so the check behaves the same on Windows and Linux. +The production audit takes no exceptions. `npm run audit:deps` audits every dependency at the same high threshold and lets an advisory through only when [`.github/audit-exceptions.json`](.github/audit-exceptions.json) lists it with an expiry and a review record, and every installed copy is a dev dependency. + ## Content model ```text diff --git a/docs/reviews/2026-10-22-braces-dev-audit-exception.md b/docs/reviews/2026-10-22-braces-dev-audit-exception.md new file mode 100644 index 0000000..74402d7 --- /dev/null +++ b/docs/reviews/2026-10-22-braces-dev-audit-exception.md @@ -0,0 +1,51 @@ +# Security review — dated dev-only exception for GHSA-vfj7-8cjw-p6xm (2026-10-22 PT) + +Status: **approved exception**. Approver: repository owner (`writingdeveloper`), in the 2026-10-22 PT working session, choosing option A of the three presented (dated dev-only exception / keep CI red until upstream fixes / remove the Next.js ESLint rules). Expires **2026-12-31** inclusive (UTC). From 2027-01-01 the full audit fails again unless the entry is removed or renewed through a new review. + +This is the separately reviewed, time-bounded policy that the [2026-10-13 Keystatic isolation review](2026-10-13-keystatic-chokidar-runtime-isolation.md) required before PR #33 could merge. + +## Identity + +- Advisory: [GHSA-vfj7-8cjw-p6xm](https://github.com/advisories/GHSA-vfj7-8cjw-p6xm) / CVE-2026-93687, high (CVSS 7.5, CWE-674), published 2026-09-18, last updated 2026-10-02. +- Affected: `braces` <= 3.0.3. No patched release exists: 3.0.3 is the latest published version on 2026-10-22. +- Impact: deeply nested brace patterns under the length limit exhaust the call stack and terminate the Node.js process with an uncaught `RangeError`. Denial of service only; no code execution or data exposure is described. +- Installed copy: `node_modules/braces` 3.0.3, marked `dev: true` in package-lock.json. +- Only path: `eslint-config-next` 16.4.2 → `@next/eslint-plugin-next` → `fast-glob` → `micromatch` → `braces`. No current `eslint-config-next` release avoids it; npm's only suggested fix is a semver-major downgrade to 14.2.35. + +## Why the risk is acceptable for a bounded period + +- `braces` is not in the production dependency graph. The 2026-10-13 Keystatic watcher override removed the runtime copy, and `npm audit --omit=dev --audit-level=high` reports 0 vulnerabilities. That CI step is unchanged and takes no exceptions. +- The copy runs only inside ESLint: local development and the CI lint step. Its inputs are this repository's lint configuration and file globs, not request data. `next build` on Next.js 16 does not run ESLint, so the Vercel build does not load it either. +- Exploiting it requires committing a crafted glob to the repository, and the result is a crashed lint run. +- Keeping CI red is worse: every pull request stays blocked while production remains on Next.js 16.3.3, which is affected by the 2026-10-22 critical Next.js advisories that PR #33 fixes. + +## Enforcement + +`.github/audit-exceptions.json` records the advisory, package, approver, approval date, expiry and this review. CI's **Audit dependencies** step runs `npm run audit:deps` (`scripts/audit-deps.ts`, logic in `src/lib/audit-gate.ts`, tests in `src/lib/__tests__/audit-gate.test.ts`), which applies the `--audit-level=high` threshold to the full report and fails when: + +- any high or critical advisory is not listed; +- a listed advisory has an installed copy that is not `dev: true` (including `devOptional` or a copy missing from the lockfile); +- the listed package differs from the advisory's package; +- the exception has expired, is malformed, or runs longer than 90 days from its approval date; +- `npm audit` returns no report (for example, the registry is unreachable). + +Every run prints each excepted advisory as a GitHub Actions warning annotation, and flags an entry that no longer matches any advisory so it can be removed. The 2026-10-13 regression test separately keeps every installed `braces` copy dev-only. + +## Verification on 2026-10-22 PT + +- `npm run audit:deps`: 0 blocking, 1 excepted (`node_modules/braces`). +- Negative runs, each exit 1: an expired entry (`expired`), an empty exception list (`no-exception`), and an unreachable registry (`audit-error`). +- 21 unit tests cover expiry (inclusive), dev-only checks, per-advisory matching, the severity threshold, stale entries, malformed entries and audit errors. +- GitHub CI evidence is recorded on PR #33 and in Plane FOLIO-45. + +## Removal and renewal + +- Remove the entry when a fixed `braces` is published or the ESLint chain drops it. The gate warns when the entry no longer matches. +- Renewal needs a new review in `docs/reviews`, a new approval date and an expiry no more than 90 days later. + +## Limitations + +- The exception accepts a developer-tool denial-of-service risk for a bounded period. It does not make `braces` safe. +- Two moderate `@vitest/mocker` advisories (GHSA-qxg6-89mg-rqv8, GHSA-xp26-3454-mjgp) sit below the threshold and are not covered here. They need a separate `vitest` update. + +Plane source of truth: FOLIO-45. diff --git a/package.json b/package.json index 3ebfd23..bfc0096 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "type-check": "tsc --noEmit", "untranslated": "npx tsx scripts/untranslated.ts", "verify:hero": "tsx scripts/verify-hero.ts", + "audit:deps": "tsx scripts/audit-deps.ts", "capture:screenshots": "tsx scripts/capture-screenshots.ts", "fetch:fonts": "tsx scripts/fetch-pretendard-subset.ts", "qa:navigation": "tsx scripts/qa-navigation.ts" diff --git a/scripts/audit-deps.ts b/scripts/audit-deps.ts new file mode 100644 index 0000000..5f9db46 --- /dev/null +++ b/scripts/audit-deps.ts @@ -0,0 +1,61 @@ +/** + * Full dependency audit with dated, dev-only exceptions. + * + * Usage: npm run audit:deps + * Fails on any high or critical advisory unless .github/audit-exceptions.json + * lists it, the exception has not expired, and every installed copy of the + * package is dev-only in package-lock.json. CI also runs + * `npm audit --omit=dev --audit-level=high`, which takes no exceptions at all. + */ +import { spawnSync } from 'child_process' +import fs from 'fs' +import path from 'path' +import { evaluateAudit, type AuditException, type LockPackages, type NpmAuditReport } from '../src/lib/audit-gate' + +const EXCEPTIONS_FILE = '.github/audit-exceptions.json' +const inActions = process.env.GITHUB_ACTIONS === 'true' + +// In GitHub Actions these lines become annotations on the run summary, so an +// excepted advisory stays visible on every run instead of passing silently. +function annotate(level: 'warning' | 'error', message: string) { + console.log(inActions ? `::${level}::${message}` : `${level.toUpperCase()} ${message}`) +} + +function readJson(file: string): T { + return JSON.parse(fs.readFileSync(path.join(process.cwd(), file), 'utf8')) as T +} + +function runAudit(): NpmAuditReport { + // npm audit exits 1 whenever it finds anything, so only its JSON is used. + const audit = spawnSync('npm', ['audit', '--json'], { + encoding: 'utf8', + maxBuffer: 64 * 1024 * 1024, + shell: process.platform === 'win32', + }) + try { + return JSON.parse(audit.stdout) as NpmAuditReport + } catch { + return { message: audit.error?.message || audit.stderr?.trim() || 'npm audit printed no JSON' } + } +} + +const { exceptions } = readJson<{ exceptions: AuditException[] }>(EXCEPTIONS_FILE) +const lock = readJson<{ packages: LockPackages }>('package-lock.json') +const today = new Date().toISOString().slice(0, 10) +const result = evaluateAudit(runAudit(), lock.packages, exceptions, today) + +for (const item of result.excepted) { + annotate( + 'warning', + `${item.id} (${item.severity}, ${item.package}) passes as dev-only until ${item.expires}: ${item.nodes.join(', ')}` + ) +} +for (const exception of result.unused) { + annotate('warning', `${exception.id} no longer appears in npm audit; remove it from ${EXCEPTIONS_FILE}`) +} +for (const item of result.blocking) { + annotate('error', `${item.id} ${item.reason}: ${item.detail}`) +} + +console.log(`\n${result.blocking.length} blocking, ${result.excepted.length} excepted (UTC ${today})`) +process.exit(result.blocking.length === 0 ? 0 : 1) diff --git a/src/lib/__tests__/audit-gate.test.ts b/src/lib/__tests__/audit-gate.test.ts new file mode 100644 index 0000000..1b234c7 --- /dev/null +++ b/src/lib/__tests__/audit-gate.test.ts @@ -0,0 +1,237 @@ +import { describe, expect, it } from 'vitest' +import { + evaluateAudit, + type AuditException, + type AuditGateResult, + type LockPackages, + type NpmAdvisory, + type NpmAuditReport, + type NpmVulnerability, + type Severity, +} from '../audit-gate' + +// Shapes mirror `npm audit --json` (auditReportVersion 2) and package-lock.json +// from npm 10.9.2 on 2026-10-22: braces carries the advisory, and every package +// above it inherits the severity through a string `via`. +const bracesAdvisory: NpmAdvisory = { + source: 1240992, + name: 'braces', + dependency: 'braces', + title: 'braces vulnerable to stack-exhaustion denial of service through deeply nested patterns', + url: 'https://github.com/advisories/GHSA-vfj7-8cjw-p6xm', + severity: 'high', + cwe: ['CWE-674'], + cvss: { score: 7.5, vectorString: 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H' }, + range: '<=3.0.3', +} + +const eslintFix = { name: 'eslint-config-next', version: '14.2.35', isSemVerMajor: true } + +function bracesChain( + bracesNodes = ['node_modules/braces'], + extraAdvisories: NpmAdvisory[] = [] +): Record { + const derived = (name: string, via: string, effects: string[], range: string): NpmVulnerability => ({ + name, + severity: 'high', + isDirect: name === 'eslint-config-next', + via: [via], + effects, + range, + nodes: [`node_modules/${name}`], + fixAvailable: eslintFix, + }) + return { + braces: { + name: 'braces', + severity: 'high', + isDirect: false, + via: [bracesAdvisory, ...extraAdvisories], + effects: ['micromatch'], + range: '*', + nodes: bracesNodes, + fixAvailable: eslintFix, + }, + micromatch: derived('micromatch', 'braces', ['fast-glob'], '>=0.2.0'), + 'fast-glob': derived('fast-glob', 'micromatch', ['@next/eslint-plugin-next'], '*'), + '@next/eslint-plugin-next': derived( + '@next/eslint-plugin-next', + 'fast-glob', + ['eslint-config-next'], + '>=14.3.0-canary.0' + ), + 'eslint-config-next': derived('eslint-config-next', '@next/eslint-plugin-next', [], '>=14.3.0-canary.0'), + } +} + +function report(vulnerabilities: Record): NpmAuditReport { + return { auditReportVersion: 2, vulnerabilities } +} + +const bracesPackage = { + version: '3.0.3', + resolved: 'https://registry.npmjs.org/braces/-/braces-3.0.3.tgz', + integrity: 'sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==', + license: 'MIT', + dependencies: { 'fill-range': '^7.1.1' }, + engines: { node: '>=8' }, +} +// npm marks a package `dev` only when nothing but devDependencies reach it. +const devOnly = { ...bracesPackage, dev: true } +// `devOptional` means an optional dependency of a production package also +// reaches it, so it ships unless optional dependencies are omitted. +const devOptional = { ...bracesPackage, devOptional: true } +const devLock: LockPackages = { 'node_modules/braces': devOnly } + +const bracesException: AuditException = { + id: 'GHSA-vfj7-8cjw-p6xm', + package: 'braces', + approvedBy: 'writingdeveloper', + approvedOn: '2026-10-22', + expires: '2026-12-31', + review: 'docs/reviews/2026-10-22-braces-dev-audit-exception.md', +} + +const reasons = (result: AuditGateResult) => result.blocking.map(({ id, reason }) => ({ id, reason })) + +describe('evaluateAudit', () => { + it('passes the dev-only braces chain through the last day of its exception', () => { + const result = evaluateAudit(report(bracesChain()), devLock, [bracesException], '2026-12-31') + expect(result.blocking).toEqual([]) + expect(result.excepted).toEqual([ + { + id: 'GHSA-vfj7-8cjw-p6xm', + package: 'braces', + severity: 'high', + expires: '2026-12-31', + nodes: ['node_modules/braces'], + }, + ]) + }) + + it('fails the day after the exception expires', () => { + const result = evaluateAudit(report(bracesChain()), devLock, [bracesException], '2027-01-01') + expect(reasons(result)).toEqual([{ id: 'GHSA-vfj7-8cjw-p6xm', reason: 'expired' }]) + expect(result.excepted).toEqual([]) + }) + + it('fails the advisory when no exception lists it', () => { + const result = evaluateAudit(report(bracesChain()), devLock, [], '2026-10-22') + expect(reasons(result)).toEqual([{ id: 'GHSA-vfj7-8cjw-p6xm', reason: 'no-exception' }]) + }) + + it.each<[string, LockPackages]>([ + [ + 'is not marked dev', + { 'node_modules/braces': devOnly, 'node_modules/chokidar/node_modules/braces': bracesPackage }, + ], + ['is dev-optional', { 'node_modules/braces': devOnly, 'node_modules/chokidar/node_modules/braces': devOptional }], + ['is missing from the lockfile', { 'node_modules/braces': devOnly }], + ])('fails an excepted advisory when a second installed copy %s', (_label, lock) => { + const chain = bracesChain(['node_modules/braces', 'node_modules/chokidar/node_modules/braces']) + const result = evaluateAudit(report(chain), lock, [bracesException], '2026-10-22') + expect(reasons(result)).toEqual([{ id: 'GHSA-vfj7-8cjw-p6xm', reason: 'not-dev-only' }]) + expect(result.excepted).toEqual([]) + }) + + it('does not let one exception cover a second advisory on the same package', () => { + // Hypothetical second advisory against the same braces release. + const second: NpmAdvisory = { + ...bracesAdvisory, + source: 1250001, + title: 'braces hypothetical second advisory', + url: 'https://github.com/advisories/GHSA-9q2f-7hm4-cjwx', + } + const result = evaluateAudit( + report(bracesChain(['node_modules/braces'], [second])), + devLock, + [bracesException], + '2026-10-22' + ) + expect(reasons(result)).toEqual([{ id: 'GHSA-9q2f-7hm4-cjwx', reason: 'no-exception' }]) + expect(result.excepted.map((item) => item.id)).toEqual(['GHSA-vfj7-8cjw-p6xm']) + }) + + it.each<[Severity, number]>([ + ['low', 0], + ['moderate', 0], + ['high', 1], + ['critical', 1], + ])('treats an unlisted %s advisory as blocking only at high or above', (severity, blocking) => { + const advisory: NpmAdvisory = { + source: 1250002, + name: 'left-pad', + dependency: 'left-pad', + title: 'left-pad hypothetical advisory', + url: 'https://github.com/advisories/GHSA-r7xg-2hcm-qp3v', + severity, + cwe: [], + cvss: { score: 0, vectorString: null }, + range: '<1.3.1', + } + const vulnerabilities: Record = { + 'left-pad': { + name: 'left-pad', + severity, + isDirect: true, + via: [advisory], + effects: [], + range: '<1.3.1', + nodes: ['node_modules/left-pad'], + fixAvailable: true, + }, + } + const lock: LockPackages = { 'node_modules/left-pad': { version: '1.3.0', dev: true } } + expect(evaluateAudit(report(vulnerabilities), lock, [], '2026-10-22').blocking).toHaveLength(blocking) + }) + + it('lists an exception that no advisory matches so it can be removed', () => { + const result = evaluateAudit(report({}), devLock, [bracesException], '2026-10-22') + expect(result.blocking).toEqual([]) + expect(result.unused.map((exception) => exception.id)).toEqual(['GHSA-vfj7-8cjw-p6xm']) + }) + + it('fails when npm audit returns an error instead of a report', () => { + // npm 10.9.2 prints this shape and no vulnerabilities when the registry + // audit endpoint cannot be reached. + const failed: NpmAuditReport = { + message: + 'request to https://registry.npmjs.org/-/npm/v1/security/audits/quick failed, reason: getaddrinfo ENOTFOUND registry.npmjs.org', + error: { summary: '', detail: '' }, + } + const result = evaluateAudit(failed, devLock, [bracesException], '2026-10-22') + expect(reasons(result)).toEqual([{ id: 'npm audit', reason: 'audit-error' }]) + }) + + it('fails when the exception names a different package than the advisory', () => { + const result = evaluateAudit( + report(bracesChain()), + devLock, + [{ ...bracesException, package: 'micromatch' }], + '2026-10-22' + ) + expect(reasons(result)).toEqual([{ id: 'GHSA-vfj7-8cjw-p6xm', reason: 'package-mismatch' }]) + }) + + it.each<[string, Partial]>([ + ['has no approver', { approvedBy: '' }], + ['has no review record', { review: '' }], + ['is not a GHSA identifier', { id: 'CVE-2026-93687' }], + ['expires before it was approved', { expires: '2026-10-21' }], + ['runs longer than 90 days', { expires: '2027-01-21' }], + ['uses an impossible date', { expires: '2026-11-31' }], + ])('ignores an exception that %s and fails its advisory', (_label, change) => { + const result = evaluateAudit(report(bracesChain()), devLock, [{ ...bracesException, ...change }], '2026-10-22') + expect(result.blocking.map((item) => item.reason)).toEqual(['invalid-exception', 'no-exception']) + }) + + it('accepts an exception that runs exactly 90 days', () => { + const result = evaluateAudit( + report(bracesChain()), + devLock, + [{ ...bracesException, expires: '2027-01-20' }], + '2026-10-22' + ) + expect(result.blocking).toEqual([]) + }) +}) diff --git a/src/lib/audit-gate.ts b/src/lib/audit-gate.ts new file mode 100644 index 0000000..a2497f9 --- /dev/null +++ b/src/lib/audit-gate.ts @@ -0,0 +1,146 @@ +export type Severity = 'info' | 'low' | 'moderate' | 'high' | 'critical' + +export interface NpmAdvisory { + source: number + name: string + dependency: string + title: string + url: string + severity: Severity + cwe: string[] + cvss: { score: number; vectorString: string | null } + range: string +} + +export interface NpmVulnerability { + name: string + severity: Severity + isDirect: boolean + via: (string | NpmAdvisory)[] + effects: string[] + range: string + nodes: string[] + fixAvailable: boolean | { name: string; version: string; isSemVerMajor: boolean } +} + +export interface NpmAuditReport { + auditReportVersion?: number + vulnerabilities?: Record + message?: string + error?: { summary?: string; detail?: string } +} + +export type LockPackages = Record + +export interface AuditException { + id: string + package: string + approvedBy: string + approvedOn: string + expires: string + review: string +} + +export type BlockingReason = + 'audit-error' | 'invalid-exception' | 'no-exception' | 'package-mismatch' | 'expired' | 'not-dev-only' + +export interface AuditGateResult { + blocking: { id: string; reason: BlockingReason; detail: string }[] + excepted: { id: string; package: string; severity: Severity; expires: string; nodes: string[] }[] + unused: AuditException[] +} + +const SEVERITY_RANK: Record = { info: 0, low: 1, moderate: 2, high: 3, critical: 4 } +// Same threshold as `npm audit --audit-level=high`. +const BLOCKING_RANK = SEVERITY_RANK.high +// An exception is a dated decision, not a standing waiver: renewing one means +// a new approval date and a new review. +const MAX_EXCEPTION_DAYS = 90 +const DAY_MS = 24 * 60 * 60 * 1000 + +function parseDay(value: string): number | null { + const time = Date.parse(`${value}T00:00:00Z`) + if (Number.isNaN(time) || new Date(time).toISOString().slice(0, 10) !== value) return null + return time +} + +function invalidReason(exception: AuditException): string | null { + if (!/^GHSA-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}$/.test(exception.id)) return 'id is not a GHSA identifier' + if (!exception.package) return 'package is missing' + if (!exception.approvedBy) return 'approvedBy is missing' + if (!exception.review) return 'review record is missing' + const approvedOn = parseDay(exception.approvedOn) + const expires = parseDay(exception.expires) + if (approvedOn === null || expires === null) return 'approvedOn and expires must be YYYY-MM-DD dates' + if (expires < approvedOn) return 'expires before it was approved' + if (expires - approvedOn > MAX_EXCEPTION_DAYS * DAY_MS) return `runs longer than ${MAX_EXCEPTION_DAYS} days` + return null +} + +function advisoryId(advisory: NpmAdvisory): string { + return advisory.url.match(/GHSA(-[a-z0-9]{4}){3}$/)?.[0] ?? `npm-${advisory.source}` +} + +/** + * Applies `--audit-level=high` to a full `npm audit --json` report, letting an + * advisory through only when an exception lists it, the exception is valid and + * unexpired (`today` and `expires` are UTC YYYY-MM-DD, `expires` inclusive), and + * every installed copy of the package is dev-only in package-lock.json. + */ +export function evaluateAudit( + report: NpmAuditReport, + lockPackages: LockPackages, + exceptions: AuditException[], + today: string +): AuditGateResult { + if (report.error || !report.vulnerabilities) { + const detail = report.message || report.error?.summary || report.error?.detail || 'no vulnerability report' + return { blocking: [{ id: 'npm audit', reason: 'audit-error', detail }], excepted: [], unused: [] } + } + + const result: AuditGateResult = { blocking: [], excepted: [], unused: [] } + const valid = new Map() + for (const exception of exceptions) { + const problem = invalidReason(exception) + if (problem) result.blocking.push({ id: exception.id, reason: 'invalid-exception', detail: problem }) + else valid.set(exception.id, exception) + } + + const matched = new Set() + for (const vulnerability of Object.values(report.vulnerabilities)) { + for (const advisory of vulnerability.via) { + // A string names another vulnerable package; its advisory is judged there. + if (typeof advisory === 'string' || SEVERITY_RANK[advisory.severity] < BLOCKING_RANK) continue + + const id = advisoryId(advisory) + const exception = valid.get(id) + if (!exception) { + result.blocking.push({ id, reason: 'no-exception', detail: `${advisory.severity} in ${advisory.name}` }) + continue + } + matched.add(id) + if (exception.package !== advisory.name) { + const detail = `the exception names ${exception.package}, the advisory affects ${advisory.name}` + result.blocking.push({ id, reason: 'package-mismatch', detail }) + } else if (today > exception.expires) { + result.blocking.push({ id, reason: 'expired', detail: `the exception expired on ${exception.expires}` }) + } else { + const shipped = vulnerability.nodes.filter((node) => lockPackages[node]?.dev !== true) + if (shipped.length > 0) { + result.blocking.push({ id, reason: 'not-dev-only', detail: `not dev-only: ${shipped.join(', ')}` }) + } else { + result.excepted.push({ + id, + package: advisory.name, + severity: advisory.severity, + expires: exception.expires, + nodes: vulnerability.nodes, + }) + } + } + } + } + + result.unused = [...valid.values()].filter((exception) => !matched.has(exception.id)) + return result +}