diff --git a/.jules/sentinel.md b/.jules/sentinel.md new file mode 100644 index 0000000..00771be --- /dev/null +++ b/.jules/sentinel.md @@ -0,0 +1,4 @@ +## 2025-02-21 - Fix SQL injection in SQLite schema validation +**Vulnerability:** SQL injection in `internal/storage/sqlite.go` due to using `fmt.Sprintf` to concatenate the table name into `PRAGMA table_info(%s);` when checking if a schema column exists. +**Learning:** Even internal toolings or internal schema validation queries against SQLite need strict parameters. While `PRAGMA` statements typically do not support parameterized variables, the table-valued function `pragma_table_info(?)` does, providing a secure alternative for metadata extraction. +**Prevention:** Avoid string formatting to construct SQL queries, especially for tables or object names. When retrieving metadata in SQLite, prefer parameterized PRAGMA functions like `SELECT * FROM pragma_table_info(?)` over direct string interpolation. diff --git a/internal/storage/sqlite.go b/internal/storage/sqlite.go index 8c0cb1e..130921f 100644 --- a/internal/storage/sqlite.go +++ b/internal/storage/sqlite.go @@ -200,7 +200,8 @@ LIMIT 1; } func sqliteColumnExists(ctx context.Context, db *sql.DB, table, column string) (bool, error) { - cols, err := db.QueryContext(ctx, fmt.Sprintf("PRAGMA table_info(%s);", table)) + // Use table-valued function to avoid SQL injection on table name + cols, err := db.QueryContext(ctx, `SELECT cid, name, type, "notnull", dflt_value, pk FROM pragma_table_info(?)`, table) if err != nil { return false, err }