Validate a single command entry from project config. Checks that the command has a valid name and is not in any blocklist. Called during hierarchy resolution to gate each project command before it is added to the effective allowed set. Args: cmd_config: Dict with comma
(cmd_config: dict)
| 693 | |
| 694 | |
| 695 | def validate_project_command(cmd_config: dict) -> tuple[bool, str]: |
| 696 | """ |
| 697 | Validate a single command entry from project config. |
| 698 | |
| 699 | Checks that the command has a valid name and is not in any blocklist. |
| 700 | Called during hierarchy resolution to gate each project command before |
| 701 | it is added to the effective allowed set. |
| 702 | |
| 703 | Args: |
| 704 | cmd_config: Dict with command configuration (name, description) |
| 705 | |
| 706 | Returns: |
| 707 | Tuple of (is_valid, error_message) |
| 708 | """ |
| 709 | if not isinstance(cmd_config, dict): |
| 710 | return False, "Command must be a dict" |
| 711 | |
| 712 | if "name" not in cmd_config: |
| 713 | return False, "Command must have 'name' field" |
| 714 | |
| 715 | name = cmd_config["name"] |
| 716 | if not isinstance(name, str) or not name: |
| 717 | return False, "Command name must be a non-empty string" |
| 718 | |
| 719 | # Reject bare wildcard - security measure to prevent matching all commands |
| 720 | if name == "*": |
| 721 | return False, "Bare wildcard '*' is not allowed (security risk: matches all commands)" |
| 722 | |
| 723 | # Check if command is in the blocklist or dangerous commands |
| 724 | base_cmd = os.path.basename(name.rstrip("*")) |
| 725 | if base_cmd in BLOCKED_COMMANDS: |
| 726 | return False, f"Command '{name}' is in the blocklist and cannot be allowed" |
| 727 | if base_cmd in DANGEROUS_COMMANDS: |
| 728 | return False, f"Command '{name}' is in the blocklist and cannot be allowed" |
| 729 | |
| 730 | # Description is optional |
| 731 | if "description" in cmd_config and not isinstance(cmd_config["description"], str): |
| 732 | return False, "Description must be a string" |
| 733 | |
| 734 | return True, "" |
| 735 | |
| 736 | |
| 737 | def get_effective_commands(project_dir: Optional[Path]) -> tuple[set[str], set[str]]: |
no outgoing calls