Skip to content

🔒 Fix potential SQL Injection in execute_sql - #11337

Open
undivisible wants to merge 4 commits into
mainfrom
fix-sqli-agent-vm-10420643532383619873
Open

🔒 Fix potential SQL Injection in execute_sql#11337
undivisible wants to merge 4 commits into
mainfrom
fix-sqli-agent-vm-10420643532383619873

Conversation

@undivisible

@undivisible undivisible commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

🎯 What:
The vulnerability fixed is a potential SQL Injection in the execute_sql function located in backend/agent_vm/main.py. The function was passing an arbitrary user-provided query directly to SQLite with only fragile string blocklists for defense.

⚠️ Risk:
If left unfixed, the agent (or an attacker guiding the agent) could craft a malicious payload (e.g. using WITH clauses or subqueries) to modify, truncate, or drop internal local state databases used by agent_vm. It could also bypass the query blocklist to execute destructive PRAGMA commands or attach other databases on the host system.

🛡️ Solution:
The fix leverages sqlite3.set_authorizer. An authorizer is explicitly attached to the SQLite connection strictly during the execute() call. The authorizer permits only read-only actions (SQLITE_SELECT, SQLITE_READ, SQLITE_FUNCTION) at the database engine level, entirely neutralizing any potential destructive operations while preserving the agent's ability to run legitimate data retrieval SELECT queries. The authorizer is then detached in a finally block to not interfere with other internal writes (e.g. run_sync).


PR created automatically by Jules for task 10420643532383619873 started by @undivisible

Review in cubic


Note

Medium Risk
Touches security-sensitive agent SQL execution on the VM database; the scoped authorizer reduces injection risk but must stay cleared so sync/upload paths can still write.

Overview
Hardens the agent VM execute_sql tool so user/agent SQL cannot mutate the shared SQLite connection beyond what the string blocklist was meant to stop.

read_only_sql_authorizer is registered on the connection only for the duration of each query (set_authorizer in a try/finally that clears it afterward). The engine allows SQLITE_SELECT, SQLITE_READ, SQLITE_FUNCTION, and PRAGMA data_version; everything else is denied. That blocks destructive or sneaky operations even if they slip past the existing SELECT-only and keyword checks, while run_sync and other internal writes stay unaffected when the authorizer is cleared.

Unit tests cover FTS5 MATCH reads, authorizer cleanup after a failed query, and rejection of DELETE, UPDATE, and multi-statement payloads without changing table data.

Reviewed by Cursor Bugbot for commit 72848cd. Configure here.

Failure-Class: none

@google-labs-jules

Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@cursor

cursor Bot commented Aug 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_fc67504b-25cd-4da1-b177-ea7258d5cc9a)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4ec7d15da3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread backend/agent_vm/main.py Outdated
Comment on lines +499 to +501
if action in (sqlite3.SQLITE_SELECT, sqlite3.SQLITE_READ, sqlite3.SQLITE_FUNCTION):
return sqlite3.SQLITE_OK
return sqlite3.SQLITE_DENY

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Allow FTS5's read-only data_version authorization

When execute_sql queries any FTS5 table, FTS5 internally requests SQLITE_PRAGMA for data_version, which this allowlist denies; I reproduced this with SQLite 3.45.1, where a SELECT ... FROM screenshots_fts ... MATCH ... returns authorization denied. This affects the uploaded production database because desktop/macos/Desktop/Sources/Rewind/Core/RewindDatabase.swift:1329-1336 creates screenshots_fts using FTS5, so permit the narrowly scoped read-only data_version action while continuing to deny other pragmas.

Useful? React with 👍 / 👎.

Comment thread backend/agent_vm/main.py Outdated
Comment on lines +498 to +501
def authorizer(action: int, arg1: str | None, arg2: str | None, dbname: str | None, source: str | None) -> int:
if action in (sqlite3.SQLITE_SELECT, sqlite3.SQLITE_READ, sqlite3.SQLITE_FUNCTION):
return sqlite3.SQLITE_OK
return sqlite3.SQLITE_DENY

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add behavioral coverage for the SQL authorization boundary

This security fix changes only production code, while the existing test at backend/tests/unit/test_agent_vm_protocol.py:676-687 still covers only a successful ordinary SELECT; it would pass if the authorizer were removed and did not expose the FTS regression above. Add behavioral tests that exercise the newly denied operation and a representative production read-only path, as the repository explicitly requires regression coverage for bug fixes.

AGENTS.md reference: AGENTS.md:L28-L28

Useful? React with 👍 / 👎.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file

Confidence score: 4/5

  • In backend/agent_vm/main.py, the SQL-injection hardening via sqlite3.set_authorizer is unverified by a regression test, so future refactors could silently weaken the protection and reintroduce a security regression—add a focused regression test that reproduces the original injection path and proves it is blocked.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="backend/agent_vm/main.py">

<violation number="1" location="backend/agent_vm/main.py:498">
P2: This is a security fix (defusing SQL injection via sqlite3.set_authorizer) but it lands without a regression test, which the repo's Definition of Done explicitly requires ('Bug fixes include the regression test that would have caught the bug'). The agent-vm DB already has a test harness in backend/tests/unit/test_agent_vm_protocol.py with an execute_sql happy-path test (test_execute_sql_serializes_sqlite_rows), so a focused test is cheap to add. Consider adding tests that (1) assert destructive statements — DROP/INSERT/UPDATE via a WITH/subquery attempt and PRAGMA — are rejected with an error rather than mutating the DB, and (2) assert the authorizer is detached in the finally block so a subsequent run_sync/upload write on the same connection still succeeds. Otherwise the security boundary has no guard against regression.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread backend/agent_vm/main.py Outdated
if not re.search(r"\bLIMIT\b", query, re.I):
query = query.rstrip().rstrip(";") + " LIMIT 200"
try:
def authorizer(action: int, arg1: str | None, arg2: str | None, dbname: str | None, source: str | None) -> int:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This is a security fix (defusing SQL injection via sqlite3.set_authorizer) but it lands without a regression test, which the repo's Definition of Done explicitly requires ('Bug fixes include the regression test that would have caught the bug'). The agent-vm DB already has a test harness in backend/tests/unit/test_agent_vm_protocol.py with an execute_sql happy-path test (test_execute_sql_serializes_sqlite_rows), so a focused test is cheap to add. Consider adding tests that (1) assert destructive statements — DROP/INSERT/UPDATE via a WITH/subquery attempt and PRAGMA — are rejected with an error rather than mutating the DB, and (2) assert the authorizer is detached in the finally block so a subsequent run_sync/upload write on the same connection still succeeds. Otherwise the security boundary has no guard against regression.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/agent_vm/main.py, line 498:

<comment>This is a security fix (defusing SQL injection via sqlite3.set_authorizer) but it lands without a regression test, which the repo's Definition of Done explicitly requires ('Bug fixes include the regression test that would have caught the bug'). The agent-vm DB already has a test harness in backend/tests/unit/test_agent_vm_protocol.py with an execute_sql happy-path test (test_execute_sql_serializes_sqlite_rows), so a focused test is cheap to add. Consider adding tests that (1) assert destructive statements — DROP/INSERT/UPDATE via a WITH/subquery attempt and PRAGMA — are rejected with an error rather than mutating the DB, and (2) assert the authorizer is detached in the finally block so a subsequent run_sync/upload write on the same connection still succeeds. Otherwise the security boundary has no guard against regression.</comment>

<file context>
@@ -495,9 +495,18 @@ def execute_sql(query: str) -> str:
     if not re.search(r"\bLIMIT\b", query, re.I):
         query = query.rstrip().rstrip(";") + " LIMIT 200"
     try:
+        def authorizer(action: int, arg1: str | None, arg2: str | None, dbname: str | None, source: str | None) -> int:
+            if action in (sqlite3.SQLITE_SELECT, sqlite3.SQLITE_READ, sqlite3.SQLITE_FUNCTION):
+                return sqlite3.SQLITE_OK
</file context>

Comment thread backend/agent_vm/main.py Outdated
@undivisible undivisible added human Human-authored pull request backend Backend Task (python) AI needs-maintainer-review Needs a human maintainer to sign off before merge needs-tests PR introduces logic that should be covered by tests and removed human Human-authored pull request labels Aug 10, 2026

@undivisible undivisible left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SQL authorizer blocks all SQLITE_PRAGMA actions, which can reject legitimate FTS5 reads that request data_version. Please allow only the required read-only pragma (or equivalent safe handling), and add tests covering FTS5 MATCH reads and destructive/injection attempts.

@cursor

cursor Bot commented Aug 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_18f3fc1e-4583-4db0-ba7c-b45c10abab8d)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 27b4a4fe87

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +725 to +727
"DELETE FROM screenshots",
"UPDATE screenshots SET id = 'changed'",
"SELECT 1; DROP TABLE screenshots",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Exercise the authorizer instead of legacy filters

The fresh parameterized cases added here are all rejected before set_authorizer is installed: DELETE and UPDATE fail the existing SELECT-only check, while the multi-statement query fails the existing semicolon check. Consequently, removing read_only_sql_authorizer entirely would leave every new denial test passing, so the security boundary remains unguarded; add a behavioral case that reaches the authorizer and requires it to return SQLITE_DENY.

AGENTS.md reference: AGENTS.md:L28-L28

Useful? React with 👍 / 👎.

Comment thread backend/agent_vm/main.py
Comment on lines +486 to +487
if action in (sqlite3.SQLITE_SELECT, sqlite3.SQLITE_READ, sqlite3.SQLITE_FUNCTION):
return sqlite3.SQLITE_OK

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Permit read-only recursive CTE authorization

When a valid query uses a recursive CTE beneath an outer SELECT, such as SELECT (WITH RECURSIVE t(x) AS (...) SELECT max(x) FROM t), SQLite emits the SQLITE_RECURSIVE authorizer action. This allowlist denies that action and returns not authorized, even though the same read-only query passed the existing checks and executed before this commit; permit SQLITE_RECURSIVE while continuing to authorize each underlying read or write action separately.

Useful? React with 👍 / 👎.

@Git-on-my-level

Copy link
Copy Markdown
Collaborator

Thanks for this security improvement, @undivisible. Good use of sqlite3.set_authorizer — the deny-by-default allowlist (SQLITE_SELECT, SQLITE_READ, SQLITE_FUNCTION) is the right pattern, and complementing the existing string denylist with an engine-level authorizer gives genuine defense-in-depth.

Verified locally:

  • Ran all 6 execute_sql tests in backend/tests/unit/test_agent_vm_protocol.py — all pass. The FTS5 read test, authorizer-cleanup-after-error test, and destructive-query parametrize (DELETE/UPDATE/stacked-statement) all confirm the intended behavior.
  • test_execute_sql_clears_authorizer_after_error confirms the finally: set_authorizer(None) works even when the query raises — subsequent direct writes via runtime.db.execute succeed, so the authorizer does not leak into run_sync or other internal writes.
  • Concurrency is safe: set_authorizer/execute/set_authorizer(None) all run under runtime.lock, so no other caller sees a partially-applied authorizer.

Two items to address before merge:

  1. CI: failure-class-protocol fails. The first commit (🔒 Fix SQL Injection...) lacks the required Failure-Class: FC-<slug> | new | none trailer. The second commit (fix(agent-vm): preserve FTS5 reads...) correctly declares Failure-Class: none, but the validator checks all fix:-prefixed commits in the range. Run scripts/pr-preflight --suggest locally and amend the first commit (or squash) so every fix: commit carries a valid declaration.

  2. The needs-tests label was applied before this head; the new tests now cover the regression. Once CI is green, that label can be removed.

No security concerns with the implementation itself — the authorizer is correctly scoped and the PRAGMA data_version exception is necessary for SQLite's internal operation (used during read transactions). Leaving for human maintainer review given the security-sensitive surface.


by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with need human response.

@Git-on-my-level Git-on-my-level added the positive-signal Good PR — positive signal, not a formal approval label Aug 10, 2026
@cursor

cursor Bot commented Aug 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_632a73e9-0790-4f95-a346-b30b6d01a5a4)

@Git-on-my-level Git-on-my-level left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the security hardening direction, @undivisible — using sqlite3.set_authorizer as a deny-by-default allowlist alongside the existing string denylist is the right defense-in-depth pattern for execute_sql.

However, the current head (4d1f2e94ae) regresses the improvements landed in the prior commit on this branch (27b4a4fe87), and a few things need to be addressed before this can merge.

1. FTS5 read regression — backend/agent_vm/main.py (lines 498-509)

The head reverts the module-level read_only_sql_authorizer back to a narrower inline authorizer that denies every SQLITE_PRAGMA action. Commit 27b4a4fe87 had specifically added a safe exception for PRAGMA data_version because SQLite requests it during FTS5 MATCH reads. I reproduced the failure locally: with the head's authorizer, SELECT ... FROM documents WHERE documents MATCH 'hello' on an fts5 virtual table returns authorization denied. The authorizer in the head only allows SQLITE_SELECT, SQLITE_READ, and SQLITE_FUNCTION:

def authorizer(action, arg1, arg2, dbname, source):
    if action in (sqlite3.SQLITE_SELECT, sqlite3.SQLITE_READ, sqlite3.SQLITE_FUNCTION):
        return sqlite3.SQLITE_OK
    return sqlite3.SQLITE_DENY

Please restore the module-level read_only_sql_authorizer with the PRAGMA data_version exception (or equivalent safe handling) from commit 27b4a4fe87.

2. Deleted regression tests — backend/tests/unit/test_agent_vm_protocol.py

The head deletes all 55 lines of tests that 27b4a4fe87 added: test_execute_sql_allows_fts5_reads, test_execute_sql_clears_authorizer_after_error, and the test_execute_sql_denies_destructive_queries parametrize (DELETE / UPDATE / stacked-statement). The net diff vs main shows zero test changes. Per the repo Definition of Done, a security bug fix must include the regression test that would have caught it. Please restore these tests.

3. Junk files at repo root — commit.txt, draft.md

Both files contain identical scratch content (security(agent-vm): restrict sql execution with sqlite authorizer / Failure-Class: none) and should not be committed to the repository. Please remove them.

4. CI — Hygiene, Formatting, PR Metadata Preflight failing

The first commit (4ec7d15da3) lacks the required Failure-Class: FC-<slug> | new | none trailer that the preflight validator checks across all fix: commits in the range. Run scripts/pr-preflight --suggest locally and ensure every fix: commit carries a valid declaration. The Hygiene/Formatting failures are likely related to the junk files above.

The core security approach is sound; the authorizer is correctly scoped under runtime.lock and the finally: set_authorizer(None) prevents leakage into run_sync/internal writes. Once the regression and tests are restored and the junk files removed, this should be in good shape.

Leaving for human maintainer review given the security-sensitive SQL-authorizer surface — the current head reintroduces the FTS5 read failure that the prior commit on this branch had fixed.


Review by glm-5.2 (Omi PR maintainer automation). No urgent security alert — the authorizer direction is correct, the issues are about an incomplete/inadvertent revert on this branch.


by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with need human response.

@Git-on-my-level Git-on-my-level removed the positive-signal Good PR — positive signal, not a formal approval label Aug 10, 2026
@undivisible
undivisible force-pushed the fix-sqli-agent-vm-10420643532383619873 branch from 4d1f2e9 to a003897 Compare August 10, 2026 21:11
@cursor

cursor Bot commented Aug 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_4ea887e5-5944-45b7-8857-63936b964f1e)

undivisible and others added 3 commits August 11, 2026 07:11
This commit addresses a potential SQL Injection vulnerability in the `backend/agent_vm/main.py:execute_sql` tool.

The `execute_sql` function previously attempted to enforce read-only operations using basic string pattern matching and `LIMIT` checks before executing arbitrary user-provided SQL. This is an anti-pattern as it does not prevent all vectors of database modification or unauthorized schema access.

We have applied SQLite's native `sqlite3.set_authorizer` mechanism around the query execution.
The authorizer strictly limits permissions, rejecting operations like `UPDATE`, `DROP`, `DELETE`, `ATTACH`, `PRAGMA` etc, and returning `SQLITE_DENY`. Only `SQLITE_SELECT`, `SQLITE_READ`, and `SQLITE_FUNCTION` are allowed. The authorizer is applied securely within a `with runtime.lock:` block and safely unset via a `finally` block to prevent permissions bleeding into other concurrent operations on the connection.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Failure-Class: none

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
@undivisible
undivisible force-pushed the fix-sqli-agent-vm-10420643532383619873 branch from a003897 to 77a90c6 Compare August 10, 2026 23:11
@cursor

cursor Bot commented Aug 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_34ff13cc-d33e-4f0b-8283-3a76494aa9dd)

@Git-on-my-level Git-on-my-level left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@undivisible — following up on the prior automation review (commit 4d1f2e94ae). The new head (77a90c60fa) addresses some points but reintroduces the same core regression, so I still need to request changes. The authorizer direction remains sound; the gap is an incomplete revert.

1. FTS5 read regression still present — backend/agent_vm/main.py (lines 498-509)

The head defines the authorizer inline inside execute_sql, allowing only SQLITE_SELECT, SQLITE_READ, and SQLITE_FUNCTION and denying everything else:

def authorizer(action, arg1, arg2, dbname, source):
    if action in (sqlite3.SQLITE_SELECT, sqlite3.SQLITE_READ, sqlite3.SQLITE_FUNCTION):
        return sqlite3.SQLITE_OK
    return sqlite3.SQLITE_DENY

This is the narrow authorizer that commit 1258274f2d ("preserve FTS5 reads under SQL authorizer") had replaced with a module-level read_only_sql_authorizer that adds a safe exception for PRAGMA data_version. SQLite requests data_version while servicing FTS5 MATCH reads, so denying all SQLITE_PRAGMA makes SELECT ... MATCH '...' against the documents fts5 virtual table return authorization denied. Please restore the module-level read_only_sql_authorizer (allowing SELECT/READ/FUNCTION plus PRAGMA data_version read-only) from 1258274f2d, or apply equivalent safe handling.

2. Regression tests deleted — backend/tests/unit/test_agent_vm_protocol.py

The head removes all 55 lines of tests that 1258274f2d added — test_execute_sql_allows_fts5_reads, test_execute_sql_clears_authorizer_after_error, and the test_execute_sql_denies_destructive_queries parametrize (DELETE / UPDATE / stacked-statement). The net test diff vs main is now zero. Per the repo Definition of Done (AGENTS.md §1: "Bug fixes include the regression test that would have caught the bug"), a security fix like this needs the regression tests that cover both the destructive-query denial and the FTS5 read path. Please restore them.

3. Junk files committed at repo root — commit.txt, draft.md

Both contain identical scratch content (security(agent-vm): restrict sql execution with sqlite authorizer / Failure-Class: none) and don't belong in the tree. Please remove both files; this should also clear the Hygiene/Formatting CI failures.

4. CI — Hygiene and Formatting failing

Both are currently failing, almost certainly from the junk files above. Removing them and running make preflight locally should resolve this.

The structural hardening is good: the authorizer is correctly acquired under runtime.lock and the finally: set_authorizer(None) prevents it from leaking into run_sync/internal writes. Once the FTS5 exception and the regression tests are restored and the scratch files removed, this is in good shape.

Security-sensitive SQL-authorizer surface reintroduces the FTS5 read failure the middle commit on this branch had already fixed — requesting changes rather than leaving a comment, because the regression is concrete and reproducible.


Review by glm-5.2 (Omi PR maintainer automation). No urgent security alert — the authorizer approach is sound, the remaining issue is an incomplete revert on this branch.

@Git-on-my-level Git-on-my-level added the security-review Touches auth, provider routing, secrets, or security-sensitive surfaces label Aug 11, 2026
Restore the read-only authorizer's safe PRAGMA data_version exception for FTS5 MATCH reads, bring back destructive-query and cleanup regression coverage, and remove committed scratch files.

Failure-Class: none

Verification: backend/tests/unit/test_agent_vm_protocol.py (29 passed); make preflight (22 checks passed)
@cursor

cursor Bot commented Aug 11, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_564a8f32-dabc-4355-8994-426017424b23)

@Git-on-my-level Git-on-my-level removed the needs-tests PR introduces logic that should be covered by tests label Aug 11, 2026
@Git-on-my-level Git-on-my-level added the positive-signal Good PR — positive signal, not a formal approval label Aug 11, 2026
@Git-on-my-level
Git-on-my-level dismissed stale reviews from themself August 11, 2026 06:30

Resolved on current head 72848cd: (1) module-level read_only_sql_authorizer with PRAGMA data_version exception restored — FTS5 MATCH reads work; (2) all 55 lines of regression tests restored (FTS5 reads, authorizer cleanup, destructive-query denial); (3) junk files commit.txt/draft.md removed; (4) CI fully green. 29/29 tests pass.

@Git-on-my-level

Copy link
Copy Markdown
Collaborator

Thanks @undivisible — the current head (72848cd6b4) resolves all four concerns from the prior automation reviews. Verified each against the code:

1. FTS5 read regression — fixed. backend/agent_vm/main.py:483-490 restores the module-level read_only_sql_authorizer with the safe PRAGMA data_version exception (arg1.casefold() == "data_version" and arg2 is None). I ran test_execute_sql_allows_fts5_reads locally and the FTS5 MATCH query returns rows correctly.

2. Regression tests — restored. All 55 lines are back in backend/tests/unit/test_agent_vm_protocol.py:690-742: test_execute_sql_allows_fts5_reads, test_execute_sql_clears_authorizer_after_error (verifies the try/finally resets the authorizer so a subsequent CREATE TABLE succeeds), and the test_execute_sql_denies_destructive_queries parametrize (DELETE / UPDATE / stacked-statement). 29/29 tests pass.

3. Junk files — removed. Neither commit.txt nor draft.md exists in the worktree.

4. CI — green. Backend Hermetic Merge Gate, Hygiene, Formatting, and PR Metadata Preflight all pass.

Technical notes on the authorizer design: the deny-by-default posture is correct — only SQLITE_SELECT, SQLITE_READ, and SQLITE_FUNCTION are allowed, plus the narrowly-scoped PRAGMA data_version read that SQLite requests internally during FTS5 MATCH operations. The authorizer is acquired under runtime.lock and cleared in a finally block (main.py:509-514), preventing it from leaking into run_sync or internal writes. The existing string denylist remains as a complementary first layer; the two work together because the string check blocks explicit PRAGMA keywords in query text while the authorizer handles PRAGMA actions SQLite generates internally.

Removing needs-tests since the regression coverage is now thorough. This is a security-sensitive SQL-access surface in the agent VM, so leaving security-review and needs-maintainer-review for human maintainer sign-off before merge.


Review by glm-5.2 via the Omi PR maintainer monitor, acting on behalf of the maintainer team.


by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with need human response.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI backend Backend Task (python) needs-maintainer-review Needs a human maintainer to sign off before merge positive-signal Good PR — positive signal, not a formal approval security-review Touches auth, provider routing, secrets, or security-sensitive surfaces

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants