From 8c12a6401eae3b46ab3a619f54565d2616d9fa22 Mon Sep 17 00:00:00 2001 From: Dan Webb Date: Mon, 8 Jun 2026 15:55:14 +0100 Subject: [PATCH] fix: harden npm shell commands Build npm commands with Shellwords and normalize command expectations.\n\nVerification:\n- cookstyle libraries/nodejs_helper.rb resources/npm_package.rb spec/unit/resources/npm_package_spec.rb\n- /opt/chef-workstation/embedded/bin/rspec spec/unit/resources/npm_package_spec.rb spec/unit/library/helper_spec.rb --format progress --- libraries/nodejs_helper.rb | 6 ++++-- resources/npm_package.rb | 19 ++++++++++--------- spec/unit/resources/npm_package_spec.rb | 6 +++--- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/libraries/nodejs_helper.rb b/libraries/nodejs_helper.rb index 4fecad5..3b6c38c 100644 --- a/libraries/nodejs_helper.rb +++ b/libraries/nodejs_helper.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'shellwords' + module NodeJs module Helper def default_install_method @@ -94,9 +96,9 @@ def npm_dist(version, url = nil) def npm_list(package, path = nil, environment = {}) require 'json' cmd = if path - Mixlib::ShellOut.new("npm list #{package} -json", cwd: path, environment: environment) + Mixlib::ShellOut.new(Shellwords.join(['npm', 'list', package, '-json']), cwd: path, environment: environment) else - Mixlib::ShellOut.new("npm list #{package} -global -json", environment: environment) + Mixlib::ShellOut.new(Shellwords.join(['npm', 'list', package, '-global', '-json']), environment: environment) end begin diff --git a/resources/npm_package.rb b/resources/npm_package.rb index f2f2434..e9cf314 100644 --- a/resources/npm_package.rb +++ b/resources/npm_package.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'shellwords' + provides :npm_package unified_mode true @@ -23,7 +25,7 @@ action :install do execute "install NPM package #{new_resource.package}" do cwd new_resource.path - command "npm install #{npm_options}" + command Shellwords.join(['npm', 'install', *npm_options]) user new_resource.user group new_resource.group environment npm_env_vars @@ -36,7 +38,7 @@ action :uninstall do execute "uninstall NPM package #{new_resource.package}" do cwd new_resource.path - command "npm uninstall #{npm_options}" + command Shellwords.join(['npm', 'uninstall', *npm_options]) user new_resource.user group new_resource.group environment npm_env_vars @@ -49,7 +51,7 @@ action :remove do execute "uninstall NPM package #{new_resource.package}" do cwd new_resource.path - command "npm uninstall #{npm_options}" + command Shellwords.join(['npm', 'uninstall', *npm_options]) user new_resource.user group new_resource.group environment npm_env_vars @@ -87,12 +89,11 @@ def no_auto_update? end def npm_options - options = +'' - options << ' -global' unless new_resource.path - new_resource.options.each do |option| - options << " #{option}" - end - options << " #{npm_package}" + options = [] + options << '-global' unless new_resource.path + options.concat(new_resource.options) + options << npm_package if npm_package + options end def npm_package diff --git a/spec/unit/resources/npm_package_spec.rb b/spec/unit/resources/npm_package_spec.rb index cc6ad5c..c2b63b2 100644 --- a/spec/unit/resources/npm_package_spec.rb +++ b/spec/unit/resources/npm_package_spec.rb @@ -11,7 +11,7 @@ npm_package 'express' end - it { is_expected.to run_execute('install NPM package express').with(command: 'npm install -global express') } + it { is_expected.to run_execute('install NPM package express').with(command: 'npm install -global express') } end context 'install a versioned package' do @@ -22,7 +22,7 @@ end end - it { is_expected.to run_execute('install NPM package async').with(command: 'npm install -global --production async@0.6.2') } + it { is_expected.to run_execute('install NPM package async').with(command: 'npm install -global --production async@0.6.2') } end context 'install with the legacy alias' do @@ -30,6 +30,6 @@ nodejs_npm 'mocha' end - it { is_expected.to run_execute('install NPM package mocha').with(command: 'npm install -global mocha') } + it { is_expected.to run_execute('install NPM package mocha').with(command: 'npm install -global mocha') } end end