GenerateNpmInstallStepsWithScope generates npm installation steps with control over global vs local installation. By default, --ignore-scripts is added to the install command to prevent pre/post install scripts from executing (supply chain security). Pass runInstallScripts=true to allow scripts.
(packageName, version, stepName, cacheKeyPrefix string, includeNodeSetup bool, isGlobal bool, runInstallScripts bool, cooldownEnabled bool)
| 217 | // By default, --ignore-scripts is added to the install command to prevent pre/post install |
| 218 | // scripts from executing (supply chain security). Pass runInstallScripts=true to allow scripts. |
| 219 | func GenerateNpmInstallStepsWithScope(packageName, version, stepName, cacheKeyPrefix string, includeNodeSetup bool, isGlobal bool, runInstallScripts bool, cooldownEnabled bool) []GitHubActionStep { |
| 220 | nodejsLog.Printf("Generating npm install steps: package=%s, version=%s, includeNodeSetup=%v, isGlobal=%v, runInstallScripts=%v", packageName, version, includeNodeSetup, isGlobal, runInstallScripts) |
| 221 | |
| 222 | var steps []GitHubActionStep |
| 223 | |
| 224 | // Add Node.js setup if requested |
| 225 | if includeNodeSetup { |
| 226 | nodejsLog.Print("Including Node.js setup step") |
| 227 | steps = append(steps, GenerateNodeJsSetupStep()) |
| 228 | } |
| 229 | |
| 230 | // Add npm install step |
| 231 | globalFlag := "" |
| 232 | if isGlobal { |
| 233 | globalFlag = "-g " |
| 234 | } |
| 235 | |
| 236 | // Add --ignore-scripts by default to prevent pre/post install scripts (supply chain security). |
| 237 | // runInstallScripts=true disables this protection (emits a warning at compile time). |
| 238 | ignoreScriptsFlag := "--ignore-scripts " |
| 239 | if runInstallScripts { |
| 240 | ignoreScriptsFlag = "" |
| 241 | } |
| 242 | |
| 243 | var installStep GitHubActionStep |
| 244 | if ExpressionPattern.MatchString(version) { |
| 245 | // Version is a GitHub Actions expression (e.g. ${{ inputs.engine-version }}). |
| 246 | // Pass it via an env var instead of direct shell interpolation to prevent injection: |
| 247 | // if the expression evaluates to a malicious string, it would otherwise be |
| 248 | // substituted verbatim into the shell command before the shell parses it. |
| 249 | nodejsLog.Printf("Version contains GitHub Actions expression, using env var for injection safety: %s", version) |
| 250 | installCmd := fmt.Sprintf(`npm install %s%s%s@"${ENGINE_VERSION}"`, ignoreScriptsFlag, globalFlag, packageName) |
| 251 | installStep = GitHubActionStep{ |
| 252 | " - name: " + stepName, |
| 253 | " run: " + installCmd, |
| 254 | " env:", |
| 255 | " ENGINE_VERSION: " + version, |
| 256 | } |
| 257 | if cooldownEnabled { |
| 258 | installStep = append(installStep, fmt.Sprintf(" NPM_CONFIG_MIN_RELEASE_AGE: '%d'", npmDefaultCooldownDays)) |
| 259 | } |
| 260 | } else { |
| 261 | installCmd := fmt.Sprintf("npm install %s%s%s@%s", ignoreScriptsFlag, globalFlag, packageName, version) |
| 262 | installStep = GitHubActionStep{ |
| 263 | " - name: " + stepName, |
| 264 | " run: " + installCmd, |
| 265 | } |
| 266 | if cooldownEnabled { |
| 267 | installStep = append(installStep, |
| 268 | " env:", |
| 269 | fmt.Sprintf(" NPM_CONFIG_MIN_RELEASE_AGE: '%d'", npmDefaultCooldownDays), |
| 270 | ) |
| 271 | } |
| 272 | } |
| 273 | steps = append(steps, installStep) |
| 274 | |
| 275 | return steps |
| 276 | } |