Commit 66db87f667ab

Vincent Demeester <vincent@sbr.pm>
2026-02-16 10:08:36
fix: avoid false positives on quoted strings
Added stripQuotedContent helper to both guardrails and nix-guardrails extensions. Patterns now match against command text with quoted content removed, preventing false positives when dangerous keywords appear inside e.g. git commit messages.
1 parent 0bfe4ad
Changed files (2)
dots
pi
agent
extensions
guardrails
nix-guardrails
dots/pi/agent/extensions/guardrails/index.ts
@@ -88,6 +88,13 @@ const softProtectedPaths = [
 
 // ── Helpers ───────────────────────────────────────────────────
 
+/** Strip content inside quotes to avoid false positives on e.g. commit messages */
+function stripQuotedContent(command: string): string {
+	return command
+		.replace(/"(?:[^"\\]|\\.)*"/g, '""')
+		.replace(/'[^']*'/g, "''");
+}
+
 function expandHome(path: string): string {
 	if (path.startsWith("~/")) {
 		return join(homedir(), path.slice(2));
@@ -285,10 +292,11 @@ export default function (pi: ExtensionAPI) {
 		// ── Bash command security ──
 		if (toolName === "bash") {
 			const command = (event.input.command as string) || "";
+			const commandForMatching = stripQuotedContent(command);
 
 			// Check dangerous commands (confirm before allowing)
 			for (const { pattern, desc } of dangerousCommands) {
-				if (pattern.test(command)) {
+				if (pattern.test(commandForMatching)) {
 					if (!ctx.hasUI) {
 						return { block: true, reason: `Blocked ${desc} (no UI to confirm)` };
 					}
dots/pi/agent/extensions/nix-guardrails/index.ts
@@ -167,6 +167,15 @@ const DEFAULT_RULES: NixCommandRule[] = [
 	},
 ];
 
+// ── Helpers ───────────────────────────────────────────────────
+
+/** Strip content inside quotes to avoid false positives on e.g. commit messages */
+function stripQuotedContent(command: string): string {
+	return command
+		.replace(/"(?:[^"\\]|\\.)*"/g, '""')
+		.replace(/'[^']*'/g, "''");
+}
+
 // ── Extension entry point ─────────────────────────────────────
 
 export default function (pi: ExtensionAPI) {
@@ -234,12 +243,13 @@ export default function (pi: ExtensionAPI) {
 		}
 
 		const command = (event.input.command as string) || "";
+		const commandForMatching = stripQuotedContent(command);
 
 		// Check each rule
 		for (const rule of rules) {
 			if (!rule.enabled) continue;
 
-			if (rule.pattern.test(command)) {
+			if (rule.pattern.test(commandForMatching)) {
 				const theme = ctx.ui.theme;
 
 				// Handle different actions