permissionRuleParser.test.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. import { describe, expect, test } from "bun:test";
  2. import {
  3. escapeRuleContent,
  4. unescapeRuleContent,
  5. permissionRuleValueFromString,
  6. permissionRuleValueToString,
  7. normalizeLegacyToolName,
  8. } from "../permissionRuleParser";
  9. describe("escapeRuleContent", () => {
  10. test("escapes backslashes first", () => {
  11. expect(escapeRuleContent("a\\b")).toBe("a\\\\b");
  12. });
  13. test("escapes opening parentheses", () => {
  14. expect(escapeRuleContent("fn(x)")).toBe("fn\\(x\\)");
  15. });
  16. test("escapes backslash before parens correctly", () => {
  17. expect(escapeRuleContent('echo "test\\nvalue"')).toBe(
  18. 'echo "test\\\\nvalue"'
  19. );
  20. });
  21. test("returns unchanged string with no special chars", () => {
  22. expect(escapeRuleContent("npm install")).toBe("npm install");
  23. });
  24. test("handles empty string", () => {
  25. expect(escapeRuleContent("")).toBe("");
  26. });
  27. });
  28. describe("unescapeRuleContent", () => {
  29. test("unescapes parentheses", () => {
  30. expect(unescapeRuleContent("fn\\(x\\)")).toBe("fn(x)");
  31. });
  32. test("unescapes backslashes", () => {
  33. expect(unescapeRuleContent("a\\\\b")).toBe("a\\b");
  34. });
  35. test("roundtrips with escapeRuleContent", () => {
  36. const original = 'python -c "print(1)"';
  37. expect(unescapeRuleContent(escapeRuleContent(original))).toBe(original);
  38. });
  39. test("handles content with backslash-paren combo", () => {
  40. const original = 'echo "test\\nvalue"';
  41. expect(unescapeRuleContent(escapeRuleContent(original))).toBe(original);
  42. });
  43. test("returns unchanged string with no escapes", () => {
  44. expect(unescapeRuleContent("npm install")).toBe("npm install");
  45. });
  46. });
  47. describe("permissionRuleValueFromString", () => {
  48. test("parses tool name only", () => {
  49. expect(permissionRuleValueFromString("Bash")).toEqual({
  50. toolName: "Bash",
  51. });
  52. });
  53. test("parses tool name with content", () => {
  54. expect(permissionRuleValueFromString("Bash(npm install)")).toEqual({
  55. toolName: "Bash",
  56. ruleContent: "npm install",
  57. });
  58. });
  59. test("handles escaped parens in content", () => {
  60. const result = permissionRuleValueFromString(
  61. 'Bash(python -c "print\\(1\\)")'
  62. );
  63. expect(result.toolName).toBe("Bash");
  64. expect(result.ruleContent).toBe('python -c "print(1)"');
  65. });
  66. test("treats empty content as tool-wide rule", () => {
  67. expect(permissionRuleValueFromString("Bash()")).toEqual({
  68. toolName: "Bash",
  69. });
  70. });
  71. test("treats wildcard content as tool-wide rule", () => {
  72. expect(permissionRuleValueFromString("Bash(*)")).toEqual({
  73. toolName: "Bash",
  74. });
  75. });
  76. test("normalizes legacy tool names", () => {
  77. const result = permissionRuleValueFromString("Task");
  78. expect(result.toolName).toBe("Agent");
  79. });
  80. test("handles MCP-style tool names", () => {
  81. expect(permissionRuleValueFromString("mcp__server__tool")).toEqual({
  82. toolName: "mcp__server__tool",
  83. });
  84. });
  85. });
  86. describe("permissionRuleValueToString", () => {
  87. test("formats tool name only", () => {
  88. expect(permissionRuleValueToString({ toolName: "Bash" })).toBe("Bash");
  89. });
  90. test("formats tool name with content", () => {
  91. expect(
  92. permissionRuleValueToString({
  93. toolName: "Bash",
  94. ruleContent: "npm install",
  95. })
  96. ).toBe("Bash(npm install)");
  97. });
  98. test("escapes parens in content", () => {
  99. expect(
  100. permissionRuleValueToString({
  101. toolName: "Bash",
  102. ruleContent: 'python -c "print(1)"',
  103. })
  104. ).toBe('Bash(python -c "print\\(1\\)")');
  105. });
  106. test("roundtrips with permissionRuleValueFromString", () => {
  107. const original = { toolName: "Bash", ruleContent: "npm install" };
  108. const str = permissionRuleValueToString(original);
  109. const parsed = permissionRuleValueFromString(str);
  110. expect(parsed).toEqual(original);
  111. });
  112. });
  113. describe("normalizeLegacyToolName", () => {
  114. test("maps Task to Agent", () => {
  115. expect(normalizeLegacyToolName("Task")).toBe("Agent");
  116. });
  117. test("maps KillShell to TaskStop", () => {
  118. expect(normalizeLegacyToolName("KillShell")).toBe("TaskStop");
  119. });
  120. test("returns unknown name as-is", () => {
  121. expect(normalizeLegacyToolName("UnknownTool")).toBe("UnknownTool");
  122. });
  123. test("preserves current canonical names", () => {
  124. expect(normalizeLegacyToolName("Bash")).toBe("Bash");
  125. expect(normalizeLegacyToolName("Agent")).toBe("Agent");
  126. });
  127. });