zeroclaw-labs/zeroclaw · error

Blocked potentially dangerous git argument: {arg}

Error message

Blocked potentially dangerous git argument: {arg}

What it means

sanitize_git_args rejected one of the whitespace-split arguments to a git tool call (git_diff, git_add, git_checkout, git_worktree) because it matched a blocklist of command-injection vectors: options that execute external programs (--exec=, --upload-pack=, --receive-pack=), TTY-coupled options (--pager=, --editor=, --no-verify), shell substitution tokens '$(' and backtick, or the metacharacters '|', ';', '>' (git_operations.rs:30-42). The whole request fails before any git subprocess spawns.

Source

Thrown at crates/zeroclaw-tools/src/git_operations.rs:42

    /// Sanitize git arguments to prevent injection attacks
    fn sanitize_git_args(&self, args: &str) -> anyhow::Result<Vec<String>> {
        let mut result = Vec::new();
        for arg in args.split_whitespace() {
            // Block dangerous git options that could lead to command injection
            let arg_lower = arg.to_lowercase();
            if arg_lower.starts_with("--exec=")
                || arg_lower.starts_with("--upload-pack=")
                || arg_lower.starts_with("--receive-pack=")
                || arg_lower.starts_with("--pager=")
                || arg_lower.starts_with("--editor=")
                || arg_lower == "--no-verify"
                || arg_lower.contains("$(")
                || arg_lower.contains('`')
                || arg.contains('|')
                || arg.contains(';')
                || arg.contains('>')
            {
                anyhow::bail!("Blocked potentially dangerous git argument: {arg}");
            }
            // Block `-c` config injection (exact match or `-c=...` prefix).
            // This must not false-positive on `--cached` or `-cached`.
            if arg_lower == "-c" || arg_lower.starts_with("-c=") {
                anyhow::bail!("Blocked potentially dangerous git argument: {arg}");
            }
            result.push(arg.to_string());
        }
        Ok(result)
    }

    /// Check if an operation requires write access
    fn requires_write_access(&self, operation: &str) -> bool {
        matches!(
            operation,
            "commit" | "add" | "checkout" | "stash" | "reset" | "revert" | "worktree"
        )
    }

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Remove the offending option or metacharacter from the args string and re-issue the call.
  2. If you need to bypass hooks (--no-verify) or set a pager, run that git command via a shell exec tool under its own policy instead of this one.
  3. Rename branches/paths that legitimately contain ';', '|', '>', '$(' before passing them to the tool.
  4. Mirror the same blocklist in the caller's pre-validation so rejection happens client-side with better context.

Example fix

// before
git_checkout(args: "checkout feature;rm -rf .cache")
// -> Blocked potentially dangerous git argument: feature;rm -rf .cache

// after
git_checkout(args: "checkout feature")
Defensive patterns

Strategy: validation

Validate before calling

// Mirror the tool's blocklist before sending args.
fn is_safe_git_arg(arg: &str) -> bool {
    let l = arg.to_lowercase();
    !(l.starts_with("--exec=") || l.starts_with("--upload-pack=")
        || l.starts_with("--receive-pack=") || l.starts_with("--pager=")
        || l.starts_with("--editor=") || l == "--no-verify"
        || l.contains("$(") || l.contains('`')
        || arg.contains('|') || arg.contains(';') || arg.contains('>'))
}
fn validate_git_args(args: &str) -> Result<(), String> {
    match args.split_whitespace().find(|a| !is_safe_git_arg(a)) {
        Some(bad) => Err(format!("blocked by policy: {bad}")),
        None => Ok(()),
    }
}

Type guard

fn is_safe_git_arg(arg: &str) -> bool {
    let l = arg.to_lowercase();
    !(l.starts_with("--exec=") || l.starts_with("--upload-pack=")
        || l.starts_with("--receive-pack=") || l.starts_with("--pager=")
        || l.starts_with("--editor=") || l == "--no-verify"
        || l.contains("$(") || l.contains('`')
        || arg.contains('|') || arg.contains(';') || arg.contains('>'))
}

Try / catch

match git_tool.execute(params).await {
    Err(e) if e.to_string().contains("Blocked potentially dangerous git argument") => {
        // extract the blocked token, surface it to the caller/LLM, and
        // re-issue the command without it; never retry the same string
    }
    r => r,
}

Prevention

When it happens

Trigger: Passing args strings like "show HEAD --pager=cat", "commit --no-verify", "log --exec=/bin/sh", or any token containing ';', '|', '>' or '$(' — including a file path or ref name that happens to embed one of those characters (e.g. a branch named 'feat;x'). The for-loop at git_operations.rs:27 checks every token from args.split_whitespace().

Common situations: LLM-driven agents forwarding shell-style habits into the structured git tool; users trying to skip hooks with --no-verify; scripts concatenating untrusted input into the args string; branch or path names containing semicolons or pipes; copy-pasted one-liners where a pipe was part of the original command.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/1449336bf13a3bb5. Report an issue: GitHub.