fix(installer): block piped privileged bootstrap

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-07-27 16:10:44 +00:00
1 parent 02f9cac163
commit c9f7fdff50
5 files changed
+47 -5

No files matched your search

+8 -1
View File
@@ -314,7 +314,9 @@ bash install.sh
The installer ensures Node.js 20 or newer is available, installs a managed
Paperclip CLI under `~/.paperclip/cli`, and starts interactive onboarding. It
can also install Paperclip as a background service on supported Linux and
macOS systems.
macOS systems. The checksum detects transfer or publishing mistakes, but it is
served from the same origin as the script; use a release-tag or commit-pinned
GitHub copy when you need an independently hosted source.
For a non-interactive managed install:
@@ -323,6 +325,11 @@ curl -fsSL https://paperclip.ing/install.sh | bash -s -- --no-prompt --no-onboar
paperclipai onboard --yes
```
The piped form requires supported Node.js, npm, and npx to already be present.
If Node.js bootstrap is required, download and review `install.sh` before
running it so no privileged dependency-install command is accepted through a
pipe.
To try Paperclip without installing anything permanently:
```bash
+6
View File
@@ -27,6 +27,12 @@ fi
bash install.sh
```
The checksum detects transfer or publishing mistakes but is served from the
same origin as the installer. Use a release-tag or commit-pinned GitHub copy
when you need an independently hosted source. Piped installs require supported
Node.js, npm, and npx to already be installed; download the script first before
allowing it to bootstrap Node.js with privileged package-manager commands.
First-time local bootstrap from a source checkout:
```sh
+13 -4
View File
@@ -30,7 +30,15 @@ The bootstrap script:
The script prints and confirms any command that requires elevated privileges.
Third-party Node.js bootstrap scripts are pinned and SHA-256 verified before
execution; the installer stops if a published script changes unexpectedly.
Use `--no-prompt` for automation and `--no-onboard` to stop after installing:
The `paperclip.ing` checksum detects transfer or publishing mistakes, but it is
served from the same origin as the script and is not an independent
authenticity proof. For an independently hosted source, download a release-tag
or commit-pinned copy from GitHub, review it, and run that local file.
Use `--no-prompt` for automation and `--no-onboard` to stop after installing.
The piped form only proceeds when supported Node.js, npm, and npx are already
installed; if Node.js bootstrap is required, download the script first so the
privileged commands are inspectable before execution:
```sh
curl -fsSL https://paperclip.ing/install.sh | bash -s -- --no-prompt --no-onboard
@@ -45,9 +53,10 @@ raw_base=https://raw.githubusercontent.com/paperclipai/paperclip
curl -fsSL "$raw_base/master/scripts/install.sh" | bash
```
For audits or incident response, pin the raw URL to a release tag or commit
SHA instead of `master`, download it first, and compare it with the published
checksum at `https://paperclip.ing/install.sh.sha256`.
For audits or incident response, pin the raw URL to a release tag or commit SHA
instead of `master` and download it first. That immutable GitHub URL provides a
separate delivery path from `paperclip.ing`; do not treat a checksum served by
the same origin as the artifact as an independent trust anchor.
Each installer flag also has a `PAPERCLIP_INSTALL_*` environment-variable
equivalent. This helps where passing arguments through a pipe is awkward.
+8
View File
@@ -20,6 +20,11 @@ INSTALL_SERVICE=0
DRY_RUN=0
VERBOSE=0
TEMP_DIR=""
PIPED_INSTALL=0
if [ -z "${BASH_SOURCE[0]:-}" ] || [ ! -f "${BASH_SOURCE[0]}" ]; then
PIPED_INSTALL=1
fi
usage() {
cat <<'EOF'
@@ -335,6 +340,9 @@ else
else
log "Node.js was not found"
fi
if [ "$PIPED_INSTALL" = "1" ]; then
fail "Node.js bootstrap is disabled for piped installs; download install.sh, review it, and run 'bash install.sh --no-prompt'"
fi
check_version_manager
log "Installing Node.js $DEFAULT_NODE_MAJOR"
if [ "$OS_NAME" = "macos" ]; then
+12
View File
@@ -111,6 +111,18 @@ echo "==> piped --no-prompt"
run_with_node piped bash -c 'cat /paperclip-scripts/install.sh | bash -s -- --no-prompt --no-onboard'
assert_line "$RESULTS_DIR/piped.args" "--yes"
echo "==> piped mode refuses privileged Node bootstrap"
if docker run --rm \
-v "$REPO_ROOT/scripts:/paperclip-scripts:ro" \
-e PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" \
ubuntu:24.04 \
bash -c 'cat /paperclip-scripts/install.sh | bash -s -- --no-prompt --no-onboard' \
>"$RESULTS_DIR/piped-no-node.out" 2>&1; then
echo "Expected piped install without Node.js to fail before privileged bootstrap" >&2
exit 1
fi
assert_line "$RESULTS_DIR/piped-no-node.out" "[paperclip] error: Node.js bootstrap is disabled for piped installs; download install.sh, review it, and run 'bash install.sh --no-prompt'"
echo "==> dry run"
run_with_node dry-run bash /paperclip-scripts/install.sh --no-prompt --dry-run --no-onboard
[ ! -e "$RESULTS_DIR/dry-run.args" ] || {