T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/bilibili_to_notion_workflow.py:14
- Finding
- Shell Command Injection and Notion Token Exposure in Workflow Execution<![CDATA[ ## Vulnerability Details **File Location**: `scripts/bilibili_to_notion_workflow.py`, lines 14-23 and 101-120 **Vulnerability Type**: OS command injection and sensitive credential exposure through process arguments **Risk Level**: Critical ### Vulnerable Code ```python def run_command(cmd, description=""): """运行命令并返回结果""" if description: print(f"\n🔍 {description}...") try: result = subprocess.run( cmd, shell=True, capture_output=True, text=True, check=True ) return json.loads(result.stdout) except subprocess.CalledProcessError as e: print(f"❌ 命令执行失败: {e.stderr}") return {"success": False, "error": e.stderr} except json.JSONDecodeError as e: print(f"❌ JSON解析失败: {e}") return {"success": False, "error": str(e)} ``` The workflow constructs shell commands from command-line arguments, downloaded metadata, and generated content: ```python download_result = run_command( f"python3 download_bilibili_cc.py --url '{args.url}' --output '{subtitles_dir}'", "下载B站字幕", ) ``` ```python cmd_parts = [ "python3 create_notion_notes_with_images.py", f"--token '{args.token}'", f"--database-id '{args.database_id}'", f"--video-title '{video_title}'", f"--video-url '{args.url}'", f"--segments '{json.dumps(segments)}'", f"--images-dir '{screenshots_dir}'", ] if markdown_content: temp_md_path = output_dir / "temp_markdown.md" temp_md_path.write_text(markdown_content) cmd_parts.append(f"--markdown-content '{temp_md_path}'") cmd = " ".join(cmd_parts) create_result = run_command(cmd, "创建Notion笔记") ``` ### Technical Analysis `subprocess.run(..., shell=True)` passes the assembled string to a command shell. Several interpolated values can originate from untrusted or indirectly controlled sources: - `args.url`, `args.output_dir`, `args.database_id`, and `args.token` - `video_title`, which can originate from remote Bilibili metadata - ...[truncated 2080 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Remove `shell=True` and pass every subprocess invocation as an argument list: ```python result = subprocess.run( cmd, shell=False, capture_output=True, text=True, check=True, ) ``` 2. Build commands as lists rather than concatenated strings: ```python cmd = [ sys.executable, str(script_dir / "download_bilibili_cc.py"), "--url", args.url, "--output", str(subtitles_dir), ] ``` 3. Resolve bundled script paths relative to `Path(__file__).resolve().parent` rather than relying on the current working directory. 4. Do not put the Notion token in process arguments. Prefer directly importing and calling the relevant Python function. If process separation is required, pass the token through a minimally scoped environment variable or protected file descriptor. 5. Validate Bilibili URLs against the expected HTTPS host and validate database identifiers according to Notion's identifier format. 6. Treat remote titles and subtitle-derived content as data only. Never interpolate them into shell commands. 7. Rotate any Notion token that may previously have been exposed through process arguments. ]]>
