T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/backtest.py:26
- Finding
- SQL Injection in Stock Data Lookup<![CDATA[ ## Vulnerability Details **File Location**: `scripts/backtest.py`, lines 26-29 **Vulnerability Type**: SQL injection through direct string interpolation **Risk Level**: Medium ### Vulnerable Code ```python df = pd.read_sql( f"SELECT * FROM daily_data WHERE code='{code}' ORDER BY date", conn ) ``` ### Technical Analysis The `code` value originates from the command-line `--stock` argument and is interpolated directly into an SQL statement. No parameter binding or strict stock-code validation is applied. An input containing SQL syntax can alter the `WHERE` clause. For example, a value equivalent to `' OR '1'='1` can cause the query to return records for every stock rather than only the requested stock. More advanced expressions may use `UNION` clauses to read other compatible data from the same SQLite database, subject to SQLite and result-column constraints. The database API may prevent execution of multiple statements, which limits some destructive payloads, but it does not prevent modification of the semantics of the existing `SELECT` statement. ### Attack Path 1. An attacker or untrusted caller obtains the ability to invoke `backtest.py` with a controlled `--stock` value. 2. The supplied value is passed to `run_backtest()` and then `load_data()`. 3. `load_data()` places the value directly inside the SQL query. 4. SQLite evaluates the injected syntax as part of the query. 5. Records outside the intended stock selection may be returned and processed by the backtest. 6. The resulting report can expose unintended database content or present misleading trading results. Example invocation: ```bash python scripts/backtest.py --stock "' OR '1'='1" ``` ### Impact Assessment Exploitation occurs with the privileges of the local process and is limited to the SQLite database at `~/.openclaw/workspace/a-stock/data.db`. A successful attacker may: - Read records for stocks other than the requested stock. - Potentially retrieve data from other d ...[truncated 337 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Use a parameterized query rather than embedding the value in SQL: ```python df = pd.read_sql( "SELECT * FROM daily_data WHERE code=? ORDER BY date", conn, params=(code,), ) ``` Apply defense-in-depth validation before accessing the database: ```python import re if not re.fullmatch(r"\d{6}", code): raise ValueError("Stock code must contain exactly six digits") ``` Additional hardening measures include: 1. Centralize stock-code normalization and validation so all scripts use identical rules. 2. Return a controlled error for invalid input rather than forwarding it to SQLite. 3. Add automated tests using quotes, SQL comments, Boolean expressions, and `UNION` payloads. 4. Grant the process only the filesystem permissions required to access its own database. ]]>
