Add human-in-the-loop confirmation for risky package install scripts#348
Add human-in-the-loop confirmation for risky package install scripts#348lmajano wants to merge 2 commits into
Conversation
Package box.json scripts tied to install-lifecycle interception points (preInstall, onInstall, postInstall, etc.) are fired automatically and run arbitrary commands via shell.callCommand. Installing a third-party package could therefore silently execute attacker-controlled commands. This adds a yes/no confirmation before CommandBox auto-runs one of these install-lifecycle scripts, printing the exact commands first so the user can decline untrusted code. - PackageService.runScript gains an `automatic` flag; only interceptor- driven runs (PackageScripts.cfc) set it, so explicit `run-script` invocations are never prompted. - Gating applies only to install/uninstall lifecycle events, not benign high-frequency points (preCommand, prePrompt, etc.). - On by default. Non-interactive shells (CI/no TTY) deny by default unless trusted via config `scripts.trustInstallScriptsNonInteractive`, env var COMMANDBOX_TRUST_INSTALL_SCRIPTS, or `install --trustScripts`. - Master switch `scripts.confirmInstallScripts` (default true) can disable. Reuses shell.confirm() and shell.isTerminalInteractive(). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015g9LPH1Ct6zP17DoDEsBUp
|
We don't make up random CommandBox env vars generally. We already have an existing system for overriding config settings. So I also don't understand the need for both The Also, all of this needs sent to the |
bdw429s
left a comment
There was a problem hiding this comment.
stupid GitHub won't let me submit this dumb form without a comment
- Collapse the two settings into a single `scripts.trustInstallScripts` (default false). When true, install scripts run without prompting. - Stop reading a bespoke COMMANDBOX_TRUST_INSTALL_SCRIPTS env var; the existing box_config_scripts_trustInstallScripts mechanism overrides the config setting automatically. Remove the isTruthy helper so a bad boolean value errors out instead of being silently treated as false. - `install --trustScripts` now defaults to the config setting and is threaded through installPackage's recursive dependency installs; confirmScriptExecution reads trust from interceptData.installArgs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015g9LPH1Ct6zP17DoDEsBUp
|
@bdw429s can you review again and if all ok I can send another PR to the boxlang branch. |
Description
A
box.jsonscriptsblock can be keyed to install-lifecycle interception points (preInstall,onInstall,postInstall, …). ThePackageScriptsinterceptor fires these automatically andPackageService.runScript()runs each entry throughshell.callCommand()— arbitrary CommandBox/native commands. This is the classic supply-chain risk: installing a third‑party package from ForgeBox/git can silently execute attacker-controlled commands with no user awareness.This PR adds a yes/no human-in-the-loop confirmation before CommandBox auto-runs an install-lifecycle script, printing the exact commands first so the user can decline untrusted code.
How it works
PackageService.runScript()gains anautomaticflag. Only interceptor-driven runs (PackageScripts.cfc) set it, so explicitrun-scriptinvocations are never prompted (the user asked for those).preInstall,preInstallAll,onInstall,postInstall,postInstallAll,preUninstall,postUninstall). Benign high-frequency points (preCommand,prePrompt,onCLIStart, …) are untouched, so there is no prompt spam.scripts.trustInstallScripts(defaultfalse) governs trust:false→ interactive shell prompts yes/no; non-interactive shell (CI / no TTY) denies since it can't prompt.true→ scripts run without prompting.config set scripts.trustInstallScripts=truebox_config_scripts_trustInstallScripts=trueenv var (uses the existing config-override mechanism inConfigService.loadOverrides()— no bespoke env-var handling in code)install --trustScripts(per-invocation; the command arg defaults to the config setting and propagates through recursive dependency installs viainterceptData.installArgs.trustScripts)A non-boolean config value is intentionally not silently coerced — it errors out rather than being treated as
false.Reused building blocks:
shell.confirm(),shell.isTerminalInteractive(),configService.getSetting()+ thebox_config_*override inConfigService.loadOverrides().Files changed
services/PackageService.cfc—automaticparam, gate,isRiskyInstallScript()/confirmScriptExecution()helpers,trustScriptsthreaded throughinstallPackage().package-commands/interceptors/PackageScripts.cfc— passautomatic=true.package-commands/commands/package/install.cfc—--trustScriptsflag defaulting to the config setting.services/ConfigService.cfc— registerscripts.trustInstallScripts.No new dependencies.
Jira Issues
No Jira issue created yet — please advise if one should be linked before merge.
Type of change
scripts.trustInstallScriptsconfig setting)Checklist
🤖 Generated with Claude Code