mirror of
https://github.com/leonvanzyl/autocoder.git
synced 2026-01-30 06:12:06 +00:00
fix: add diagnostic warnings for config loading failures (#91)
When config files have errors, users had no way to know why their settings weren't being applied. Added logging.warning() calls to diagnose: - Empty config files - Missing 'version' field - Invalid structure (not a dict) - Invalid command entries - Exceeding 100 command limit - YAML parse errors - File read errors Also added .resolve() to project path to handle symlinks correctly. Fixes: leonvanzyl/autocoder#91 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
41
security.py
41
security.py
@@ -499,36 +499,45 @@ def load_org_config() -> Optional[dict]:
|
|||||||
config = yaml.safe_load(f)
|
config = yaml.safe_load(f)
|
||||||
|
|
||||||
if not config:
|
if not config:
|
||||||
|
logger.warning(f"Org config at {config_path} is empty")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Validate structure
|
# Validate structure
|
||||||
if not isinstance(config, dict):
|
if not isinstance(config, dict):
|
||||||
|
logger.warning(f"Org config at {config_path} must be a YAML dictionary")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if "version" not in config:
|
if "version" not in config:
|
||||||
|
logger.warning(f"Org config at {config_path} missing required 'version' field")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Validate allowed_commands if present
|
# Validate allowed_commands if present
|
||||||
if "allowed_commands" in config:
|
if "allowed_commands" in config:
|
||||||
allowed = config["allowed_commands"]
|
allowed = config["allowed_commands"]
|
||||||
if not isinstance(allowed, list):
|
if not isinstance(allowed, list):
|
||||||
|
logger.warning(f"Org config at {config_path}: 'allowed_commands' must be a list")
|
||||||
return None
|
return None
|
||||||
for cmd in allowed:
|
for i, cmd in enumerate(allowed):
|
||||||
if not isinstance(cmd, dict):
|
if not isinstance(cmd, dict):
|
||||||
|
logger.warning(f"Org config at {config_path}: allowed_commands[{i}] must be a dict")
|
||||||
return None
|
return None
|
||||||
if "name" not in cmd:
|
if "name" not in cmd:
|
||||||
|
logger.warning(f"Org config at {config_path}: allowed_commands[{i}] missing 'name'")
|
||||||
return None
|
return None
|
||||||
# Validate that name is a non-empty string
|
# Validate that name is a non-empty string
|
||||||
if not isinstance(cmd["name"], str) or cmd["name"].strip() == "":
|
if not isinstance(cmd["name"], str) or cmd["name"].strip() == "":
|
||||||
|
logger.warning(f"Org config at {config_path}: allowed_commands[{i}] has invalid 'name'")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Validate blocked_commands if present
|
# Validate blocked_commands if present
|
||||||
if "blocked_commands" in config:
|
if "blocked_commands" in config:
|
||||||
blocked = config["blocked_commands"]
|
blocked = config["blocked_commands"]
|
||||||
if not isinstance(blocked, list):
|
if not isinstance(blocked, list):
|
||||||
|
logger.warning(f"Org config at {config_path}: 'blocked_commands' must be a list")
|
||||||
return None
|
return None
|
||||||
for cmd in blocked:
|
for i, cmd in enumerate(blocked):
|
||||||
if not isinstance(cmd, str):
|
if not isinstance(cmd, str):
|
||||||
|
logger.warning(f"Org config at {config_path}: blocked_commands[{i}] must be a string")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Validate pkill_processes if present
|
# Validate pkill_processes if present
|
||||||
@@ -550,7 +559,11 @@ def load_org_config() -> Optional[dict]:
|
|||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
except (yaml.YAMLError, IOError, OSError):
|
except yaml.YAMLError as e:
|
||||||
|
logger.warning(f"Failed to parse org config at {config_path}: {e}")
|
||||||
|
return None
|
||||||
|
except (IOError, OSError) as e:
|
||||||
|
logger.warning(f"Failed to read org config at {config_path}: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@@ -564,7 +577,7 @@ def load_project_commands(project_dir: Path) -> Optional[dict]:
|
|||||||
Returns:
|
Returns:
|
||||||
Dict with parsed YAML config, or None if file doesn't exist or is invalid
|
Dict with parsed YAML config, or None if file doesn't exist or is invalid
|
||||||
"""
|
"""
|
||||||
config_path = project_dir / ".autocoder" / "allowed_commands.yaml"
|
config_path = project_dir.resolve() / ".autocoder" / "allowed_commands.yaml"
|
||||||
|
|
||||||
if not config_path.exists():
|
if not config_path.exists():
|
||||||
return None
|
return None
|
||||||
@@ -574,31 +587,39 @@ def load_project_commands(project_dir: Path) -> Optional[dict]:
|
|||||||
config = yaml.safe_load(f)
|
config = yaml.safe_load(f)
|
||||||
|
|
||||||
if not config:
|
if not config:
|
||||||
|
logger.warning(f"Project config at {config_path} is empty")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Validate structure
|
# Validate structure
|
||||||
if not isinstance(config, dict):
|
if not isinstance(config, dict):
|
||||||
|
logger.warning(f"Project config at {config_path} must be a YAML dictionary")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if "version" not in config:
|
if "version" not in config:
|
||||||
|
logger.warning(f"Project config at {config_path} missing required 'version' field")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
commands = config.get("commands", [])
|
commands = config.get("commands", [])
|
||||||
if not isinstance(commands, list):
|
if not isinstance(commands, list):
|
||||||
|
logger.warning(f"Project config at {config_path}: 'commands' must be a list")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Enforce 100 command limit
|
# Enforce 100 command limit
|
||||||
if len(commands) > 100:
|
if len(commands) > 100:
|
||||||
|
logger.warning(f"Project config at {config_path} exceeds 100 command limit ({len(commands)} commands)")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Validate each command entry
|
# Validate each command entry
|
||||||
for cmd in commands:
|
for i, cmd in enumerate(commands):
|
||||||
if not isinstance(cmd, dict):
|
if not isinstance(cmd, dict):
|
||||||
|
logger.warning(f"Project config at {config_path}: commands[{i}] must be a dict")
|
||||||
return None
|
return None
|
||||||
if "name" not in cmd:
|
if "name" not in cmd:
|
||||||
|
logger.warning(f"Project config at {config_path}: commands[{i}] missing 'name'")
|
||||||
return None
|
return None
|
||||||
# Validate name is a string
|
# Validate name is a non-empty string
|
||||||
if not isinstance(cmd["name"], str):
|
if not isinstance(cmd["name"], str) or cmd["name"].strip() == "":
|
||||||
|
logger.warning(f"Project config at {config_path}: commands[{i}] has invalid 'name'")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Validate pkill_processes if present
|
# Validate pkill_processes if present
|
||||||
@@ -620,7 +641,11 @@ def load_project_commands(project_dir: Path) -> Optional[dict]:
|
|||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
except (yaml.YAMLError, IOError, OSError):
|
except yaml.YAMLError as e:
|
||||||
|
logger.warning(f"Failed to parse project config at {config_path}: {e}")
|
||||||
|
return None
|
||||||
|
except (IOError, OSError) as e:
|
||||||
|
logger.warning(f"Failed to read project config at {config_path}: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user