T09 · Insecure Skill Coding Practices
- Location
scripts/list_openapi_meta_apis.py:30- Finding
Unrestricted Output Path Allows Writes Outside the Designated Skill Directory
- Content
View full analysis
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: MediumVulnerable 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--versionwithout validating or constraining them. The output directory is u ...[truncated 3006 chars]- Remediation
View remediation
Remediation Suggestions
-
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") -
If custom output locations are unnecessary, remove
--output-dirand always use the documented output directory. -
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}") -
Resolve each final destination and verify that it remains beneath the approved root before writing.
-
Consider refusing to overwrite existing files or requiring an explicit
--forceoption. -
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.
-
