Skip to content

Commit b7e7388

Browse files
committed
Add direct coverage for isSingleShellCommand's comment-handling edges
grantScopeMatches consolidation's sibling PR fixed a live disagreement on comment-trailing chains, but the shared realShellSegments predicate also silently narrows pure-comment input from single-command to not-a-command, and treats a leading-comment-then-chain by its trailing real segment only. Neither was covered by a test, so a future segmenting change could flip either back with nothing catching it.
1 parent 1f16d50 commit b7e7388

1 file changed

Lines changed: 21 additions & 1 deletion

File tree

src/permission/permission.test.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
} from "./command.js";
1515
import { matchesPattern, escapeGlobLiteral } from "./matcher.js";
1616
import { evaluateApprovals } from "./authz-grants.js";
17-
import { classifyTool, buildRequests, isAutoAllowedShellCall } from "./classify.js";
17+
import { classifyTool, buildRequests, isAutoAllowedShellCall, isSingleShellCommand } from "./classify.js";
1818
import { createPermissionGate } from "./gate.js";
1919
import { createMcpToolPermissionRegistry, registerMcpClientTools } from "../mcp/tool-permissions.js";
2020
import { listWorktreeRoots, createWorktreeRootsProvider } from "./worktree-roots.js";
@@ -2025,6 +2025,26 @@ describe("preApprove", () => {
20252025

20262026
expect(preApproveTreatsAsSingle).toBe(scopeLadderTreatsAsSingle);
20272027
});
2028+
2029+
test("isSingleShellCommand narrows a pure-comment command to false", () => {
2030+
// Before the shared realShellSegments predicate, gate.ts's own
2031+
// isSingleShellCommand did not filter comment-only segments, so a
2032+
// pure-comment "command" like "# just a comment" counted as one real
2033+
// segment and was treated as single. The shared predicate filters it
2034+
// out, leaving zero segments, so this must now be false.
2035+
expect(isSingleShellCommand("# just a comment")).toBe(false);
2036+
});
2037+
2038+
test("isSingleShellCommand treats a leading-comment-then-chain as its trailing real segment", () => {
2039+
// splitChainedCommand splits on "&&" before recognizing that "#" extends
2040+
// a comment to end of line, so "# a && b" splits into ["# a", "b"] even
2041+
// though a real shell treats the whole line as one comment (nothing
2042+
// after "#" ever runs). Filtering the comment-only "# a" segment leaves
2043+
// exactly one real segment, "b", so this is scored as a single command —
2044+
// matching shellApprovalScopes' existing behavior, not a regression
2045+
// introduced here.
2046+
expect(isSingleShellCommand("# a && b")).toBe(true);
2047+
});
20282048
});
20292049

20302050
describe("isAutoAllowedShellCall", () => {

0 commit comments

Comments
 (0)