From bdb2a1fc7f0f8a40a404ad48b3e6a588ac9f69aa Mon Sep 17 00:00:00 2001 From: Adrian Duke Date: Wed, 8 Apr 2026 17:31:32 +0100 Subject: [PATCH] just: Introduce pre-commit and pre-push installer Copies the template script into the git hooks DIR to prevent a potential attacker overwriting its contents. Also introduces a check before running the hook against sensitive files from `master`, if there are changes between the branch and `master`, asks user to confirm continuation of hook execution. --- justfile | 14 ++++++++++ scripts/git-hook-template.sh | 53 ++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 scripts/git-hook-template.sh diff --git a/justfile b/justfile index 8e9110ce..8d2fbb1e 100644 --- a/justfile +++ b/justfile @@ -1,6 +1,20 @@ default: @just --list +# SECURITY: We COPY the hook template instead of symlinking it. This ensures that +# checking out an untrusted patch won't overwrite your local git hooks. The copied +# script also checks if sensitive files (like build.rs or justfile) were modified +# in the patch and prompts for confirmation, preventing arbitrary code execution. +# +# Install git hooks +[group('hooks')] +install-hooks: + @cp scripts/git-hook-template.sh .git/hooks/pre-commit + @chmod +x .git/hooks/pre-commit + @cp scripts/git-hook-template.sh .git/hooks/pre-push + @chmod +x .git/hooks/pre-push + @echo "✅ pre-commit and pre-push hooks installed." + # Run pre-commit checks [group('hooks')] pre-commit: format-rust check-rust check-docs check-typos check-spelling check-scripts check-keywords format-nix diff --git a/scripts/git-hook-template.sh b/scripts/git-hook-template.sh new file mode 100644 index 00000000..37586d32 --- /dev/null +++ b/scripts/git-hook-template.sh @@ -0,0 +1,53 @@ +#! /usr/bin/env bash +set -euo pipefail + +readonly HOOK_NAME="$(basename "$0")" + +if ! [[ "$HOOK_NAME" =~ ^(pre-(commit|push)|post-checkout)$ ]] +then + echo "Unknown hook '${HOOK_NAME}'." + exit 1 +fi + +readonly SENSITIVE_FILES=("justfile" "build.rs" "rust-toolchain.toml") +readonly BASE_BRANCH="master" + +# Check which files were modified compared to the base branch. +mapfile -t CHANGED_FILES < <(comm -12 \ + <(git diff --name-only "${BASE_BRANCH}" | sort) \ + <(IFS=$'\n'; echo "${SENSITIVE_FILES[*]}" | sort) \ +) + +if [ ${#CHANGED_FILES[@]} -gt 0 ]; then + echo "⚠️ WARNING: Sensitive files have been modified relative to $BASE_BRANCH." + echo "Executing this hook may run arbitrary code from the modified files." + echo "" + + git --no-pager diff "$BASE_BRANCH" -- "${SENSITIVE_FILES[@]}" + + # Read from /dev/tty because stdin is not attached to the terminal in Git hooks. + exec < /dev/tty + + read -r -p "Do you want to continue executing the ${HOOK_NAME} hooks? [y/N] " response + case "$response" in + [yY][eE][sS]|[yY]) + echo "Continuing with '${HOOK_NAME}' hook..." + ;; + *) + echo "Skipping '${HOOK_NAME}' hook." + exit 0 + ;; + esac +fi + +# Execute the appropriate just recipe based on the hook name +if [ "$HOOK_NAME" = "pre-commit" ]; then + echo "Running pre-commit checks..." + just pre-commit +elif [ "$HOOK_NAME" = "pre-push" ]; then + echo "Running pre-push checks..." + just pre-push +else + echo "Unknown hook: $HOOK_NAME" + exit 1 +fi