@@ -4,6 +4,8 @@ import (
8+ "github.com/stretchr/testify/require"
9func TestCommandBlocking(t *testing.T) {
11func TestCommandBlocking(t *testing.T) {
@@ -56,10 +58,7 @@ func TestCommandBlocking(t *testing.T) {
57 name: "block npm global install with -g",
59 name: "block npm global install with -g",
58 blockFuncs: []BlockFunc{
60 blockFuncs: []BlockFunc{
59− ArgumentsBlocker([][]string{
61+ ArgumentsBlocker("npm", []string{"install"}, []string{"-g"}),
60− {"npm", "install", "-g"},
61− {"npm", "install", "--global"},
64 command: "npm install -g typescript",
63 command: "npm install -g typescript",
65 shouldBlock: true,
64 shouldBlock: true,
@@ -67,10 +66,7 @@ func TestCommandBlocking(t *testing.T) {
68 name: "block npm global install with --global",
67 name: "block npm global install with --global",
69 blockFuncs: []BlockFunc{
68 blockFuncs: []BlockFunc{
70− ArgumentsBlocker([][]string{
69+ ArgumentsBlocker("npm", []string{"install"}, []string{"--global"}),
71− {"npm", "install", "-g"},
72− {"npm", "install", "--global"},
75 command: "npm install --global typescript",
71 command: "npm install --global typescript",
76 shouldBlock: true,
72 shouldBlock: true,
@@ -78,10 +74,8 @@ func TestCommandBlocking(t *testing.T) {
79 name: "allow npm local install",
75 name: "allow npm local install",
80 blockFuncs: []BlockFunc{
76 blockFuncs: []BlockFunc{
81− ArgumentsBlocker([][]string{
77+ ArgumentsBlocker("npm", []string{"install"}, []string{"-g"}),
82− {"npm", "install", "-g"},
83− {"npm", "install", "--global"},
78+ ArgumentsBlocker("npm", []string{"install"}, []string{"--global"}),
86 command: "npm install typescript",
80 command: "npm install typescript",
87 shouldBlock: false,
81 shouldBlock: false,
@@ -116,3 +110,232 @@ func TestCommandBlocking(t *testing.T) {
114+func TestArgumentsBlocker(t *testing.T) {
123+ // Basic command blocking
125+ name: "block exact command match",
127+ args: []string{"install"},
129+ input: []string{"npm", "install", "package"},
133+ name: "allow different command",
135+ args: []string{"install"},
137+ input: []string{"yarn", "install", "package"},
141+ name: "allow different subcommand",
143+ args: []string{"install"},
145+ input: []string{"npm", "list"},
149+ // Flag-based blocking
151+ name: "block with single flag",
153+ args: []string{"install"},
154+ flags: []string{"-g"},
155+ input: []string{"npm", "install", "-g", "typescript"},
159+ name: "block with flag in different position",
161+ args: []string{"install"},
162+ flags: []string{"-g"},
163+ input: []string{"npm", "install", "typescript", "-g"},
167+ name: "allow without required flag",
169+ args: []string{"install"},
170+ flags: []string{"-g"},
171+ input: []string{"npm", "install", "typescript"},
175+ name: "block with multiple flags",
177+ args: []string{"install"},
178+ flags: []string{"--user"},
179+ input: []string{"pip", "install", "--user", "--upgrade", "package"},
183+ // Complex argument patterns
185+ name: "block multi-arg subcommand",
187+ args: []string{"global", "add"},
189+ input: []string{"yarn", "global", "add", "typescript"},
193+ name: "allow partial multi-arg match",
195+ args: []string{"global", "add"},
197+ input: []string{"yarn", "global", "list"},
203+ name: "handle empty input",
205+ args: []string{"install"},
211+ name: "handle command only",
213+ args: []string{"install"},
215+ input: []string{"npm"},
219+ name: "block pacman with -S flag",
222+ flags: []string{"-S"},
223+ input: []string{"pacman", "-S", "package"},
227+ name: "allow pacman without -S flag",
230+ flags: []string{"-S"},
231+ input: []string{"pacman", "-Q", "package"},
236+ for _, tt := range tests {
237+ t.Run(tt.name, func(t *testing.T) {
238+ blocker := ArgumentsBlocker(tt.cmd, tt.args, tt.flags)
239+ result := blocker(tt.input)
240+ require.Equal(t, tt.shouldBlock, result,
241+ "Expected block=%v for input %v", tt.shouldBlock, tt.input)
246+func TestCommandsBlocker(t *testing.T) {
254+ name: "block single banned command",
255+ banned: []string{"curl"},
256+ input: []string{"curl", "https://example.com"},
260+ name: "allow non-banned command",
261+ banned: []string{"curl", "wget"},
262+ input: []string{"echo", "hello"},
266+ name: "block from multiple banned",
267+ banned: []string{"curl", "wget", "nc"},
268+ input: []string{"wget", "https://example.com"},
272+ name: "handle empty input",
273+ banned: []string{"curl"},
278+ name: "case sensitive matching",
279+ banned: []string{"curl"},
280+ input: []string{"CURL", "https://example.com"},
285+ for _, tt := range tests {
286+ t.Run(tt.name, func(t *testing.T) {
287+ blocker := CommandsBlocker(tt.banned)
288+ result := blocker(tt.input)
289+ require.Equal(t, tt.shouldBlock, result,
290+ "Expected block=%v for input %v", tt.shouldBlock, tt.input)
295+func TestSplitArgsFlags(t *testing.T) {
304+ input: []string{"install", "package", "another"},
305+ wantArgs: []string{"install", "package", "another"},
306+ wantFlags: []string{},
310+ input: []string{"-g", "--verbose", "-f"},
311+ wantArgs: []string{},
312+ wantFlags: []string{"-g", "--verbose", "-f"},
315+ name: "mixed args and flags",
316+ input: []string{"install", "-g", "package", "--verbose"},
317+ wantArgs: []string{"install", "package"},
318+ wantFlags: []string{"-g", "--verbose"},
321+ name: "empty input",
323+ wantArgs: []string{},
324+ wantFlags: []string{},
327+ name: "single dash flag",
328+ input: []string{"-S", "package"},
329+ wantArgs: []string{"package"},
330+ wantFlags: []string{"-S"},
334+ for _, tt := range tests {
335+ t.Run(tt.name, func(t *testing.T) {
336+ args, flags := splitArgsFlags(tt.input)
337+ require.Equal(t, tt.wantArgs, args, "args mismatch")
338+ require.Equal(t, tt.wantFlags, flags, "flags mismatch")