-
Notifications
You must be signed in to change notification settings - Fork 5.8k
feat(ext/node): support path-scoped FFI for SQLite extension loading #31428
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Walkthroughopen_db and load_extension now borrow_mut the PermissionsContainer and perform granular, path-aware FFI permission checks instead of global checks. If an extension path is provided, a path-scoped partial FFI check is performed; if allow_extension is true without a path, a path-agnostic partial FFI check is done and SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION is toggled via db_config. load_extension uses per-path partial FFI validation and permits enabling the C API only for extension loading. Tests were added to validate scoped FFI via subprocesses and the test runner now includes --allow-run. Sequence DiagramsequenceDiagram
participant TestRunner as Test Runner
participant Subprocess as Deno Subprocess
participant SQLiteOps as SQLite Ops
participant Perms as PermissionsContainer
rect rgb(200,240,220)
Note over TestRunner,Subprocess: Allowed extension path flow
TestRunner->>Subprocess: spawn with --allow-read --allow-ffi=/allowed/ext.so --allow-run
Subprocess->>SQLiteOps: open_db(...) -> attempt load_extension(/allowed/ext.so)
SQLiteOps->>Perms: borrow_mut -> check partial_ffi (partial_with_path=/allowed/ext.so)
Perms-->>SQLiteOps: allowed
SQLiteOps-->>Subprocess: load extension -> run query -> return OK
end
rect rgb(240,200,200)
Note over TestRunner,Subprocess: Denied extension path flow
TestRunner->>Subprocess: spawn with --allow-ffi=/other/path --allow-run
Subprocess->>SQLiteOps: open_db(...) -> attempt load_extension(/denied/ext.so)
SQLiteOps->>Perms: borrow_mut -> check partial_ffi (partial_with_path=/denied/ext.so)
Perms-->>SQLiteOps: denied (NotCapable)
SQLiteOps-->>Subprocess: propagate permission error -> stderr contains NotCapable
end
Pre-merge checks and finishing touches✅ Passed checks (5 passed)
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
tests/sqlite_extension_test/sqlite_extension_test.ts (1)
97-102: Consider consolidating the extension existence check.Both new tests duplicate the same skip logic. A helper function or
beforeAll-style check could reduce duplication, but this is minor for test code.// Optional: Extract to a helper at the top of the file function skipIfExtensionNotFound(): boolean { try { Deno.statSync(extensionPath); return false; } catch { return true; } }Also applies to: 149-154
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
ext/node/ops/sqlite/database.rs(6 hunks)tests/sqlite_extension_test/sqlite_extension_test.ts(1 hunks)tests/sqlite_extension_test/tests/integration_tests.rs(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.rs
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.rs: For debugging Rust code, set breakpoints in IDE debuggers (VS Code with rust-analyzer, IntelliJ IDEA) or uselldbdirectly
Useeprintln!()ordbg!()macros for debug prints in Rust code
Files:
ext/node/ops/sqlite/database.rstests/sqlite_extension_test/tests/integration_tests.rs
**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,js}: For JavaScript runtime debugging, enable V8 inspector with--inspect-brkflag and connect Chrome DevTools tochrome://inspect
Useconsole.log()for debug prints in JavaScript runtime code
Files:
tests/sqlite_extension_test/sqlite_extension_test.ts
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: denoland/deno PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:19:37.808Z
Learning: Applies to runtime/permissions.rs : Permission system is implemented in `runtime/permissions.rs`
📚 Learning: 2025-11-24T16:19:37.808Z
Learnt from: CR
Repo: denoland/deno PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:19:37.808Z
Learning: Applies to runtime/permissions.rs : Permission system is implemented in `runtime/permissions.rs`
Applied to files:
ext/node/ops/sqlite/database.rstests/sqlite_extension_test/tests/integration_tests.rs
🧬 Code graph analysis (1)
tests/sqlite_extension_test/sqlite_extension_test.ts (2)
ext/node/ops/sqlite/mod.rs (1)
code(174-189)cli/js/40_test.js (1)
exitCode(98-98)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
- GitHub Check: lint debug windows-x86_64
- GitHub Check: lint debug macos-x86_64
- GitHub Check: lint debug linux-x86_64
- GitHub Check: test debug linux-x86_64
- GitHub Check: test debug linux-aarch64
- GitHub Check: test debug windows-x86_64
- GitHub Check: test release linux-x86_64
- GitHub Check: test release macos-aarch64
- GitHub Check: test debug macos-x86_64
- GitHub Check: test debug macos-aarch64
- GitHub Check: build libs
🔇 Additional comments (6)
ext/node/ops/sqlite/database.rs (3)
294-294: Permission scoping implemented correctly.The switch from
check_ffi_all()tocheck_ffi_partial_no_path()at database open time aligns with the PR objective: requiring partial FFI permission (any scope) rather than unrestricted FFI whenallowExtension: true. This defers the path-specific check to the actualloadExtension()call.Also applies to: 332-332, 347-347
719-742: Path-scoped FFI check inload_extensionlooks correct.The updated doc comment accurately describes the two-step requirement. The
check_ffi_partial_with_path(Cow::Borrowed(Path::new(path)))call enforces that FFI permission must cover the specific extension path being loaded, matching the--allow-ffi=/path/to/extension.sobehavior described in PR objectives.
277-277: I'll help you verify whetherborrow_mut()is required at line 277 in the SQLite database code. Let me search for the relevant permission check method signatures and how they're used.
<function_calls>
find ext/node/ops/sqlite -name "*.rs" | head -20
</function_calls>Let me search for the permission check methods to verify their signatures:
<function_calls>
rg -n "fn check_ffi_partial" --type rust
</function_calls>Let me also look at the specific file and context around line 277:
<function_calls>
sed -n '270,290p' ext/node/ops/sqlite/database.rs
</function_calls><function_calls>
wc -l ext/node/ops/sqlite/database.rs
</function_calls><function_calls>
rg -n "check_ffi_partial_no_path|check_ffi_partial_with_path" ext/node/ops/sqlite/database.rs
</function_calls><function_calls>
rg -n "check_ffi" --type rust -A 3
</function_calls>tests/sqlite_extension_test/tests/integration_tests.rs (1)
53-53: Necessary addition for subprocess-based tests.The
--allow-runflag is required to enable the new scoped FFI permission tests insqlite_extension_test.tsthat useDeno.Commandto spawn subprocesses with specific permission flags.tests/sqlite_extension_test/sqlite_extension_test.ts (2)
93-142: Well-structured test for scoped FFI success case.The test correctly validates that
--allow-ffi=${extensionPath}permits loading only that specific extension. The subprocess pattern is appropriate sinceDeno.testpermissions don't support scoped FFI.
144-203: Good negative test for path restriction enforcement.This test properly validates that FFI permission for a different path (
/some/other/path) does not grant access to load the actual extension. The explicit check forDeno.errors.NotCapableconfirms the expected permission error.One minor consideration: the subprocess doesn't check exit code, only stdout. Since the test catches the error and continues to
db.close(), exit code 0 is expected. This is fine.
|
@coderabbitai generate docstrings |
✅ Actions performedInitiated docstring generation; will generate only if new commits exist. |
|
Note Docstrings generation - SUCCESS |
Docstrings generation was requested by @charles-dyfis-net. * #31428 (comment) The following files were modified: * `ext/node/ops/sqlite/database.rs`
cb9591e to
d5e862c
Compare
|
I'm unclear on the docstring coverage limit being flagged -- is this perhaps applying to tests? (Do folks really want docstrings for tests?) |
d5e862c to
69cfa6b
Compare
|
In attempting to address automated review feedback about docstrings, I appear to have broken the build -- it looks like |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIU this does not prevent loading extension from SQL like so:
SELECT load_extension("a.dll");and that was the reason sqlite required unscoped ffi perms.
Ah! Thank you for that clarification. As I read https://sqlite.org/c3ref/c_dbconfig_defensive.html#sqlitedbconfigenableloadextension, it should be possible to enable only the C call and not the SQL -- I'll go back and extend the tests to look for that specific state, and then see about determining how to get into it. |
6f0e03a to
3e9464f
Compare
|
On updating the tests to distinguish between a normal pass and a test that was short-circuited due to the extension not being compiled, it looks like the extension tests were always being skipped because they were attempting to load the extension from the wrong location, triggering the early I suspect we'll want a separate PR to fix the extension used for test coverage before this one can be considered -- I'll get a separate ticket and PR together. |
|
Filed ticket #31454 for the issues preventing this from being correctly tested. |
3e9464f to
1780863
Compare
|
About to push an updated version of this PR stacked on top of #31455. The expectation is that it will remain in draft until the latter (or something equivalent) is merged. |
…_extension_test to permit loadable_extension feature This allows the rusqlite `loadable_extension` feature to be used -- without it, we were building an extension that triggered a segfault on load (because it wasn't using the pointer provided during extension load to access the previously-initialized sqlite library instance). Because that feature conflicts with other features used _outside_ the `sqlite_extension_test` module, we split off to a separate workspace. The test harness stays in `sqlite_extension_test`, as it depends on other parts of the workspace.
Previously, using `allowExtension: true` or calling `loadExtension()` required unrestricted `--allow-ffi` permission. This made it impossible to sandbox code that needs to load only specific, pre-approved SQLite extensions. This change allows scoped FFI permissions: - `allowExtension: true` no longer runs an up-front / connection-time check (the `partial` check function did not return true as expected in practice when only scoped FFI permissions exist) - `loadExtension(path)` requires FFI permission covering that specific path Example: `--allow-ffi=/path/to/extension.so` now permits loading only that extension, rather than granting unrestricted FFI access. Note that this now universally disables the SQL `load_extension()` function, whether or not FFI is globally enabled. Fixes: denoland#31426
1780863 to
b2eabbf
Compare
Previously, using
allowExtension: trueor callingloadExtension()required unrestricted--allow-ffipermission. This made it impossible to sandbox code that needs to load only specific, pre-approved SQLite extensions.This change allows scoped FFI permissions:
allowExtension: truenow requires partial FFI permission (any scope)loadExtension(path)requires FFI permission covering that specific pathExample:
--allow-ffi=/path/to/extension.sonow permits loading only that extension, rather than granting unrestricted FFI access.Fixes: #31426