Skip to content

fix(csv_sql): prevent SQL injection via DuckDB parameter binding#1408

Merged
Hundao merged 6 commits into
aden-hive:mainfrom
Jbheemeswar:fix/1256-csv-sql-safe-path
Mar 30, 2026
Merged

fix(csv_sql): prevent SQL injection via DuckDB parameter binding#1408
Hundao merged 6 commits into
aden-hive:mainfrom
Jbheemeswar:fix/1256-csv-sql-safe-path

Conversation

@Jbheemeswar

@Jbheemeswar Jbheemeswar commented Jan 27, 2026

Copy link
Copy Markdown
Contributor

Fixes #1256

Description

Prevents SQL injection in csv_sql by removing SQL string interpolation and using DuckDB parameter binding for CSV paths.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)

Changes Made

  • Replaced DuckDB SQL f-string interpolation with parameter binding
  • Blocked multi-statement and comment-based injection attempts (;, --, /* */)
  • Enforced SELECT-only queries

Testing

  • Unit tests pass (pytest tools/tests/tools/test_csv_tool.py)
  • Lint passes (ruff format and ruff check)
  • Manual testing performed

Note: Full test suite has known Windows / PYTHONPATH issues. Targeted csv_tool tests pass locally.

Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my code
  • I have commented my code where necessary
  • I have made corresponding changes to the documentation (not required)
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective
  • New and existing relevant unit tests pass locally

Summary by CodeRabbit

  • Bug Fixes
    • Enhanced CSV SQL validation to allow WITH/CTE queries, detect and reject destructive or multi-statement/comment patterns, and use parameterized CSV loading to safely handle filenames with special characters.
  • Tests
    • Added regression and security tests covering WITH queries, rejection of disallowed SQL patterns, and filenames containing special characters (e.g., apostrophes).

@mishrapravin114

Copy link
Copy Markdown
Contributor

Consider adding a test that uses a path containing a single quote (e.g. O'Reilly.csv) to lock in the parameter-binding behavior and avoid regressions on valid filenames. That would directly reinforce the fix for #1256. FYI @vincentjiang777

@Jbheemeswar

Copy link
Copy Markdown
Contributor Author

Thanks! Added a regression test for a CSV filename containing a single quote (O'Reilly.csv) to lock in parameter binding behavior and avoid regressions.

@Jbheemeswar

Copy link
Copy Markdown
Contributor Author

Hi maintainers - this PR fixes #1256.

All required checks are passing, but there is 1 workflow awaiting approval.
Could someone please approve the workflow run so CI can execute?

Thanks!

Juttiga Bheem and others added 5 commits March 30, 2026 22:59
Removed detailed docstring arguments and return information for the CSV query function. Improved security checks for SQL queries.
Added security regression tests to reject non-SELECT queries and multi-statement queries.
@Hundao
Hundao force-pushed the fix/1256-csv-sql-safe-path branch from 1919e63 to da75728 Compare March 30, 2026 15:02
@coderabbitai

coderabbitai Bot commented Mar 30, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a6716e5b-6b28-4a90-ae43-c8358b570c4a

📥 Commits

Reviewing files that changed from the base of the PR and between da75728 and 8c2db7c.

📒 Files selected for processing (2)
  • tools/src/aden_tools/tools/csv_tool/csv_tool.py
  • tools/tests/tools/test_csv_tool.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • tools/src/aden_tools/tools/csv_tool/csv_tool.py
  • tools/tests/tools/test_csv_tool.py

📝 Walkthrough

Walkthrough

Enhanced csv_sql: added SQL validation (allow leading SELECT or WITH; reject multi-statement/comment tokens), switched CSV loading to DuckDB parameter binding for the file path, and added tests verifying quoted paths and rejected malicious SQL patterns.

Changes

Cohort / File(s) Summary
CSV Tool Security Enhancement
tools/src/aden_tools/tools/csv_tool/csv_tool.py
Rewrote csv_sql validation to accept SELECT or WITH, detect destructive keywords with word-boundary regex, reject multi-statement/comment tokens (;, --, /*, */), and load CSV via parameter binding (read_csv_auto(?)) instead of string interpolation.
CSV Tool Test Coverage
tools/tests/tools/test_csv_tool.py
Added TestCsvSql cases: successful query with single-quote filename (e.g., O'Reilly.csv), allowed WITH/CTE queries, acceptance of column names like created_at/updated_at, and rejections for non-SELECT/WITH statements, semicolon multi-statements, and SQL line comments.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I nibble at bugs and hop secure,
I bind the path so queries are pure.
Semicolons shooed, comments kept at bay,
WITH and SELECT may safely play.
Hooray—safe CSVs for another day!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: preventing SQL injection via DuckDB parameter binding in csv_sql, which directly corresponds to the primary objective of the PR.
Linked Issues check ✅ Passed All coding requirements from issue #1256 are met: parameter binding replaces f-string interpolation, multi-statement/comment tokens (;, --, /* */) are rejected, SELECT-only validation is enforced, and comprehensive unit tests validate all requirements.
Out of Scope Changes check ✅ Passed All changes are directly scoped to addressing issue #1256: csv_sql validation logic, parameter binding implementation, and related test coverage. No unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
tools/src/aden_tools/tools/csv_tool/csv_tool.py (1)

333-336: Consider adopting BigQuery's word-boundary regex for more robust keyword detection.

The current substring check can produce false positives on column/table names containing blocked keywords (e.g., SELECT created_at FROM data would be rejected because "CREATE" is in "CREATED_AT"). The BigQuery tool uses a more robust approach with word-boundary anchors:

WRITE_PATTERN = re.compile(r"\bINSERT\b|\bUPDATE\b|...", re.IGNORECASE)
if WRITE_PATTERN.search(query):
    return {"error": "..."}

This is existing behavior (lines 339-352 are unchanged), so not blocking this PR, but worth considering as a follow-up improvement.

Also applies to: 339-352

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/src/aden_tools/tools/csv_tool/csv_tool.py` around lines 333 - 336,
Replace the naive startswith check on query_upper in the CSV tool with a
regex-based word-boundary detection similar to BigQuery's approach: compile a
WRITE_PATTERN (using re.compile with
r"\b(INSERT|UPDATE|DELETE|CREATE|DROP|ALTER|TRUNCATE|MERGE|REPLACE)\b",
re.IGNORECASE)) and use WRITE_PATTERN.search(query) to detect prohibited
keywords anywhere as whole words; update the code that currently checks
query/query_upper.startswith(...) to use this pattern and return the same error
payload when a match is found, referencing the existing query variable and the
new WRITE_PATTERN for locating the change.
tools/tests/tools/test_csv_tool.py (1)

756-780: Security regression tests cover key acceptance criteria.

These tests validate rejection of non-SELECT queries and multi-statement attempts. Consider adding a few more cases for completeness:

  1. Comment token rejection (validates -- and /* */ blocking):
def test_reject_sql_comment_dash(self, csv_tools, products_csv, tmp_path):
    """Reject queries with SQL line comments."""
    with patch("aden_tools.tools.file_system_toolkits.security.WORKSPACES_DIR", str(tmp_path)):
        result = csv_tools["csv_sql"](
            path=products_csv.name,
            workspace_id=TEST_WORKSPACE_ID,
            agent_id=TEST_AGENT_ID,
            session_id=TEST_SESSION_ID,
            query="SELECT * FROM data -- WHERE id = 1",
        )
    assert "error" in result
  1. WITH statement positive case (validates CTEs work):
def test_with_cte_allowed(self, csv_tools, products_csv, tmp_path):
    """Allow valid WITH (CTE) queries."""
    with patch("aden_tools.tools.file_system_toolkits.security.WORKSPACES_DIR", str(tmp_path)):
        result = csv_tools["csv_sql"](
            path=products_csv.name,
            workspace_id=TEST_WORKSPACE_ID,
            agent_id=TEST_AGENT_ID,
            session_id=TEST_SESSION_ID,
            query="WITH electronics AS (SELECT * FROM data WHERE category = 'Electronics') SELECT * FROM electronics",
        )
    assert result["success"] is True
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/tests/tools/test_csv_tool.py` around lines 756 - 780, Add two pytest
cases to tools/tests/tools/test_csv_tool.py to cover SQL comment rejection and a
positive WITH/CTE case: create test_reject_sql_comment_dash that patches
aden_tools.tools.file_system_toolkits.security.WORKSPACES_DIR to tmp_path and
calls csv_tools["csv_sql"](path=products_csv.name,
workspace_id=TEST_WORKSPACE_ID, agent_id=TEST_AGENT_ID,
session_id=TEST_SESSION_ID, query="SELECT * FROM data -- WHERE id = 1") and
assert that "error" is in the result; and create test_with_cte_allowed that
similarly patches WORKSPACES_DIR and calls csv_tools["csv_sql"] with a CTE query
"WITH electronics AS (SELECT * FROM data WHERE category = 'Electronics') SELECT
* FROM electronics" and assert result["success"] is True; keep naming consistent
with existing tests (test_reject_non_select, test_reject_multi_statement) and
use the same fixtures (csv_tools, products_csv, tmp_path) and constants
(TEST_WORKSPACE_ID, TEST_AGENT_ID, TEST_SESSION_ID).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tools/src/aden_tools/tools/csv_tool/csv_tool.py`:
- Around line 333-336: Replace the naive startswith check on query_upper in the
CSV tool with a regex-based word-boundary detection similar to BigQuery's
approach: compile a WRITE_PATTERN (using re.compile with
r"\b(INSERT|UPDATE|DELETE|CREATE|DROP|ALTER|TRUNCATE|MERGE|REPLACE)\b",
re.IGNORECASE)) and use WRITE_PATTERN.search(query) to detect prohibited
keywords anywhere as whole words; update the code that currently checks
query/query_upper.startswith(...) to use this pattern and return the same error
payload when a match is found, referencing the existing query variable and the
new WRITE_PATTERN for locating the change.

In `@tools/tests/tools/test_csv_tool.py`:
- Around line 756-780: Add two pytest cases to
tools/tests/tools/test_csv_tool.py to cover SQL comment rejection and a positive
WITH/CTE case: create test_reject_sql_comment_dash that patches
aden_tools.tools.file_system_toolkits.security.WORKSPACES_DIR to tmp_path and
calls csv_tools["csv_sql"](path=products_csv.name,
workspace_id=TEST_WORKSPACE_ID, agent_id=TEST_AGENT_ID,
session_id=TEST_SESSION_ID, query="SELECT * FROM data -- WHERE id = 1") and
assert that "error" is in the result; and create test_with_cte_allowed that
similarly patches WORKSPACES_DIR and calls csv_tools["csv_sql"] with a CTE query
"WITH electronics AS (SELECT * FROM data WHERE category = 'Electronics') SELECT
* FROM electronics" and assert result["success"] is True; keep naming consistent
with existing tests (test_reject_non_select, test_reject_multi_statement) and
use the same fixtures (csv_tools, products_csv, tmp_path) and constants
(TEST_WORKSPACE_ID, TEST_AGENT_ID, TEST_SESSION_ID).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a7e4247f-8cde-45bd-86e1-51b8a9ec8d39

📥 Commits

Reviewing files that changed from the base of the PR and between eba7524 and da75728.

📒 Files selected for processing (2)
  • tools/src/aden_tools/tools/csv_tool/csv_tool.py
  • tools/tests/tools/test_csv_tool.py

Substring matching caused false positives on column names like
created_at, updated_at, deleted_at. Switch to \b word-boundary regex.
Also add tests for comment rejection, CTE queries, and keyword-in-column-name.
@Hundao
Hundao merged commit e9fd015 into aden-hive:main Mar 30, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Security]: csv_sql should avoid SQL string interpolation for CSV path (use parameter binding)

3 participants