Skip to content

Commit 19468ec

Browse files
sjnimsclaude
andauthored
fix(security): use jq for safe JSON output in example hooks (#149)
## Description Replace string concatenation with proper `jq` JSON construction in example hook scripts. This prevents potential issues with special characters in file paths being interpreted as JSON syntax. **Before (vulnerable to JSON injection):** ```bash echo '{"systemMessage": "Path traversal in: '"$file_path"'"}' >&2 ``` **After (safe):** ```bash jq -n --arg path "$file_path" \ '{"systemMessage": "Path traversal in: \($path)"}' >&2 ``` Using `jq --arg` properly escapes special characters like quotes, backslashes, and control characters that could break JSON parsing or cause injection issues. ## Type of Change - [x] Bug fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] Documentation update (improvements to README, CLAUDE.md, or component docs) - [ ] Refactoring (code change that neither fixes a bug nor adds a feature) - [ ] Configuration change (changes to .markdownlint.json, plugin.json, etc.) ## Component(s) Affected - [ ] Commands (`/plugin-dev:*`) - [x] Skills (methodology and best practices) - [ ] Agents (requirements-assistant) - [ ] Hooks (UserPromptSubmit) - [ ] Documentation (README.md, CLAUDE.md, SECURITY.md) - [ ] Configuration (.markdownlint.json, plugin.json, marketplace.json) - [ ] Issue/PR templates - [ ] Other (please specify): ## Motivation and Context Example hooks used string concatenation to build JSON output, which could fail or produce malformed JSON when file paths contain special characters like quotes, backslashes, or newlines. This sets a bad example for plugin developers who might copy these patterns. Using `jq` for JSON construction: - Properly escapes all special characters - Guarantees valid JSON output - Demonstrates best practices for hook development ## How Has This Been Tested? **Test Configuration**: - Claude Code version: Latest - GitHub CLI version: 2.x - OS: macOS - Testing repository: N/A **Test Steps**: 1. Test with normal file paths - JSON output is valid 2. Test with paths containing quotes (`test"file.txt`) - properly escaped 3. Test with paths containing backslashes - properly escaped 4. Validate JSON output with `jq` parser ## Checklist ### General - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas (if applicable) - [x] My changes generate no new warnings or errors ### Documentation - [ ] I have updated the documentation accordingly (README.md, CLAUDE.md, or component docs) - [ ] I have updated YAML frontmatter (if applicable) - [ ] I have verified all links work correctly ### Markdown - [x] I have run `markdownlint` and fixed all issues - [x] My markdown follows the repository style (ATX headers, dash lists, fenced code blocks) - [ ] I have verified special HTML elements are properly closed (`<example>`, `<commentary>`, etc.) ### Component-Specific Checks N/A - These are shell script example files, not commands/skills/agents. ### Testing - [ ] I have tested the plugin locally with `cc --plugin-dir plugins/plugin-dev` - [ ] I have tested the full workflow (if applicable) - [ ] I have verified GitHub CLI integration works (if applicable) - [ ] I have tested in a clean repository (not my development repo) ### Version Management (if applicable) - [ ] I have updated version numbers in both `plugin.json` and `marketplace.json` (if this is a release) - [ ] I have updated CHANGELOG.md with relevant changes ## Screenshots (if applicable) N/A ## Additional Notes Files modified: - `plugins/plugin-dev/skills/hook-development/examples/validate-write.sh` (3 JSON outputs) - `plugins/plugin-dev/skills/plugin-settings/examples/read-settings-hook.sh` (1 JSON output) ## Reviewer Notes **Areas that need special attention**: - Verify `jq` string interpolation syntax `\($var)` is correct - Ensure the JSON structure matches the original output **Known limitations or trade-offs**: - Requires `jq` to be installed (standard on most systems, already used elsewhere in the scripts) --- 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <noreply@anthropic.com>
1 parent 1cf02a6 commit 19468ec

2 files changed

Lines changed: 8 additions & 4 deletions

File tree

plugins/plugin-dev/skills/hook-development/examples/validate-write.sh

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,22 @@ fi
1818

1919
# Check for path traversal
2020
if [[ "$file_path" == *".."* ]]; then
21-
echo '{"hookSpecificOutput": {"permissionDecision": "deny"}, "systemMessage": "Path traversal detected in: '"$file_path"'"}' >&2
21+
jq -n --arg path "$file_path" \
22+
'{"hookSpecificOutput": {"permissionDecision": "deny"}, "systemMessage": "Path traversal detected in: \($path)"}' >&2
2223
exit 2
2324
fi
2425

2526
# Check for system directories
2627
if [[ "$file_path" == /etc/* ]] || [[ "$file_path" == /sys/* ]] || [[ "$file_path" == /usr/* ]]; then
27-
echo '{"hookSpecificOutput": {"permissionDecision": "deny"}, "systemMessage": "Cannot write to system directory: '"$file_path"'"}' >&2
28+
jq -n --arg path "$file_path" \
29+
'{"hookSpecificOutput": {"permissionDecision": "deny"}, "systemMessage": "Cannot write to system directory: \($path)"}' >&2
2830
exit 2
2931
fi
3032

3133
# Check for sensitive files
3234
if [[ "$file_path" == *.env ]] || [[ "$file_path" == *secret* ]] || [[ "$file_path" == *credentials* ]]; then
33-
echo '{"hookSpecificOutput": {"permissionDecision": "ask"}, "systemMessage": "Writing to potentially sensitive file: '"$file_path"'"}' >&2
35+
jq -n --arg path "$file_path" \
36+
'{"hookSpecificOutput": {"permissionDecision": "ask"}, "systemMessage": "Writing to potentially sensitive file: \($path)"}' >&2
3437
exit 2
3538
fi
3639

plugins/plugin-dev/skills/plugin-settings/examples/read-settings-hook.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ if [[ -n "$MAX_SIZE" ]] && [[ "$MAX_SIZE" =~ ^[0-9]+$ ]]; then
5656
content_size=${#content}
5757

5858
if [[ $content_size -gt $MAX_SIZE ]]; then
59-
echo '{"hookSpecificOutput": {"permissionDecision": "deny"}, "systemMessage": "File exceeds configured max size: '"$MAX_SIZE"' bytes"}' >&2
59+
jq -n --arg size "$MAX_SIZE" \
60+
'{"hookSpecificOutput": {"permissionDecision": "deny"}, "systemMessage": "File exceeds configured max size: \($size) bytes"}' >&2
6061
exit 2
6162
fi
6263
fi

0 commit comments

Comments
 (0)