fix(security): validate ticker before using as path component (#618)
The ticker symbol reaches three filesystem-path construction sites (load_ohlcv cache filename, checkpointer DB path, _log_state results directory) without validation. A value containing path separators or "../" escapes the configured cache / checkpoints / results directory. Two attack vectors: - Programmatic callers passing arbitrary ticker to propagate() - Prompt injection via fetched news content steering the LLM into tool calls with attacker-chosen ticker Fix: new safe_ticker_component() validator in tradingagents/dataflows/ utils.py applied at all three sites. Allows the standard ticker character set ([A-Za-z0-9._\-\^], up to 32 chars) and explicitly rejects dot-only values like "." and ".." which would otherwise pass the regex but traverse parent directories. Seven test cases cover the accepted formats (BRK-B, 7203.T, ^GSPC, etc.) and the rejected inputs (path separators, null bytes, whitespace, empty values, overlong strings, dot-only values). Closes #618.
This commit is contained in:
5 files changed
+103
-4
No files matched your search
@@ -13,12 +13,16 @@ from typing import Generator
|
||||
|
||||
from langgraph.checkpoint.sqlite import SqliteSaver
|
||||
|
||||
from tradingagents.dataflows.utils import safe_ticker_component
|
||||
|
||||
|
||||
def _db_path(data_dir: str | Path, ticker: str) -> Path:
|
||||
"""Return the SQLite checkpoint DB path for a ticker."""
|
||||
# Reject ticker values that would escape the checkpoints directory.
|
||||
safe = safe_ticker_component(ticker).upper()
|
||||
p = Path(data_dir) / "checkpoints"
|
||||
p.mkdir(parents=True, exist_ok=True)
|
||||
return p / f"{ticker.upper()}.db"
|
||||
return p / f"{safe}.db"
|
||||
|
||||
|
||||
def thread_id(ticker: str, date: str) -> str:
|
||||
|
||||
@@ -18,6 +18,7 @@ from tradingagents.llm_clients import create_llm_client
|
||||
from tradingagents.agents import *
|
||||
from tradingagents.default_config import DEFAULT_CONFIG
|
||||
from tradingagents.agents.utils.memory import TradingMemoryLog
|
||||
from tradingagents.dataflows.utils import safe_ticker_component
|
||||
from tradingagents.agents.utils.agent_states import (
|
||||
AgentState,
|
||||
InvestDebateState,
|
||||
@@ -378,8 +379,10 @@ class TradingAgentsGraph:
|
||||
"final_trade_decision": final_state["final_trade_decision"],
|
||||
}
|
||||
|
||||
# Save to file
|
||||
directory = Path(self.config["results_dir"]) / self.ticker / "TradingAgentsStrategy_logs"
|
||||
# Save to file. Reject ticker values that would escape the
|
||||
# results directory when joined as a path component.
|
||||
safe_ticker = safe_ticker_component(self.ticker)
|
||||
directory = Path(self.config["results_dir"]) / safe_ticker / "TradingAgentsStrategy_logs"
|
||||
directory.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
log_path = directory / f"full_states_log_{trade_date}.json"
|
||||
|
||||
Reference in new issue
Block a user