charmbracelet/crush · error

command is not allowed for security reasons: %q

Error message

command is not allowed for security reasons: %q

What it means

This is the command-blocking middleware built from blockFuncs in run.go. For each executed command it checks the argument list against every registered block function; if any returns true, the command is refused with "command is not allowed for security reasons: %q" before reaching the interpreter. It is an intentional policy denial, not an internal failure.

Source

Thrown at internal/shell/run.go:343

				return h(ctx, args, hc.Stdin, hc.Stdout, hc.Stderr)
			}
			return next(ctx, args)
		}
	}
}

// blockHandler returns middleware that rejects commands matched by any of
// the provided [BlockFunc]s before they reach the underlying exec path.
// A nil or empty blockFuncs slice is a no-op.
func blockHandler(blockFuncs []BlockFunc) execMiddleware {
	return func(next interp.ExecHandlerFunc) interp.ExecHandlerFunc {
		return func(ctx context.Context, args []string) error {
			if len(args) == 0 {
				return next(ctx, args)
			}
			for _, blockFunc := range blockFuncs {
				if blockFunc(args) {
					return fmt.Errorf("command is not allowed for security reasons: %q", args[0])
				}
			}
			return next(ctx, args)
		}
	}
}

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Use a command permitted by the project's security policy instead of the blocked one.
  2. If the block is too broad, adjust the BlockFuncs predicates to match only genuinely dangerous argument patterns.
  3. If the command is safe and required, add it to the allow-list/configure permissions so the blocking predicate no longer matches.
  4. Check args[0] in the message: it names the exact blocked command token.

Example fix

// before
block := func(args []string) bool { return args[0] == "rm" }
// after — allow safe variants
block := func(args []string) bool {
	return args[0] == "rm" && !slices.Contains(args, "-rf")
}
Defensive patterns

Strategy: try-catch

Validate before calling

func isBlocked(cmd string, blockFuncs []func([]string) bool) bool {
	args := strings.Fields(cmd)
	if len(args) == 0 { return false }
	for _, bf := range blockFuncs { if bf(args) { return true } }
	return false
}

Try / catch

err := shell.Run(ctx, opts)
if err != nil && strings.Contains(err.Error(), "not allowed for security reasons") {
	// treat as user-facing policy denial: show message, do not retry
}

Prevention

When it happens

Trigger: Calling shell.Run with RunOpts.BlockFuncs containing a predicate that matches the command (e.g. blocking 'rm', 'curl', or commands whose first arg is on a deny list). Empty argument lists fall through to next, so only non-empty commands can be blocked.

Common situations: Users of Crush running a command the project's permission policy disallows; tests (TestCommandBlocking, TestArgumentsBlocker, TestCommandsBlocker) verifying deny lists; misconfigured allow-lists that accidentally match benign commands like 'git push'.


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/9fdc27a22e2dc63f. Report an issue: GitHub.