From f6abf10f1d20b4ed376750b7208f0285a232c915 Mon Sep 17 00:00:00 2001 From: ootakazuhiko Date: Thu, 23 Jul 2026 08:40:21 +0900 Subject: [PATCH 1/4] chore: define install-script policy --- .github/workflows/book-qa.yml | 9 +- package-lock.json | 382 ++++++++++++------------- package.json | 20 +- scripts/check-install-script-policy.js | 119 ++++++++ 4 files changed, 327 insertions(+), 203 deletions(-) create mode 100644 scripts/check-install-script-policy.js diff --git a/.github/workflows/book-qa.yml b/.github/workflows/book-qa.yml index 1567c06..fa80db0 100644 --- a/.github/workflows/book-qa.yml +++ b/.github/workflows/book-qa.yml @@ -31,7 +31,9 @@ jobs: with: node-version: '20' cache: npm - cache-dependency-path: book-formatter/package-lock.json + cache-dependency-path: | + package-lock.json + book-formatter/package-lock.json - name: Root dependency security audit run: npm run check:security @@ -45,6 +47,11 @@ jobs: - name: Install book dependencies for contract checks run: npm ci --ignore-scripts + - name: Check install-script allowlist contract + run: | + node scripts/check-install-script-policy.js + node scripts/check-install-script-policy.js --self-test + - name: Check Chapter 8 CodeQL workflow contract run: | node scripts/check-chapter08-codeql-contract.js diff --git a/package-lock.json b/package-lock.json index 611419b..6bd671a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ }, "optionalDependencies": { "puppeteer": "^24.43.1", - "sharp": "^0.34.5" + "sharp": "^0.35.3" } }, "node_modules/@babel/code-frame": { @@ -51,9 +51,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", + "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", "license": "MIT", "optional": true, "dependencies": { @@ -71,9 +71,9 @@ } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.3.tgz", + "integrity": "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==", "cpu": [ "arm64" ], @@ -83,19 +83,19 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" + "@img/sharp-libvips-darwin-arm64": "1.3.2" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.3.tgz", + "integrity": "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==", "cpu": [ "x64" ], @@ -105,19 +105,38 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" + "@img/sharp-libvips-darwin-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.3.tgz", + "integrity": "sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==", + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.2.tgz", + "integrity": "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==", "cpu": [ "arm64" ], @@ -131,9 +150,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.2.tgz", + "integrity": "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==", "cpu": [ "x64" ], @@ -147,15 +166,12 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.2.tgz", + "integrity": "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==", "cpu": [ "arm" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -166,15 +182,12 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.2.tgz", + "integrity": "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==", "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -185,15 +198,12 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.2.tgz", + "integrity": "sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==", "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -204,15 +214,12 @@ } }, "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.2.tgz", + "integrity": "sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==", "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -223,15 +230,12 @@ } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.2.tgz", + "integrity": "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==", "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -242,15 +246,12 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.2.tgz", + "integrity": "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==", "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -261,15 +262,12 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.2.tgz", + "integrity": "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==", "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -280,15 +278,12 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.2.tgz", + "integrity": "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==", "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -299,228 +294,220 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.3.tgz", + "integrity": "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==", "cpu": [ "arm" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" + "@img/sharp-libvips-linux-arm": "1.3.2" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.3.tgz", + "integrity": "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==", "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" + "@img/sharp-libvips-linux-arm64": "1.3.2" } }, "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.3.tgz", + "integrity": "sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==", "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" + "@img/sharp-libvips-linux-ppc64": "1.3.2" } }, "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.3.tgz", + "integrity": "sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==", "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" + "@img/sharp-libvips-linux-riscv64": "1.3.2" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.3.tgz", + "integrity": "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==", "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" + "@img/sharp-libvips-linux-s390x": "1.3.2" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.3.tgz", + "integrity": "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==", "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" + "@img/sharp-libvips-linux-x64": "1.3.2" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.3.tgz", + "integrity": "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==", "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.3.tgz", + "integrity": "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==", "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + "@img/sharp-libvips-linuxmusl-x64": "1.3.2" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.3.tgz", + "integrity": "sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==", + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.3.tgz", + "integrity": "sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==", "cpu": [ "wasm32" ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "license": "Apache-2.0", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.7.0" + "@img/sharp-wasm32": "0.35.3" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.3.tgz", + "integrity": "sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==", "cpu": [ "arm64" ], @@ -530,16 +517,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.3.tgz", + "integrity": "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==", "cpu": [ "ia32" ], @@ -549,16 +536,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": "^20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.3.tgz", + "integrity": "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==", "cpu": [ "x64" ], @@ -568,7 +555,7 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" @@ -877,9 +864,9 @@ } }, "node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", + "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", "dev": true, "license": "MIT", "dependencies": { @@ -1991,9 +1978,9 @@ "optional": true }, "node_modules/linkify-it": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.1.tgz", - "integrity": "sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.2.tgz", + "integrity": "sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==", "dev": true, "funding": [ { @@ -2021,9 +2008,9 @@ } }, "node_modules/markdown-it": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.2.0.tgz", - "integrity": "sha512-1TGiQiJVRQ3NPmZH6sx5Cfnmg6GQm9jvC1ch4TK511NjSJvjzKLzn5pPfZRNZkRPZP0HqCioSndqH8v2nRaWVQ==", + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.3.0.tgz", + "integrity": "sha512-RCEsPjR+sr0x+AuYp601tKTkgFG4YEPLCzHST3cQ/fhlJkqAkz1L2/Qbp1j9qw5SBwQHFBoW8+hoN5xssOF0Tw==", "dev": true, "funding": [ { @@ -2038,8 +2025,8 @@ "license": "MIT", "dependencies": { "argparse": "^2.0.1", - "entities": "^4.4.0", - "linkify-it": "^5.0.1", + "entities": "^4.5.0", + "linkify-it": "^5.0.2", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" @@ -3095,9 +3082,9 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "license": "ISC", "optional": true, "bin": { @@ -3108,48 +3095,53 @@ } }, "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", - "hasInstallScript": true, + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.3.tgz", + "integrity": "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==", "license": "Apache-2.0", "optional": true, "dependencies": { - "@img/colour": "^1.0.0", + "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", - "semver": "^7.7.3" + "semver": "^7.8.5" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" + "@img/sharp-darwin-arm64": "0.35.3", + "@img/sharp-darwin-x64": "0.35.3", + "@img/sharp-freebsd-wasm32": "0.35.3", + "@img/sharp-libvips-darwin-arm64": "1.3.2", + "@img/sharp-libvips-darwin-x64": "1.3.2", + "@img/sharp-libvips-linux-arm": "1.3.2", + "@img/sharp-libvips-linux-arm64": "1.3.2", + "@img/sharp-libvips-linux-ppc64": "1.3.2", + "@img/sharp-libvips-linux-riscv64": "1.3.2", + "@img/sharp-libvips-linux-s390x": "1.3.2", + "@img/sharp-libvips-linux-x64": "1.3.2", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2", + "@img/sharp-libvips-linuxmusl-x64": "1.3.2", + "@img/sharp-linux-arm": "0.35.3", + "@img/sharp-linux-arm64": "0.35.3", + "@img/sharp-linux-ppc64": "0.35.3", + "@img/sharp-linux-riscv64": "0.35.3", + "@img/sharp-linux-s390x": "0.35.3", + "@img/sharp-linux-x64": "0.35.3", + "@img/sharp-linuxmusl-arm64": "0.35.3", + "@img/sharp-linuxmusl-x64": "0.35.3", + "@img/sharp-webcontainers-wasm32": "0.35.3", + "@img/sharp-win32-arm64": "0.35.3", + "@img/sharp-win32-ia32": "0.35.3", + "@img/sharp-win32-x64": "0.35.3" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, "node_modules/side-channel": { diff --git a/package.json b/package.json index 1004500..6394b09 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "deploy": "bash scripts/deploy.sh", "clean": "rm -rf _site public output temp", "lint:light": "markdownlint --config .markdownlint-src.json 'src/**/*.md' --ignore node_modules", - "test:light": "npm run check:security && npm run check:metadata && npm run check:chapter08-codeql && npm run check:chapter08-codeql:self-test && npm run check:chapter08-dependency-review && npm run check:chapter08-dependency-review:self-test && npm run check:chapter13-codex-action && npm run check:chapter13-codex-action:self-test && npm run check:issue-240-ux && npm run lint:light && npm run build", + "test:light": "npm run check:security && npm run check:install-script-policy && npm run check:install-script-policy:self-test && npm run check:metadata && npm run check:chapter08-codeql && npm run check:chapter08-codeql:self-test && npm run check:chapter08-dependency-review && npm run check:chapter08-dependency-review:self-test && npm run check:chapter13-codex-action && npm run check:chapter13-codex-action:self-test && npm run check:issue-240-ux && npm run lint:light && npm run build", "help": "echo 'Available commands:\n npm run setup - Interactive setup\n npm run build - Build the book\n npm run preview - Local preview\n npm run deploy - Deploy to GitHub Pages'", "build:generate": "node scripts/build-simple.js", "check:metadata": "node scripts/check-metadata-consistency.js", @@ -30,7 +30,9 @@ "check:chapter08-dependency-review:self-test": "node scripts/check-chapter08-dependency-review-contract.js --self-test", "check:issue-240-ux": "node scripts/check-issue-240-ux.js", "check:chapter13-codex-action": "node scripts/check-chapter13-codex-action-contract.js", - "check:chapter13-codex-action:self-test": "node scripts/check-chapter13-codex-action-contract.js --self-test" + "check:chapter13-codex-action:self-test": "node scripts/check-chapter13-codex-action-contract.js --self-test", + "check:install-script-policy": "node scripts/check-install-script-policy.js", + "check:install-script-policy:self-test": "node scripts/check-install-script-policy.js --self-test" }, "dependencies": { "fs-extra": "^11.3.5", @@ -43,13 +45,14 @@ }, "optionalDependencies": { "puppeteer": "^24.43.1", - "sharp": "^0.34.5" + "sharp": "^0.35.3" }, "overrides": { "minimatch": "10.2.5", - "markdown-it": "14.2.0", - "linkify-it": "5.0.1", - "js-yaml": "4.3.0" + "markdown-it": "14.3.0", + "linkify-it": "5.0.2", + "js-yaml": "4.3.0", + "brace-expansion": "5.0.7" }, "keywords": [ "github", @@ -61,5 +64,8 @@ "bugs": { "url": "https://github.com/itdojp/github-workflow-book/issues" }, - "homepage": "https://itdojp.github.io/github-workflow-book/" + "homepage": "https://itdojp.github.io/github-workflow-book/", + "allowScripts": { + "puppeteer": false + } } diff --git a/scripts/check-install-script-policy.js b/scripts/check-install-script-policy.js new file mode 100644 index 0000000..fbc58bc --- /dev/null +++ b/scripts/check-install-script-policy.js @@ -0,0 +1,119 @@ +#!/usr/bin/env node +'use strict'; + +const fs = require('node:fs'); + +const EXPECTED = Object.freeze({ + sharpRange: '^0.35.3', + sharpVersion: '0.35.3', + puppeteerRange: '^24.43.1', + defaultPdfEngine: 'pandoc', +}); + +function readJson(path) { + return JSON.parse(fs.readFileSync(path, 'utf8')); +} + +function clone(value) { + return JSON.parse(JSON.stringify(value)); +} + +function validate(state) { + const errors = []; + const { pkg, lock, config, buildSource, imageOptimizerSource, pdfSource, readme } = state; + const sharp = lock.packages?.['node_modules/sharp']; + const puppeteer = lock.packages?.['node_modules/puppeteer']; + + if (pkg.optionalDependencies?.sharp !== EXPECTED.sharpRange) { + errors.push(`optionalDependencies.sharp must be ${EXPECTED.sharpRange}`); + } + if (pkg.optionalDependencies?.puppeteer !== EXPECTED.puppeteerRange) { + errors.push(`optionalDependencies.puppeteer must be ${EXPECTED.puppeteerRange}`); + } + if (pkg.allowScripts?.puppeteer !== false) { + errors.push('allowScripts must explicitly deny the Puppeteer download script'); + } + const unexpectedApprovals = Object.entries(pkg.allowScripts ?? {}) + .filter(([, allowed]) => allowed === true) + .map(([name]) => name) + .filter(Boolean); + if (unexpectedApprovals.length) { + errors.push(`unexpected install-script approvals: ${unexpectedApprovals.join(', ')}`); + } + if (sharp?.version !== EXPECTED.sharpVersion || sharp?.hasInstallScript === true) { + errors.push(`lockfile must resolve script-free sharp@${EXPECTED.sharpVersion}`); + } + if (puppeteer?.version !== '24.43.1' || puppeteer?.hasInstallScript !== true) { + errors.push('lockfile must resolve install-script-bearing puppeteer@24.43.1'); + } + if (!buildSource.includes("require('./image-optimizer')")) { + errors.push('build:full must retain the active image optimizer'); + } + if (!imageOptimizerSource.includes("require('sharp')")) { + errors.push('the approved Sharp install script must have an active consumer'); + } + if (!pdfSource.includes("require('puppeteer')")) { + errors.push('the denied Puppeteer package must remain an explicit optional consumer'); + } + if (config.pdf?.engine !== EXPECTED.defaultPdfEngine) { + errors.push(`default PDF engine must remain ${EXPECTED.defaultPdfEngine}`); + } + if (!readme.includes('PUPPETEER_SKIP_DOWNLOAD=true npm ci')) { + errors.push('README must retain the no-browser-download installation path'); + } + for (const command of [ + 'npm run check:install-script-policy', + 'npm run check:install-script-policy:self-test', + ]) { + if (!pkg.scripts?.['test:light']?.includes(command)) { + errors.push(`test:light must run ${command}`); + } + } + return errors; +} + +function loadState() { + return { + pkg: readJson('package.json'), + lock: readJson('package-lock.json'), + config: readJson('book-config.json'), + buildSource: fs.readFileSync('scripts/build.js', 'utf8'), + imageOptimizerSource: fs.readFileSync('scripts/image-optimizer.js', 'utf8'), + pdfSource: fs.readFileSync('scripts/build-pdf.js', 'utf8'), + readme: fs.readFileSync('README.md', 'utf8'), + }; +} + +function expectRejected(base, name, mutate, marker) { + const state = clone(base); + mutate(state); + const errors = validate(state); + if (!errors.some((error) => error.includes(marker))) { + throw new Error(`self-test ${name}: mutation was not rejected (${errors.join('; ')})`); + } +} + +const state = loadState(); +const errors = validate(state); +if (errors.length) { + console.error('Install-script policy check failed:'); + errors.forEach((error) => console.error(`- ${error}`)); + process.exit(1); +} + +if (process.argv.includes('--self-test')) { + const cases = [ + ['unnecessary Sharp approval', (s) => { s.pkg.allowScripts.sharp = true; }, 'unexpected install-script approvals'], + ['Puppeteer approval', (s) => { s.pkg.allowScripts.puppeteer = true; }, 'explicitly deny'], + ['blanket extra approval', (s) => { s.pkg.allowScripts['unknown@1.0.0'] = true; }, 'unexpected install-script approvals'], + ['Sharp install script returns', (s) => { s.lock.packages['node_modules/sharp'].hasInstallScript = true; }, 'script-free'], + ['Sharp version drift', (s) => { s.lock.packages['node_modules/sharp'].version = '0.35.2'; }, `sharp@${EXPECTED.sharpVersion}`], + ['missing Sharp consumer', (s) => { s.imageOptimizerSource = s.imageOptimizerSource.replace("require('sharp')", "require('not-sharp')"); }, 'active consumer'], + ['Puppeteer becomes default', (s) => { s.config.pdf.engine = 'puppeteer'; }, 'default PDF engine'], + ['download guard removed', (s) => { s.readme = s.readme.replace('PUPPETEER_SKIP_DOWNLOAD=true npm ci', 'npm ci'); }, 'no-browser-download'], + ]; + cases.forEach(([name, mutate, marker]) => expectRejected(state, name, mutate, marker)); + console.log(`Install-script policy self-test passed: ${cases.length} negative mutations rejected.`); +} else { + console.log(`Install-script policy passed: sharp@${EXPECTED.sharpVersion} is script-free; Puppeteer download denied.`); +} From ad0ea37a534ca6914be31f5bff5d54b6ba6b4dd0 Mon Sep 17 00:00:00 2001 From: ootakazuhiko Date: Thu, 23 Jul 2026 08:47:44 +0900 Subject: [PATCH 2/4] test: harden install-script contract --- AGENTS.md | 3 +-- package-lock.json | 2 +- package.json | 2 +- scripts/check-install-script-policy.js | 16 +++++++++++++++- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index ba1db30..e806dc8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -29,7 +29,7 @@ ## 5. ローカル検証(推奨) -前提: Node.js `>=20`(`node --version` で確認)、Python 3 系。 +前提: Node.js `^20.9.0 || >=22`(`node --version` で確認)、Python 3 系。 - `python3 scripts/validate_links.py docs`(内部/外部リンクの存在チェック) - `npm ci`(初回のみ) @@ -44,4 +44,3 @@ - 章参照や内部リンクが壊れていない - 変更範囲が過度に広がっていない - GitHub Actions の `Book QA` が PASS する - diff --git a/package-lock.json b/package-lock.json index 6bd671a..927e400 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "markdownlint-cli": "^0.48.0" }, "engines": { - "node": "20 || >=22" + "node": "^20.9.0 || >=22" }, "optionalDependencies": { "puppeteer": "^24.43.1", diff --git a/package.json b/package.json index 6394b09..1ad50e9 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "url": "https://github.com/itdojp/github-workflow-book.git" }, "engines": { - "node": "20 || >=22" + "node": "^20.9.0 || >=22" }, "scripts": { "setup": "node easy-setup.js", diff --git a/scripts/check-install-script-policy.js b/scripts/check-install-script-policy.js index fbc58bc..9f2daa0 100644 --- a/scripts/check-install-script-policy.js +++ b/scripts/check-install-script-policy.js @@ -7,6 +7,7 @@ const EXPECTED = Object.freeze({ sharpRange: '^0.35.3', sharpVersion: '0.35.3', puppeteerRange: '^24.43.1', + nodeRange: '^20.9.0 || >=22', defaultPdfEngine: 'pandoc', }); @@ -23,6 +24,10 @@ function validate(state) { const { pkg, lock, config, buildSource, imageOptimizerSource, pdfSource, readme } = state; const sharp = lock.packages?.['node_modules/sharp']; const puppeteer = lock.packages?.['node_modules/puppeteer']; + const installScriptPackages = Object.entries(lock.packages ?? {}) + .filter(([, metadata]) => metadata.hasInstallScript === true) + .map(([path, metadata]) => `${path}@${metadata.version}`) + .sort(); if (pkg.optionalDependencies?.sharp !== EXPECTED.sharpRange) { errors.push(`optionalDependencies.sharp must be ${EXPECTED.sharpRange}`); @@ -30,6 +35,9 @@ function validate(state) { if (pkg.optionalDependencies?.puppeteer !== EXPECTED.puppeteerRange) { errors.push(`optionalDependencies.puppeteer must be ${EXPECTED.puppeteerRange}`); } + if (pkg.engines?.node !== EXPECTED.nodeRange) { + errors.push(`engines.node must be ${EXPECTED.nodeRange}`); + } if (pkg.allowScripts?.puppeteer !== false) { errors.push('allowScripts must explicitly deny the Puppeteer download script'); } @@ -46,11 +54,15 @@ function validate(state) { if (puppeteer?.version !== '24.43.1' || puppeteer?.hasInstallScript !== true) { errors.push('lockfile must resolve install-script-bearing puppeteer@24.43.1'); } + const expectedInstallScripts = ['node_modules/puppeteer@24.43.1']; + if (JSON.stringify(installScriptPackages) !== JSON.stringify(expectedInstallScripts)) { + errors.push(`unexpected install-script package set: ${JSON.stringify(installScriptPackages)}`); + } if (!buildSource.includes("require('./image-optimizer')")) { errors.push('build:full must retain the active image optimizer'); } if (!imageOptimizerSource.includes("require('sharp')")) { - errors.push('the approved Sharp install script must have an active consumer'); + errors.push('the script-free Sharp dependency must retain an active consumer'); } if (!pdfSource.includes("require('puppeteer')")) { errors.push('the denied Puppeteer package must remain an explicit optional consumer'); @@ -106,7 +118,9 @@ if (process.argv.includes('--self-test')) { ['unnecessary Sharp approval', (s) => { s.pkg.allowScripts.sharp = true; }, 'unexpected install-script approvals'], ['Puppeteer approval', (s) => { s.pkg.allowScripts.puppeteer = true; }, 'explicitly deny'], ['blanket extra approval', (s) => { s.pkg.allowScripts['unknown@1.0.0'] = true; }, 'unexpected install-script approvals'], + ['Node floor drift', (s) => { s.pkg.engines.node = '20 || >=22'; }, 'engines.node'], ['Sharp install script returns', (s) => { s.lock.packages['node_modules/sharp'].hasInstallScript = true; }, 'script-free'], + ['new unreviewed install script', (s) => { s.lock.packages['node_modules/unreviewed'] = { version: '1.0.0', hasInstallScript: true }; }, 'install-script package set'], ['Sharp version drift', (s) => { s.lock.packages['node_modules/sharp'].version = '0.35.2'; }, `sharp@${EXPECTED.sharpVersion}`], ['missing Sharp consumer', (s) => { s.imageOptimizerSource = s.imageOptimizerSource.replace("require('sharp')", "require('not-sharp')"); }, 'active consumer'], ['Puppeteer becomes default', (s) => { s.config.pdf.engine = 'puppeteer'; }, 'default PDF engine'], From cca221416d0eaf61e5ce3009f1338f57795b0844 Mon Sep 17 00:00:00 2001 From: ootakazuhiko Date: Thu, 23 Jul 2026 09:03:32 +0900 Subject: [PATCH 3/4] ci: pin npm for install-script policy --- .github/workflows/book-qa.yml | 9 ++++++++- AGENTS.md | 2 +- package-lock.json | 2 +- package.json | 5 +++-- scripts/check-install-script-policy.js | 9 +++++++-- 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/.github/workflows/book-qa.yml b/.github/workflows/book-qa.yml index fa80db0..4df94a8 100644 --- a/.github/workflows/book-qa.yml +++ b/.github/workflows/book-qa.yml @@ -29,12 +29,19 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v6 with: - node-version: '20' + node-version: '22.22.2' cache: npm cache-dependency-path: | package-lock.json book-formatter/package-lock.json + - name: Pin npm for install-script policy + shell: bash + run: | + set -euo pipefail + npm install --global npm@12.0.1 + test "$(npm --version)" = "12.0.1" + - name: Root dependency security audit run: npm run check:security diff --git a/AGENTS.md b/AGENTS.md index e806dc8..486569d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -29,7 +29,7 @@ ## 5. ローカル検証(推奨) -前提: Node.js `^20.9.0 || >=22`(`node --version` で確認)、Python 3 系。 +前提: Node.js `^22.22.2 || ^24.15.0 || >=26.0.0` と npm `12.0.1`(`node --version` / `npm --version` で確認)、Python 3 系。npm版は `package.json#packageManager` を正本とします。 - `python3 scripts/validate_links.py docs`(内部/外部リンクの存在チェック) - `npm ci`(初回のみ) diff --git a/package-lock.json b/package-lock.json index 927e400..487778e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "markdownlint-cli": "^0.48.0" }, "engines": { - "node": "^20.9.0 || >=22" + "node": "^22.22.2 || ^24.15.0 || >=26.0.0" }, "optionalDependencies": { "puppeteer": "^24.43.1", diff --git a/package.json b/package.json index 1ad50e9..11086d1 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "url": "https://github.com/itdojp/github-workflow-book.git" }, "engines": { - "node": "^20.9.0 || >=22" + "node": "^22.22.2 || ^24.15.0 || >=26.0.0" }, "scripts": { "setup": "node easy-setup.js", @@ -67,5 +67,6 @@ "homepage": "https://itdojp.github.io/github-workflow-book/", "allowScripts": { "puppeteer": false - } + }, + "packageManager": "npm@12.0.1" } diff --git a/scripts/check-install-script-policy.js b/scripts/check-install-script-policy.js index 9f2daa0..adaffae 100644 --- a/scripts/check-install-script-policy.js +++ b/scripts/check-install-script-policy.js @@ -7,7 +7,8 @@ const EXPECTED = Object.freeze({ sharpRange: '^0.35.3', sharpVersion: '0.35.3', puppeteerRange: '^24.43.1', - nodeRange: '^20.9.0 || >=22', + nodeRange: '^22.22.2 || ^24.15.0 || >=26.0.0', + packageManager: 'npm@12.0.1', defaultPdfEngine: 'pandoc', }); @@ -38,6 +39,9 @@ function validate(state) { if (pkg.engines?.node !== EXPECTED.nodeRange) { errors.push(`engines.node must be ${EXPECTED.nodeRange}`); } + if (pkg.packageManager !== EXPECTED.packageManager) { + errors.push(`packageManager must be ${EXPECTED.packageManager}`); + } if (pkg.allowScripts?.puppeteer !== false) { errors.push('allowScripts must explicitly deny the Puppeteer download script'); } @@ -118,7 +122,8 @@ if (process.argv.includes('--self-test')) { ['unnecessary Sharp approval', (s) => { s.pkg.allowScripts.sharp = true; }, 'unexpected install-script approvals'], ['Puppeteer approval', (s) => { s.pkg.allowScripts.puppeteer = true; }, 'explicitly deny'], ['blanket extra approval', (s) => { s.pkg.allowScripts['unknown@1.0.0'] = true; }, 'unexpected install-script approvals'], - ['Node floor drift', (s) => { s.pkg.engines.node = '20 || >=22'; }, 'engines.node'], + ['Node floor drift', (s) => { s.pkg.engines.node = '>=22'; }, 'engines.node'], + ['npm version drift', (s) => { s.pkg.packageManager = 'npm@11.0.0'; }, 'packageManager'], ['Sharp install script returns', (s) => { s.lock.packages['node_modules/sharp'].hasInstallScript = true; }, 'script-free'], ['new unreviewed install script', (s) => { s.lock.packages['node_modules/unreviewed'] = { version: '1.0.0', hasInstallScript: true }; }, 'install-script package set'], ['Sharp version drift', (s) => { s.lock.packages['node_modules/sharp'].version = '0.35.2'; }, `sharp@${EXPECTED.sharpVersion}`], From 9400c31ce2283a740cfeb3eadb215d41f559a491 Mon Sep 17 00:00:00 2001 From: ootakazuhiko Date: Thu, 23 Jul 2026 09:06:36 +0900 Subject: [PATCH 4/4] ci: invoke the pinned npm without self-update --- .github/workflows/book-qa.yml | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/.github/workflows/book-qa.yml b/.github/workflows/book-qa.yml index 4df94a8..7d7c9c3 100644 --- a/.github/workflows/book-qa.yml +++ b/.github/workflows/book-qa.yml @@ -35,15 +35,12 @@ jobs: package-lock.json book-formatter/package-lock.json - - name: Pin npm for install-script policy + - name: Verify pinned npm CLI shell: bash - run: | - set -euo pipefail - npm install --global npm@12.0.1 - test "$(npm --version)" = "12.0.1" + run: test "$(npx --yes npm@12.0.1 --version)" = "12.0.1" - name: Root dependency security audit - run: npm run check:security + run: npx --yes npm@12.0.1 audit - name: Book metadata consistency check run: node scripts/check-metadata-consistency.js @@ -51,13 +48,24 @@ jobs: - name: Check reader-facing checklist and figure contracts run: node scripts/check-issue-240-ux.js - - name: Install book dependencies for contract checks - run: npm ci --ignore-scripts + - name: Install book dependencies with policy npm + run: npx --yes npm@12.0.1 ci - name: Check install-script allowlist contract + shell: bash run: | + set -euo pipefail node scripts/check-install-script-policy.js node scripts/check-install-script-policy.js --self-test + report="$RUNNER_TEMP/install-scripts.json" + npx --yes npm@12.0.1 install-scripts ls --json > "$report" + node - "$report" <<'NODE' + const report = require(process.argv[2]); + if (!Array.isArray(report.allowScripts) || report.allowScripts.length !== 0) { + throw new Error('pending install scripts remain: ' + JSON.stringify(report)); + } + console.log('npm install-scripts pending count: 0'); + NODE - name: Check Chapter 8 CodeQL workflow contract run: |