T09 · Insecure Skill Coding Practices
- Location
scripts/feishu_file_sender.py:116- Finding
Unrestricted Local File Upload to Feishu
- Content
View full analysis
Vulnerability Details
File Location:
scripts/feishu_file_sender.py, lines 116–133 and 195–224
Vulnerability Type: Arbitrary local file disclosure through insufficient path validation
Risk Level: MediumVulnerable Code
python def upload_file(token: str, file_path: Path, file_type: str) -> str: headers = {"Authorization": f"Bearer {token}"} with file_path.open("rb") as f: files = {"file": (file_path.name, f)} data = { "file_type": file_type, "file_name": file_path.name, } resp = requests.post( FEISHU_UPLOAD_URL, headers=headers, data=data, files=files, timeout=30, ) resp.raise_for_status() data = resp.json() if data.get("code") != 0: raise RuntimeError(f"Upload failed: {data}") return data["data"]["file_key"]python def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser(description="Upload file to Feishu and send") parser.add_argument("--file", required=True, help="Local file path") parser.add_argument("--receive-id", default=None, help="chat_id or open_id") parser.add_argument( "--receive-id-type", default=None, help="chat_id / open_id / user_id (auto-detect if omitted)", ) parser.add_argument( "--file-type", default="stream", help="file_type for upload, default stream", ) return parser.parse_args() def main() -> None: args = parse_args() file_path = Path(args.file) if not file_path.exists(): raise FileNotFoundError(f"File not found: {file_path}") config = load_openclaw_config() app_id, app_secret = resolve_feishu_account(config) receive_id = resolve_receive_id(args.receive_id) receive_id_type = infer_receive_id_type(receive_id, args.receive ...[truncated 2755 chars]- Remediation
View remediation
Remediation Suggestions
- Resolve the requested path using
Path.resolve(strict=True)before opening it. - Restrict uploads to approved OpenStoryline output roots, such as the current session's render directory.
- Use
Path.relative_to()against a trusted canonical output root and reject paths outside that root. - Require
Path.is_file()and reject symbolic links or resolve them before containment validation. - Allow only expected video extensions such as
.mp4, and validate the actual media format rather than relying solely on the filename. - Associate the file with the active
session_idand require it to match a generatedoutput_*.mp4artifact. - Display the canonical file path, size, and Feishu recipient and require explicit user confirmation before upload.
- Validate
receive_id_typeagainst a fixed allowlist and ensure the recipient matches the active conversation where possible. - Avoid logging access tokens, application secrets, or complete API responses that could contain sensitive metadata.
- Resolve the requested path using
