From 837f0abd61943864266c5bc0ec0eb79d58337366 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Feb 2026 10:37:58 +0000 Subject: [PATCH 1/4] Initial plan From 93c02fa54b2a5f0750c52a9cac5ba1c19c6ce869 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Feb 2026 10:41:03 +0000 Subject: [PATCH 2/4] Add guardrails feature to prevent dangerous shell commands Co-authored-by: joone <1979160+joone@users.noreply.github.com> --- src/guardrails/index.ts | 33 ++++++++++++++++++ src/loz.ts | 3 ++ test/guardrails.test.ts | 75 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 src/guardrails/index.ts create mode 100644 test/guardrails.test.ts diff --git a/src/guardrails/index.ts b/src/guardrails/index.ts new file mode 100644 index 0000000..ac01ee9 --- /dev/null +++ b/src/guardrails/index.ts @@ -0,0 +1,33 @@ +// List of dangerous commands that should be blocked +const DENYLIST = [ + "rm -rf /", + "shutdown", + "reboot", + ":(){ :|:& };:", + "mkfs", + "dd if=", +]; + +export interface CommandAction { + cmd: string; +} + +/** + * Enforces guardrails to prevent execution of dangerous shell commands + * @param command The command to check + * @param allowShell Whether shell commands are allowed + * @throws Error if the command is blocked by guardrails + */ +export function enforceGuardrails(command: string, allowShell: boolean): void { + if (!allowShell) { + throw new Error("Shell commands are disabled by configuration."); + } + + const lower = command.toLowerCase(); + const blocked = DENYLIST.find((token) => + lower.includes(token.toLowerCase()), + ); + if (blocked) { + throw new Error(`Command blocked by guardrails: ${blocked}`); + } +} diff --git a/src/loz.ts b/src/loz.ts index ed03ee0..05afa8d 100644 --- a/src/loz.ts +++ b/src/loz.ts @@ -15,6 +15,7 @@ import { requestApiKey, } from "./config"; import { Git } from "./git"; +import { enforceGuardrails } from "./guardrails"; // Get the path to the home directory const HOME_PATH = os.homedir() || ""; @@ -563,6 +564,8 @@ export class Loz { if (answer.toLowerCase() === "y") { for (const cmd of commands) { try { + // Enforce guardrails to prevent dangerous commands + enforceGuardrails(cmd, true); await runCommand(cmd); } catch (error: any) { if (typeof error === "string" && error.indexOf("No output") === 0) { diff --git a/test/guardrails.test.ts b/test/guardrails.test.ts new file mode 100644 index 0000000..8b15c69 --- /dev/null +++ b/test/guardrails.test.ts @@ -0,0 +1,75 @@ +import { expect } from "chai"; +import "mocha"; +import { enforceGuardrails } from "../src/guardrails"; + +describe("Guardrails Test", () => { + describe("enforceGuardrails", () => { + it("should throw error when shell is disabled", () => { + expect(() => enforceGuardrails("ls -la", false)).to.throw( + "Shell commands are disabled by configuration.", + ); + }); + + it("should allow safe commands", () => { + expect(() => enforceGuardrails("ls -la", true)).to.not.throw(); + expect(() => enforceGuardrails("cd /tmp", true)).to.not.throw(); + expect(() => enforceGuardrails("echo hello", true)).to.not.throw(); + expect(() => enforceGuardrails("cat file.txt", true)).to.not.throw(); + }); + + it("should block rm -rf /", () => { + expect(() => enforceGuardrails("rm -rf /", true)).to.throw( + "Command blocked by guardrails: rm -rf /", + ); + expect(() => enforceGuardrails("RM -RF /", true)).to.throw( + "Command blocked by guardrails: rm -rf /", + ); + }); + + it("should block shutdown", () => { + expect(() => enforceGuardrails("shutdown now", true)).to.throw( + "Command blocked by guardrails: shutdown", + ); + expect(() => enforceGuardrails("SHUTDOWN", true)).to.throw( + "Command blocked by guardrails: shutdown", + ); + }); + + it("should block reboot", () => { + expect(() => enforceGuardrails("reboot", true)).to.throw( + "Command blocked by guardrails: reboot", + ); + expect(() => enforceGuardrails("sudo reboot", true)).to.throw( + "Command blocked by guardrails: reboot", + ); + }); + + it("should block fork bomb", () => { + expect(() => enforceGuardrails(":(){ :|:& };:", true)).to.throw( + "Command blocked by guardrails: :(){ :|:& };:", + ); + }); + + it("should block mkfs", () => { + expect(() => enforceGuardrails("mkfs /dev/sda", true)).to.throw( + "Command blocked by guardrails: mkfs", + ); + expect(() => enforceGuardrails("mkfs.ext4 /dev/sda1", true)).to.throw( + "Command blocked by guardrails: mkfs", + ); + }); + + it("should block dd if=", () => { + expect(() => + enforceGuardrails("dd if=/dev/zero of=/dev/sda", true), + ).to.throw("Command blocked by guardrails: dd if="); + }); + + it("should allow commands with similar but safe patterns", () => { + // These should be allowed as they don't match the blocked patterns + expect(() => enforceGuardrails("rm file.txt", true)).to.not.throw(); + expect(() => enforceGuardrails("rm -r /tmp/test", true)).to.not.throw(); + expect(() => enforceGuardrails("dd status=progress", true)).to.not.throw(); + }); + }); +}); From 62d6a07ee5dccf5568a6c7f8c1afdf4ef35c1643 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Feb 2026 10:43:33 +0000 Subject: [PATCH 3/4] Improve guardrails with bypass protection and optimizations Co-authored-by: joone <1979160+joone@users.noreply.github.com> --- src/guardrails/index.ts | 20 ++++++++++++-------- test/guardrails.test.ts | 9 +++++++++ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/guardrails/index.ts b/src/guardrails/index.ts index ac01ee9..c715838 100644 --- a/src/guardrails/index.ts +++ b/src/guardrails/index.ts @@ -1,6 +1,9 @@ -// List of dangerous commands that should be blocked +// List of dangerous command patterns that should be blocked +// Patterns are case-insensitive and stored in lowercase for efficiency const DENYLIST = [ "rm -rf /", + "rm -rf/*", + "rm -rf /.", "shutdown", "reboot", ":(){ :|:& };:", @@ -8,9 +11,8 @@ const DENYLIST = [ "dd if=", ]; -export interface CommandAction { - cmd: string; -} +// Pre-computed lowercase versions for efficient checking +const DENYLIST_LOWER = DENYLIST.map((token) => token.toLowerCase()); /** * Enforces guardrails to prevent execution of dangerous shell commands @@ -24,10 +26,12 @@ export function enforceGuardrails(command: string, allowShell: boolean): void { } const lower = command.toLowerCase(); - const blocked = DENYLIST.find((token) => - lower.includes(token.toLowerCase()), + const blockedIndex = DENYLIST_LOWER.findIndex((token) => + lower.includes(token), ); - if (blocked) { - throw new Error(`Command blocked by guardrails: ${blocked}`); + if (blockedIndex !== -1) { + throw new Error( + `Command blocked by guardrails: ${DENYLIST[blockedIndex]}`, + ); } } diff --git a/test/guardrails.test.ts b/test/guardrails.test.ts index 8b15c69..251f1c9 100644 --- a/test/guardrails.test.ts +++ b/test/guardrails.test.ts @@ -26,6 +26,15 @@ describe("Guardrails Test", () => { ); }); + it("should block rm -rf / bypass attempts", () => { + expect(() => enforceGuardrails("rm -rf/*", true)).to.throw( + "Command blocked by guardrails", + ); + expect(() => enforceGuardrails("rm -rf /.", true)).to.throw( + "Command blocked by guardrails", + ); + }); + it("should block shutdown", () => { expect(() => enforceGuardrails("shutdown now", true)).to.throw( "Command blocked by guardrails: shutdown", From 34ccef276a8d21fb6eb400c1050a6b8f2693b132 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Feb 2026 10:45:11 +0000 Subject: [PATCH 4/4] Add documentation comments for future improvements Co-authored-by: joone <1979160+joone@users.noreply.github.com> --- src/guardrails/index.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/guardrails/index.ts b/src/guardrails/index.ts index c715838..89b6575 100644 --- a/src/guardrails/index.ts +++ b/src/guardrails/index.ts @@ -1,5 +1,12 @@ // List of dangerous command patterns that should be blocked // Patterns are case-insensitive and stored in lowercase for efficiency +// +// NOTE: This is a basic substring-based approach that provides a first line of defense. +// Future improvements could include: +// - More comprehensive pattern list (sudo variants, different spacing, etc.) +// - Word boundary checking to avoid false positives on filenames +// - Command structure parsing to identify actual operations +// - Allowlist approach for additional security const DENYLIST = [ "rm -rf /", "rm -rf/*",