VYPR
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.

PackageAffected versionsPatched versions
denocrates.io
>= 2.2.0, < 2.2.52.2.5
deno_nodecrates.io
>= 0.129.0, < 0.134.00.134.0

Affected products

1

Patches

1
31a97803995b

fix(ext/node): restrict ATTACH DATABASE statement (#28513)

https://github.com/denoland/denoDivy SrivastavaMar 18, 2025via ghsa
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

News mentions

0

No linked articles in our index yet.