Skip to content

Harden SQL handling against injection; document safe-query rules.#3

Merged
UnityZappy merged 1 commit into
mainfrom
bugfix/sql-sanitization
Jun 1, 2026
Merged

Harden SQL handling against injection; document safe-query rules.#3
UnityZappy merged 1 commit into
mainfrom
bugfix/sql-sanitization

Conversation

@UnityZappy

Copy link
Copy Markdown
Collaborator

The report path executes SQL via ExecuteQuery(string sql), which Cycode SAST flagged as unsanitized input. All callers pass internally-constructed SQL (ReportSql constants; DownstreamStats interpolates a numeric long), so there is no live injection vector — but the dynamic-SQL builders were inconsistent and the sink had no least-privilege control. This tightens both and codifies the rules so they stay enforced.

Code:

  • Open report/analysis DB connections read-only (SQLite Mode=ReadOnly, DuckDB ACCESS_MODE=READ_ONLY) so a malformed query reaching ExecuteQuery cannot modify or drop data; export writers stay read-write. Covers Sqlite/DuckDbReportQueries, MultiSnapshotReportBuilder, SummaryReportRunner.
  • DuckDbReportQueries.HasColumn: replace quote-escaped identifier interpolation with positional bind parameters against information_schema.columns.
  • MultiSnapshotReportBuilder: bind the native-type filter value as a parameter (DuckDB '?', SQLite '$nativeType') instead of interpolating it.
  • Document the ExecuteQuery trust/read-only contract on IReportQueryBackend.

Docs & guidance:

  • Add docs/sql-safety.md (best practices for SQLite + DuckDB) and wire it into the mkdocs nav.
  • Add CLAUDE.md with the enforced project rule for SQL handling.
  • Add SECURITY.md with reporting policy and a triage-log entry for the Cycode finding (disposition: false positive; mitigations + Unity suppression steps).

Verified: build clean, 19/19 tests pass, and report/summary/multi-report run end-to-end on real DuckDB and SQLite databases over read-only connections.

The report path executes SQL via ExecuteQuery(string sql), which Cycode SAST flagged as unsanitized input. All callers pass internally-constructed SQL (ReportSql constants; DownstreamStats interpolates a numeric long), so there is no live injection vector — but the dynamic-SQL builders were inconsistent and the sink had no least-privilege control. This tightens both and codifies the rules so they stay enforced.

Code:
- Open report/analysis DB connections read-only (SQLite Mode=ReadOnly, DuckDB ACCESS_MODE=READ_ONLY) so a malformed query reaching ExecuteQuery cannot modify or drop data; export writers stay read-write. Covers Sqlite/DuckDbReportQueries, MultiSnapshotReportBuilder, SummaryReportRunner.
- DuckDbReportQueries.HasColumn: replace quote-escaped identifier interpolation with positional bind parameters against information_schema.columns.
- MultiSnapshotReportBuilder: bind the native-type filter value as a parameter (DuckDB '?', SQLite '$nativeType') instead of interpolating it.
- Document the ExecuteQuery trust/read-only contract on IReportQueryBackend.

Docs & guidance:
- Add docs/sql-safety.md (best practices for SQLite + DuckDB) and wire it into the mkdocs nav.
- Add CLAUDE.md with the enforced project rule for SQL handling.
- Add SECURITY.md with reporting policy and a triage-log entry for the Cycode finding (disposition: false positive; mitigations + Unity suppression steps).

Verified: build clean, 19/19 tests pass, and report/summary/multi-report run end-to-end on real DuckDB and SQLite databases over read-only connections.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@UnityZappy UnityZappy requested a review from a team as a code owner June 1, 2026 16:52
@UnityZappy UnityZappy merged commit 58452f7 into main Jun 1, 2026
4 checks passed
@UnityZappy UnityZappy deleted the bugfix/sql-sanitization branch June 1, 2026 17:06
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.

1 participant