T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/fetch_holidays.py:118
- Finding
- Unvalidated Input Can Corrupt the Executable Query Script<![CDATA[ ## Vulnerability Details **File Location**: `scripts/fetch_holidays.py:79-107` and `scripts/fetch_holidays.py:118-141` **Vulnerability Type**: Unsafe self-modification and insufficient input validation **Risk Level**: Medium ### Vulnerable Code ```python def update_workday_script(year: int, holidays: list[str], extra_workdays: list[str]): """将新年份数据插入 workday_query.py""" script_path = SKILL_DIR / "scripts" / "workday_query.py" content = script_path.read_text(encoding="utf-8") # 检查是否已有该年份数据 if f"HOLIDAYS_{year}" in content: print(f"ℹ️ workday_query.py 已包含 {year} 年数据,跳过更新", file=sys.stderr) return new_block = generate_workday_data_block(year, holidays, extra_workdays) # 在 ALL_HOLIDAYS 定义之前插入新数据块 insert_marker = "ALL_HOLIDAYS = " content = content.replace(insert_marker, new_block + "\n" + insert_marker) # 更新 ALL_HOLIDAYS 和 ALL_EXTRA_WORKDAYS 合并集合 content = re.sub( r"ALL_HOLIDAYS = ([^\n]+)", lambda m: m.group(0).rstrip() + f" | HOLIDAYS_{year}", content ) content = re.sub( r"ALL_EXTRA_WORKDAYS = ([^\n]+)", lambda m: m.group(0).rstrip() + f" | WORKDAYS_{year}", content ) script_path.write_text(content, encoding="utf-8") ``` ```python year = int(args[0]) mode = args[1] if len(args) > 1 else "--check" if mode == "--check": result = check_local(year) if result: print(json.dumps({"status": "exists", "year": year, "path": result["path"]})) else: print(json.dumps({"status": "missing", "year": year})) elif mode == "--save": # 此模式由 Claude 在获取到官网数据后调用 # 数据通过 stdin 传入(JSON 格式) data = json.loads(sys.stdin.read()) holidays = data.get("holidays", []) extra_workdays = data.get("extra_workdays", []) md_content = data.get("md_content", "") if md_content: save_references_md(year, md_content) if holidays: update_workday_script(year, hol ...[truncated 2734 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Validate the year before using it** - Require an integer in Python's supported calendar range, such as `1` through `9999`. - Apply a narrower operational range if the Skill only supports modern calendar years. 2. **Validate the complete input schema** - Require `holidays` and `extra_workdays` to be arrays of strings. - Reject unexpected types and malformed JSON. - Parse every value with `date.fromisoformat()`. - Verify that every parsed date belongs to the requested year. - Reject duplicate or conflicting dates appearing in both collections. - Apply reasonable collection-size limits. 3. **Avoid constructing identifiers from untrusted input** - Store data in a dictionary keyed by an integer year rather than generating variable names: ```python HOLIDAYS_BY_YEAR = { 2025: {...}, 2026: {...}, } ``` 4. **Validate generated source before installation** - Generate the complete candidate file in memory. - Parse it with `ast.parse()` and optionally compile it with `py_compile`. - Do not alter the active script if validation fails. 5. **Use atomic replacement** - Write the candidate to a temporary file in the same directory. - Flush and synchronize it as appropriate. - Atomically replace the target with `os.replace()` only after successful validation. - Preserve a backup or implement rollback. 6. **Check update markers** - Confirm that `ALL_HOLIDAYS` and `ALL_EXTRA_WORKDAYS` markers are each found exactly once. - Abort without writing if the expected source structure is absent or ambiguous. 7. **Keep coverage metadata synchronized** - Update `COVERED_YEARS` through the same validated transaction, or derive coverage directly from the year-keyed data structure. 8. **Add regression tests** - Test negative years, years above `9999`, malformed dates, mismatched years, incorrect JSON types, duplicate dates, conflicting holiday/workday entries, abse ...[truncated 51 chars]
