T09 · Insecure Skill Coding Practices
- Location
scripts/router.py:48- Finding
Unhandled Empty Candidate Set Causes Denial of Service
- Content
View full analysis
Vulnerability Details
File Location:
scripts/router.py, lines 48–49 and 77–79
Vulnerability Type: Unchecked empty list access
Risk Level: LowVulnerable Code
python # Apply min_capability filter if provided if min_capability: candidates = [m for m in candidates if min_capability in m.get("capabilities", [])] # ... # Fallback: return cheapest overall m = candidates[0] reason = f"fallback cheapest (score={task_score})" return {"model": m, "reason": reason}Technical Analysis
The router filters its candidate list when
min_capabilityis supplied but does not verify that any candidates remain. The fallback branch unconditionally accessescandidates[0]. If no configured model provides the requested capability, Python raises anIndexError, terminating the routing operation.The same failure occurs when the models JSON contains an empty list. Because
pick_model()is exposed as both CLI and library functionality, the exception can propagate into middleware or batch-processing systems that do not isolate failures.Attack Path
-
An attacker or untrusted caller controls the requested minimum capability, or an untrusted or malformed configuration supplies an empty model list.
-
The caller requests an unavailable capability, for example:
bash python3 scripts/router.py \ --models examples/models.json \ --task "Route this request" \ --min-capability nonexistent -
The capability filter removes every candidate.
-
Neither the low-complexity nor high-complexity selection branch returns a model.
-
The fallback executes
candidates[0]. -
An
IndexErrorterminates the process and prevents the request from being routed.
Impact Assessment
Successful exploitation does not grant additional privileges, execute arbitrary code, or expose confidential data. Its impact is limited to availability: an attacker who can influence routing parameters or model configuration can reliably fail indiv ...[truncated 193 chars]
-
- Remediation
View remediation
Remediation Suggestions
- Validate that the decoded JSON root is a non-empty list before routing.
- Validate every model entry, including the types of
name,provider,cost_score,power_score, andcapabilities. - Check whether
candidatesis empty immediately after capability filtering. - Raise a specific, controlled exception or return a structured “no matching model” result rather than indexing an empty list.
- Catch validation and selection errors in the CLI entry point, emit a concise diagnostic to standard error, and return a nonzero exit status.
- Ensure middleware calling
pick_model()handles the controlled failure without terminating a worker.
Example hardening:
python def load_models(path: str) -> List[Dict]: with open(path, "r", encoding="utf-8") as f: models = json.load(f) if not isinstance(models, list) or not models: raise ValueError("Models configuration must be a non-empty list") return models def pick_model(models: List[Dict], task: str, min_capability: str = None, prefer: List[str] = None) -> Dict: if not models: raise ValueError("No models are configured") task_score = score_task(task) candidates = sorted( models, key=lambda m: ( m.get("cost_score", 1000), -m.get("power_score", 0), ), ) if min_capability: candidates = [ m for m in candidates if min_capability in m.get("capabilities", []) ] if not candidates: raise ValueError( f"No model satisfies capability: {min_capability}" ) # Continue with model selection.
