Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,5 @@ pip-wheel-metadata

# for running AWS Lambda tests using AWS SAM
sam.template.yaml

.worktrees/
19 changes: 19 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ help:
@echo
@echo "make apidocs: Build the API documentation"
@echo "make aws-lambda-layer: Build AWS Lambda layer directory for serverless integration"
@echo "make worktree-create NAME=<name>: Create a worktree with a new feature branch and virtual environment"
@echo "make worktree-delete NAME=<name>: Remove a worktree (prompts to delete branch)"
@echo "make worktree-list: List all active worktrees"
@echo
@echo "Also make sure to read ./CONTRIBUTING.md"
@echo
Expand All @@ -33,3 +36,19 @@ aws-lambda-layer: dist
$(VENV_PATH)/bin/pip install -r requirements-aws-lambda-layer.txt
$(VENV_PATH)/bin/python -m scripts.build_aws_lambda_layer
.PHONY: aws-lambda-layer

worktree-create:
@test -n "$(NAME)" || (echo "Error: NAME is required. Usage: make worktree-create NAME=<name>" && false)
@echo "$(NAME)" | grep -qE '^[a-zA-Z0-9_/-]+$$' || (echo "Error: NAME contains invalid characters" && false)
./scripts/worktree-create.sh "$(NAME)"
.PHONY: worktree-create

worktree-delete:
@test -n "$(NAME)" || (echo "Error: NAME is required. Usage: make worktree-delete NAME=<name>" && false)
@echo "$(NAME)" | grep -qE '^[a-zA-Z0-9_/-]+$$' || (echo "Error: NAME contains invalid characters" && false)
./scripts/worktree-delete.sh "$(NAME)"
.PHONY: worktree-delete

worktree-list:
@git worktree list
.PHONY: worktree-list
47 changes: 47 additions & 0 deletions scripts/worktree-create.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/usr/bin/env bash
set -euo pipefail

if [[ $# -lt 1 ]]; then
echo "Usage: $0 <name>" >&2
exit 1
fi

POSITIONAL_ARGS=("$@")

# For simplicity, we use the same value for both the worktree name and branch name.
WORKTREE_NAME="${POSITIONAL_ARGS[0]}"
BRANCH_NAME="${POSITIONAL_ARGS[0]}"

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
WORKTREE_DIR="$REPO_ROOT/.worktrees/$WORKTREE_NAME"

Check warning on line 17 in scripts/worktree-create.sh

View workflow job for this annotation

GitHub Actions / warden: find-bugs

Path traversal allows worktree creation outside intended directory

The Makefile regex validation `^[a-zA-Z0-9_/-]+The Makefile regex validation permits forward slashes in the NAME parameter. This allows path traversal sequences like `../../foo` to create worktrees outside the intended `.worktrees` directory. An attacker with access to the Makefile target could create worktrees in arbitrary locations within the filesystem (relative to repo root), potentially overwriting or polluting other directories.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Path traversal allows worktree creation outside intended directory

The Makefile regex validation ^[a-zA-Z0-9_/-]+The Makefile regex validation permits forward slashes in the NAME parameter. This allows path traversal sequences like ../../footo create worktrees outside the intended.worktrees` directory. An attacker with access to the Makefile target could create worktrees in arbitrary locations within the filesystem (relative to repo root), potentially overwriting or polluting other directories.

Suggested fix: Add validation in the script to reject names containing path traversal patterns, or modify the Makefile regex to disallow forward slashes.

Suggested change
WORKTREE_DIR="$REPO_ROOT/.worktrees/$WORKTREE_NAME"
if [[ "$WORKTREE_NAME" == *..* ]]; then
echo "Error: worktree name cannot contain '..'" >&2
exit 1
fi
Also found at 1 additional location
  • scripts/worktree-delete.sh:13-13

Identified by Warden [find-bugs] · QA3-AZZ


if [[ -d "$WORKTREE_DIR" ]]; then
echo "Error: worktree directory already exists: $WORKTREE_DIR" >&2
exit 1
fi

if git -C "$REPO_ROOT" branch --list "$BRANCH_NAME" | grep -q .; then
echo "Error: branch '$BRANCH_NAME' already exists. Delete it first or choose a different name." >&2
exit 1
fi

echo "Creating worktree '$WORKTREE_NAME' on branch '$BRANCH_NAME'..."
git -C "$REPO_ROOT" worktree add "$WORKTREE_DIR" -b "$BRANCH_NAME"

if command -v uv &>/dev/null; then
echo "Setting up virtual environment with uv..."
uv venv "$WORKTREE_DIR/.venv"
else
echo "uv not found — falling back to python -m venv..."
python -m venv "$WORKTREE_DIR/.venv"
fi

echo ""
echo "Worktree ready!"
echo " Path: $WORKTREE_DIR"
echo " Branch: $BRANCH_NAME"
echo ""
echo "To start working:"
echo " cd $WORKTREE_DIR"
echo " source .venv/bin/activate"
41 changes: 41 additions & 0 deletions scripts/worktree-delete.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/usr/bin/env bash
set -euo pipefail

if [[ $# -lt 1 ]]; then
echo "Usage: $0 <name>" >&2
exit 1
fi

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"

for WORKTREE_NAME in "$@"; do
WORKTREE_DIR="$REPO_ROOT/.worktrees/$WORKTREE_NAME"

Check warning on line 13 in scripts/worktree-delete.sh

View workflow job for this annotation

GitHub Actions / warden: find-bugs

[QA3-AZZ] Path traversal allows worktree creation outside intended directory (additional location)

The Makefile regex validation `^[a-zA-Z0-9_/-]+The Makefile regex validation permits forward slashes in the NAME parameter. This allows path traversal sequences like `../../foo` to create worktrees outside the intended `.worktrees` directory. An attacker with access to the Makefile target could create worktrees in arbitrary locations within the filesystem (relative to repo root), potentially overwriting or polluting other directories.

if [[ ! -d "$WORKTREE_DIR" ]]; then
echo "Warning: worktree directory not found: $WORKTREE_DIR — skipping" >&2
continue
fi

# Capture branch name before removal
BRANCH_NAME="$(git -C "$WORKTREE_DIR" branch --show-current 2>/dev/null || true)"

echo "Removing worktree '$WORKTREE_NAME'..."
git -C "$REPO_ROOT" worktree remove "$WORKTREE_DIR"
echo " Removed: $WORKTREE_DIR"

if [[ -n "$BRANCH_NAME" ]]; then
if [[ -t 0 ]]; then
read -r -p " Delete branch '$BRANCH_NAME'? [y/N] " REPLY
echo
else
REPLY="n"
fi
if [[ "$REPLY" == "y" || "$REPLY" == "Y" ]]; then
git -C "$REPO_ROOT" branch -d "$BRANCH_NAME"
echo " Deleted branch: $BRANCH_NAME"
fi
fi

echo " Done."
done
Loading