From bbb2ed45eab9bdef8fd36d2134461d9c75605e9e Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Thu, 29 Jan 2026 21:24:01 -0600 Subject: [PATCH] fix: address code review feedback - Replace unsafe JSON string concatenation with jq in cmd_create() and cmd_update() - Add HTTP status code checking and error message extraction in api_call() - Prevent JSON injection vulnerabilities from special characters - Improve error messages with actual API responses --- packages/skills/brain/brain.sh | 99 +++++++++++++++++----------------- 1 file changed, 48 insertions(+), 51 deletions(-) diff --git a/packages/skills/brain/brain.sh b/packages/skills/brain/brain.sh index 3bf5959..cd84d4c 100755 --- a/packages/skills/brain/brain.sh +++ b/packages/skills/brain/brain.sh @@ -54,13 +54,30 @@ api_call() { -H "Content-Type: application/json" -H "x-workspace-id: ${WORKSPACE_ID}" -s + -w "\n%{http_code}" ) if [[ -n "$data" ]]; then args+=(-d "$data") fi - curl "${args[@]}" "$url" + local response + response=$(curl "${args[@]}" "$url") + + # Extract HTTP status code from last line + local http_code + http_code=$(echo "$response" | tail -n1) + local body + body=$(echo "$response" | sed '$d') + + # Check for errors + if [[ "$http_code" -ge 400 ]]; then + local error_msg + error_msg=$(echo "$body" | jq -r '.message // .error // "API request failed"' 2>/dev/null || echo "API request failed") + error "HTTP $http_code: $error_msg" + fi + + echo "$body" } # Commands @@ -142,20 +159,22 @@ cmd_create() { [[ -z "$content" ]] && error "Content required" - # Build JSON payload - local payload="{\"content\": \"$content\"" - [[ -n "$title" ]] && payload+=", \"title\": \"$title\"" - [[ -n "$category" ]] && payload+=", \"category\": \"$category\"" - [[ -n "$status" ]] && payload+=", \"status\": \"$status\"" - [[ -n "$priority" ]] && payload+=", \"priority\": \"$priority\"" - - if [[ -n "$tags" ]]; then - local tags_json - tags_json=$(echo "$tags" | jq -R 'split(",") | map(gsub("^\\s+|\\s+$";""))') - payload+=", \"tags\": $tags_json" - fi - - payload+="}" + # Build JSON payload using jq for safety + local payload + payload=$(jq -n \ + --arg content "$content" \ + --arg title "$title" \ + --arg category "$category" \ + --arg status "$status" \ + --arg priority "$priority" \ + --arg tags "$tags" \ + '{content: $content} + + (if $title != "" then {title: $title} else {} end) + + (if $category != "" then {category: $category} else {} end) + + (if $status != "" then {status: $status} else {} end) + + (if $priority != "" then {priority: $priority} else {} end) + + (if $tags != "" then {tags: ($tags | split(",") | map(gsub("^\\s+|\\s+$";"")))} else {} end)' + ) response=$(api_call POST "/api/ideas" "$payload") echo "$response" | jq -r '.id // empty' > /dev/null || error "Failed to create idea" @@ -257,42 +276,20 @@ cmd_update() { esac done - # Build update payload - local payload="{" - local first=true - - if [[ -n "$title" ]]; then - payload+="\"title\": \"$title\"" - first=false - fi - - if [[ -n "$content" ]]; then - [[ "$first" == false ]] && payload+=", " - payload+="\"content\": \"$content\"" - first=false - fi - - if [[ -n "$status" ]]; then - [[ "$first" == false ]] && payload+=", " - payload+="\"status\": \"$status\"" - first=false - fi - - if [[ -n "$category" ]]; then - [[ "$first" == false ]] && payload+=", " - payload+="\"category\": \"$category\"" - first=false - fi - - if [[ -n "$tags" ]]; then - [[ "$first" == false ]] && payload+=", " - local tags_json - tags_json=$(echo "$tags" | jq -R 'split(",") | map(gsub("^\\s+|\\s+$";""))') - payload+="\"tags\": $tags_json" - first=false - fi - - payload+="}" + # Build update payload using jq for safety + local payload + payload=$(jq -n \ + --arg title "$title" \ + --arg content "$content" \ + --arg status "$status" \ + --arg category "$category" \ + --arg tags "$tags" \ + '(if $title != "" then {title: $title} else {} end) + + (if $content != "" then {content: $content} else {} end) + + (if $status != "" then {status: $status} else {} end) + + (if $category != "" then {category: $category} else {} end) + + (if $tags != "" then {tags: ($tags | split(",") | map(gsub("^\\s+|\\s+$";"")))} else {} end)' + ) response=$(api_call PATCH "/api/ideas/${idea_id}" "$payload") echo "$response" | jq -r '.id // empty' > /dev/null || error "Failed to update idea"