Skip to content

Commit 3c56e6c

Browse files
authored
fix(ext/node): enforce -RW perms on node:sqlite (denoland#27928)
require RW permission on the database path except when using in-memory mode.
1 parent 540fe7d commit 3c56e6c

File tree

3 files changed

+34
-1
lines changed

3 files changed

+34
-1
lines changed

ext/node/ops/sqlite/database.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use std::rc::Rc;
66

77
use deno_core::op2;
88
use deno_core::GarbageCollected;
9+
use deno_core::OpState;
10+
use deno_permissions::PermissionsContainer;
911
use serde::Deserialize;
1012

1113
use super::SqliteError;
@@ -41,6 +43,19 @@ pub struct DatabaseSync {
4143

4244
impl GarbageCollected for DatabaseSync {}
4345

46+
fn check_perms(state: &mut OpState, location: &str) -> Result<(), SqliteError> {
47+
if location != ":memory:" {
48+
state
49+
.borrow::<PermissionsContainer>()
50+
.check_read_with_api_name(location, Some("node:sqlite"))?;
51+
state
52+
.borrow::<PermissionsContainer>()
53+
.check_write_with_api_name(location, Some("node:sqlite"))?;
54+
}
55+
56+
Ok(())
57+
}
58+
4459
// Represents a single connection to a SQLite database.
4560
#[op2]
4661
impl DatabaseSync {
@@ -53,12 +68,15 @@ impl DatabaseSync {
5368
#[constructor]
5469
#[cppgc]
5570
fn new(
71+
state: &mut OpState,
5672
#[string] location: String,
5773
#[serde] options: Option<DatabaseSyncOptions>,
5874
) -> Result<DatabaseSync, SqliteError> {
5975
let options = options.unwrap_or_default();
6076

6177
let db = if options.open {
78+
check_perms(state, &location)?;
79+
6280
let db = rusqlite::Connection::open(&location)?;
6381
if options.enable_foreign_key_constraints {
6482
db.execute("PRAGMA foreign_keys = ON", [])?;
@@ -81,11 +99,12 @@ impl DatabaseSync {
8199
// via the constructor. An exception is thrown if the database is
82100
// already opened.
83101
#[fast]
84-
fn open(&self) -> Result<(), SqliteError> {
102+
fn open(&self, state: &mut OpState) -> Result<(), SqliteError> {
85103
if self.conn.borrow().is_some() {
86104
return Err(SqliteError::AlreadyOpen);
87105
}
88106

107+
check_perms(state, &self.location)?;
89108
let db = rusqlite::Connection::open(&self.location)?;
90109
if self.options.enable_foreign_key_constraints {
91110
db.execute("PRAGMA foreign_keys = ON", [])?;

ext/node/ops/sqlite/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@ mod database;
44
mod statement;
55

66
pub use database::DatabaseSync;
7+
use deno_permissions::PermissionCheckError;
78
pub use statement::StatementSync;
89

910
#[derive(Debug, thiserror::Error, deno_error::JsError)]
1011
pub enum SqliteError {
12+
#[class(inherit)]
13+
#[error(transparent)]
14+
Permission(#[from] PermissionCheckError),
1115
#[class(generic)]
1216
#[error(transparent)]
1317
SqliteError(#[from] rusqlite::Error),

tests/unit_node/sqlite_test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,13 @@ Deno.test("[node/sqlite] StatementSync blob are Uint8Array", () => {
9191
const row = obj[0] as Record<string, Uint8Array>;
9292
assert(row["cast('test' as blob)"] instanceof Uint8Array);
9393
});
94+
95+
Deno.test({
96+
name: "[node/sqlite] sqlite permissions",
97+
permissions: { read: false, write: false },
98+
fn() {
99+
assertThrows(() => {
100+
new DatabaseSync("test.db");
101+
}, Deno.errors.NotCapable);
102+
},
103+
});

0 commit comments

Comments
 (0)