[object Object]

← back to Cli Printing Press

fix(cli): validate ListIDs resourceType to close SQL injection (#1000)

242a56bc6a2d88cc8b93ac29d90254a7f7d6e1e9 · 2026-05-11 10:48:01 -0400 · brennaman

* fix(cli): validate ListIDs resourceType to close SQL injection

Store.ListIDs previously interpolated resourceType directly into the
SELECT, allowing a crafted value to read or corrupt the database.
Resolve the table name via a parameterized sqlite_master lookup and
substitute only the trusted name (double-quoted) into the query;
fall back to the generic resources table when the lookup misses.

Mirrored into the outlook-calendar PR (mvanhorn/printing-press-library#408)
so this PR ships clean; every future regen now picks up the fix too.

* Update internal/generator/templates/store.go.tmpl

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: Trevin Chow <trevin@trevinchow.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

Files touched

Diff

commit 242a56bc6a2d88cc8b93ac29d90254a7f7d6e1e9
Author: brennaman <4397563+brennaman@users.noreply.github.com>
Date:   Mon May 11 10:48:01 2026 -0400

    fix(cli): validate ListIDs resourceType to close SQL injection (#1000)
    
    * fix(cli): validate ListIDs resourceType to close SQL injection
    
    Store.ListIDs previously interpolated resourceType directly into the
    SELECT, allowing a crafted value to read or corrupt the database.
    Resolve the table name via a parameterized sqlite_master lookup and
    substitute only the trusted name (double-quoted) into the query;
    fall back to the generic resources table when the lookup misses.
    
    Mirrored into the outlook-calendar PR (mvanhorn/printing-press-library#408)
    so this PR ships clean; every future regen now picks up the fix too.
    
    * Update internal/generator/templates/store.go.tmpl
    
    Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Trevin Chow <trevin@trevinchow.com>
    Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
---
 internal/generator/templates/store.go.tmpl | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)

diff --git a/internal/generator/templates/store.go.tmpl b/internal/generator/templates/store.go.tmpl
index a022117d..e86553d7 100644
--- a/internal/generator/templates/store.go.tmpl
+++ b/internal/generator/templates/store.go.tmpl
@@ -1041,11 +1041,21 @@ func (s *Store) GetSyncCursor(resourceType string) string {
 
 // ListIDs returns all IDs from a resource's domain table, or from the generic
 // resources table if no domain table exists. Used by dependent sync to iterate parents.
+//
+// resourceType is never interpolated into SQL directly. We resolve it to a real
+// table name via a parameterized sqlite_master lookup; only that trusted name is
+// substituted (double-quoted) into the SELECT. Callers may pass any string.
 func (s *Store) ListIDs(resourceType string) ([]string, error) {
-	// Try domain table first (tables are named after the resource type)
-	query := fmt.Sprintf("SELECT id FROM %s", resourceType)
-	rows, err := s.db.Query(query)
-	if err != nil {
+	var table string
+	err := s.db.QueryRow(
+		`SELECT name FROM sqlite_master WHERE type='table' AND name=?`,
+		resourceType,
+	).Scan(&table)
+	var rows *sql.Rows
+	if err == nil && table != "" {
+		rows, err = s.db.Query(fmt.Sprintf(`SELECT id FROM "%s"`, strings.ReplaceAll(table, `"`, `""`)))
+	}
+	if err != nil || table == "" {
 		// Fall back to generic resources table
 		rows, err = s.db.Query("SELECT id FROM resources WHERE resource_type = ?", resourceType)
 		if err != nil {

← 76db1cc4 chore(main): release 4.3.0 (#898)  ·  back to Cli Printing Press  ·  fix(cli): emit --body-json fallback for oneOf/anyOf request 910c228d →