{"record":{"id":"77df6310ef7c75f6","repo":"HKUDS/Vibe-Trading","slug":"invalid-preset-name-name-r","errorCode":null,"errorMessage":"invalid preset name: {name!r}","messagePattern":"invalid preset name: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/swarm/presets.py","lineNumber":62,"sourceCode":"        return str(path)\n\n\ndef _validate_preset_name(name: str) -> str:\n    \"\"\"Reject names that are empty or could escape a presets directory.\n\n    Preset names map directly to ``<dir>/<name>.yaml``; a name carrying a\n    path separator or ``..`` would resolve outside the directory. The bundled\n    path had the same latent exposure — validating here covers both.\n\n    Returns:\n        The stripped name.\n\n    Raises:\n        ValueError: Empty name, path separators, or parent references.\n    \"\"\"\n    cleaned = (name or \"\").strip()\n    if not cleaned or cleaned in {\".\", \"..\"} or \"/\" in cleaned or \"\\\\\" in cleaned:\n        raise ValueError(f\"invalid preset name: {name!r}\")\n    return cleaned\n\n\ndef _preset_search_dirs() -> tuple[Path, ...]:\n    \"\"\"Directories searched for presets, highest priority first.\"\"\"\n    return (USER_PRESETS_DIR, PRESETS_DIR)\n\n\ndef resolve_preset_path(name: str) -> Path | None:\n    \"\"\"Return the YAML path for *name* (user dir first), or ``None``.\"\"\"\n    cleaned = _validate_preset_name(name)\n    for directory in _preset_search_dirs():\n        path = directory / f\"{cleaned}.yaml\"\n        if path.is_file():\n            return path\n    return None\n\n","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/swarm/presets.py#L44-L80","documentation":"_validate_preset_name in swarm/presets.py sanitizes the preset name used to build filesystem paths. It raises ValueError for empty names, '.'/'..', or any name containing '/' or '\\\\' — a path-traversal guard. Called by resolve_preset_path.","triggerScenarios":"Calling load_preset/resolve_preset_path with '../../etc/passwd', 'foo/bar', 'C:\\\\presets\\\\x', an empty string, or None.","commonSituations":"Passing user-supplied preset names straight from CLI/web input; building nested preset paths assuming subdirectories are supported (they are not — presets are flat *.yaml files).","solutions":["Use a bare preset name like 'default' or 'research-full' (flat name, no directories)","Validate/normalize external input before calling load_preset","If you need hierarchy, encode it in the filename, e.g. 'research-full.yaml'"],"exampleFix":"# before\nload_preset('user/configs/default')\n# after\nload_preset('default')","handlingStrategy":"type-guard","validationCode":"def safe_preset_name(name: str) -> bool:\n    n = (name or '').strip()\n    return bool(n) and n not in {'.','..'} and '/' not in n and '\\\\' not in n","typeGuard":"def is_bare_preset_name(name) -> bool:\n    return isinstance(name, str) and bool(name.strip()) and name.strip() not in {'.','..'} and '/' not in name and '\\\\' not in name","tryCatchPattern":"try:\n    load_preset(name)\nexcept ValueError as e:\n    if 'invalid preset name' in str(e): return 400 to the caller / strip separators and retry\n    else: raise","preventionTips":["Never pass raw user input as preset names","Keep presets flat: one name, one yaml file","Validate at the API boundary with the same rules"],"tags":["python","path-traversal","input-validation"],"backgroundTag":"path-traversal-rejected","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}