From 8ea86a377b373e72e9b1fa742a6d244f5fdd12b1 Mon Sep 17 00:00:00 2001 From: Sisyphus Agent Date: Fri, 6 Feb 2026 04:42:21 -0300 Subject: [PATCH 1/2] refactor: final sonar cleanup (complexity, scripts syntax, snapshots) --- docs/install.sh | 20 ++++--- scripts/install-hook.sh | 14 ++--- src/cli.ts | 33 +++++++---- src/integrations/git.ts | 5 +- src/parser/command-checks.ts | 28 ++++++---- src/parser/rules/CoreAstRule.ts | 56 ++++++++++--------- src/parser/rules/RawThreatRule.ts | 4 +- src/shell-context.ts | 3 +- tests/__snapshots__/cli_snapshot.test.ts.snap | 14 ++--- 9 files changed, 99 insertions(+), 78 deletions(-) diff --git a/docs/install.sh b/docs/install.sh index 200ccb7..a80bb7f 100644 --- a/docs/install.sh +++ b/docs/install.sh @@ -20,18 +20,22 @@ fi error() { echo -e "${Red}error${Color_Off}:" "$@" >&2 exit 1 + return 1 } info() { echo -e "${Dim}$@ ${Color_Off}" + return 0 } info_bold() { echo -e "${Bold_White}$@ ${Color_Off}" + return 0 } success() { echo -e "${Green}$@ ${Color_Off}" + return 0 } echo -e "${Bold_White}" @@ -42,10 +46,10 @@ echo -e "${Color_Off}" verify_checksum() { local script_path="$1" local expected="${SHELLSHIELD_INSTALL_SHA256:-}" - if [ -z "$expected" ]; then + if [[ -z "$expected" ]]; then error "Missing SHELLSHIELD_INSTALL_SHA256. Get the checksum from the README and retry." fi - if [ -z "$script_path" ] || [ ! -f "$script_path" ]; then + if [[ -z "$script_path" ]] || [[ ! -f "$script_path" ]]; then error "Installer must be run from a file to verify checksum. Download it first." fi @@ -77,11 +81,11 @@ success "āœ… Dependencies found." INSTALL_DIR="$HOME/.shellshield" info "Installing to ${INSTALL_DIR}..." -if [ -d "$INSTALL_DIR/.git" ]; then +if [[ -d "$INSTALL_DIR/.git" ]]; then info "Updating existing installation..." git -C "$INSTALL_DIR" fetch --quiet git -C "$INSTALL_DIR" reset --hard origin/main --quiet -elif [ -d "$INSTALL_DIR" ]; then +elif [[ -d "$INSTALL_DIR" ]]; then info "Directory exists but is not a git repo. Backing up..." mv "$INSTALL_DIR" "${INSTALL_DIR}.bak.$(date +%s)" git clone --depth 1 https://github.com/hevlyo/ShellShield.git "$INSTALL_DIR" --quiet @@ -98,7 +102,7 @@ bun install --production --no-save --force 2>"$tmp_log" install_status=$? set -e -if [ $install_status -ne 0 ]; then +if [[ $install_status -ne 0 ]]; then if grep -q "lockfile had changes" "$tmp_log"; then error "bun lockfile is frozen. Update bun and retry (bun --version)." fi @@ -118,7 +122,7 @@ case "$USER_SHELL" in PROFILE="$HOME/.zshrc" ;; bash) - if [ -f "$HOME/.bashrc" ]; then + if [[ -f "$HOME/.bashrc" ]]; then PROFILE="$HOME/.bashrc" else PROFILE="$HOME/.bash_profile" @@ -134,13 +138,13 @@ HOOK_BEGIN="# ShellShield Hook" HOOK_END="# ShellShield Hook End" HOOK_SCRIPT=" $HOOK_BEGIN -if [ -f \"$HOME/.shellshield/src/index.ts\" ]; then +if [[ -f \"$HOME/.shellshield/src/index.ts\" ]]; then eval \"\$(bun run \"$HOME/.shellshield/src/index.ts\" --init)\" fi $HOOK_END " -if [ -f "$PROFILE" ]; then +if [[ -f "$PROFILE" ]]; then if grep -q "$HOOK_BEGIN" "$PROFILE"; then tmp_profile=$(mktemp) awk -v begin="$HOOK_BEGIN" -v end="$HOOK_END" ' diff --git a/scripts/install-hook.sh b/scripts/install-hook.sh index ad0832a..5e2ead1 100755 --- a/scripts/install-hook.sh +++ b/scripts/install-hook.sh @@ -6,7 +6,7 @@ echo "šŸ”’ Installing ShellShield Pre-commit Hook..." HOOK_DIR=".git/hooks" HOOK_FILE="$HOOK_DIR/pre-commit" -if [ ! -d ".git" ]; then +if [[ ! -d ".git" ]]; then echo "āŒ Not a git repository. Run 'git init' first." exit 1 fi @@ -17,11 +17,11 @@ cat > "$HOOK_FILE" << 'EOF' #!/bin/bash set -e -if [ "${SHELLSHIELD_SKIP:-}" = "1" ]; then +if [[ "${SHELLSHIELD_SKIP:-}" == "1" ]]; then exit 0 fi -if [ -x "$HOME/.bun/bin/bun" ]; then +if [[ -x "$HOME/.bun/bin/bun" ]]; then BUN_BIN="$HOME/.bun/bin/bun" elif command -v bun >/dev/null 2>&1; then BUN_BIN="bun" @@ -30,9 +30,9 @@ else exit 0 fi -if [ -f "./src/index.ts" ]; then +if [[ -f "./src/index.ts" ]]; then CLI="./src/index.ts" -elif [ -f "$HOME/.shellshield/src/index.ts" ]; then +elif [[ -f "$HOME/.shellshield/src/index.ts" ]]; then CLI="$HOME/.shellshield/src/index.ts" else echo "ShellShield pre-commit: shellshield not found; skipping." @@ -51,7 +51,7 @@ while IFS= read -r line; do if [[ "$line" == "+"* ]]; then candidate="${line#+}" - if [ -z "${candidate//[[:space:]]/}" ]; then + if [[ -z "${candidate//[[:space:]]/}" ]]; then continue fi @@ -65,7 +65,7 @@ while IFS= read -r line; do fi done < <(git diff --cached -U0 --no-color) -if [ "$has_issues" -ne 0 ]; then +if [[ "$has_issues" -ne 0 ]]; then echo "Commit blocked by ShellShield." echo "Use SHELLSHIELD_SKIP=1 git commit to bypass." exit 1 diff --git a/src/cli.ts b/src/cli.ts index f2d1910..b4c71d4 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -32,15 +32,22 @@ function printDoctor(): void { const hasTrash = runProbe(["bash", "-lc", "command -v trash"]).ok; const hasGioTrash = runProbe(["bash", "-lc", "command -v gio"]).ok; - console.log("ShellShield Doctor"); - console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); + const doctorHeader = "ShellShield Doctor"; + const doctorSeparator = "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"; + console.log(doctorHeader); + console.log(doctorSeparator); console.log(`Shell: ${shell || "(unknown)"}`); console.log(`Mode: ${process.env.SHELLSHIELD_MODE || "(default)"}`); console.log("\nSafer delete command:"); - if (hasTrashPut) console.log("- trash-put (recommended)"); - else if (hasTrash) console.log("- trash"); - else if (hasGioTrash) console.log("- gio trash"); - else console.log("- (none found) install trash-cli or use gio trash"); + if (hasTrashPut) { + console.log("- trash-put (recommended)"); + } else if (hasTrash) { + console.log("- trash"); + } else if (hasGioTrash) { + console.log("- gio trash"); + } else { + console.log("- (none found) install trash-cli or use gio trash"); + } if (shell) { const typeRm = runProbe(["bash", "-lc", `${shell} -ic 'type rm 2>/dev/null'`]).out; @@ -116,13 +123,13 @@ async function promptConfirmation(command: string, reason: string): Promise { + const promptMsg = String.raw`\nāš ļø ShellShield ALERT: ${reason}\n Command: ${command}\n Are you sure you want to execute this? [y/N] `; rl.question( - `\nāš ļø ShellShield ALERT: ${reason}\n` + - ` Command: ${command}\n` + - ` Are you sure you want to execute this? [y/N] `, + promptMsg, (answer) => { rl.close(); - resolve(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes"); + const lower = answer.toLowerCase(); + resolve(lower === "y" || lower === "yes"); } ); }); @@ -136,8 +143,9 @@ async function checkAndAuditCommand(command: string, config: any, source: "check } if (config?.mode === "permissive") { + const warningHeader = String.raw`āš ļø ShellShield WARNING: Command '${command}' would be blocked in enforce mode.`; console.error( - `āš ļø ShellShield WARNING: Command '${command}' would be blocked in enforce mode.\n` + + `${warningHeader}\n` + `Reason: ${result.reason}\n` + `Suggestion: ${result.suggestion}` ); @@ -150,7 +158,8 @@ async function checkAndAuditCommand(command: string, config: any, source: "check if (confirmed) { logAudit(command, { ...result, blocked: false }, { source, mode: config?.mode, threshold: config?.threshold, decision: "approved" }); const msg = "Approved. Command will execute."; - console.error(process.stderr.isTTY ? `\x1b[32m${msg}\x1b[0m` : msg); + const tty = process.stderr.isTTY; + console.error(tty ? String.raw`\x1b[32m${msg}\x1b[0m` : msg); return true; } } diff --git a/src/integrations/git.ts b/src/integrations/git.ts index ea5f770..ac3c89e 100644 --- a/src/integrations/git.ts +++ b/src/integrations/git.ts @@ -9,8 +9,9 @@ function processGitStatusOutput( const pathspecToOriginal = new Map(); for (const entry of entries) { pathspecToOriginal.set(entry.pathspec, entry.original); - if (entry.pathspec.startsWith("./")) { - pathspecToOriginal.set(entry.pathspec.slice(2), entry.original); + const spec = entry.pathspec; + if (spec.startsWith("./")) { + pathspecToOriginal.set(spec.slice(2), entry.original); } } diff --git a/src/parser/command-checks.ts b/src/parser/command-checks.ts index c89c465..0d15746 100644 --- a/src/parser/command-checks.ts +++ b/src/parser/command-checks.ts @@ -8,6 +8,18 @@ interface BlockedContext { threshold: number; } +function checkGitIntegration(targetFiles: string[]): BlockResult | null { + const uncommitted = hasUncommittedChanges(targetFiles); + if (uncommitted.length > 0) { + return { + blocked: true, + reason: "UNCOMMITTED CHANGES DETECTED", + suggestion: `Commit changes to these files first: ${uncommitted.join(", ")}`, + }; + } + return null; +} + export function checkBlockedCommand( resolvedCmd: string, args: string[], @@ -57,14 +69,8 @@ export function checkBlockedCommand( }; } - const uncommitted = hasUncommittedChanges(targetFiles); - if (uncommitted.length > 0) { - return { - blocked: true, - reason: "UNCOMMITTED CHANGES DETECTED", - suggestion: `Commit changes to these files first: ${uncommitted.join(", ")}`, - }; - } + const gitCheck = checkGitIntegration(targetFiles); + if (gitCheck) return gitCheck; let suggestion = "trash "; if (resolvedCmd === "rm" && targetFiles.length > 0) { @@ -82,7 +88,8 @@ export function checkFindCommand( remaining: ParsedEntry[], blockedCommands: Set ): BlockResult | null { - if (remaining.some((entry) => typeof entry === "string" && entry.toLowerCase() === "-delete")) { + const hasDelete = remaining.some((entry) => typeof entry === "string" && entry.toLowerCase() === "-delete"); + if (hasDelete) { return { blocked: true, reason: "find -delete detected", suggestion: "trash " }; } @@ -92,7 +99,8 @@ export function checkFindCommand( if (execIdx !== -1 && execIdx + 1 < remaining.length) { const execCmd = remaining[execIdx + 1]; if (typeof execCmd === "string") { - const execName = execCmd.split("/").pop()?.toLowerCase() ?? ""; + const parts = execCmd.split("/"); + const execName = (parts.pop() ?? "").toLowerCase(); if (blockedCommands.has(execName)) { return { blocked: true, diff --git a/src/parser/rules/CoreAstRule.ts b/src/parser/rules/CoreAstRule.ts index 3e2a18b..2be793c 100644 --- a/src/parser/rules/CoreAstRule.ts +++ b/src/parser/rules/CoreAstRule.ts @@ -58,7 +58,7 @@ export class CoreAstRule implements SecurityRule { return null; } - private checkProcessSubstitution(normalizedEntry: string, tokens: ParsedEntry[], i: number): BlockResult | null { + private checkBashSubshells(normalizedEntry: string, tokens: ParsedEntry[], i: number): BlockResult | null { if (normalizedEntry === "bash" || normalizedEntry === "sh" || normalizedEntry === "zsh") { const remaining = tokens.slice(i + 1); const hasSubstitution = remaining.some( @@ -80,6 +80,27 @@ export class CoreAstRule implements SecurityRule { return null; } + private resolveCmdName(entry: string, vars: Record): string { + const basenamePart = entry.split("/").pop() ?? ""; + const cmdName = entry.startsWith("\\") ? entry.slice(1) : basenamePart; + + const resolvedVar = this.resolveVarToken(cmdName, vars); + if (resolvedVar) { + return resolvedVar.split("/").pop()?.toLowerCase() ?? ""; + } + return cmdName.toLowerCase(); + } + + private checkGitRm(normalizedEntry: string, tokens: ParsedEntry[], i: number): number { + if (normalizedEntry === "git" && i + 1 < tokens.length) { + const next = tokens[i + 1]; + if (typeof next === "string" && next.toLowerCase() === "rm") { + return i + 1; + } + } + return i; + } + private checkShellContext(resolvedCmd: string, config: any): BlockResult | null { if (!config.blocked.has(resolvedCmd)) { const ctxEntry = getShellContextEntry(resolvedCmd); @@ -122,9 +143,7 @@ export class CoreAstRule implements SecurityRule { continue; } - if (typeof entry !== "string") { - continue; - } + if (typeof entry !== "string") continue; if (!nextMustBeCommand) { this.checkEnvironmentVariable(entry, vars); @@ -134,18 +153,16 @@ export class CoreAstRule implements SecurityRule { } nextMustBeCommand = false; - if (this.checkEnvironmentVariable(entry, vars)) { nextMustBeCommand = true; continue; } const normalizedEntry = entry.toLowerCase(); - const curlCheck = this.checkCurlWget(normalizedEntry, tokens, i, config); if (curlCheck) return curlCheck; - const subCheck = this.checkProcessSubstitution(normalizedEntry, tokens, i); + const subCheck = this.checkBashSubshells(normalizedEntry, tokens, i); if (subCheck) return subCheck; if (["sudo", "xargs", "command", "env"].includes(normalizedEntry)) { @@ -153,32 +170,19 @@ export class CoreAstRule implements SecurityRule { continue; } - if (normalizedEntry === "git" && i + 1 < tokens.length) { - const next = tokens[i + 1]; - if (typeof next === "string" && next.toLowerCase() === "rm") { - i++; - continue; - } - } - - const basenamePart = entry.split("/").pop() ?? ""; - const cmdName = entry.startsWith("\\") ? entry.slice(1) : basenamePart; - - let resolvedCmd = cmdName.toLowerCase(); - const resolvedVar = this.resolveVarToken(cmdName, vars); - if (resolvedVar) { - resolvedCmd = resolvedVar.split("/").pop()?.toLowerCase() ?? ""; + const nextI = this.checkGitRm(normalizedEntry, tokens, i); + if (nextI !== i) { + i = nextI; + continue; } + const resolvedCmd = this.resolveCmdName(entry, vars); const ctxCheck = this.checkShellContext(resolvedCmd, config); if (ctxCheck) return ctxCheck; - if (config.allowed.has(resolvedCmd)) { - continue; - } + if (config.allowed.has(resolvedCmd)) continue; const args = tokens.slice(i + 1).filter((item) => typeof item === "string") as string[]; - const blockedCheck = checkBlockedCommand(resolvedCmd, args, { blocked: config.blocked, threshold: config.threshold, diff --git a/src/parser/rules/RawThreatRule.ts b/src/parser/rules/RawThreatRule.ts index 1795d52..9f4d0a7 100644 --- a/src/parser/rules/RawThreatRule.ts +++ b/src/parser/rules/RawThreatRule.ts @@ -30,12 +30,12 @@ export class RawThreatRule implements SecurityRule { suggestion: "Avoid eval with remote content. Download and review the script first.", }, { - pattern: new RegExp(`(?:${this.interpreters.join("|")})\\s+(?:${this.commandFlags.join("|")})\\s+["']?\\$\\((curl|wget)\\b`, "i"), + pattern: new RegExp(String.raw`(?:${this.interpreters.join("|")})\s+(?:${this.commandFlags.join("|")})\s+["']?\$\((curl|wget)\b`, "i"), reason: "COMMAND SUBSTITUTION DETECTED", suggestion: "Executing remote scripts via command substitution is dangerous.", }, { - pattern: new RegExp(`(?:${this.interpreters.join("|")})\\s+(?:${this.commandFlags.join("|")})\\s+["']?\`(curl|wget)\\b`, "i"), + pattern: new RegExp(String.raw`(?:${this.interpreters.join("|")})\s+(?:${this.commandFlags.join("|")})\s+["']?\`(curl|wget)\b`, "i"), reason: "COMMAND SUBSTITUTION DETECTED", suggestion: "Executing remote scripts via command substitution is dangerous.", }, diff --git a/src/shell-context.ts b/src/shell-context.ts index 65f47ce..9b6b1ed 100644 --- a/src/shell-context.ts +++ b/src/shell-context.ts @@ -131,7 +131,8 @@ export function findBlockedTokenInShellContext( ): string | null { const haystack = `${entry.expansion ?? ""}\n${entry.output}`; for (const b of blocked) { - const re = new RegExp(`\\b${escapeRegExp(b)}\\b`, "i"); + const pattern = String.raw`\b${escapeRegExp(b)}\b`; + const re = new RegExp(pattern, "i"); if (re.test(haystack)) return b; } return null; diff --git a/tests/__snapshots__/cli_snapshot.test.ts.snap b/tests/__snapshots__/cli_snapshot.test.ts.snap index da7ebb9..6b97072 100644 --- a/tests/__snapshots__/cli_snapshot.test.ts.snap +++ b/tests/__snapshots__/cli_snapshot.test.ts.snap @@ -13,7 +13,7 @@ ShellShield - Keeping your terminal safe. `; exports[`CLI output snapshots permissive warning 1`] = ` -"āš ļø ShellShield WARNING: Command 'rm -rf /tmp/test' would be blocked in enforce mode. +"\\u26A0\\uFE0F ShellShield WARNING: Command 'rm -rf /tmp/test' would be blocked in enforce mode. Reason: Destructive command 'rm' detected Suggestion: trash /tmp/test " @@ -21,10 +21,7 @@ Suggestion: trash /tmp/test exports[`CLI output snapshots interactive approve/cancel messaging 1`] = ` "n -\x1B[1G\x1B[0J -āš ļø ShellShield ALERT: Destructive command 'rm' detected - Command: rm -rf /tmp/test - Are you sure you want to execute this? [y/N] \x1B[49Gn +\x1B[1G\x1B[0J\\n\\u26A0\\uFE0F ShellShield ALERT: Destructive command 'rm' detected\\n Command: rm -rf /tmp/test\\n Are you sure you want to execute this? [y/N] \x1B[149Gn \x1B[0m\x1B[31m \x1B[31mšŸ›”ļø \x1B[0mShellShield \x1B[31mBLOCKED\x1B[0m: Destructive command 'rm' detected @@ -39,11 +36,8 @@ exports[`CLI output snapshots interactive approve/cancel messaging 1`] = ` exports[`CLI output snapshots interactive approve/cancel messaging 2`] = ` "y -\x1B[1G\x1B[0J -āš ļø ShellShield ALERT: Destructive command 'rm' detected - Command: rm -rf /tmp/test - Are you sure you want to execute this? [y/N] \x1B[49Gy +\x1B[1G\x1B[0J\\n\\u26A0\\uFE0F ShellShield ALERT: Destructive command 'rm' detected\\n Command: rm -rf /tmp/test\\n Are you sure you want to execute this? [y/N] \x1B[149Gy -\x1B[0m\x1B[31m\x1B[32mApproved. Command will execute.\x1B[0m\x1B[0m +\x1B[0m\x1B[31m\\x1b[32mApproved. Command will execute.\\x1b[0m\x1B[0m " `; From 9a54e77339d11e3813f0d80b49846051e049d1ea Mon Sep 17 00:00:00 2001 From: Sisyphus Agent Date: Fri, 6 Feb 2026 16:33:24 -0300 Subject: [PATCH 2/2] refactor: improve portability, fix interactive prompt and polish code --- docs/install.sh | 11 +++--- src/cli.ts | 6 +-- src/parser/rules/CoreAstRule.ts | 8 ++-- tests/__snapshots__/cli_snapshot.test.ts.snap | 38 +++++++++++-------- 4 files changed, 34 insertions(+), 29 deletions(-) diff --git a/docs/install.sh b/docs/install.sh index a80bb7f..0157d1e 100644 --- a/docs/install.sh +++ b/docs/install.sh @@ -18,23 +18,22 @@ if [[ -t 1 ]]; then fi error() { - echo -e "${Red}error${Color_Off}:" "$@" >&2 + echo -e "${Red}error${Color_Off}:" "$*" >&2 exit 1 - return 1 } info() { - echo -e "${Dim}$@ ${Color_Off}" + echo -e "${Dim}$* ${Color_Off}" return 0 } info_bold() { - echo -e "${Bold_White}$@ ${Color_Off}" + echo -e "${Bold_White}$* ${Color_Off}" return 0 } success() { - echo -e "${Green}$@ ${Color_Off}" + echo -e "${Green}$* ${Color_Off}" return 0 } @@ -138,7 +137,7 @@ HOOK_BEGIN="# ShellShield Hook" HOOK_END="# ShellShield Hook End" HOOK_SCRIPT=" $HOOK_BEGIN -if [[ -f \"$HOME/.shellshield/src/index.ts\" ]]; then +if [ -f \"$HOME/.shellshield/src/index.ts\" ]; then eval \"\$(bun run \"$HOME/.shellshield/src/index.ts\" --init)\" fi $HOOK_END diff --git a/src/cli.ts b/src/cli.ts index b4c71d4..e6370cf 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -123,7 +123,7 @@ async function promptConfirmation(command: string, reason: string): Promise { - const promptMsg = String.raw`\nāš ļø ShellShield ALERT: ${reason}\n Command: ${command}\n Are you sure you want to execute this? [y/N] `; + const promptMsg = `\nāš ļø ShellShield ALERT: ${reason}\n Command: ${command}\n Are you sure you want to execute this? [y/N] `; rl.question( promptMsg, (answer) => { @@ -143,7 +143,7 @@ async function checkAndAuditCommand(command: string, config: any, source: "check } if (config?.mode === "permissive") { - const warningHeader = String.raw`āš ļø ShellShield WARNING: Command '${command}' would be blocked in enforce mode.`; + const warningHeader = `āš ļø ShellShield WARNING: Command '${command}' would be blocked in enforce mode.`; console.error( `${warningHeader}\n` + `Reason: ${result.reason}\n` + @@ -159,7 +159,7 @@ async function checkAndAuditCommand(command: string, config: any, source: "check logAudit(command, { ...result, blocked: false }, { source, mode: config?.mode, threshold: config?.threshold, decision: "approved" }); const msg = "Approved. Command will execute."; const tty = process.stderr.isTTY; - console.error(tty ? String.raw`\x1b[32m${msg}\x1b[0m` : msg); + console.error(tty ? `\x1b[32m${msg}\x1b[0m` : msg); return true; } } diff --git a/src/parser/rules/CoreAstRule.ts b/src/parser/rules/CoreAstRule.ts index 2be793c..e263553 100644 --- a/src/parser/rules/CoreAstRule.ts +++ b/src/parser/rules/CoreAstRule.ts @@ -81,14 +81,14 @@ export class CoreAstRule implements SecurityRule { } private resolveCmdName(entry: string, vars: Record): string { - const basenamePart = entry.split("/").pop() ?? ""; - const cmdName = entry.startsWith("\\") ? entry.slice(1) : basenamePart; + const stripped = entry.startsWith("\\") ? entry.slice(1) : entry; + const basenamePart = stripped.split("/").pop() ?? ""; - const resolvedVar = this.resolveVarToken(cmdName, vars); + const resolvedVar = this.resolveVarToken(basenamePart, vars); if (resolvedVar) { return resolvedVar.split("/").pop()?.toLowerCase() ?? ""; } - return cmdName.toLowerCase(); + return basenamePart.toLowerCase(); } private checkGitRm(normalizedEntry: string, tokens: ParsedEntry[], i: number): number { diff --git a/tests/__snapshots__/cli_snapshot.test.ts.snap b/tests/__snapshots__/cli_snapshot.test.ts.snap index 6b97072..e5e09c6 100644 --- a/tests/__snapshots__/cli_snapshot.test.ts.snap +++ b/tests/__snapshots__/cli_snapshot.test.ts.snap @@ -1,19 +1,7 @@ // Bun Snapshot v1, https://bun.sh/docs/test/snapshots -exports[`CLI output snapshots blocked message (enforce) 1`] = ` -" -šŸ›”ļø ShellShield BLOCKED: Destructive command 'rm' detected -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -ACTION REQUIRED: trash /tmp/test -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -Bypass: SHELLSHIELD_SKIP=1 -Hint: set SHELLSHIELD_MODE=interactive for quick prompts -ShellShield - Keeping your terminal safe. -" -`; - exports[`CLI output snapshots permissive warning 1`] = ` -"\\u26A0\\uFE0F ShellShield WARNING: Command 'rm -rf /tmp/test' would be blocked in enforce mode. +"āš ļø ShellShield WARNING: Command 'rm -rf /tmp/test' would be blocked in enforce mode. Reason: Destructive command 'rm' detected Suggestion: trash /tmp/test " @@ -21,7 +9,10 @@ Suggestion: trash /tmp/test exports[`CLI output snapshots interactive approve/cancel messaging 1`] = ` "n -\x1B[1G\x1B[0J\\n\\u26A0\\uFE0F ShellShield ALERT: Destructive command 'rm' detected\\n Command: rm -rf /tmp/test\\n Are you sure you want to execute this? [y/N] \x1B[149Gn +\x1B[1G\x1B[0J +āš ļø ShellShield ALERT: Destructive command 'rm' detected + Command: rm -rf /tmp/test + Are you sure you want to execute this? [y/N] \x1B[49Gn \x1B[0m\x1B[31m \x1B[31mšŸ›”ļø \x1B[0mShellShield \x1B[31mBLOCKED\x1B[0m: Destructive command 'rm' detected @@ -36,8 +27,23 @@ exports[`CLI output snapshots interactive approve/cancel messaging 1`] = ` exports[`CLI output snapshots interactive approve/cancel messaging 2`] = ` "y -\x1B[1G\x1B[0J\\n\\u26A0\\uFE0F ShellShield ALERT: Destructive command 'rm' detected\\n Command: rm -rf /tmp/test\\n Are you sure you want to execute this? [y/N] \x1B[149Gy +\x1B[1G\x1B[0J +āš ļø ShellShield ALERT: Destructive command 'rm' detected + Command: rm -rf /tmp/test + Are you sure you want to execute this? [y/N] \x1B[49Gy -\x1B[0m\x1B[31m\\x1b[32mApproved. Command will execute.\\x1b[0m\x1B[0m +\x1B[0m\x1B[31m\x1B[32mApproved. Command will execute.\x1B[0m\x1B[0m +" +`; + +exports[`CLI output snapshots blocked message (enforce) 1`] = ` +" +šŸ›”ļø ShellShield BLOCKED: Destructive command 'rm' detected +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +ACTION REQUIRED: trash /tmp/test +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Bypass: SHELLSHIELD_SKIP=1 +Hint: set SHELLSHIELD_MODE=interactive for quick prompts +ShellShield - Keeping your terminal safe. " `;