feat(sqlite): block ATTACH/DETACH, PRAGMA deny list, dependabot rule#1507
Merged
feat(sqlite): block ATTACH/DETACH, PRAGMA deny list, dependabot rule#1507
Conversation
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.
Deploying with
|
| 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.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
9 tasks
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)_
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three small, independent hardenings to the embedded sqlite builtin (#1502 follow-up):
.github/dependabot.ymlexcludesturso_*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.ATTACH/DETACHrejected 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 lightweightparser::leading_keywordhelper.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 viaSqliteLimits::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
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 beforeengine.execute.SqliteLimitsgainspragma_deny: Vec<String>(defaults populated fromDEFAULT_PRAGMA_DENY); builder method normalises to lower-case.Tests
tm_sql_009,tm_sql_010) intests/sqlite_security_tests.rs.Test plan
cargo fmt --all -- --checkcargo clippy --workspace --all-targets --all-features -- -D warningscargo test --features sqlite -p bashkit --lib→ 2289 passedcargo test --features sqlite -p bashkit --test sqlite_*→ 37 passedcargo test -p bashkit-cli→ 95 passedcargo run --example sqlite_basic --features sqlite -p bashkit→ okcargo run --example sqlite_workflow --features sqlite -p bashkit→ all 8 steps okcargo vet --locked→ succeedsGenerated by Claude Code