From b0a1029d5571ddcddc10a241a42f20a8c3221050 Mon Sep 17 00:00:00 2001 From: Cole Murray Date: Fri, 7 Nov 2025 05:00:10 -0800 Subject: [PATCH] fix(checkpoint-postgres): Replace f-string SQL formatting with parameterized queries in migration statements (#6328) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Replace f-string SQL formatting with parameterized queries to prevent potential SQL injection in checkpoint migration code. ## Changes Updated the migration version tracking INSERT statements in all checkpoint saver classes to use parameterized queries instead of f-string formatting: - `PostgresSaver` (libs/checkpoint-postgres/langgraph/checkpoint/postgres/__init__.py:100) - `AsyncPostgresSaver` (libs/checkpoint-postgres/langgraph/checkpoint/postgres/aio.py:104-106) - `ShallowPostgresSaver` (libs/checkpoint-postgres/langgraph/checkpoint/postgres/shallow.py:255) - `AsyncShallowPostgresSaver` (libs/checkpoint-postgres/langgraph/checkpoint/postgres/shallow.py:617-619) **Before (vulnerable to SQL injection):** ```python cur.execute(f"INSERT INTO checkpoint_migrations (v) VALUES ({v})") ``` **After (using parameterized query):** ```python cur.execute("INSERT INTO checkpoint_migrations (v) VALUES (%s)", (v,)) ``` ## Risk Assessment The practical risk is low since `v` is an integer loop variable controlled by the codebase. However, using string formatting in SQL queries is a well-known anti-pattern that can lead to SQL injection vulnerabilities, especially if the code is later refactored or copied to other contexts. ## Testing - ✅ All 216 tests passing on PostgreSQL 15 and 16 - ✅ Linting and type checking passing - ✅ No functional changes to behavior --- .../langgraph/checkpoint/postgres/__init__.py | 2 +- .../langgraph/checkpoint/postgres/aio.py | 4 +++- .../langgraph/checkpoint/postgres/shallow.py | 6 ++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/libs/checkpoint-postgres/langgraph/checkpoint/postgres/__init__.py b/libs/checkpoint-postgres/langgraph/checkpoint/postgres/__init__.py index 9adf8c4bf..01816c8f2 100644 --- a/libs/checkpoint-postgres/langgraph/checkpoint/postgres/__init__.py +++ b/libs/checkpoint-postgres/langgraph/checkpoint/postgres/__init__.py @@ -97,7 +97,7 @@ class PostgresSaver(BasePostgresSaver): strict=False, ): cur.execute(migration) - cur.execute(f"INSERT INTO checkpoint_migrations (v) VALUES ({v})") + cur.execute("INSERT INTO checkpoint_migrations (v) VALUES (%s)", (v,)) if self.pipe: self.pipe.sync() diff --git a/libs/checkpoint-postgres/langgraph/checkpoint/postgres/aio.py b/libs/checkpoint-postgres/langgraph/checkpoint/postgres/aio.py index 66013ea24..1d507b893 100644 --- a/libs/checkpoint-postgres/langgraph/checkpoint/postgres/aio.py +++ b/libs/checkpoint-postgres/langgraph/checkpoint/postgres/aio.py @@ -102,7 +102,9 @@ class AsyncPostgresSaver(BasePostgresSaver): strict=False, ): await cur.execute(migration) - await cur.execute(f"INSERT INTO checkpoint_migrations (v) VALUES ({v})") + await cur.execute( + "INSERT INTO checkpoint_migrations (v) VALUES (%s)", (v,) + ) if self.pipe: await self.pipe.sync() diff --git a/libs/checkpoint-postgres/langgraph/checkpoint/postgres/shallow.py b/libs/checkpoint-postgres/langgraph/checkpoint/postgres/shallow.py index 024df3848..d3d5c8f0b 100644 --- a/libs/checkpoint-postgres/langgraph/checkpoint/postgres/shallow.py +++ b/libs/checkpoint-postgres/langgraph/checkpoint/postgres/shallow.py @@ -252,7 +252,7 @@ class ShallowPostgresSaver(BasePostgresSaver): strict=False, ): cur.execute(migration) - cur.execute(f"INSERT INTO checkpoint_migrations (v) VALUES ({v})") + cur.execute("INSERT INTO checkpoint_migrations (v) VALUES (%s)", (v,)) if self.pipe: self.pipe.sync() @@ -614,7 +614,9 @@ class AsyncShallowPostgresSaver(BasePostgresSaver): strict=False, ): await cur.execute(migration) - await cur.execute(f"INSERT INTO checkpoint_migrations (v) VALUES ({v})") + await cur.execute( + "INSERT INTO checkpoint_migrations (v) VALUES (%s)", (v,) + ) if self.pipe: await self.pipe.sync()