command_block_test.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. package shell
  2. import (
  3. "context"
  4. "os"
  5. "strings"
  6. "testing"
  7. )
  8. func TestCommandBlocking(t *testing.T) {
  9. tests := []struct {
  10. name string
  11. blockFuncs []BlockFunc
  12. command string
  13. shouldBlock bool
  14. }{
  15. {
  16. name: "block simple command",
  17. blockFuncs: []BlockFunc{
  18. func(args []string) bool {
  19. return len(args) > 0 && args[0] == "curl"
  20. },
  21. },
  22. command: "curl https://example.com",
  23. shouldBlock: true,
  24. },
  25. {
  26. name: "allow non-blocked command",
  27. blockFuncs: []BlockFunc{
  28. func(args []string) bool {
  29. return len(args) > 0 && args[0] == "curl"
  30. },
  31. },
  32. command: "echo hello",
  33. shouldBlock: false,
  34. },
  35. {
  36. name: "block subcommand",
  37. blockFuncs: []BlockFunc{
  38. func(args []string) bool {
  39. return len(args) >= 2 && args[0] == "brew" && args[1] == "install"
  40. },
  41. },
  42. command: "brew install wget",
  43. shouldBlock: true,
  44. },
  45. {
  46. name: "allow different subcommand",
  47. blockFuncs: []BlockFunc{
  48. func(args []string) bool {
  49. return len(args) >= 2 && args[0] == "brew" && args[1] == "install"
  50. },
  51. },
  52. command: "brew list",
  53. shouldBlock: false,
  54. },
  55. {
  56. name: "block npm global install with -g",
  57. blockFuncs: []BlockFunc{
  58. ArgumentsBlocker([][]string{
  59. {"npm", "install", "-g"},
  60. {"npm", "install", "--global"},
  61. }),
  62. },
  63. command: "npm install -g typescript",
  64. shouldBlock: true,
  65. },
  66. {
  67. name: "block npm global install with --global",
  68. blockFuncs: []BlockFunc{
  69. ArgumentsBlocker([][]string{
  70. {"npm", "install", "-g"},
  71. {"npm", "install", "--global"},
  72. }),
  73. },
  74. command: "npm install --global typescript",
  75. shouldBlock: true,
  76. },
  77. {
  78. name: "allow npm local install",
  79. blockFuncs: []BlockFunc{
  80. ArgumentsBlocker([][]string{
  81. {"npm", "install", "-g"},
  82. {"npm", "install", "--global"},
  83. }),
  84. },
  85. command: "npm install typescript",
  86. shouldBlock: false,
  87. },
  88. }
  89. for _, tt := range tests {
  90. t.Run(tt.name, func(t *testing.T) {
  91. // Create a temporary directory for each test
  92. tmpDir, err := os.MkdirTemp("", "shell-test-*")
  93. if err != nil {
  94. t.Fatalf("Failed to create temp dir: %v", err)
  95. }
  96. defer os.RemoveAll(tmpDir)
  97. shell := NewShell(&Options{
  98. WorkingDir: tmpDir,
  99. BlockFuncs: tt.blockFuncs,
  100. })
  101. _, _, err = shell.Exec(context.Background(), tt.command)
  102. if tt.shouldBlock {
  103. if err == nil {
  104. t.Errorf("Expected command to be blocked, but it was allowed")
  105. } else if !strings.Contains(err.Error(), "not allowed for security reasons") {
  106. t.Errorf("Expected security error, got: %v", err)
  107. }
  108. } else {
  109. // For non-blocked commands, we might get other errors (like command not found)
  110. // but we shouldn't get the security error
  111. if err != nil && strings.Contains(err.Error(), "not allowed for security reasons") {
  112. t.Errorf("Command was unexpectedly blocked: %v", err)
  113. }
  114. }
  115. })
  116. }
  117. }