Moderate severityNVD Advisory· Published Jun 4, 2025· Updated Jun 4, 2025
Deno has --allow-read / --allow-write permission bypass in `node:sqlite`
CVE-2025-48935
Description
Deno is a JavaScript, TypeScript, and WebAssembly runtime. Starting in version 2.2.0 and prior to versions 2.2.5, it is possible to bypass Deno's permission read/write db permission check by using ATTACH DATABASE statement. Version 2.2.5 contains a patch for the issue.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
denocrates.io | >= 2.2.0, < 2.2.5 | 2.2.5 |
deno_nodecrates.io | >= 0.129.0, < 0.134.0 | 0.134.0 |
Affected products
1Patches
131a97803995bfix(ext/node): restrict ATTACH DATABASE statement (#28513)
7 files changed · +107 −43
Cargo.lock+23 −32 modified@@ -524,16 +524,16 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.69.5" +version = "0.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" dependencies = [ "bitflags 2.8.0", "cexpr", "clang-sys", - "itertools 0.10.5", - "lazy_static", - "lazycell", + "itertools 0.13.0", + "log", + "prettyplease", "proc-macro2", "quote", "regex", @@ -544,20 +544,18 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.70.1" +version = "0.71.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" dependencies = [ "bitflags 2.8.0", "cexpr", "clang-sys", "itertools 0.13.0", - "log", - "prettyplease", "proc-macro2", "quote", "regex", - "rustc-hash 1.1.0", + "rustc-hash 2.1.1", "shlex", "syn 2.0.87", ] @@ -2309,7 +2307,6 @@ dependencies = [ "ipnetwork", "k256", "libc", - "libsqlite3-sys", "libz-sys", "md-5", "md4", @@ -2858,9 +2855,9 @@ dependencies = [ [[package]] name = "denokv_proto" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5b77de4d3b9215e14624d4f4eb16cb38c0810e3f5860ba3b3fc47d0537f9a4d" +checksum = "fdc7c5c829ce15275d0898c94eecc243e2a47269a3f8ec5a1da45fe268a90886" dependencies = [ "async-trait", "chrono", @@ -2874,9 +2871,9 @@ dependencies = [ [[package]] name = "denokv_remote" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6497c28eec268ed99f1e8664f0842935f02d1508529c67d94c57ca5d893d743" +checksum = "ecd57015ff7b5d51cd7a61b83baec8e38367631cd13dc77140412fe5143e15fb" dependencies = [ "async-stream", "async-trait", @@ -2900,9 +2897,9 @@ dependencies = [ [[package]] name = "denokv_sqlite" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc0f21a450a35eb85760761401fddf9bfff9840127be07a6ca5c31863127913d" +checksum = "01024c5ad6ce7838d27dc35cfcc0877eee57e07a25126ccaac8eb2b61a0cf04f" dependencies = [ "async-stream", "async-trait", @@ -4187,11 +4184,11 @@ dependencies = [ [[package]] name = "hashlink" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.15.2", ] [[package]] @@ -5070,12 +5067,6 @@ dependencies = [ "spin", ] -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "lcms2" version = "6.1.0" @@ -5162,11 +5153,11 @@ dependencies = [ [[package]] name = "libsqlite3-sys" -version = "0.30.1" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +checksum = "fbb8270bb4060bd76c6e96f20c52d80620f1d82a3470885694e41e0f81ef6fe7" dependencies = [ - "bindgen 0.69.5", + "bindgen 0.71.1", "cc", "pkg-config", "vcpkg", @@ -6949,14 +6940,14 @@ dependencies = [ [[package]] name = "rusqlite" -version = "0.32.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cdbe9230a57259b37f7257d0aff38b8c9dbda3513edba2105e59b130189d82f" +checksum = "37e34486da88d8e051c7c0e23c3f15fd806ea8546260aa2fec247e97242ec143" dependencies = [ "bitflags 2.8.0", "fallible-iterator", "fallible-streaming-iterator", - "hashlink 0.9.1", + "hashlink 0.10.0", "libsqlite3-sys", "smallvec", ]
Cargo.toml+4 −4 modified@@ -70,10 +70,10 @@ eszip = "0.83.0" napi_sym = { version = "0.125.0", path = "./ext/napi/sym" } test_util = { package = "test_server", path = "./tests/util/server" } -denokv_proto = "0.9.0" -denokv_remote = "0.9.0" +denokv_proto = "0.10.0" +denokv_remote = "0.10.0" # denokv_sqlite brings in bundled sqlite if we don't disable the default features -denokv_sqlite = { default-features = false, version = "0.9.0" } +denokv_sqlite = { default-features = false, version = "0.10.0" } # exts deno_broadcast_channel = { version = "0.189.0", path = "./ext/broadcast_channel" } @@ -269,7 +269,7 @@ regex = "^1.7.0" reqwest = { version = "=0.12.5", default-features = false, features = ["rustls-tls", "stream", "gzip", "brotli", "socks", "json", "http2"] } # pinned because of https://github.com/seanmonstar/reqwest/pull/1955 ring = "^0.17.0" ripemd = "0.1.3" -rusqlite = { version = "0.32.0", features = ["unlock_notify", "bundled", "session"] } +rusqlite = { version = "0.34.0", features = ["unlock_notify", "bundled", "session", "modern_sqlite", "limits"] } # "modern_sqlite": need sqlite >= 3.49.0 for some db configs rustc-hash = "2.1.1" rustls = { version = "0.23.11", default-features = false, features = ["logging", "std", "tls12", "ring"] } rustls-pemfile = "2"
ext/node/Cargo.toml+0 −1 modified@@ -57,7 +57,6 @@ idna.workspace = true ipnetwork.workspace = true k256.workspace = true libc.workspace = true -libsqlite3-sys.workspace = true libz-sys.workspace = true md-5 = { workspace = true, features = ["oid"] } md4.workspace = true
ext/node/ops/sqlite/database.rs+67 −4 modified@@ -15,13 +15,18 @@ use deno_core::v8; use deno_core::GarbageCollected; use deno_core::OpState; use deno_permissions::PermissionsContainer; +use rusqlite::ffi as libsqlite3_sys; +use rusqlite::limits::Limit; use serde::Deserialize; use super::session::SessionOptions; use super::Session; use super::SqliteError; use super::StatementSync; +const SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION: i32 = 1005; +const SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE: i32 = 1021; + #[derive(Deserialize)] #[serde(rename_all = "camelCase")] struct DatabaseSyncOptions { @@ -61,31 +66,89 @@ pub struct DatabaseSync { impl GarbageCollected for DatabaseSync {} +fn set_db_config( + conn: &rusqlite::Connection, + config: i32, + value: bool, +) -> bool { + // SAFETY: call to sqlite3_db_config is safe because the connection + // handle is valid and the parameters are correct. + unsafe { + let mut set = 0; + let r = libsqlite3_sys::sqlite3_db_config( + conn.handle(), + config, + value as i32, + &mut set, + ); + + if r != libsqlite3_sys::SQLITE_OK { + panic!("Failed to set db config"); + } + + set == value as i32 + } +} + fn open_db( state: &mut OpState, readonly: bool, location: &str, ) -> Result<rusqlite::Connection, SqliteError> { if location == ":memory:" { - return Ok(rusqlite::Connection::open_in_memory()?); + let conn = rusqlite::Connection::open_in_memory()?; + assert!(set_db_config( + &conn, + SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE, + false + )); + assert!(set_db_config( + &conn, + SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, + false + )); + + conn.set_limit(Limit::SQLITE_LIMIT_ATTACHED, 0)?; + return Ok(conn); } state .borrow::<PermissionsContainer>() .check_read_with_api_name(location, Some("node:sqlite"))?; if readonly { - return Ok(rusqlite::Connection::open_with_flags( + let conn = rusqlite::Connection::open_with_flags( location, rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY, - )?); + )?; + assert!(set_db_config( + &conn, + SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE, + false + )); + assert!(set_db_config( + &conn, + SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, + false + )); + + conn.set_limit(Limit::SQLITE_LIMIT_ATTACHED, 0)?; + return Ok(conn); } state .borrow::<PermissionsContainer>() .check_write_with_api_name(location, Some("node:sqlite"))?; - Ok(rusqlite::Connection::open(location)?) + let conn = rusqlite::Connection::open(location)?; + assert!(set_db_config( + &conn, + SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, + false + )); + conn.set_limit(Limit::SQLITE_LIMIT_ATTACHED, 0)?; + + Ok(conn) } // Represents a single connection to a SQLite database.
ext/node/ops/sqlite/session.rs+1 −1 modified@@ -7,7 +7,7 @@ use std::rc::Rc; use deno_core::op2; use deno_core::GarbageCollected; -use libsqlite3_sys as ffi; +use rusqlite::ffi; use serde::Deserialize; use super::SqliteError;
ext/node/ops/sqlite/statement.rs+1 −1 modified@@ -8,7 +8,7 @@ use deno_core::op2; use deno_core::v8; use deno_core::v8::GetPropertyNamesArgs; use deno_core::GarbageCollected; -use libsqlite3_sys as ffi; +use rusqlite::ffi; use serde::Serialize; use super::SqliteError;
tests/unit_node/sqlite_test.ts+11 −0 modified@@ -153,6 +153,17 @@ Deno.test({ ); db.close(); } + { + const db = new DatabaseSync(":memory:"); + assertThrows( + () => { + db.exec("ATTACH DATABASE 'test.db' AS test"); + }, + Error, + "too many attached databases - max 0", + ); + db.close(); + } }, });
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-8vxj-4cph-c596ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-48935ghsaADVISORY
- github.com/denoland/deno/commit/31a97803995bd94629528ba841b2418d3ca01860ghsax_refsource_MISCWEB
- github.com/denoland/deno/security/advisories/GHSA-8vxj-4cph-c596ghsax_refsource_CONFIRMWEB
- rustsec.org/advisories/RUSTSEC-2025-0138.htmlghsaWEB
News mentions
0No linked articles in our index yet.