T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/list_openapi_meta_apis.py:30
- Finding
- Unrestricted Output Path Allows Writes Outside the Designated Skill Directory## Vulnerability Details **File Location**: `scripts/list_openapi_meta_apis.py`, lines 30-73 **Vulnerability Type**: Unrestricted file output path and insufficient path validation **Risk Level**: Medium **Vulnerable Code**: ```python 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)}", "", ] md_lines.extend([f"- `{name}`" for name in api_names]) md_file.write_text("\n".join(md_lines) + "\n", encoding="utf-8") ``` ### Technical Analysis The script accepts `--output-dir`, `--product-code`, and `--version` without validating or constraining them. The output directory is u ...[truncated 3006 chars]
- Remediation
- ## Remediation Suggestions 1. Resolve all output paths against an approved output root: ```python APPROVED_ROOT = pathlib.Path( "output/alicloud-security-id-verification-cloudauth" ).resolve() output_dir = pathlib.Path(args.output_dir).resolve() if output_dir != APPROVED_ROOT and APPROVED_ROOT not in output_dir.parents: raise ValueError("Output directory must remain under the approved output root") ``` 2. If custom output locations are unnecessary, remove `--output-dir` and always use the documented output directory. 3. Validate product and version arguments using strict allowlists. For example, permit only letters, digits, periods, underscores, and hyphens: ```python import re SAFE_IDENTIFIER = re.compile(r"^[A-Za-z0-9._-]+$") for label, value in ( ("product-code", args.product_code), ("version", args.version), ): if not SAFE_IDENTIFIER.fullmatch(value): raise ValueError(f"Invalid {label}") if value in {".", ".."}: raise ValueError(f"Invalid {label}") ``` 4. Resolve each final destination and verify that it remains beneath the approved root before writing. 5. Consider refusing to overwrite existing files or requiring an explicit `--force` option. 6. If external output directories are a required feature, require explicit trusted-user authorization and clearly document that the option can write outside the skill output directory.
