T09 · Insecure Skill Coding Practices
- Location
scripts/classify_data.py:271- Finding
CSV Formula Injection in Exported Classification Results
- Content
View full analysis
Vulnerability Details
File Location:
scripts/classify_data.py:271-296
Vulnerability Type: CSV formula injection
Risk Level: MediumVulnerable Code
python def output_row(r: Result, include_finance: bool) -> list[str]: type_comment = f"{r.dtype} {r.comment}".strip() row = [r.table, r.field, type_comment, r.general_category, r.general_level] if include_finance: row += [r.finance_category, str(r.finance_level or "待确认"), r.finance_candidates, r.dual_label] row += [f"{r.confidence:.2f}", ";".join(x for x in [r.basis, r.review] if x)] return row def render_markdown(results: list[Result], include_finance: bool) -> str: headers = output_headers(include_finance) lines = ["|" + "|".join(headers) + "|", "|" + "|".join(["---"] * len(headers)) + "|"] for r in results: lines.append("|" + "|".join(str(c).replace("|", "/") for c in output_row(r, include_finance)) + "|") lines.append("") lines.append(f"覆盖校验:输入/解析字段 {len(results)} 个,已分类 {len(results)} 个,遗漏 0 个。") return "\n".join(lines) def render_csv(results: list[Result], include_finance: bool) -> str: out = StringIO() headers = output_headers(include_finance) writer = csv.writer(out) writer.writerow(headers) for r in results: writer.writerow(output_row(r, include_finance)) return out.getvalue()Relevant user-controlled input reaches these rows through
scripts/classify_data.py:303-318:python src.add_argument("--field", help="single field name") src.add_argument("--fields", help="comma/newline separated field names or a path to a text file") src.add_argument("--sql", help="path to SQL DDL file") if args.field: items = [FieldItem(table="", name=args.field)] elif args.fields: p = Path(args.fields) raw = p.read_text(encoding="utf-8") if p.exists() else args.fields names = [x.strip() for x in re.split(r"[,\n]", raw) if x.strip()] items = [FieldItem(table="", name=x) for x in names ...[truncated 2607 chars]- Remediation
View remediation
Remediation Suggestions
Apply spreadsheet-safe encoding to every potentially user-derived CSV cell before calling
writer.writerow():- Convert each value to a string.
- Detect the first non-whitespace character.
- If it is
=,+,-, or@, prefix the value with an apostrophe. - Consider tabs, carriage returns, line feeds, and Unicode whitespace that could precede a formula marker.
- Apply the protection to all exported cells rather than only field names, because table names, data types, and comments may also be attacker-controlled.
- Add regression tests covering each dangerous prefix, leading whitespace, SQL comments, and benign values.
Example hardening approach:
python FORMULA_PREFIXES = ("=", "+", "-", "@") def spreadsheet_safe(value: object) -> str: text = str(value) effective = text.lstrip() if effective.startswith(FORMULA_PREFIXES): return "'" + text return text def render_csv(results: list[Result], include_finance: bool) -> str: out = StringIO() writer = csv.writer(out) writer.writerow([spreadsheet_safe(v) for v in output_headers(include_finance)]) for result in results: writer.writerow([ spreadsheet_safe(v) for v in output_row(result, include_finance) ]) return out.getvalue()For stronger separation of concerns, offer a documented spreadsheet-safe export mode and make it the default for files intended to be opened in office applications. Clearly warn consumers if a raw, unmodified CSV mode remains available.
