T09 · Insecure Skill Coding Practices
- Location
scripts/stock_analyzer.py:26- Finding
Unrestricted File Access and Unsafe Predictable Temporary Paths
- Content
View full analysis
Vulnerability Details
File Location:
scripts/stock_analyzer.py:26-45,scripts/stock_analyzer.py:59-78,scripts/stock_analyzer.py:92-111, andscripts/stock_analyzer.py:125-148
Vulnerability Type: Path traversal, unrestricted caller-controlled file access, and unsafe temporary-file handling
Risk Level: MediumVulnerable Code
python # query_realtime_price if time is None: time = datetime.now().strftime("%Y-%m-%d %H:%M:%00") if file_path is None: file_path = f"/tmp/{ticker.replace('.', '_')}_realtime.csv" result = kimi_finance( ticker=ticker, time=time, type="realtime_price", file_path=file_path ) # Read the CSV file and return as dict if os.path.exists(file_path): df = pd.read_csv(file_path) if not df.empty: return df.to_dict('records')[0]python # query_technical_indicators if time is None: time = datetime.now().strftime("%Y-%m-%d %H:%M:%00") if file_path is None: file_path = f"/tmp/{ticker.replace('.', '_')}_tech.csv" result = kimi_finance( ticker=ticker, time=time, type="realtime_tech", file_path=file_path ) # Read the CSV file and return as dict if os.path.exists(file_path): df = pd.read_csv(file_path) if not df.empty: return df.to_dict('records')[0]python # query_open_summary if time is None: time = datetime.now().strftime("%Y-%m-%d 09:30:00") if file_path is None: file_path = f"/tmp/{ticker.replace('.', '_')}_open.csv" result = kimi_finance( ticker=ticker, time=time, type="open_summary", file_path=file_path ) # Read the CSV file and return as dict if os.path.exists(file_path): df = pd.read_csv(file_path) if not df.empty: return df.to_dict('records')[0]python # query_close_summary if time is None: time = datetime.now().strftime("%Y-%m-%d 15:00:00") ...[truncated 3251 chars]- Remediation
View remediation
Remediation Suggestions
-
Validate stock symbols with a strict allowlist before using them:
python import re TICKER_RE = re.compile(r"^\d{6}\.(SH|SZ)$") def validate_ticker(ticker: str) -> str: if not TICKER_RE.fullmatch(ticker): raise ValueError("Invalid A-share ticker") return ticker -
Do not expose unrestricted filesystem paths through the public API. Prefer accepting only a filename or generating the destination internally.
-
Create a private temporary directory and securely generated output file:
python from pathlib import Path import tempfile output_dir = Path(tempfile.mkdtemp(prefix="stock-analyzer-")) output_path = output_dir / "result.csv" -
If caller-selected filenames are required, resolve the path and verify containment in a dedicated approved directory:
python from pathlib import Path BASE_DIR = Path("/var/tmp/stock-analyzer").resolve() def safe_output_path(filename: str) -> Path: if Path(filename).name != filename: raise ValueError("Directory components are not allowed") destination = (BASE_DIR / filename).resolve() if destination.parent != BASE_DIR: raise ValueError("Output path escapes the approved directory") return destination -
Reject absolute paths,
..components, path separators, unexpected extensions, and symbolic-link destinations. Open files with exclusive-creation and no-follow semantics where supported. -
Avoid predictable shared
/tmpfilenames. Ensure temporary files and directories are accessible only to the current process user and remove them after parsing. -
Validate that the resulting file is a regular file owned by the expected user before reading it. Apply file-size and CSV schema limits to prevent resource exhaustion or unintended data exposure.
-
Add tests covering absolute paths, traversal seque ...[truncated 94 chars]
-
