Skip to content

feat(sqlite): block ATTACH/DETACH, PRAGMA deny list, dependabot rule#1507

Merged
chaliy merged 2 commits intomainfrom
claude/sqlite-attach-pragma-renovate-A
May 2, 2026
Merged

feat(sqlite): block ATTACH/DETACH, PRAGMA deny list, dependabot rule#1507
chaliy merged 2 commits intomainfrom
claude/sqlite-attach-pragma-renovate-A

Conversation

@chaliy
Copy link
Copy Markdown
Contributor

@chaliy chaliy commented May 2, 2026

Summary

Three small, independent hardenings to the embedded sqlite builtin (#1502 follow-up):

  1. Dependabot rule for turso.github/dependabot.yml excludes turso_* from the weekly aggregated rollup so each bump (and its 30+ transitive crates) gets a standalone PR for manual review. Turso is BETA upstream; we don't want it riding on an unattended weekly group.
  2. ATTACH / DETACH rejected by policy — both keywords blocked before SQL reaches turso. Cross-DB access would bypass VFS isolation. The check is comment- and case-aware via a lightweight parser::leading_keyword helper.
  3. SqliteLimits::pragma_deny — pre-execution check refuses resource/FS-shaped PRAGMAs by default: cache_size, mmap_size, page_size, max_page_count, temp_store_directory, data_store_directory, compile_options, locking_mode, shared_cache. Common operational PRAGMAs (user_version, wal_checkpoint, foreign_keys, journal_mode) pass through. Schema-qualified PRAGMAs (PRAGMA main.cache_size) also match. Override via SqliteLimits::pragma_deny([]).

Why

These are the three lowest-risk items from the post-launch plan: a Dependabot rule keeps a BETA dep on a tighter leash, and the two policy checks close known bypasses (ATTACH escaping the VFS, PRAGMAs sidestepping the resource caps).

How

  • New module-private helpers parser::leading_keyword, parser::pragma_name, parser::strip_leading_noise — small lexer, comment- and case-aware, used for policy decisions only (real parsing stays with turso).
  • check_sql_policy(&sql, limits) runs in the dispatch loop, rejecting before engine.execute.
  • SqliteLimits gains pragma_deny: Vec<String> (defaults populated from DEFAULT_PRAGMA_DENY); builder method normalises to lower-case.
  • TM rows added to spec; rustdoc guide section updated.

Tests

  • 11 new unit tests + 6 new parser helper tests in the sqlite module.
  • 2 new TM cases (tm_sql_009, tm_sql_010) in tests/sqlite_security_tests.rs.
  • All previous green tests still green: 2289 lib + 14 integration + 11 security + 8 compat + 4 fuzz + 95 CLI.

Test plan

  • cargo fmt --all -- --check
  • cargo clippy --workspace --all-targets --all-features -- -D warnings
  • cargo test --features sqlite -p bashkit --lib → 2289 passed
  • cargo test --features sqlite -p bashkit --test sqlite_* → 37 passed
  • cargo test -p bashkit-cli → 95 passed
  • cargo run --example sqlite_basic --features sqlite -p bashkit → ok
  • cargo run --example sqlite_workflow --features sqlite -p bashkit → all 8 steps ok
  • cargo vet --locked → succeeds
  • CI green

Generated by Claude Code

PR A of the post-launch follow-up plan (#1502). Three small,
independent hardenings that fit in a single PR:

1. **`.github/dependabot.yml`** — exclude `turso_*` from the aggregated
   `chore(deps)` PR so each turso bump (and its 30+ transitive crates)
   ships as a standalone PR for manual review. Turso is BETA upstream;
   we don't want a weekly rollup carrying it through unattended.

2. **ATTACH / DETACH rejected by policy** — both keywords are now blocked
   before the SQL reaches turso. Cross-database access would bypass VFS
   isolation: ATTACH could open VFS paths the operator never staged, and
   on the VFS backend it would also build new `MemoryIO`/`VfsIO` state
   outside our `:memory:bashkit-N` registry. The policy is comment- and
   case-aware via a tiny `parser::leading_keyword` helper that strips
   whitespace + line/block comments before matching.

3. **`SqliteLimits::pragma_deny`** — pre-execution check refuses
   resource- and FS-shaped PRAGMAs by default: `cache_size`, `mmap_size`,
   `page_size`, `max_page_count`, `temp_store_directory`,
   `data_store_directory`, `compile_options`, `locking_mode`,
   `shared_cache`. Operational PRAGMAs (`user_version`, `wal_checkpoint`,
   `foreign_keys`, `journal_mode`) pass through. Schema-qualified
   PRAGMAs (`PRAGMA main.cache_size`) also match — closing the obvious
   bypass. Override via `SqliteLimits::pragma_deny([])`.

### Tests

- 11 new unit tests in `src/builtins/sqlite/tests.rs` covering both
  policies, schema-qualification, comment-aware leading-keyword sniff,
  custom deny override, and the `pragma_log` table-name regression.
- 6 new helper-level tests in `src/builtins/sqlite/parser.rs` for
  `leading_keyword` / `pragma_name` / `strip_leading_noise`.
- 2 new TM tests (`tm_sql_009`, `tm_sql_010`) in
  `tests/sqlite_security_tests.rs` driving `Bash::exec` end-to-end.
- All previously-green tests still green: 2289 lib + 14 integration +
  11 security + 8 compat + 4 fuzz + 95 CLI.

Spec / docs / threat table updated; new threats numbered TM-SQL-009
(ATTACH/DETACH), TM-SQL-010 (PRAGMA deny). The previous TM-SQL-009
(error sanitisation) renumbered to TM-SQL-011 to keep IDs aligned with
test names.
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 2, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
bashkit ef98be5 Commit Preview URL

Branch Preview URL
May 02 2026, 04:03 PM

CI's `cargo doc --no-deps --all-features` runs with
`RUSTDOCFLAGS=-D warnings`, which promotes
`rustdoc::private_intra_doc_links` to error. The previous prose linked
to `DEFAULT_PRAGMA_DENY` (private), so it tripped the lint. Inline the
default list into the doc-comment so the reader still gets the
information without needing to follow a link into private territory.
@chaliy chaliy merged commit c2d03f8 into main May 2, 2026
34 checks passed
@chaliy chaliy deleted the claude/sqlite-attach-pragma-renovate-A branch May 2, 2026 16:43
@codecov
Copy link
Copy Markdown

codecov Bot commented May 2, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

chaliy added a commit that referenced this pull request May 2, 2026
## Summary

Expose the embedded SQLite builtin (#1502) to the Python (PyO3) and
JavaScript (NAPI) SDKs by mirroring the existing `python=True` opt-in
pattern. PR B of the post-launch plan.

## Why

After #1502 / #1507, the builtin only reached users via the Rust API or
the `bashkit` CLI. Anyone consuming bashkit through the language
bindings (the primary surface for LLM agent integrations) had to
hand-roll `Bash::builder().sqlite()` or shell out to the CLI. This
closes the gap.

## How

### Python (`bashkit-python`)

- `Bash.__init__(..., sqlite: bool = False)` and the matching
`from_snapshot` static constructor accept the new keyword.
- New `apply_sqlite_config()` helper, called from both `new()` and the
`reset()` rebuild path. Sets up the builtin and injects
`BASHKIT_ALLOW_INPROCESS_SQLITE=1` so the runtime gate is satisfied
transparently — same shape as `apply_python_config()`.
- The flag survives across `reset()` (covered by
`test_sqlite_survives_reset`).
- `_bashkit.pyi` updated with the keyword + docstring describing the
default policy (4 MiB script cap, 256 MiB DB cap, 30 s deadline, ATTACH
and resource-PRAGMAs rejected).

### JavaScript (`bashkit-js`)

- `BashOptions.sqlite?: boolean` added to the public option struct.
- `default_opts()` includes it; `shared_state_from_opts()` threads it
through `SharedState`.
- Builder hook applies `.sqlite()` + env opt-in symmetrically to the
existing python branch.
- `.d.ts` regenerated by NAPI from the struct (no manual stub).

### Cargo features

Both `bashkit-python/Cargo.toml` and `bashkit-js/Cargo.toml` now include
`sqlite` in the `bashkit` feature list. Neither had previously enabled
it, so the binding now actually pulls in turso when built.

## Tests

- `tests/test_sqlite.py` (Python, 12 cases): opt-in gate, basic queries
(CRUD, headers, CSV, JSON), VFS persistence, dot-commands (`.tables`,
`.dump`/`.read` round-trip), security policy surfacing (ATTACH/DETACH,
PRAGMA deny), reset preservation.
- `__test__/runtime-compat/sqlite.test.mjs` (JS, 8 cases): same shape
from JS.
- All previously-green tests still green: **2289 lib + 95 CLI**.
- `ruff check` and `ruff format --check` clean.
- `cargo vet --locked` succeeds.

## Test plan

- [x] `cargo build -p bashkit-python` → ok
- [x] `cargo build -p bashkit-js` → ok
- [x] `cargo fmt --all -- --check`
- [x] `cargo clippy --workspace --all-targets --all-features -- -D
warnings`
- [x] `cargo test --features sqlite -p bashkit --lib` → 2289 passed
- [x] `cargo test -p bashkit-cli` → 95 passed
- [x] `cargo vet --locked` → succeeds
- [x] `ruff check crates/bashkit-python && ruff format --check
crates/bashkit-python` → clean
- [ ] CI green (Python + JS suites)

---
_Generated by [Claude
Code](https://claude.ai/code/session_018ERUz5RrSCoZ5Hjku3URK2)_
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant