Skip to main content
Glama

vet_command

Vet a shell command for destructive patterns before execution. Returns verdict (CLEAN/CAUTION/etc.) and risk score (0-100) to prevent data loss.

Instructions

Vet a single shell command for destructive patterns BEFORE execution. Detects rm -rf nested in chains, package-manager glob removal (apt remove 'nvidia'), dd/mkfs/wipefs filesystem destruction, chmod 777 on system paths, curl|bash network-exfil, chained shutdown/reboot, git destructive ops (push --force, reset --hard), and DROP DATABASE / TRUNCATE via cli. Returns verdict (CLEAN / CAUTION / REVIEW / BLOCK / UNVERIFIED), risk_score (0-100), and per-finding rule_id + severity + recommendation. Sub-second, local, no API key. Use inline before approving any agent-proposed command.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
commandYesThe shell command to vet (single command or pipeline)

Implementation Reference

  • The 'vet_command' tool is registered in the `list_tools` handler with its name, description, and inputSchema (accepts a 'command' string).
    return [
        Tool(
            name="vet_command",
            description=(
                "Vet a single shell command for destructive patterns BEFORE execution. "
                "Detects rm -rf nested in chains, package-manager glob removal "
                "(apt remove '*nvidia*'), dd/mkfs/wipefs filesystem destruction, "
                "chmod 777 on system paths, curl|bash network-exfil, chained "
                "shutdown/reboot, git destructive ops (push --force, reset --hard), "
                "and DROP DATABASE / TRUNCATE via cli. Returns verdict (CLEAN / "
                "CAUTION / REVIEW / BLOCK / UNVERIFIED), risk_score (0-100), and "
                "per-finding rule_id + severity + recommendation. Sub-second, local, "
                "no API key. Use inline before approving any agent-proposed command."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "command": {
                        "type": "string",
                        "description": "The shell command to vet (single command or pipeline)",
                    },
                },
                "required": ["command"],
            },
        ),
        Tool(
            name="vet_command_chain",
            description=(
                "Vet a chained / multi-statement shell command — same rules as "
                "`vet_command`, but escalates LOW→MEDIUM and MEDIUM→HIGH because "
                "destructive fragments nested deep inside a chain (after `&&`, `;`, "
                "or `|`) are easier for the operator to overlook on a quick read. "
                "Use this for any command containing &&, ||, ;, or piped subshells. "
                "The exact failure mode this targets: r/LocalLLaMA 'one bash "
                "permission slipped' (1.5k upvotes) — agent proposed a chained "
                "command, operator pattern-matched the lede, missed `rm -rf` deep "
                "in the chain."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "command": {
                        "type": "string",
                        "description": "The chained shell command to vet",
                    },
                },
                "required": ["command"],
            },
        ),
        Tool(
            name="list_detection_rules",
            description=(
                "Return the catalog of every detection rule the scanner applies — "
                "rule_id, severity, pattern_kind, description, example_match. "
                "Use this to audit coverage, document detection scope to your "
                "compliance/security team, or build a custom allowlist. 30 rules "
                "across 8 families: DESTRUCTIVE / PACKAGE / PRIVILEGED / SHUTDOWN "
                "/ EXFIL / DATABASE / GIT / SUSPICIOUS."
            ),
            inputSchema={
                "type": "object",
                "properties": {},
            },
        ),
    ]
  • The `vet_command` function is the core implementation — it parses the command, runs regex-based detection rules, optionally escalates severity for chained commands, computes risk score and verdict, and returns a CommandVetReport.
    def vet_command(command: str, *, command_chain: bool = False) -> CommandVetReport:
        """Scan a shell command for destructive patterns. Returns a CommandVetReport.
    
        `command_chain=True` raises severity by one level for chained commands
        (because nested destructive fragments are easier to overlook on quick read).
        """
        if not command.strip():
            return CommandVetReport(
                verdict=Verdict.UNVERIFIED,
                risk_score=0,
                finding_count=0,
                findings=[],
                summary="No command provided.",
                parse_error=None,
            )
    
        parsed_ok, parse_error = _try_bashlex_parse(command)
    
        findings = _scan_with_regex(command)
    
        # If chain mode + we have findings, escalate any LOW/MEDIUM by one tier (because
        # nested patterns in chained commands are easier to overlook on quick read).
        if command_chain and findings:
            escalated: list[CommandFinding] = []
            for f in findings:
                if f.severity == Severity.LOW:
                    escalated.append(f.model_copy(update={"severity": Severity.MEDIUM}))
                elif f.severity == Severity.MEDIUM:
                    escalated.append(f.model_copy(update={"severity": Severity.HIGH}))
                else:
                    escalated.append(f)
            findings = escalated
    
        # Sort by severity desc, then position asc
        severity_rank = {Severity.CRITICAL: 4, Severity.HIGH: 3, Severity.MEDIUM: 2, Severity.LOW: 1, Severity.INFO: 0}
        findings.sort(key=lambda f: (-severity_rank[f.severity], f.position or 0))
    
        score = _risk_score(findings)
        verdict = _verdict_from_findings(findings)
    
        if not parsed_ok and not findings:
            # Can't parse + nothing matched regex — be honest
            return CommandVetReport(
                verdict=Verdict.UNVERIFIED,
                risk_score=0,
                finding_count=0,
                findings=[],
                summary="Could not parse the input as bash; no regex rules matched either. Inspect manually.",
                parse_error=parse_error,
            )
    
        if not findings:
            summary = "No destructive patterns detected. Command appears safe to execute."
        elif verdict == Verdict.BLOCK:
            worst = findings[0]
            summary = (
                f"BLOCK — {len(findings)} finding(s); worst is {worst.severity.upper()} "
                f"({worst.rule_id}): {worst.description}"
            )
        elif verdict == Verdict.REVIEW:
            summary = f"REVIEW — {len(findings)} medium-severity finding(s). Sandbox-test or pair-review before running."
        else:  # CAUTION
            summary = f"CAUTION — {len(findings)} low-severity finding(s). Likely safe but document if intentional."
    
        return CommandVetReport(
            verdict=verdict,
            risk_score=score,
            finding_count=len(findings),
            findings=findings,
            summary=summary,
            parse_error=parse_error if not parsed_ok else None,
        )
  • CommandVetReport is the return type for vet_command — contains verdict, risk_score, finding_count, findings, summary, and parse_error.
    class CommandVetReport(BaseModel):
        """Response for `vet_command` and `vet_command_chain`."""
    
        model_config = ConfigDict(frozen=True)
    
        verdict: Verdict
        risk_score: int
        """0–100. Severity-weighted: CRITICAL=40, HIGH=15, MEDIUM=5, LOW=1, INFO=0; capped at 100."""
        finding_count: int
        findings: list[CommandFinding]
        summary: str
        parse_error: str | None = None
        """Set if the input wasn't parseable as bash. Verdict will be UNVERIFIED."""
  • `_scan_with_regex` iterates over all detection rules and produces findings — the core detection logic called by vet_command.
    def _scan_with_regex(command: str) -> list[CommandFinding]:
        findings: list[CommandFinding] = []
        seen: set[tuple[str, str]] = set()
        for rule in _RULES:
            regex = re.compile(rule[3], re.IGNORECASE)
            for m in regex.finditer(command):
                snippet = command[max(0, m.start() - 5) : min(len(command), m.end() + 30)].strip()
                key = (rule[0], snippet)
                if key in seen:
                    continue
                seen.add(key)
                findings.append(_make_finding(rule, snippet, m.start()))
        return findings
  • `_risk_score` and `_verdict_from_findings` are helper functions used by vet_command to compute risk score (0-100) and determine the final verdict (CLEAN/CAUTION/REVIEW/BLOCK/UNVERIFIED).
    def _risk_score(findings: list[CommandFinding]) -> int:
        score = sum(_SEVERITY_WEIGHT[f.severity] for f in findings)
        return min(score, 100)
    
    
    def _verdict_from_findings(findings: list[CommandFinding]) -> Verdict:
        if not findings:
            return Verdict.CLEAN
        severities = {f.severity for f in findings}
        if Severity.CRITICAL in severities or Severity.HIGH in severities:
            return Verdict.BLOCK
        if Severity.MEDIUM in severities:
            return Verdict.REVIEW
        if Severity.LOW in severities:
            return Verdict.CAUTION
        return Verdict.CLEAN
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description fully discloses the tool's behavior: it detects various destructive patterns, returns a verdict with risk_score and per-finding details, and operates sub-second locally with no API key. There are no annotations, so the description carries the full burden, which it meets.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the main purpose, followed by an enumeration of detections and return fields. Every sentence adds value, and there is no redundant information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the single parameter and no output schema, the description is complete: it explains what the tool does, what it detects, what it returns, and its performance characteristics (sub-second, local, no API key).

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% for the single 'command' parameter, so baseline is 3. The description does not add significant parameter-specific semantics beyond what the schema already provides.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: vetting a single shell command for destructive patterns before execution. It lists specific patterns detected, distinguishing it from sibling tools like vet_command_chain.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides clear context: 'Use inline before approving any agent-proposed command' and mentions performance characteristics. However, it does not explicitly contrast with the sibling tool vet_command_chain for when to use this vs. the chain variant.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/temurkhan13/bash-vet-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server