T09 · Insecure Skill Coding Practices
- Location
converter.py:82- Finding
Unvalidated Workflow Name Allows Path Traversal and Arbitrary File Overwrite
- Content
View full analysis
Vulnerability Details
File Location:
converter.py:82-84, with the affected argument defined atconverter.py:570
Vulnerability Type: Path traversal and arbitrary file write
Risk Level: MediumVulnerable Code
python output_file = os.path.join(yaml_dir, app_name + ".yaml") node_list = []The resulting path is subsequently opened for writing at
converter.py:283-284:python with open(output_file, 'w', encoding='utf-8') as yaml_file: yaml.dump(general_template, yaml_file, allow_unicode=True, default_flow_style=False)The workflow name is accepted without validation at
converter.py:570:python parser.add_argument('--name', type=str, required=True, help='Workflow name')The shell wrapper also forwards this value unchanged at
bash_converter.sh:24-34:bash NAME="${2:-workflow}" OUTPUT_PATH="${3:-${DEFAULT_OUTPUT_PATH}}" TYPE="${4:-dify}" python "${SCRIPT_DIR}/converter.py" \ --json_path "${JSON_PATH}" \ --name "${NAME}" \ --output_path "${OUTPUT_PATH}" \ --type "${TYPE}"Technical Analysis
resolve_safe_output_path()validates only the user-supplied output directory. It does not validate the workflow name used to construct the final filename.Because
app_namecan contain absolute paths,..components, or path separators,os.path.join(yaml_dir, app_name + ".yaml")does not guarantee that the result remains beneathyaml_dir. An absoluteapp_namecauses Python to discard the preceding directory. A relative name containing traversal components can resolve outside the approved output directory.The Dify conversion path then opens the constructed path using write mode. If the destination exists and is writable, it is truncated and replaced with generated YAML. If the necessary parent directories already exist, a new file can also be created outside the configured output directory.
...[truncated 2059 chars]
- Remediation
View remediation
Remediation Suggestions
-
Restrict workflow names to a conservative basename format:
python import re WORKFLOW_NAME_RE = re.compile(r"^[A-Za-z0-9_-]+$") def validate_workflow_name(name: str) -> str: if not WORKFLOW_NAME_RE.fullmatch(name): raise ValueError( "Workflow name may contain only ASCII letters, digits, " "underscores, and hyphens." ) return name -
Explicitly reject absolute paths,
.and.., path separators, NUL characters, and platform-specific alternate separators. -
Validate every complete destination after combining its directory and filename:
python def safe_child_path(parent: str, filename: str) -> str: parent_real = os.path.realpath(parent) candidate = os.path.realpath(os.path.join(parent_real, filename)) if os.path.commonpath([parent_real, candidate]) != parent_real: raise ValueError("Output path escapes its approved directory") return candidate -
Apply the containment check independently to:
- Dify YAML output.
- Coze temporary YAML output.
- Coze workflow staging directories.
- Final Coze ZIP output.
-
Avoid silently rewriting unsafe input. Reject it with a clear error so callers cannot mistakenly believe their requested path was used.
-
Add regression tests covering absolute paths,
../, nested traversal, Windows separators, drive-qualified paths, UNC paths, symlinked directories, and ordinary valid names. -
Correct
CONVERTER_USAGE.mdso its output-containment claims match the behavior actually enforced by the implementation.
-
