From 17dbe70dee34bc308354ef32bd557442ad0f2d78 Mon Sep 17 00:00:00 2001 From: marcusquinn <6428977+marcusquinn@users.noreply.github.com> Date: Wed, 25 Feb 2026 17:28:31 +0000 Subject: [PATCH] fix: exclude currency/pricing patterns from shell-local-params rule false-positives The shell-local-params TTSR rule regex $[1-9] was matching dollar amounts like $5/mo, $9 per month in markdown tables and pricing text. Expanded negative lookaheads across all three enforcement points (TTSR plugin, linters-local.sh, pre-commit-hook.sh) to exclude: - $[1-9] followed by pipe (markdown table cell boundary) - $[1-9] followed by common currency unit words (per, mo, month, year, flat, each, off, fee, plan, tier, user, seat, unit, addon, setup, trial, credit, annual, quarterly, monthly) Closes #2194 --- .agents/plugins/opencode-aidevops/index.mjs | 8 +++++--- .agents/scripts/linters-local.sh | 6 +++++- .agents/scripts/pre-commit-hook.sh | 7 ++++--- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/.agents/plugins/opencode-aidevops/index.mjs b/.agents/plugins/opencode-aidevops/index.mjs index e90c60145..20c5d0f93 100644 --- a/.agents/plugins/opencode-aidevops/index.mjs +++ b/.agents/plugins/opencode-aidevops/index.mjs @@ -1510,9 +1510,11 @@ const BUILTIN_TTSR_RULES = [ // Only match bare $N at the start of a line or after whitespace in what looks // like a shell assignment/command context — avoids matching $1 inside prose, // documentation, quoted examples, or tool output from file reads. - // Excludes currency/pricing patterns: $[1-9] followed by digits, decimal, comma, - // or slash (e.g. $28/mo, $1.99, $1,000) to prevent false-positives in markdown tables. - pattern: "^\\s+(?:echo|printf|return|if|\\[\\[).*\\$[1-9](?![0-9.,/])(?!.*local\\s+\\w+=)", + // Excludes currency/pricing patterns: + // - $[1-9] followed by digits, decimal, comma, or slash (e.g. $28/mo, $1.99, $1,000) + // - $[1-9] followed by pipe (markdown table cell boundary) + // - $[1-9] followed by common currency/pricing unit words (per, mo, month, flat, etc.) + pattern: "^\\s+(?:echo|printf|return|if|\\[\\[).*\\$[1-9](?![0-9.,/])(?!\\s*[|])(?!\\s+(?:per|mo(?:nth)?|year|yr|day|week|hr|hour|flat|each|off|fee|plan|tier|user|seat|unit|addon|setup|trial|credit|annual|quarterly|monthly)\\b)(?!.*local\\s+\\w+=)", correction: "Use `local var=\"$1\"` pattern — never use positional parameters directly (SonarCloud S7679).", severity: "warn", systemPrompt: "Shell scripts: use `local var=\"$1\"` — never use $1 directly in function bodies.", diff --git a/.agents/scripts/linters-local.sh b/.agents/scripts/linters-local.sh index 6a7482a4d..1b51287de 100755 --- a/.agents/scripts/linters-local.sh +++ b/.agents/scripts/linters-local.sh @@ -188,8 +188,12 @@ check_positional_parameters() { /print.*\$[1-9]/ { next } /Usage:/ { next } # Skip currency/pricing patterns: $[1-9] followed by digit, decimal, comma, - # or slash (e.g. $28/mo, $1.99, $1,000) — false-positives in markdown tables. + # slash (e.g. $28/mo, $1.99, $1,000), pipe (markdown table), or common + # currency/pricing unit words (per, mo, month, flat, etc.). /\$[1-9][0-9.,\/]/ { next } + /\$[1-9][[:space:]]*\|/ { next } + /\$[1-9][[:space:]]+(per|mo(nth)?|year|yr|day|week|hr|hour|flat|each|off|fee|plan|tier|user|seat|unit|addon|setup|trial|credit|annual|quarterly|monthly)[[:space:][:punct:]]/ { next } + /\$[1-9][[:space:]]+(per|mo(nth)?|year|yr|day|week|hr|hour|flat|each|off|fee|plan|tier|user|seat|unit|addon|setup|trial|credit|annual|quarterly|monthly)$/ { next } in_func && /\$[1-9]/ && !/local.*=.*\$[1-9]/ { print FILENAME ":" NR ": " $0 } diff --git a/.agents/scripts/pre-commit-hook.sh b/.agents/scripts/pre-commit-hook.sh index 1e0e981cd..7fa4e9f70 100755 --- a/.agents/scripts/pre-commit-hook.sh +++ b/.agents/scripts/pre-commit-hook.sh @@ -78,10 +78,11 @@ validate_positional_parameters() { for file in "$@"; do # Exclude currency/pricing patterns: $[1-9] followed by digit, decimal, comma, - # or slash (e.g. $28/mo, $1.99, $1,000) — false-positives in markdown tables. - if [[ -f "$file" ]] && grep -n '\$[1-9]' "$file" | grep -v 'local.*=.*\$[1-9]' | grep -vE '\$[1-9][0-9.,/]' >/dev/null; then + # slash (e.g. $28/mo, $1.99, $1,000), pipe (markdown table cell), or common + # currency/pricing unit words (per, mo, month, flat, etc.). + if [[ -f "$file" ]] && grep -n '\$[1-9]' "$file" | grep -v 'local.*=.*\$[1-9]' | grep -vE '\$[1-9][0-9.,/]' | grep -vE '\$[1-9]\s*\|' | grep -vE '\$[1-9]\s+(per|mo(nth)?|year|yr|day|week|hr|hour|flat|each|off|fee|plan|tier|user|seat|unit|addon|setup|trial|credit|annual|quarterly|monthly)\b' >/dev/null; then print_error "Direct positional parameter usage in $file" - grep -n '\$[1-9]' "$file" | grep -v 'local.*=.*\$[1-9]' | grep -vE '\$[1-9][0-9.,/]' | head -3 + grep -n '\$[1-9]' "$file" | grep -v 'local.*=.*\$[1-9]' | grep -vE '\$[1-9][0-9.,/]' | grep -vE '\$[1-9]\s*\|' | grep -vE '\$[1-9]\s+(per|mo(nth)?|year|yr|day|week|hr|hour|flat|each|off|fee|plan|tier|user|seat|unit|addon|setup|trial|credit|annual|quarterly|monthly)\b' | head -3 ((violations++)) fi done