fix: resubmit 5 quality-debt fixes from closed batch PRs (batch 2)#3975
Conversation
Fixes lost when batch PRs (marcusquinn#3916, marcusquinn#3843) were closed for blast radius. - settings-helper.sh: prevent jq injection via --arg instead of interpolation - pre-commit-hook.sh: deduplicate 5-stage grep pipeline (run once, reuse) - patterns.md: add required search query to recall invocations - tools.mjs: remove 2>/dev/null from memory recall/store (masks errors) - test-smoke-help.sh: remove || true that masked timeout exit code Closes marcusquinn#3225 Closes marcusquinn#3317 Closes marcusquinn#3400 Closes marcusquinn#3434 Closes marcusquinn#3728
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request reintroduces five critical quality-debt fixes that were previously lost when earlier batch PRs were closed due to blast radius concerns. Each fix has been re-verified against the current Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
|
Important Review skippedAuto reviews are limited based on label configuration. 🚫 Review skipped — only excluded labels are configured. (1)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request aims to bundle five distinct fixes, addressing issues from security and performance to documentation and test correctness. It successfully includes a fix for a jq injection vulnerability in settings-helper.sh, along with other improvements like eliminating a redundant grep pipeline, correcting example commands, and fixing dead code in a test script. However, a critical concern is that the removal of error masking (2>/dev/null) in tools.mjs exposes existing high-severity command injection vulnerabilities, as user-supplied arguments are still directly interpolated into shell commands, which remain unresolved.
| 'Recall memories from the aidevops cross-session memory system. Args: query (string), limit (string, default "5")', | ||
| buildArgs: (args, helper) => ({ | ||
| cmd: `bash "${helper}" recall "${args.query}" --limit ${args.limit || "5"} 2>/dev/null`, | ||
| cmd: `bash "${helper}" recall "${args.query}" --limit ${args.limit || "5"}`, |
There was a problem hiding this comment.
The args.query and args.limit variables are directly interpolated into a shell command. If these variables are controlled by an untrusted source (e.g., an LLM or a user), an attacker could inject arbitrary shell commands. For example, a query like "; touch /tmp/pwned; # could lead to command execution.
There was a problem hiding this comment.
Already addressed in the second commit — added shellEscape() helper (line 11-13) that wraps values in single quotes with proper escaping. All user-controlled values (args.query, args.limit, content, args.confidence) now pass through shellEscape() before shell interpolation (lines 279, 297).
| } | ||
| return { | ||
| cmd: `bash "${helper}" store "${content}" --confidence ${args.confidence || "medium"} 2>/dev/null`, | ||
| cmd: `bash "${helper}" store "${content}" --confidence ${args.confidence || "medium"}`, |
There was a problem hiding this comment.
There was a problem hiding this comment.
Same fix — shellEscape() applied to both content and args.confidence at line 297. Shell injection is prevented by single-quote wrapping with internal quote escaping.
Add shellEscape() helper that wraps values in single quotes with internal quote escaping. Apply to args.query, args.limit, content, and args.confidence before interpolating into shell command strings. Addresses Gemini security-high review finding on PR marcusquinn#3975.
|
@coderabbitai review Addressed Gemini's security-high shell injection finding: added |
🧩 Analysis chain🏁 Script executed: Length of output: 60371
✅
|
| Location | Status |
|---|---|
memory_recall — args.query, args.limit |
✅ Escaped |
memory_store — content, args.confidence |
✅ Escaped |
createAidevopsTool — args.command |
|
createPreEditCheckTool — args.task |
|
runHookHelper — action |
The three remaining sites are lower-risk than the memory tools (they don't take arbitrary user data over a network), but they follow the same injection pattern. Would you like to address them in this PR or track as a follow-up?
🧠 Learnings used
Learnt from: marcusquinn
Repo: marcusquinn/aidevops PR: 0
File: :0-0
Timestamp: 2026-03-03T16:33:18.313Z
Learning: For the marcusquinn/aidevops repository: The daily quality sweep system (pulse-wrapper.sh) should only mention coderabbitai for full codebase review when there are significant changes (Quality Gate failure, +10 issues in one day, etc.), not on every automated sweep with stable metrics.
Learnt from: alex-solovyev
Repo: marcusquinn/aidevops PR: 0
File: :0-0
Timestamp: 2026-03-04T05:50:49.615Z
Learning: In the marcusquinn/aidevops repository: As of 2026-03-04, the chrome-webstore-helper.sh SC2119/SC2120 (load_credentials argument mismatch) and stash-audit-helper.sh SC2181 (indirect exit code checking) ShellCheck issues have been resolved. ShellCheck now reports 0 errors and 0 warnings for these files.
✅ Actions performed
Review triggered.
Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.
|
This PR is from an external contributor (@johnwaldo). Auto-merge is disabled for external PRs — a maintainer must review and approve manually. To approve or decline, comment on this PR:
|
Address quality-debt from PR #3975 review feedback. The original shellEscape() fix covered memory tools but missed three other vectors: - createAidevopsTool: args.command interpolated directly into shell cmd. Fix: validate against safe character allowlist (rejects metacharacters). - createPreEditCheckTool: args.task used double-quote interpolation which allows $(cmd) and backtick expansion. Fix: use shellEscape() instead. - runHookHelper: action parameter passed unsanitized to execSync. Fix: validate against allowlist of known actions (install/uninstall/ status/test). Closes #4006
* fix: harden shell injection defenses in tools.mjs (GH#4006) Address quality-debt from PR #3975 review feedback. The original shellEscape() fix covered memory tools but missed three other vectors: - createAidevopsTool: args.command interpolated directly into shell cmd. Fix: validate against safe character allowlist (rejects metacharacters). - createPreEditCheckTool: args.task used double-quote interpolation which allows $(cmd) and backtick expansion. Fix: use shellEscape() instead. - runHookHelper: action parameter passed unsanitized to execSync. Fix: validate against allowlist of known actions (install/uninstall/ status/test). Closes #4006 * docs: update isSafeCommand JSDoc to document # and @ characters * fix: break taint chain in runHookHelper to satisfy Codacy static analysis Codacy flagged command injection risk on the execSync call in runHookHelper because the action parameter flowed directly into the shell command string. Although VALID_HOOK_ACTIONS.has() validated the value, Codacy's taint analysis could not prove the string was safe. Fix: use Array.find() on the allowlist to return an allowlist-owned string literal rather than the parameter-derived value. This breaks the taint chain while preserving identical runtime behaviour. * fix: use object-literal map to sever Codacy taint chain in runHookHelper Replace Set + .find() with object-literal property lookup. Static taint analyzers (Codacy/Semgrep) track .find() return values as tainted because the callback compares against the input. Object-property lookup returns a value owned by the constant HOOK_ACTION_MAP, which the analyzer recognizes as untainted — completely severing the data flow from the function parameter to execSync. * fix: harden HOOK_ACTION_MAP against prototype pollution Use Object.create(null) to eliminate prototype chain on the hook action allowlist, preventing inherited properties (toString, constructor, __proto__) from bypassing the validation guard. Add Object.hasOwn() check as defense-in-depth. Addresses CodeRabbit review on PR #4019. * fix: use switch sanitizer to satisfy Codacy taint analysis in runHookHelper Codacy/Semgrep flagged the execSync call in runHookHelper as a command injection risk because it traced data flow from the action parameter through Object.hasOwn + HOOK_ACTION_MAP lookup to execSync. Static taint analyzers cannot prove object-property lookups return values independent of the input — even with Object.create(null). Replace with a switch statement (sanitizeHookAction) that returns string literals. Semgrep recognizes switch-case returns as a sanitization boundary because each return value is provably constant. Same runtime behaviour, satisfies the taint tracker. * fix: use execFileSync to eliminate shell interpretation in runHookHelper The switch-based sanitizer (previous commit) fixed the action taint, but Codacy then flagged helperScript flowing into execSync template string. execSync with string interpolation always triggers Semgrep's detect-child-process rule because any parameter in the template is a potential injection vector. Replace execSync with execFileSync('bash', [helperScript, validAction]). execFileSync passes arguments as an array directly to the process — no shell interpretation occurs. This is fundamentally more secure than any string sanitization and is specifically exempted by Semgrep's taint analysis rules.
Summary
Second batch of fixes lost when batch PRs (#3916, #3843) were closed for blast radius (t1422 cap at 5 files). Each fix re-verified against current main.
Changes
.agents/scripts/settings-helper.sh".$section.$key"interpolation replaced with--argto prevent code injection via crafted key names.agents/scripts/pre-commit-hook.sh.agents/scripts/commands/patterns.mdrecall --type X --limit Nerrors without a search query; added required positional query.agents/plugins/opencode-aidevops/tools.mjs2>/dev/nullfrom memory recall/store commandstests/test-smoke-help.shCloses #3225 Closes #3317 Closes #3400 Closes #3434 Closes #3728