From d155db19f0c6804452f9f90299990beae1b534e7 Mon Sep 17 00:00:00 2001 From: Katrina Liu Date: Mon, 24 Nov 2025 14:07:33 -0800 Subject: [PATCH 1/3] initial setup update workflow tests and mcp add session start hook --- .github/scripts/check-plugin-version.sh | 60 ++++++++ .github/workflows/test.yml | 89 +++++++++++ CHANGELOG.md | 3 + CONTRIBUTING.md | 13 ++ tests/test_hooks.sh | 48 ++++++ tests/test_post_tool_use_hook.sh | 190 ++++++++++++++++++++++++ tests/test_session_start_hook.sh | 36 +++++ tests/test_utils.sh | 44 ++++++ 8 files changed, 483 insertions(+) create mode 100755 .github/scripts/check-plugin-version.sh create mode 100644 .github/workflows/test.yml create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md create mode 100755 tests/test_hooks.sh create mode 100755 tests/test_post_tool_use_hook.sh create mode 100755 tests/test_session_start_hook.sh create mode 100755 tests/test_utils.sh diff --git a/.github/scripts/check-plugin-version.sh b/.github/scripts/check-plugin-version.sh new file mode 100755 index 0000000..d31cf2d --- /dev/null +++ b/.github/scripts/check-plugin-version.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +# Script to check if plugin files changed and version was bumped appropriately +# Used in CI to enforce version bumping when plugin code changes + +set -e + +BASE_REF=${1:-"main"} + +echo "Comparing changes against origin/$BASE_REF..." + +# Check if any files in plugin/ directory changed +PLUGIN_CHANGES=$(git diff --name-only origin/$BASE_REF...HEAD | grep '^plugin/' || true) + +if [ -n "$PLUGIN_CHANGES" ]; then + echo "✓ Plugin files have changed:" + echo "$PLUGIN_CHANGES" + echo "" + + # Check if plugin.json exists in base branch + if git cat-file -e origin/$BASE_REF:plugin/.claude-plugin/plugin.json 2>/dev/null; then + echo "Checking for version bump in existing plugin..." + + # Check if plugin.json version changed + VERSION_CHANGED=$(git diff origin/$BASE_REF...HEAD -- plugin/.claude-plugin/plugin.json | grep '"version"' || true) + + if [ -z "$VERSION_CHANGED" ]; then + echo "❌ ERROR: Plugin files were modified but version was not bumped!" + echo "" + echo "Please update the version in: plugin/.claude-plugin/plugin.json" + echo "" + echo "Changed files:" + echo "$PLUGIN_CHANGES" + exit 1 + else + echo "✓ Plugin version has been updated:" + echo "$VERSION_CHANGED" + + # Extract and display old and new versions + OLD_VERSION=$(git show origin/$BASE_REF:plugin/.claude-plugin/plugin.json | grep '"version"' | sed 's/.*"version": "\(.*\)".*/\1/') + NEW_VERSION=$(cat plugin/.claude-plugin/plugin.json | grep '"version"' | sed 's/.*"version": "\(.*\)".*/\1/') + echo "" + echo "Version change: $OLD_VERSION → $NEW_VERSION" + fi + else + echo "✓ New plugin detected - checking that version is set..." + + # For new plugins, just verify a version exists + NEW_VERSION=$(cat plugin/.claude-plugin/plugin.json | grep '"version"' | sed 's/.*"version": "\(.*\)".*/\1/' || true) + + if [ -z "$NEW_VERSION" ]; then + echo "❌ ERROR: plugin.json must have a version field!" + exit 1 + else + echo "✓ Plugin version is set to: $NEW_VERSION" + fi + fi +else + echo "✓ No plugin files changed, version bump not required" +fi diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..634f991 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,89 @@ +name: Test Plugin + +on: + push: + +jobs: + # First job: Check version bump on PR + check-version-bump: + runs-on: ubuntu-latest + steps: + - name: Checkout PR branch + uses: actions/checkout@v3 + with: + fetch-depth: 0 # Fetch all history for comparison + + - name: Check if plugin files changed and version bumped + run: | + # Get the base branch (usually main) + git fetch origin ${{ github.base_ref }} + + # Run the version check script + chmod +x .github/scripts/check-plugin-version.sh + ./.github/scripts/check-plugin-version.sh ${{ github.base_ref }} + + # Second job: Read the minimum version from file + read-version: + runs-on: ubuntu-latest + outputs: + min-version: ${{ steps.read-version.outputs.version }} + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Read minimum Semgrep version + id: read-version + run: | + VERSION=$(cat semgrep-version) + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "Minimum Semgrep version: $VERSION" + + # Third job: Test against multiple versions + test: + needs: read-version + runs-on: ubuntu-latest + strategy: + matrix: + semgrep-version: + - ${{ needs.read-version.outputs.min-version }} # Minimum version + - 'latest' # Latest stable version + fail-fast: false # Continue testing other versions even if one fails + + name: test (${{ matrix.semgrep-version }}) + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Install Semgrep + run: | + if [ "${{ matrix.semgrep-version }}" = "latest" ]; then + pip install semgrep + else + pip install semgrep==${{ matrix.semgrep-version }} + fi + + - name: Verify Semgrep installation + run: | + semgrep --version + INSTALLED=$(semgrep --version | head -n1 | awk '{print $1}') + echo "Installed version: $INSTALLED" + echo "Required minimum: ${{ needs.read-version.outputs.min-version }}" + + - name: Check version compatibility + run: | + export CLAUDE_PLUGIN_ROOT="${GITHUB_WORKSPACE}/plugin" + chmod +x plugin/scripts/check_version.sh + ./plugin/scripts/check_version.sh + + # Run plugin hook tests + - name: Test plugin functionality + run: | + echo "Testing plugin with Semgrep ${{ matrix.semgrep-version }}" + export CLAUDE_PLUGIN_ROOT="${GITHUB_WORKSPACE}/plugin" + chmod +x tests/test_hooks.sh + ./tests/test_hooks.sh diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..51c6461 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +# Change Log +## 0.1.0 +Initial release \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..a15be8d --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,13 @@ +## Local development +- If you are in the directory of this repo, you can add the marketplace to your Claude Code instance by simply running +``` +/plugin marketplace add ./ +``` + +## Versioning +- Update the plugin version (in `plugin/.claude-plugin/plugin.json`) whenever the plugin changes +- Update `semgrep-version` if the change made to the plugin requires a newer version of `semgrep` +- `main` should always work on all versions of `semgrep` greater than the version stored in `semgrep-version` + +## Testing +- In addition to the tests in CI, we should manually test that the plugin still works in Claude Code \ No newline at end of file diff --git a/tests/test_hooks.sh b/tests/test_hooks.sh new file mode 100755 index 0000000..cf80845 --- /dev/null +++ b/tests/test_hooks.sh @@ -0,0 +1,48 @@ +#!/bin/bash +set -e + +# Main test runner for all hook tests + +# Get the directory where this script is located +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" + +# Source common utilities +source "${SCRIPT_DIR}/test_utils.sh" + +# Print overall test header +print_separator +echo "Testing Claude Code Hooks" +print_separator +echo "CLAUDE_PLUGIN_ROOT: ${CLAUDE_PLUGIN_ROOT}" + +# Track overall test status +ALL_TESTS_PASSED=true + +# Run SessionStart hook test +echo "" +if bash "${SCRIPT_DIR}/test_session_start_hook.sh"; then + : +else + ALL_TESTS_PASSED=false +fi + +# Run PostToolUse hook test +echo "" +if bash "${SCRIPT_DIR}/test_post_tool_use_hook.sh"; then + : +else + ALL_TESTS_PASSED=false +fi + +# Print final summary +echo "" +print_separator +if [ "$ALL_TESTS_PASSED" = true ]; then + print_success "All hook tests passed!" + print_separator + exit 0 +else + print_error "Some hook tests failed" + print_separator + exit 1 +fi diff --git a/tests/test_post_tool_use_hook.sh b/tests/test_post_tool_use_hook.sh new file mode 100755 index 0000000..471763a --- /dev/null +++ b/tests/test_post_tool_use_hook.sh @@ -0,0 +1,190 @@ +#!/bin/bash +set -e + +# Source common utilities +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +source "${SCRIPT_DIR}/test_utils.sh" + +# Test: PostToolUse Hook with Write tool +test_post_tool_use_write() { + print_test_header "PostToolUse Hook with Write Tool" + + # Create a temporary directory for test files + local TEST_DIR=$(mktemp -d) + echo "Created test directory: ${TEST_DIR}" + + # Create a test Python file with a potential security issue + local TEST_FILE="${TEST_DIR}/test_file.py" + cat > "${TEST_FILE}" << 'EOF' +import subprocess + +def execute_command(user_input): + # This should trigger a security warning + subprocess.call(user_input, shell=True) + +def safe_function(): + return "safe" +EOF + + echo "Created test file: ${TEST_FILE}" + + # Create mock hook input JSON (simulating what Claude Code sends via stdin) + local HOOK_INPUT=$(cat < "${TEST_FILE}" << 'EOF' +import subprocess + +def execute_command(user_input): + # This should trigger a security warning + subprocess.call(user_input, shell=True) + +def safe_function(): + return "safe" +EOF + + echo "Created test file: ${TEST_FILE}" + + local HOOK_INPUT_EDIT=$(cat < Date: Mon, 24 Nov 2025 16:13:41 -0800 Subject: [PATCH 2/3] update test.yml --- .github/workflows/test.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 634f991..f0f272a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,11 +1,12 @@ name: Test Plugin on: - push: + pull_request: jobs: # First job: Check version bump on PR check-version-bump: + if: github.event_name == 'pull_request' runs-on: ubuntu-latest steps: - name: Checkout PR branch @@ -16,11 +17,11 @@ jobs: - name: Check if plugin files changed and version bumped run: | # Get the base branch (usually main) - git fetch origin ${{ github.base_ref }} + git fetch origin ${{ github.event.pull_request.base.ref }} # Run the version check script chmod +x .github/scripts/check-plugin-version.sh - ./.github/scripts/check-plugin-version.sh ${{ github.base_ref }} + ./.github/scripts/check-plugin-version.sh ${{ github.event.pull_request.base.ref }} # Second job: Read the minimum version from file read-version: From 5964619e41b6fa00b372e5689643bf8db01de84b Mon Sep 17 00:00:00 2001 From: Katrina Liu Date: Mon, 8 Dec 2025 13:58:44 -0800 Subject: [PATCH 3/3] add test for secure default hook --- tests/test_hooks.sh | 8 ++++ tests/test_secure_defaults_hook.sh | 76 ++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100755 tests/test_secure_defaults_hook.sh diff --git a/tests/test_hooks.sh b/tests/test_hooks.sh index cf80845..73d9266 100755 --- a/tests/test_hooks.sh +++ b/tests/test_hooks.sh @@ -34,6 +34,14 @@ else ALL_TESTS_PASSED=false fi +# Run Inject Secure Defaults hook test +echo "" +if bash "${SCRIPT_DIR}/test_secure_defaults_hook.sh"; then + : +else + ALL_TESTS_PASSED=false +fi + # Print final summary echo "" print_separator diff --git a/tests/test_secure_defaults_hook.sh b/tests/test_secure_defaults_hook.sh new file mode 100755 index 0000000..f04400a --- /dev/null +++ b/tests/test_secure_defaults_hook.sh @@ -0,0 +1,76 @@ +#!/bin/bash +set -e + +# Source common utilities +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +source "${SCRIPT_DIR}/test_utils.sh" + + +# Test: inject-secure-defaults-short +test_inject_secure_defaults() { + print_test_header "Inject Secure Defaults ($1) Hook" + + local HOOK_INPUT=$(cat <