T09 · Insecure Skill Coding Practices
- Location
scripts/document_parser.py:446- Finding
Arbitrary URL Fetching Enables Server-Side Request Forgery
- Content
View full analysis
Dict[str, Any]: """URL에서 웹페이지 파싱""" if not HAS_WEB_PARSER: logger.error("웹 파싱 라이브러리가 설치되지 않음 (requests, BeautifulSoup 필요)") raise ImportError("웹 파싱을 위해 requests와 BeautifulSoup4를 설치하세요.") try: logger.info(f"URL 파싱 시작: {url}") response = requests.get(url, timeout=10) response.encoding = response.apparent_encoding or 'utf-8' response.raise_for_status() soup = BeautifulSoup(response.content, 'html.parser') # 메인 콘텐츠 추출 full_text = self._extract_main_content(soup) return { "metadata": { "source_file": url, "source_type": "url", "parsed_at": datetime.utcnow().isoformat() + "Z", "page_count": 1, "parser_version": self.parser_version }, "content": { "full_text": full_text, "sections": self._extract_sections(full_text), "tables": self._extract_tables_from_html(soup) } } except requests.RequestException as e: logger.error(f"URL 요청 실패: {url}, 오류: {str(e)}") raise ``` ### Technical Analysis The parser accepts any user-provided HTTP or HTTPS URL and passes it directly to `requests.get()`. It does not validate the resolved destination, reject private or reserved addresses, restrict allowed domains, or validate redirect targets. Automatic redirects are enabled by default. Consequently, an apparently public URL can redirect the request to loopback, link-local, private-network, or cloud metadata addresses. The response is also loaded without a maximum body-size limit, creating an additional denial-of-service risk. Fetching public announcement URLs i ...[truncated 1126 chars]- Remediation
View remediation
