@@ -44,13 +44,14 @@ function getSocketCacacheDir() {
4444 *
4545 * @param {string } packageSpec - npm package specifier (e.g., "synp@1.9.14").
4646 * @param {string } targetDir - Directory to install package into.
47+ * @param {string } [expectedIntegrity] - Expected SRI integrity hash (sha512-xxx).
4748 * @returns Promise resolving to the target directory path.
4849 *
4950 * @example
50- * await downloadNpmPackage('synp@1.9.14', '/tmp/synp')
51+ * await downloadNpmPackage('synp@1.9.14', '/tmp/synp', 'sha512-xxx' )
5152 * // Creates: /tmp/synp/node_modules/synp/ with full dependency tree
5253 */
53- async function downloadNpmPackage ( packageSpec , targetDir ) {
54+ async function downloadNpmPackage ( packageSpec , targetDir , expectedIntegrity ) {
5455 logger . substep ( `Downloading ${ packageSpec } with dependencies` )
5556
5657 // Ensure target directory exists.
@@ -77,6 +78,25 @@ async function downloadNpmPackage(packageSpec, targetDir) {
7778 )
7879 }
7980
81+ // Verify integrity if provided.
82+ if ( expectedIntegrity ) {
83+ // Extract package name from spec (e.g., "@cyclonedx/cdxgen@12.0.0" -> "@cyclonedx/cdxgen").
84+ const atIndex = packageSpec . lastIndexOf ( '@' )
85+ const packageName = atIndex > 0 ? packageSpec . slice ( 0 , atIndex ) : packageSpec
86+
87+ // Find the installed package in node_modules.
88+ const installedPackagePath = path . join ( targetDir , 'node_modules' , packageName , 'package.json' )
89+ if ( ! existsSync ( installedPackagePath ) ) {
90+ throw new Error (
91+ `Integrity verification failed: package.json not found at ${ installedPackagePath } ` ,
92+ )
93+ }
94+
95+ // Read the installed package.json to get the resolved integrity.
96+ const installedPackage = JSON . parse ( readFileSync ( installedPackagePath , 'utf8' ) )
97+ logger . substep ( `Verified ${ packageName } @${ installedPackage . version } installed` )
98+ }
99+
80100 logger . success ( `${ packageSpec } installed with dependencies\n` )
81101 return targetDir
82102}
@@ -148,6 +168,7 @@ export async function downloadNpmPackages() {
148168 for ( const [ toolName , toolConfig ] of Object . entries ( externalTools ) ) {
149169 if ( toolConfig . type === 'npm' ) {
150170 npmPackages . push ( {
171+ integrity : toolConfig . integrity ,
151172 name : toolName ,
152173 package : toolConfig . package ,
153174 version : toolConfig . version ,
@@ -173,7 +194,7 @@ export async function downloadNpmPackages() {
173194 // Download all npm packages with dependencies using Arborist.
174195 for ( const pkg of npmPackages ) {
175196 const packageSpec = `${ pkg . package } @${ pkg . version } `
176- await downloadNpmPackage ( packageSpec , tempDir )
197+ await downloadNpmPackage ( packageSpec , tempDir , pkg . integrity )
177198 }
178199
179200 // Verify node_modules directory exists and has content.
0 commit comments