@@ -238,8 +238,9 @@ def write_pending(self, record: PendingApproval) -> None:
238238 record = replace (record , prev_event_hash = prev_event_hash )
239239 values = _record_values (record )
240240 placeholders = ", " .join ("?" for _ in _COLUMNS )
241+ # SQL is safe: _COLUMNS is static and values are bound.
241242 self ._conn .execute (
242- f"INSERT INTO pending_approvals ({ ', ' .join (_COLUMNS )} ) VALUES ({ placeholders } )" ,
243+ f"INSERT INTO pending_approvals ({ ', ' .join (_COLUMNS )} ) VALUES ({ placeholders } )" , # nosec B608
243244 values ,
244245 )
245246 if not append_only :
@@ -254,8 +255,9 @@ def get_pending(self, request_id: str) -> PendingApproval | None:
254255 """Return the approval record for a request ID, if present."""
255256
256257 with self ._lock :
258+ # SQL is safe: _COLUMNS is static and request_id is bound.
257259 row = self ._conn .execute (
258- f"SELECT { ', ' .join (_COLUMNS )} FROM pending_approvals WHERE request_id = ?" ,
260+ f"SELECT { ', ' .join (_COLUMNS )} FROM pending_approvals WHERE request_id = ?" , # nosec B608
259261 (request_id ,),
260262 ).fetchone ()
261263 return None if row is None else _row_to_record (row )
@@ -265,14 +267,16 @@ def list_pending(self, *, since_timestamp: int | None = None) -> list[PendingApp
265267
266268 with self ._lock :
267269 if since_timestamp is None :
270+ # SQL is safe: _COLUMNS is static and status is bound.
268271 rows = self ._conn .execute (
269- f"SELECT { ', ' .join (_COLUMNS )} FROM pending_approvals WHERE status = ? "
272+ f"SELECT { ', ' .join (_COLUMNS )} FROM pending_approvals WHERE status = ? " # nosec B608
270273 "ORDER BY created_at, request_id" ,
271274 (ApprovalStatus .PENDING .value ,),
272275 ).fetchall ()
273276 else :
277+ # SQL is safe: _COLUMNS is static and filters are bound.
274278 rows = self ._conn .execute (
275- f"SELECT { ', ' .join (_COLUMNS )} FROM pending_approvals WHERE status = ? "
279+ f"SELECT { ', ' .join (_COLUMNS )} FROM pending_approvals WHERE status = ? " # nosec B608
276280 "AND created_at >= ? ORDER BY created_at, request_id" ,
277281 (ApprovalStatus .PENDING .value , since_timestamp ),
278282 ).fetchall ()
@@ -290,8 +294,9 @@ def transition(self, request_id: str, new_status: str, **fields: Any) -> Pending
290294 with self ._lock :
291295 self ._begin ()
292296 try :
297+ # SQL is safe: _COLUMNS is static and request_id is bound.
293298 row = self ._conn .execute (
294- f"SELECT { ', ' .join (_COLUMNS )} FROM pending_approvals WHERE request_id = ?" ,
299+ f"SELECT { ', ' .join (_COLUMNS )} FROM pending_approvals WHERE request_id = ?" , # nosec B608
295300 (request_id ,),
296301 ).fetchone ()
297302 if row is None :
@@ -317,8 +322,9 @@ def transition(self, request_id: str, new_status: str, **fields: Any) -> Pending
317322 updates ["status" ] = normalized
318323 self ._validate_transition_fields (updates )
319324 assignments = ", " .join (f"{ column } = ?" for column in updates )
325+ # SQL is safe: assignment columns are restricted and values are bound.
320326 self ._conn .execute (
321- f"UPDATE pending_approvals SET { assignments } WHERE request_id = ?" ,
327+ f"UPDATE pending_approvals SET { assignments } WHERE request_id = ?" , # nosec B608
322328 (* updates .values (), request_id ),
323329 )
324330 self ._rebuild_chain_locked ()
@@ -438,8 +444,9 @@ def list_records(
438444 params .extend (ids )
439445 where = f"WHERE { ' AND ' .join (clauses )} " if clauses else ""
440446 with self ._lock :
447+ # SQL is safe: _COLUMNS is static and where clauses are validated.
441448 rows = self ._conn .execute (
442- f"SELECT { ', ' .join (_COLUMNS )} FROM pending_approvals "
449+ f"SELECT { ', ' .join (_COLUMNS )} FROM pending_approvals " # nosec B608
443450 f"{ where } ORDER BY created_at, request_id" ,
444451 params ,
445452 ).fetchall ()
@@ -465,8 +472,9 @@ def find_active_similar_grant(
465472 )
466473 placeholders = ", " .join ("?" for _ in reusable_statuses )
467474 with self ._lock :
475+ # SQL is safe: _COLUMNS is static and filters are bound.
468476 row = self ._conn .execute (
469- f"SELECT { ', ' .join (_COLUMNS )} FROM pending_approvals "
477+ f"SELECT { ', ' .join (_COLUMNS )} FROM pending_approvals " # nosec B608
470478 f"WHERE status IN ({ placeholders } ) "
471479 "AND approval_scope = ? AND granted_scope_expires_at > ? "
472480 "AND downstream_server = ? AND tool_name = ? AND risk_class = ? "
@@ -494,16 +502,18 @@ def vacuum_terminal_records(self, *, before_timestamp: int) -> int:
494502 with self ._lock :
495503 self ._begin ()
496504 try :
505+ # SQL is safe: terminal status placeholders and cutoff are controlled.
497506 row = self ._conn .execute (
498507 "SELECT COUNT(*) FROM pending_approvals "
499- f"WHERE status IN ({ placeholders } ) AND created_at < ?" ,
508+ f"WHERE status IN ({ placeholders } ) AND created_at < ?" , # nosec B608
500509 (* terminal , int (before_timestamp )),
501510 ).fetchone ()
502511 deleted = int (row [0 ])
503512 if deleted :
513+ # SQL is safe: terminal status placeholders and cutoff are controlled.
504514 self ._conn .execute (
505515 "DELETE FROM pending_approvals "
506- f"WHERE status IN ({ placeholders } ) AND created_at < ?" ,
516+ f"WHERE status IN ({ placeholders } ) AND created_at < ?" , # nosec B608
507517 (* terminal , int (before_timestamp )),
508518 )
509519 self ._rebuild_chain_locked ()
@@ -607,8 +617,9 @@ def _migrate_v3_to_v4_locked(self) -> None:
607617 # SQLite cannot relax a NOT NULL column constraint in place; rebuild the table.
608618 columns = ", " .join (_COLUMNS )
609619 self ._conn .execute (_CREATE_PENDING_APPROVALS_MIGRATION_SQL )
620+ # SQL is safe: migration columns are restricted to static _COLUMNS.
610621 self ._conn .execute (
611- f"INSERT INTO pending_approvals_new ({ columns } ) "
622+ f"INSERT INTO pending_approvals_new ({ columns } ) " # nosec B608
612623 f"SELECT { columns } FROM pending_approvals"
613624 )
614625 self ._conn .execute ("DROP TABLE pending_approvals" )
@@ -624,8 +635,9 @@ def _set_schema_version_locked(self, version: int) -> None:
624635 )
625636
626637 def _compute_chain_link_for_insert_locked (self , record : PendingApproval ) -> tuple [str , bool ]:
638+ # SQL is safe: _COLUMNS is static.
627639 row = self ._conn .execute (
628- f"SELECT { ', ' .join (_COLUMNS )} FROM pending_approvals "
640+ f"SELECT { ', ' .join (_COLUMNS )} FROM pending_approvals " # nosec B608
629641 "ORDER BY created_at DESC, request_id DESC LIMIT 1"
630642 ).fetchone ()
631643 if row is None :
@@ -639,8 +651,9 @@ def _compute_chain_link_for_insert_locked(self, record: PendingApproval) -> tupl
639651 return GENESIS_PREV_EVENT_HASH , False
640652
641653 def _rebuild_chain_locked (self ) -> None :
654+ # SQL is safe: _COLUMNS is static.
642655 rows = self ._conn .execute (
643- f"SELECT { ', ' .join (_COLUMNS )} FROM pending_approvals ORDER BY created_at, request_id"
656+ f"SELECT { ', ' .join (_COLUMNS )} FROM pending_approvals ORDER BY created_at, request_id" # nosec B608
644657 ).fetchall ()
645658 prev_hash = GENESIS_PREV_EVENT_HASH
646659 for row in rows :
@@ -654,8 +667,9 @@ def _rebuild_chain_locked(self) -> None:
654667 prev_hash = record_hash (data )
655668
656669 def _validate_chain_locked (self ) -> None :
670+ # SQL is safe: _COLUMNS is static.
657671 rows = self ._conn .execute (
658- f"SELECT { ', ' .join (_COLUMNS )} FROM pending_approvals ORDER BY created_at, request_id"
672+ f"SELECT { ', ' .join (_COLUMNS )} FROM pending_approvals ORDER BY created_at, request_id" # nosec B608
659673 ).fetchall ()
660674 prev_hash = GENESIS_PREV_EVENT_HASH
661675 for row in rows :
0 commit comments