T09 · Insecure Skill Coding Practices
- Location
scripts/v2.15_prediction.py:55- Finding
SQL Injection Through an Unvalidated SQLite Table Identifier
- Content
View full analysis
Vulnerability Details
File Location:
scripts/v2.15_prediction.py, lines 55-73 and 287-296
Vulnerability Type: SQL injection through an unvalidated identifier
Risk Level: MediumVulnerable Code
python def get_history_data(conn: sqlite3.Connection, lottery_type: str = "ssq", limit: int = None) -> List[Dict]: """获取历史数据""" conn.row_factory = sqlite3.Row cursor = conn.cursor() query = f"SELECT * FROM {lottery_type} ORDER BY issue DESC" if limit: query += f" LIMIT {limit}" cursor.execute(query) rows = cursor.fetchall() return [dict(row) for row in rows] def get_latest_issue(conn: sqlite3.Connection, lottery_type: str = "ssq") -> str: """获取最新已开奖期号""" cursor = conn.cursor() cursor.execute(f"SELECT issue FROM {lottery_type} ORDER BY issue DESC LIMIT 1") row = cursor.fetchone() return row[0] if row else NoneThe affected value originates from an unrestricted command-line argument:
python parser.add_argument("--lottery", type=str, default="ssq", help="彩种(ssq/dlt)")It is passed directly to the vulnerable functions:
python latest_issue = get_latest_issue(conn, args.lottery) history = get_history_data(conn, args.lottery, limit=3430)Technical Analysis
The
--lotteryargument is controlled by the caller and is interpolated directly into two SQL statements as a table identifier. No allowlist or identifier validation is applied.SQLite parameter placeholders cannot be used for table identifiers. Consequently, dynamic identifiers must be selected from a fixed mapping or strictly validated before query construction. The current implementation permits a caller to introduce SQL syntax such as a subquery, compound query, comment marker, or computationally expensive expression.
Python's
sqlite3.Cursor.execute()generally prevents execution of multiple statements in one call, w ...[truncated 2155 chars]- Remediation
View remediation
Remediation Suggestions
-
Restrict the command-line argument to supported values:
python parser.add_argument( "--lottery", choices=("ssq", "dlt"), default="ssq", help="Lottery type" ) -
Map logical values to fixed identifiers at the database boundary:
python LOTTERY_TABLES = { "ssq": "ssq", "dlt": "dlt", } def resolve_lottery_table(lottery_type: str) -> str: try: return LOTTERY_TABLES[lottery_type] except KeyError: raise ValueError("Unsupported lottery type") -
Use only the resolved table name when constructing SQL:
python table = resolve_lottery_table(lottery_type) query = f'SELECT * FROM "{table}" ORDER BY issue DESC LIMIT ?' cursor.execute(query, (limit,)) -
Validate the value again inside every database-facing function rather than relying only on command-line parsing. This protects future non-CLI callers.
-
Avoid returning raw database exception text to untrusted callers. Return a stable error code and record detailed diagnostics only in a protected local log.
-
Add negative tests using unexpected identifiers, SQL comment markers, subqueries, and compound-query syntax. Verify that all such input is rejected before reaching SQLite.
-
