T09 · Insecure Skill Coding Practices
- Location
scripts/siyuan_export.py:124- Finding
SQL Injection in Authenticated SiYuan Document Queries
- Content
View full analysis
list[dict]: """按文档名搜索""" stmt = f"SELECT id, content, hpath, created, updated FROM blocks WHERE content LIKE '%{keyword}%' AND type = 'd' ORDER BY updated DESC LIMIT 20" result = api_call("/api/query/sql", {"stmt": stmt}, config) if result.get("code") == 0: return result.get("data", []) return [] def get_child_docs(doc_id: str, config: dict) -> list[dict]: """获取子文档列表""" doc_info = api_call("/api/query/sql", {"stmt": f"SELECT hpath FROM blocks WHERE id = '{doc_id}' AND type = 'd'"}, config) if doc_info.get("code") != 0 or not doc_info.get("data"): return [] parent_hpath = doc_info["data"][0]["hpath"] escaped_hpath = parent_hpath.replace("'", "''") result = api_call("/api/query/sql", {"stmt": f"SELECT id, content FROM blocks WHERE hpath LIKE '{escaped_hpath}/%' AND type = 'd' ORDER BY hpath"}, config) ``` ### Technical Analysis The `keyword` and `doc_id` values originate from the `--search` and `--doc-id` command-line arguments. They are inserted directly into SQL statements using Python formatted strings and then submitted to the authenticated SiYuan `/api/query/sql` endpoint. No SQL escaping, parameter binding, or strict identifier validation is applied to either input: - `keyword` is placed inside a `LIKE '%...%'` expression. - `doc_id` is placed inside a quoted equality expression. - Although `parent_hpath` is escaped before the final query, it is obtained using the already-injectable `doc_id` query. The later escaping therefore does not eliminate the initial injection point. An attacker who can influence the arguments used to invoke the Skill can close the SQL string literal and modify the query structure. Depending on restr ...[truncated 2254 chars]- Remediation
View remediation
