T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/list_openapi_meta_apis.py:28
- Finding
- Path Traversal Through Unvalidated Output Filename Components<![CDATA[ ## Vulnerability Details **File Location**: `scripts/list_openapi_meta_apis.py:28-30, 42-59` **Vulnerability Type**: Path traversal and unintended file overwrite **Risk Level**: Medium ### Vulnerable Code ```python parser = argparse.ArgumentParser() parser.add_argument("--product-code", default=DEFAULT_PRODUCT_CODE) parser.add_argument("--version", default=DEFAULT_VERSION) parser.add_argument("--output-dir", default=str(OUTPUT_DIR)) args = parser.parse_args() timeout = int(os.getenv("OPENAPI_META_TIMEOUT", "20")) output_dir = pathlib.Path(args.output_dir) output_dir.mkdir(parents=True, exist_ok=True) url = ( f"https://api.aliyun.com/meta/v1/products/{args.product_code}" f"/versions/{args.version}/api-docs.json" ) payload = fetch_json(url, timeout) raw_apis = payload.get("apis", {}) if isinstance(raw_apis, dict): api_names = sorted(raw_apis.keys()) elif isinstance(raw_apis, list): names = [] for item in raw_apis: if isinstance(item, dict): name = item.get("name") or item.get("apiName") if name: names.append(name) elif isinstance(item, str): names.append(item) api_names = sorted(set(names)) else: api_names = [] json_file = output_dir / f"{args.product_code}_{args.version}_api_docs.json" md_file = output_dir / f"{args.product_code}_{args.version}_api_list.md" json_file.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8") md_lines = [ f"# {args.product_code} {args.version} API List", "", f"- Source: {url}", f"- API count: {len(api_names)}", "", ] ``` ### Technical Analysis The command-line values `args.product_code` and `args.version` are inserted directly into local filenames. Neither value is validated to ensure that it is a single safe filename component. If either value contains path separators and traversal components such as `../`, the `/` operator used by `pathlib.Path` resolves those components as ...[truncated 2157 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Validate each argument before using it in either a URL or filename: - Restrict product codes to a conservative allowlist pattern such as `[A-Za-z0-9_-]+`. - Restrict versions to the expected format, such as `YYYY-MM-DD`. - Reject values containing `/`, `\`, `..`, null bytes, or platform-specific path separators. 2. URL-encode validated product and version values as individual URL path segments rather than interpolating raw input. 3. Construct and resolve each destination path, then enforce output-directory containment before writing: ```python import re from urllib.parse import quote PRODUCT_RE = re.compile(r"^[A-Za-z0-9_-]+$") VERSION_RE = re.compile(r"^\d{4}-\d{2}-\d{2}$") if not PRODUCT_RE.fullmatch(args.product_code): parser.error("Invalid product code") if not VERSION_RE.fullmatch(args.version): parser.error("Invalid version") output_dir = pathlib.Path(args.output_dir).resolve() output_dir.mkdir(parents=True, exist_ok=True) product_segment = quote(args.product_code, safe="") version_segment = quote(args.version, safe="") url = ( f"https://api.aliyun.com/meta/v1/products/{product_segment}" f"/versions/{version_segment}/api-docs.json" ) json_file = (output_dir / f"{args.product_code}_{args.version}_api_docs.json").resolve() md_file = (output_dir / f"{args.product_code}_{args.version}_api_list.md").resolve() for destination in (json_file, md_file): if output_dir not in destination.parents: raise ValueError("Output path escapes the configured output directory") ``` 4. If arbitrary product or version text must be supported, derive filenames from a safe encoding or stable hash instead of embedding raw values. 5. Where overwriting is unnecessary, use exclusive file creation or explicitly require confirmation before replacing an existing artifact. ]]>
