summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2024-09-16 21:39:37 +0100
committerGitHub <noreply@github.com>2024-09-16 21:39:37 +0100
commit62e952559f600e72d7498c9b12f906cb0b1ba150 (patch)
tree6dbcce6592973358ef4bf6341888b0bbbdb98cc5 /ext
parente0b9c745c15720914f14996bf357d5b375e2dbd8 (diff)
refactor(permissions): split up Descriptor into Allow, Deny, and Query (#25508)
This makes the permission system more versatile.
Diffstat (limited to 'ext')
-rw-r--r--ext/fetch/lib.rs27
-rw-r--r--ext/ffi/call.rs4
-rw-r--r--ext/ffi/callback.rs2
-rw-r--r--ext/ffi/dlfcn.rs18
-rw-r--r--ext/ffi/lib.rs23
-rw-r--r--ext/ffi/repr.rs42
-rw-r--r--ext/fs/lib.rs80
-rw-r--r--ext/fs/ops.rs302
-rw-r--r--ext/kv/sqlite.rs74
-rw-r--r--ext/napi/lib.rs16
-rw-r--r--ext/net/lib.rs45
-rw-r--r--ext/net/ops.rs21
-rw-r--r--ext/net/ops_tls.rs9
-rw-r--r--ext/net/ops_unix.rs42
-rw-r--r--ext/node/lib.rs35
-rw-r--r--ext/node/ops/fs.rs71
16 files changed, 467 insertions, 344 deletions
diff --git a/ext/fetch/lib.rs b/ext/fetch/lib.rs
index 79659771e..88f303852 100644
--- a/ext/fetch/lib.rs
+++ b/ext/fetch/lib.rs
@@ -299,10 +299,15 @@ impl Drop for ResourceToBodyAdapter {
pub trait FetchPermissions {
fn check_net_url(
&mut self,
- _url: &Url,
+ url: &Url,
api_name: &str,
) -> Result<(), AnyError>;
- fn check_read(&mut self, _p: &Path, api_name: &str) -> Result<(), AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
+ fn check_read<'a>(
+ &mut self,
+ p: &'a Path,
+ api_name: &str,
+ ) -> Result<Cow<'a, Path>, AnyError>;
}
impl FetchPermissions for deno_permissions::PermissionsContainer {
@@ -316,12 +321,16 @@ impl FetchPermissions for deno_permissions::PermissionsContainer {
}
#[inline(always)]
- fn check_read(
+ fn check_read<'a>(
&mut self,
- path: &Path,
+ path: &'a Path,
api_name: &str,
- ) -> Result<(), AnyError> {
- deno_permissions::PermissionsContainer::check_read(self, path, api_name)
+ ) -> Result<Cow<'a, Path>, AnyError> {
+ deno_permissions::PermissionsContainer::check_read_path(
+ self,
+ path,
+ Some(api_name),
+ )
}
}
@@ -359,7 +368,11 @@ where
type_error("NetworkError when attempting to fetch resource")
})?;
let permissions = state.borrow_mut::<FP>();
- permissions.check_read(&path, "fetch()")?;
+ let path = permissions.check_read(&path, "fetch()")?;
+ let url = match path {
+ Cow::Owned(path) => Url::from_file_path(path).unwrap(),
+ Cow::Borrowed(_) => url,
+ };
if method != Method::GET {
return Err(type_error(format!(
diff --git a/ext/ffi/call.rs b/ext/ffi/call.rs
index 380fc03a1..3572b9e81 100644
--- a/ext/ffi/call.rs
+++ b/ext/ffi/call.rs
@@ -287,7 +287,7 @@ where
{
let mut state = state.borrow_mut();
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
};
let symbol = PtrSymbol::new(pointer, &def)?;
@@ -384,7 +384,7 @@ where
{
let mut state = state.borrow_mut();
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
};
let symbol = PtrSymbol::new(pointer, &def)?;
diff --git a/ext/ffi/callback.rs b/ext/ffi/callback.rs
index 7d0114131..6fa166f52 100644
--- a/ext/ffi/callback.rs
+++ b/ext/ffi/callback.rs
@@ -557,7 +557,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
let thread_id: u32 = LOCAL_THREAD_ID.with(|s| {
let value = *s.borrow();
diff --git a/ext/ffi/dlfcn.rs b/ext/ffi/dlfcn.rs
index 2bae5d223..10199bf85 100644
--- a/ext/ffi/dlfcn.rs
+++ b/ext/ffi/dlfcn.rs
@@ -19,7 +19,6 @@ use serde_value::ValueDeserializer;
use std::borrow::Cow;
use std::collections::HashMap;
use std::ffi::c_void;
-use std::path::PathBuf;
use std::rc::Rc;
pub struct DynamicLibraryResource {
@@ -121,15 +120,13 @@ pub fn op_ffi_load<'scope, FP>(
where
FP: FfiPermissions + 'static,
{
- let path = args.path;
-
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(Some(&PathBuf::from(&path)))?;
+ let path = permissions.check_partial_with_path(&args.path)?;
let lib = Library::open(&path).map_err(|e| {
dlopen2::Error::OpeningLibraryError(std::io::Error::new(
std::io::ErrorKind::Other,
- format_error(e, path),
+ format_error(e, &path),
))
})?;
let mut resource = DynamicLibraryResource {
@@ -290,7 +287,10 @@ fn sync_fn_impl<'s>(
// `path` is only used on Windows.
#[allow(unused_variables)]
-pub(crate) fn format_error(e: dlopen2::Error, path: String) -> String {
+pub(crate) fn format_error(
+ e: dlopen2::Error,
+ path: &std::path::Path,
+) -> String {
match e {
#[cfg(target_os = "windows")]
// This calls FormatMessageW with library path
@@ -300,7 +300,6 @@ pub(crate) fn format_error(e: dlopen2::Error, path: String) -> String {
//
// https://github.com/denoland/deno/issues/11632
dlopen2::Error::OpeningLibraryError(e) => {
- use std::ffi::OsStr;
use std::os::windows::ffi::OsStrExt;
use winapi::shared::minwindef::DWORD;
use winapi::shared::winerror::ERROR_INSUFFICIENT_BUFFER;
@@ -324,7 +323,8 @@ pub(crate) fn format_error(e: dlopen2::Error, path: String) -> String {
let mut buf = vec![0; 500];
- let path = OsStr::new(&path)
+ let path = path
+ .as_os_str()
.encode_wide()
.chain(Some(0))
.collect::<Vec<_>>();
@@ -384,7 +384,7 @@ mod tests {
std::io::Error::from_raw_os_error(0x000000C1),
);
assert_eq!(
- format_error(err, "foo.dll".to_string()),
+ format_error(err, &std::path::PathBuf::from("foo.dll")),
"foo.dll is not a valid Win32 application.\r\n".to_string(),
);
}
diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs
index 59d241c5a..77ec3c85e 100644
--- a/ext/ffi/lib.rs
+++ b/ext/ffi/lib.rs
@@ -5,7 +5,7 @@ use deno_core::error::AnyError;
use std::mem::size_of;
use std::os::raw::c_char;
use std::os::raw::c_short;
-use std::path::Path;
+use std::path::PathBuf;
mod call;
mod callback;
@@ -41,13 +41,28 @@ const _: () = {
pub const UNSTABLE_FEATURE_NAME: &str = "ffi";
pub trait FfiPermissions {
- fn check_partial(&mut self, path: Option<&Path>) -> Result<(), AnyError>;
+ fn check_partial_no_path(&mut self) -> Result<(), AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
+ fn check_partial_with_path(
+ &mut self,
+ path: &str,
+ ) -> Result<PathBuf, AnyError>;
}
impl FfiPermissions for deno_permissions::PermissionsContainer {
#[inline(always)]
- fn check_partial(&mut self, path: Option<&Path>) -> Result<(), AnyError> {
- deno_permissions::PermissionsContainer::check_ffi_partial(self, path)
+ fn check_partial_no_path(&mut self) -> Result<(), AnyError> {
+ deno_permissions::PermissionsContainer::check_ffi_partial_no_path(self)
+ }
+
+ #[inline(always)]
+ fn check_partial_with_path(
+ &mut self,
+ path: &str,
+ ) -> Result<PathBuf, AnyError> {
+ deno_permissions::PermissionsContainer::check_ffi_partial_with_path(
+ self, path,
+ )
}
}
diff --git a/ext/ffi/repr.rs b/ext/ffi/repr.rs
index f56537475..315e6d53b 100644
--- a/ext/ffi/repr.rs
+++ b/ext/ffi/repr.rs
@@ -21,7 +21,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
Ok(ptr_number as *mut c_void)
}
@@ -36,7 +36,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
Ok(a == b)
}
@@ -50,7 +50,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
Ok(buf as *mut c_void)
}
@@ -64,7 +64,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
let Some(buf) = buf.get_backing_store() else {
return Ok(0 as _);
@@ -85,7 +85,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid pointer to offset, pointer is null"));
@@ -115,7 +115,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
Ok(ptr as usize)
}
@@ -132,7 +132,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid ArrayBuffer pointer, pointer is null"));
@@ -164,7 +164,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if src.is_null() {
Err(type_error("Invalid ArrayBuffer pointer, pointer is null"))
@@ -195,7 +195,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid CString pointer, pointer is null"));
@@ -221,7 +221,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid bool pointer, pointer is null"));
@@ -241,7 +241,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid u8 pointer, pointer is null"));
@@ -263,7 +263,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid i8 pointer, pointer is null"));
@@ -285,7 +285,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid u16 pointer, pointer is null"));
@@ -307,7 +307,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid i16 pointer, pointer is null"));
@@ -329,7 +329,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid u32 pointer, pointer is null"));
@@ -349,7 +349,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid i32 pointer, pointer is null"));
@@ -372,7 +372,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid u64 pointer, pointer is null"));
@@ -398,7 +398,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid i64 pointer, pointer is null"));
@@ -421,7 +421,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid f32 pointer, pointer is null"));
@@ -441,7 +441,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid f64 pointer, pointer is null"));
@@ -461,7 +461,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid pointer pointer, pointer is null"));
diff --git a/ext/fs/lib.rs b/ext/fs/lib.rs
index a6f273323..bd49078b2 100644
--- a/ext/fs/lib.rs
+++ b/ext/fs/lib.rs
@@ -24,6 +24,7 @@ use deno_core::error::AnyError;
use deno_io::fs::FsError;
use std::borrow::Cow;
use std::path::Path;
+use std::path::PathBuf;
pub trait FsPermissions {
fn check_open<'a>(
@@ -34,8 +35,18 @@ pub trait FsPermissions {
path: &'a Path,
api_name: &str,
) -> Result<std::borrow::Cow<'a, Path>, FsError>;
- fn check_read(&mut self, path: &Path, api_name: &str)
- -> Result<(), AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
+ fn check_read(
+ &mut self,
+ path: &str,
+ api_name: &str,
+ ) -> Result<PathBuf, AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
+ fn check_read_path<'a>(
+ &mut self,
+ path: &'a Path,
+ api_name: &str,
+ ) -> Result<Cow<'a, Path>, AnyError>;
fn check_read_all(&mut self, api_name: &str) -> Result<(), AnyError>;
fn check_read_blind(
&mut self,
@@ -43,16 +54,24 @@ pub trait FsPermissions {
display: &str,
api_name: &str,
) -> Result<(), AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
fn check_write(
&mut self,
- path: &Path,
+ path: &str,
api_name: &str,
- ) -> Result<(), AnyError>;
+ ) -> Result<PathBuf, AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
+ fn check_write_path<'a>(
+ &mut self,
+ path: &'a Path,
+ api_name: &str,
+ ) -> Result<Cow<'a, Path>, AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
fn check_write_partial(
&mut self,
- path: &Path,
+ path: &str,
api_name: &str,
- ) -> Result<(), AnyError>;
+ ) -> Result<PathBuf, AnyError>;
fn check_write_all(&mut self, api_name: &str) -> Result<(), AnyError>;
fn check_write_blind(
&mut self,
@@ -96,25 +115,44 @@ impl FsPermissions for deno_permissions::PermissionsContainer {
// If somehow read or write aren't specified, use read
let read = read || !write;
+ let mut path: Cow<'a, Path> = Cow::Borrowed(path);
if read {
- FsPermissions::check_read(self, path, api_name)
+ let resolved_path = FsPermissions::check_read_path(self, &path, api_name)
.map_err(|_| FsError::NotCapable("read"))?;
+ if let Cow::Owned(resolved_path) = resolved_path {
+ path = Cow::Owned(resolved_path);
+ }
}
if write {
- FsPermissions::check_write(self, path, api_name)
- .map_err(|_| FsError::NotCapable("write"))?;
+ let resolved_path =
+ FsPermissions::check_write_path(self, &path, api_name)
+ .map_err(|_| FsError::NotCapable("write"))?;
+ if let Cow::Owned(resolved_path) = resolved_path {
+ path = Cow::Owned(resolved_path);
+ }
}
- Ok(Cow::Borrowed(path))
+ Ok(path)
}
fn check_read(
&mut self,
- path: &Path,
+ path: &str,
api_name: &str,
- ) -> Result<(), AnyError> {
+ ) -> Result<PathBuf, AnyError> {
deno_permissions::PermissionsContainer::check_read(self, path, api_name)
}
+ fn check_read_path<'a>(
+ &mut self,
+ path: &'a Path,
+ api_name: &str,
+ ) -> Result<Cow<'a, Path>, AnyError> {
+ deno_permissions::PermissionsContainer::check_read_path(
+ self,
+ path,
+ Some(api_name),
+ )
+ }
fn check_read_blind(
&mut self,
path: &Path,
@@ -128,17 +166,27 @@ impl FsPermissions for deno_permissions::PermissionsContainer {
fn check_write(
&mut self,
- path: &Path,
+ path: &str,
api_name: &str,
- ) -> Result<(), AnyError> {
+ ) -> Result<PathBuf, AnyError> {
deno_permissions::PermissionsContainer::check_write(self, path, api_name)
}
+ fn check_write_path<'a>(
+ &mut self,
+ path: &'a Path,
+ api_name: &str,
+ ) -> Result<Cow<'a, Path>, AnyError> {
+ deno_permissions::PermissionsContainer::check_write_path(
+ self, path, api_name,
+ )
+ }
+
fn check_write_partial(
&mut self,
- path: &Path,
+ path: &str,
api_name: &str,
- ) -> Result<(), AnyError> {
+ ) -> Result<PathBuf, AnyError> {
deno_permissions::PermissionsContainer::check_write_partial(
self, path, api_name,
)
diff --git a/ext/fs/ops.rs b/ext/fs/ops.rs
index f25cd944d..150d3b955 100644
--- a/ext/fs/ops.rs
+++ b/ext/fs/ops.rs
@@ -5,6 +5,7 @@ use std::io;
use std::io::SeekFrom;
use std::path::Path;
use std::path::PathBuf;
+use std::path::StripPrefixError;
use std::rc::Rc;
use deno_core::anyhow::bail;
@@ -105,8 +106,9 @@ pub fn op_fs_chdir<P>(
where
P: FsPermissions + 'static,
{
- let d = PathBuf::from(&directory);
- state.borrow_mut::<P>().check_read(&d, "Deno.chdir()")?;
+ let d = state
+ .borrow_mut::<P>()
+ .check_read(directory, "Deno.chdir()")?;
state
.borrow::<FileSystemRc>()
.chdir(&d)
@@ -188,11 +190,9 @@ pub fn op_fs_mkdir_sync<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
-
let mode = mode.unwrap_or(0o777) & 0o777;
- state
+ let path = state
.borrow_mut::<P>()
.check_write(&path, "Deno.mkdirSync()")?;
@@ -213,14 +213,12 @@ pub async fn op_fs_mkdir_async<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
-
let mode = mode.unwrap_or(0o777) & 0o777;
- let fs = {
+ let (fs, path) = {
let mut state = state.borrow_mut();
- state.borrow_mut::<P>().check_write(&path, "Deno.mkdir()")?;
- state.borrow::<FileSystemRc>().clone()
+ let path = state.borrow_mut::<P>().check_write(&path, "Deno.mkdir()")?;
+ (state.borrow::<FileSystemRc>().clone(), path)
};
fs.mkdir_async(path.clone(), recursive, mode)
@@ -239,8 +237,7 @@ pub fn op_fs_chmod_sync<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
- state
+ let path = state
.borrow_mut::<P>()
.check_write(&path, "Deno.chmodSync()")?;
let fs = state.borrow::<FileSystemRc>();
@@ -257,11 +254,10 @@ pub async fn op_fs_chmod_async<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
- let fs = {
+ let (fs, path) = {
let mut state = state.borrow_mut();
- state.borrow_mut::<P>().check_write(&path, "Deno.chmod()")?;
- state.borrow::<FileSystemRc>().clone()
+ let path = state.borrow_mut::<P>().check_write(&path, "Deno.chmod()")?;
+ (state.borrow::<FileSystemRc>().clone(), path)
};
fs.chmod_async(path.clone(), mode)
.await
@@ -279,8 +275,7 @@ pub fn op_fs_chown_sync<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
- state
+ let path = state
.borrow_mut::<P>()
.check_write(&path, "Deno.chownSync()")?;
let fs = state.borrow::<FileSystemRc>();
@@ -299,11 +294,10 @@ pub async fn op_fs_chown_async<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
- let fs = {
+ let (fs, path) = {
let mut state = state.borrow_mut();
- state.borrow_mut::<P>().check_write(&path, "Deno.chown()")?;
- state.borrow::<FileSystemRc>().clone()
+ let path = state.borrow_mut::<P>().check_write(&path, "Deno.chown()")?;
+ (state.borrow::<FileSystemRc>().clone(), path)
};
fs.chown_async(path.clone(), uid, gid)
.await
@@ -320,11 +314,9 @@ pub fn op_fs_remove_sync<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
-
- state
+ let path = state
.borrow_mut::<P>()
- .check_write(&path, "Deno.removeSync()")?;
+ .check_write(path, "Deno.removeSync()")?;
let fs = state.borrow::<FileSystemRc>();
fs.remove_sync(&path, recursive)
@@ -342,21 +334,19 @@ pub async fn op_fs_remove_async<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
-
- let fs = {
+ let (fs, path) = {
let mut state = state.borrow_mut();
- if recursive {
+ let path = if recursive {
state
.borrow_mut::<P>()
- .check_write(&path, "Deno.remove()")?;
+ .check_write(&path, "Deno.remove()")?
} else {
state
.borrow_mut::<P>()
- .check_write_partial(&path, "Deno.remove()")?;
- }
+ .check_write_partial(&path, "Deno.remove()")?
+ };
- state.borrow::<FileSystemRc>().clone()
+ (state.borrow::<FileSystemRc>().clone(), path)
};
fs.remove_async(path.clone(), recursive)
@@ -375,12 +365,9 @@ pub fn op_fs_copy_file_sync<P>(
where
P: FsPermissions + 'static,
{
- let from = PathBuf::from(from);
- let to = PathBuf::from(to);
-
let permissions = state.borrow_mut::<P>();
- permissions.check_read(&from, "Deno.copyFileSync()")?;
- permissions.check_write(&to, "Deno.copyFileSync()")?;
+ let from = permissions.check_read(from, "Deno.copyFileSync()")?;
+ let to = permissions.check_write(to, "Deno.copyFileSync()")?;
let fs = state.borrow::<FileSystemRc>();
fs.copy_file_sync(&from, &to)
@@ -398,15 +385,12 @@ pub async fn op_fs_copy_file_async<P>(
where
P: FsPermissions + 'static,
{
- let from = PathBuf::from(from);
- let to = PathBuf::from(to);
-
- let fs = {
+ let (fs, from, to) = {
let mut state = state.borrow_mut();
let permissions = state.borrow_mut::<P>();
- permissions.check_read(&from, "Deno.copyFile()")?;
- permissions.check_write(&to, "Deno.copyFile()")?;
- state.borrow::<FileSystemRc>().clone()
+ let from = permissions.check_read(&from, "Deno.copyFile()")?;
+ let to = permissions.check_write(&to, "Deno.copyFile()")?;
+ (state.borrow::<FileSystemRc>().clone(), from, to)
};
fs.copy_file_async(from.clone(), to.clone())
@@ -425,8 +409,7 @@ pub fn op_fs_stat_sync<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
- state
+ let path = state
.borrow_mut::<P>()
.check_read(&path, "Deno.statSync()")?;
let fs = state.borrow::<FileSystemRc>();
@@ -445,12 +428,11 @@ pub async fn op_fs_stat_async<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
- let fs = {
+ let (fs, path) = {
let mut state = state.borrow_mut();
let permissions = state.borrow_mut::<P>();
- permissions.check_read(&path, "Deno.stat()")?;
- state.borrow::<FileSystemRc>().clone()
+ let path = permissions.check_read(&path, "Deno.stat()")?;
+ (state.borrow::<FileSystemRc>().clone(), path)
};
let stat = fs
.stat_async(path.clone())
@@ -468,8 +450,7 @@ pub fn op_fs_lstat_sync<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
- state
+ let path = state
.borrow_mut::<P>()
.check_read(&path, "Deno.lstatSync()")?;
let fs = state.borrow::<FileSystemRc>();
@@ -488,12 +469,11 @@ pub async fn op_fs_lstat_async<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
- let fs = {
+ let (fs, path) = {
let mut state = state.borrow_mut();
let permissions = state.borrow_mut::<P>();
- permissions.check_read(&path, "Deno.lstat()")?;
- state.borrow::<FileSystemRc>().clone()
+ let path = permissions.check_read(&path, "Deno.lstat()")?;
+ (state.borrow::<FileSystemRc>().clone(), path)
};
let stat = fs
.lstat_async(path.clone())
@@ -511,11 +491,9 @@ pub fn op_fs_realpath_sync<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
-
let fs = state.borrow::<FileSystemRc>().clone();
let permissions = state.borrow_mut::<P>();
- permissions.check_read(&path, "Deno.realPathSync()")?;
+ let path = permissions.check_read(&path, "Deno.realPathSync()")?;
if path.is_relative() {
permissions.check_read_blind(&fs.cwd()?, "CWD", "Deno.realPathSync()")?;
}
@@ -536,18 +514,16 @@ pub async fn op_fs_realpath_async<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
-
- let fs;
- {
+ let (fs, path) = {
let mut state = state.borrow_mut();
- fs = state.borrow::<FileSystemRc>().clone();
+ let fs = state.borrow::<FileSystemRc>().clone();
let permissions = state.borrow_mut::<P>();
- permissions.check_read(&path, "Deno.realPath()")?;
+ let path = permissions.check_read(&path, "Deno.realPath()")?;
if path.is_relative() {
permissions.check_read_blind(&fs.cwd()?, "CWD", "Deno.realPath()")?;
}
- }
+ (fs, path)
+ };
let resolved_path = fs
.realpath_async(path.clone())
.await
@@ -566,9 +542,7 @@ pub fn op_fs_read_dir_sync<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
-
- state
+ let path = state
.borrow_mut::<P>()
.check_read(&path, "Deno.readDirSync()")?;
@@ -587,14 +561,12 @@ pub async fn op_fs_read_dir_async<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
-
- let fs = {
+ let (fs, path) = {
let mut state = state.borrow_mut();
- state
+ let path = state
.borrow_mut::<P>()
.check_read(&path, "Deno.readDir()")?;
- state.borrow::<FileSystemRc>().clone()
+ (state.borrow::<FileSystemRc>().clone(), path)
};
let entries = fs
@@ -614,13 +586,10 @@ pub fn op_fs_rename_sync<P>(
where
P: FsPermissions + 'static,
{
- let oldpath = PathBuf::from(oldpath);
- let newpath = PathBuf::from(newpath);
-
let permissions = state.borrow_mut::<P>();
- permissions.check_read(&oldpath, "Deno.renameSync()")?;
- permissions.check_write(&oldpath, "Deno.renameSync()")?;
- permissions.check_write(&newpath, "Deno.renameSync()")?;
+ let _ = permissions.check_read(&oldpath, "Deno.renameSync()")?;
+ let oldpath = permissions.check_write(&oldpath, "Deno.renameSync()")?;
+ let newpath = permissions.check_write(&newpath, "Deno.renameSync()")?;
let fs = state.borrow::<FileSystemRc>();
fs.rename_sync(&oldpath, &newpath)
@@ -638,16 +607,13 @@ pub async fn op_fs_rename_async<P>(
where
P: FsPermissions + 'static,
{
- let oldpath = PathBuf::from(oldpath);
- let newpath = PathBuf::from(newpath);
-
- let fs = {
+ let (fs, oldpath, newpath) = {
let mut state = state.borrow_mut();
let permissions = state.borrow_mut::<P>();
- permissions.check_read(&oldpath, "Deno.rename()")?;
- permissions.check_write(&oldpath, "Deno.rename()")?;
- permissions.check_write(&newpath, "Deno.rename()")?;
- state.borrow::<FileSystemRc>().clone()
+ _ = permissions.check_read(&oldpath, "Deno.rename()")?;
+ let oldpath = permissions.check_write(&oldpath, "Deno.rename()")?;
+ let newpath = permissions.check_write(&newpath, "Deno.rename()")?;
+ (state.borrow::<FileSystemRc>().clone(), oldpath, newpath)
};
fs.rename_async(oldpath.clone(), newpath.clone())
@@ -666,14 +632,11 @@ pub fn op_fs_link_sync<P>(
where
P: FsPermissions + 'static,
{
- let oldpath = PathBuf::from(oldpath);
- let newpath = PathBuf::from(newpath);
-
let permissions = state.borrow_mut::<P>();
- permissions.check_read(&oldpath, "Deno.linkSync()")?;
- permissions.check_write(&oldpath, "Deno.linkSync()")?;
- permissions.check_read(&newpath, "Deno.linkSync()")?;
- permissions.check_write(&newpath, "Deno.linkSync()")?;
+ _ = permissions.check_read(oldpath, "Deno.linkSync()")?;
+ let oldpath = permissions.check_write(oldpath, "Deno.linkSync()")?;
+ _ = permissions.check_read(newpath, "Deno.linkSync()")?;
+ let newpath = permissions.check_write(newpath, "Deno.linkSync()")?;
let fs = state.borrow::<FileSystemRc>();
fs.link_sync(&oldpath, &newpath)
@@ -691,17 +654,14 @@ pub async fn op_fs_link_async<P>(
where
P: FsPermissions + 'static,
{
- let oldpath = PathBuf::from(&oldpath);
- let newpath = PathBuf::from(&newpath);
-
- let fs = {
+ let (fs, oldpath, newpath) = {
let mut state = state.borrow_mut();
let permissions = state.borrow_mut::<P>();
- permissions.check_read(&oldpath, "Deno.link()")?;
- permissions.check_write(&oldpath, "Deno.link()")?;
- permissions.check_read(&newpath, "Deno.link()")?;
- permissions.check_write(&newpath, "Deno.link()")?;
- state.borrow::<FileSystemRc>().clone()
+ _ = permissions.check_read(&oldpath, "Deno.link()")?;
+ let oldpath = permissions.check_write(&oldpath, "Deno.link()")?;
+ _ = permissions.check_read(&newpath, "Deno.link()")?;
+ let newpath = permissions.check_write(&newpath, "Deno.link()")?;
+ (state.borrow::<FileSystemRc>().clone(), oldpath, newpath)
};
fs.link_async(oldpath.clone(), newpath.clone())
@@ -772,9 +732,7 @@ pub fn op_fs_read_link_sync<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
-
- state
+ let path = state
.borrow_mut::<P>()
.check_read(&path, "Deno.readLink()")?;
@@ -794,14 +752,12 @@ pub async fn op_fs_read_link_async<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
-
- let fs = {
+ let (fs, path) = {
let mut state = state.borrow_mut();
- state
+ let path = state
.borrow_mut::<P>()
.check_read(&path, "Deno.readLink()")?;
- state.borrow::<FileSystemRc>().clone()
+ (state.borrow::<FileSystemRc>().clone(), path)
};
let target = fs
@@ -821,11 +777,9 @@ pub fn op_fs_truncate_sync<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
-
- state
+ let path = state
.borrow_mut::<P>()
- .check_write(&path, "Deno.truncateSync()")?;
+ .check_write(path, "Deno.truncateSync()")?;
let fs = state.borrow::<FileSystemRc>();
fs.truncate_sync(&path, len)
@@ -843,14 +797,12 @@ pub async fn op_fs_truncate_async<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
-
- let fs = {
+ let (fs, path) = {
let mut state = state.borrow_mut();
- state
+ let path = state
.borrow_mut::<P>()
.check_write(&path, "Deno.truncate()")?;
- state.borrow::<FileSystemRc>().clone()
+ (state.borrow::<FileSystemRc>().clone(), path)
};
fs.truncate_async(path.clone(), len)
@@ -872,9 +824,7 @@ pub fn op_fs_utime_sync<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
-
- state.borrow_mut::<P>().check_write(&path, "Deno.utime()")?;
+ let path = state.borrow_mut::<P>().check_write(path, "Deno.utime()")?;
let fs = state.borrow::<FileSystemRc>();
fs.utime_sync(&path, atime_secs, atime_nanos, mtime_secs, mtime_nanos)
@@ -895,12 +845,10 @@ pub async fn op_fs_utime_async<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
-
- let fs = {
+ let (fs, path) = {
let mut state = state.borrow_mut();
- state.borrow_mut::<P>().check_write(&path, "Deno.utime()")?;
- state.borrow::<FileSystemRc>().clone()
+ let path = state.borrow_mut::<P>().check_write(&path, "Deno.utime()")?;
+ (state.borrow::<FileSystemRc>().clone(), path)
};
fs.utime_async(
@@ -920,15 +868,18 @@ where
#[string]
pub fn op_fs_make_temp_dir_sync<P>(
state: &mut OpState,
- #[string] dir: Option<String>,
+ #[string] dir_arg: Option<String>,
#[string] prefix: Option<String>,
#[string] suffix: Option<String>,
) -> Result<String, AnyError>
where
P: FsPermissions + 'static,
{
- let (dir, fs) =
- make_temp_check_sync::<P>(state, dir, "Deno.makeTempDirSync()")?;
+ let (dir, fs) = make_temp_check_sync::<P>(
+ state,
+ dir_arg.as_deref(),
+ "Deno.makeTempDirSync()",
+ )?;
let mut rng = thread_rng();
@@ -936,7 +887,11 @@ where
for _ in 0..MAX_TRIES {
let path = tmp_name(&mut rng, &dir, prefix.as_deref(), suffix.as_deref())?;
match fs.mkdir_sync(&path, false, 0o700) {
- Ok(_) => return path_into_string(path.into_os_string()),
+ Ok(_) => {
+ // PERMISSIONS: ensure the absolute path is not leaked
+ let path = strip_dir_prefix(&dir, dir_arg.as_deref(), path)?;
+ return path_into_string(path.into_os_string());
+ }
Err(FsError::Io(ref e)) if e.kind() == io::ErrorKind::AlreadyExists => {
continue;
}
@@ -955,14 +910,18 @@ where
#[string]
pub async fn op_fs_make_temp_dir_async<P>(
state: Rc<RefCell<OpState>>,
- #[string] dir: Option<String>,
+ #[string] dir_arg: Option<String>,
#[string] prefix: Option<String>,
#[string] suffix: Option<String>,
) -> Result<String, AnyError>
where
P: FsPermissions + 'static,
{
- let (dir, fs) = make_temp_check_async::<P>(state, dir, "Deno.makeTempDir()")?;
+ let (dir, fs) = make_temp_check_async::<P>(
+ state,
+ dir_arg.as_deref(),
+ "Deno.makeTempDir()",
+ )?;
let mut rng = thread_rng();
@@ -970,7 +929,11 @@ where
for _ in 0..MAX_TRIES {
let path = tmp_name(&mut rng, &dir, prefix.as_deref(), suffix.as_deref())?;
match fs.clone().mkdir_async(path.clone(), false, 0o700).await {
- Ok(_) => return path_into_string(path.into_os_string()),
+ Ok(_) => {
+ // PERMISSIONS: ensure the absolute path is not leaked
+ let path = strip_dir_prefix(&dir, dir_arg.as_deref(), path)?;
+ return path_into_string(path.into_os_string());
+ }
Err(FsError::Io(ref e)) if e.kind() == io::ErrorKind::AlreadyExists => {
continue;
}
@@ -989,15 +952,18 @@ where
#[string]
pub fn op_fs_make_temp_file_sync<P>(
state: &mut OpState,
- #[string] dir: Option<String>,
+ #[string] dir_arg: Option<String>,
#[string] prefix: Option<String>,
#[string] suffix: Option<String>,
) -> Result<String, AnyError>
where
P: FsPermissions + 'static,
{
- let (dir, fs) =
- make_temp_check_sync::<P>(state, dir, "Deno.makeTempFileSync()")?;
+ let (dir, fs) = make_temp_check_sync::<P>(
+ state,
+ dir_arg.as_deref(),
+ "Deno.makeTempFileSync()",
+ )?;
let open_opts = OpenOptions {
write: true,
@@ -1011,7 +977,11 @@ where
for _ in 0..MAX_TRIES {
let path = tmp_name(&mut rng, &dir, prefix.as_deref(), suffix.as_deref())?;
match fs.open_sync(&path, open_opts, None) {
- Ok(_) => return path_into_string(path.into_os_string()),
+ Ok(_) => {
+ // PERMISSIONS: ensure the absolute path is not leaked
+ let path = strip_dir_prefix(&dir, dir_arg.as_deref(), path)?;
+ return path_into_string(path.into_os_string());
+ }
Err(FsError::Io(ref e)) if e.kind() == io::ErrorKind::AlreadyExists => {
continue;
}
@@ -1030,15 +1000,18 @@ where
#[string]
pub async fn op_fs_make_temp_file_async<P>(
state: Rc<RefCell<OpState>>,
- #[string] dir: Option<String>,
+ #[string] dir_arg: Option<String>,
#[string] prefix: Option<String>,
#[string] suffix: Option<String>,
) -> Result<String, AnyError>
where
P: FsPermissions + 'static,
{
- let (dir, fs) =
- make_temp_check_async::<P>(state, dir, "Deno.makeTempFile()")?;
+ let (dir, fs) = make_temp_check_async::<P>(
+ state,
+ dir_arg.as_deref(),
+ "Deno.makeTempFile()",
+ )?;
let open_opts = OpenOptions {
write: true,
@@ -1053,7 +1026,11 @@ where
for _ in 0..MAX_TRIES {
let path = tmp_name(&mut rng, &dir, prefix.as_deref(), suffix.as_deref())?;
match fs.clone().open_async(path.clone(), open_opts, None).await {
- Ok(_) => return path_into_string(path.into_os_string()),
+ Ok(_) => {
+ // PERMISSIONS: ensure the absolute path is not leaked
+ let path = strip_dir_prefix(&dir, dir_arg.as_deref(), path)?;
+ return path_into_string(path.into_os_string());
+ }
Err(FsError::Io(ref e)) if e.kind() == io::ErrorKind::AlreadyExists => {
continue;
}
@@ -1067,9 +1044,26 @@ where
.context("tmpfile")
}
+fn strip_dir_prefix(
+ resolved_dir: &Path,
+ dir_arg: Option<&str>,
+ result_path: PathBuf,
+) -> Result<PathBuf, StripPrefixError> {
+ if resolved_dir.is_absolute() {
+ match &dir_arg {
+ Some(dir_arg) => {
+ Ok(Path::new(dir_arg).join(result_path.strip_prefix(resolved_dir)?))
+ }
+ None => Ok(result_path),
+ }
+ } else {
+ Ok(result_path)
+ }
+}
+
fn make_temp_check_sync<P>(
state: &mut OpState,
- dir: Option<String>,
+ dir: Option<&str>,
api_name: &str,
) -> Result<(PathBuf, FileSystemRc), AnyError>
where
@@ -1077,11 +1071,7 @@ where
{
let fs = state.borrow::<FileSystemRc>().clone();
let dir = match dir {
- Some(dir) => {
- let dir = PathBuf::from(dir);
- state.borrow_mut::<P>().check_write(&dir, api_name)?;
- dir
- }
+ Some(dir) => state.borrow_mut::<P>().check_write(dir, api_name)?,
None => {
let dir = fs.tmp_dir().context("tmpdir")?;
state
@@ -1095,7 +1085,7 @@ where
fn make_temp_check_async<P>(
state: Rc<RefCell<OpState>>,
- dir: Option<String>,
+ dir: Option<&str>,
api_name: &str,
) -> Result<(PathBuf, FileSystemRc), AnyError>
where
@@ -1104,11 +1094,7 @@ where
let mut state = state.borrow_mut();
let fs = state.borrow::<FileSystemRc>().clone();
let dir = match dir {
- Some(dir) => {
- let dir = PathBuf::from(dir);
- state.borrow_mut::<P>().check_write(&dir, api_name)?;
- dir
- }
+ Some(dir) => state.borrow_mut::<P>().check_write(dir, api_name)?,
None => {
let dir = fs.tmp_dir().context("tmpdir")?;
state
diff --git a/ext/kv/sqlite.rs b/ext/kv/sqlite.rs
index b31fd1736..8027ff03d 100644
--- a/ext/kv/sqlite.rs
+++ b/ext/kv/sqlite.rs
@@ -1,5 +1,6 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::HashMap;
use std::env::current_dir;
@@ -36,19 +37,37 @@ pub struct SqliteDbHandler<P: SqliteDbHandlerPermissions + 'static> {
}
pub trait SqliteDbHandlerPermissions {
- fn check_read(&mut self, p: &Path, api_name: &str) -> Result<(), AnyError>;
- fn check_write(&mut self, p: &Path, api_name: &str) -> Result<(), AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
+ fn check_read(
+ &mut self,
+ p: &str,
+ api_name: &str,
+ ) -> Result<PathBuf, AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
+ fn check_write<'a>(
+ &mut self,
+ p: &'a Path,
+ api_name: &str,
+ ) -> Result<Cow<'a, Path>, AnyError>;
}
impl SqliteDbHandlerPermissions for deno_permissions::PermissionsContainer {
#[inline(always)]
- fn check_read(&mut self, p: &Path, api_name: &str) -> Result<(), AnyError> {
+ fn check_read(
+ &mut self,
+ p: &str,
+ api_name: &str,
+ ) -> Result<PathBuf, AnyError> {
deno_permissions::PermissionsContainer::check_read(self, p, api_name)
}
#[inline(always)]
- fn check_write(&mut self, p: &Path, api_name: &str) -> Result<(), AnyError> {
- deno_permissions::PermissionsContainer::check_write(self, p, api_name)
+ fn check_write<'a>(
+ &mut self,
+ p: &'a Path,
+ api_name: &str,
+ ) -> Result<Cow<'a, Path>, AnyError> {
+ deno_permissions::PermissionsContainer::check_write_path(self, p, api_name)
}
}
@@ -74,28 +93,35 @@ impl<P: SqliteDbHandlerPermissions> DatabaseHandler for SqliteDbHandler<P> {
state: Rc<RefCell<OpState>>,
path: Option<String>,
) -> Result<Self::DB, AnyError> {
- // Validate path
- if let Some(path) = &path {
- if path != ":memory:" {
- if path.is_empty() {
- return Err(type_error("Filename cannot be empty"));
- }
- if path.starts_with(':') {
- return Err(type_error(
- "Filename cannot start with ':' unless prefixed with './'",
- ));
- }
- let path = Path::new(path);
- {
- let mut state = state.borrow_mut();
- let permissions = state.borrow_mut::<P>();
- permissions.check_read(path, "Deno.openKv")?;
- permissions.check_write(path, "Deno.openKv")?;
- }
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
+ fn validate_path<P: SqliteDbHandlerPermissions + 'static>(
+ state: &RefCell<OpState>,
+ path: Option<String>,
+ ) -> Result<Option<String>, AnyError> {
+ let Some(path) = path else {
+ return Ok(None);
+ };
+ if path == ":memory:" {
+ return Ok(Some(path));
+ }
+ if path.is_empty() {
+ return Err(type_error("Filename cannot be empty"));
+ }
+ if path.starts_with(':') {
+ return Err(type_error(
+ "Filename cannot start with ':' unless prefixed with './'",
+ ));
+ }
+ {
+ let mut state = state.borrow_mut();
+ let permissions = state.borrow_mut::<P>();
+ let path = permissions.check_read(&path, "Deno.openKv")?;
+ let path = permissions.check_write(&path, "Deno.openKv")?;
+ Ok(Some(path.to_string_lossy().to_string()))
}
}
- let path = path.clone();
+ let path = validate_path::<P>(&state, path)?;
let default_storage_dir = self.default_storage_dir.clone();
type ConnGen =
Arc<dyn Fn() -> rusqlite::Result<rusqlite::Connection> + Send + Sync>;
diff --git a/ext/napi/lib.rs b/ext/napi/lib.rs
index c9dc62a8b..4500c66fd 100644
--- a/ext/napi/lib.rs
+++ b/ext/napi/lib.rs
@@ -16,7 +16,6 @@ use deno_core::OpState;
use deno_core::V8CrossThreadTaskSpawner;
use std::cell::RefCell;
use std::collections::HashMap;
-use std::path::Path;
use std::path::PathBuf;
use std::rc::Rc;
use std::thread_local;
@@ -482,15 +481,15 @@ deno_core::extension!(deno_napi,
);
pub trait NapiPermissions {
- fn check(&mut self, path: Option<&Path>)
- -> std::result::Result<(), AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
+ fn check(&mut self, path: &str) -> std::result::Result<PathBuf, AnyError>;
}
// NOTE(bartlomieju): for now, NAPI uses `--allow-ffi` flag, but that might
// change in the future.
impl NapiPermissions for deno_permissions::PermissionsContainer {
#[inline(always)]
- fn check(&mut self, path: Option<&Path>) -> Result<(), AnyError> {
+ fn check(&mut self, path: &str) -> Result<PathBuf, AnyError> {
deno_permissions::PermissionsContainer::check_ffi(self, path)
}
}
@@ -501,7 +500,7 @@ unsafe impl Send for NapiModuleHandle {}
struct NapiModuleHandle(*const NapiModule);
static NAPI_LOADED_MODULES: std::sync::LazyLock<
- RwLock<HashMap<String, NapiModuleHandle>>,
+ RwLock<HashMap<PathBuf, NapiModuleHandle>>,
> = std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
#[op2(reentrant)]
@@ -519,15 +518,16 @@ where
{
// We must limit the OpState borrow because this function can trigger a
// re-borrow through the NAPI module.
- let (async_work_sender, cleanup_hooks, external_ops_tracker) = {
+ let (async_work_sender, cleanup_hooks, external_ops_tracker, path) = {
let mut op_state = op_state.borrow_mut();
let permissions = op_state.borrow_mut::<NP>();
- permissions.check(Some(&PathBuf::from(&path)))?;
+ let path = permissions.check(&path)?;
let napi_state = op_state.borrow::<NapiState>();
(
op_state.borrow::<V8CrossThreadTaskSpawner>().clone(),
napi_state.env_cleanup_hooks.clone(),
op_state.external_ops_tracker.clone(),
+ path,
)
};
@@ -612,7 +612,7 @@ where
} else {
return Err(type_error(format!(
"Unable to find register Node-API module at {}",
- path
+ path.display()
)));
};
diff --git a/ext/net/lib.rs b/ext/net/lib.rs
index 098d220db..0ef3e85c4 100644
--- a/ext/net/lib.rs
+++ b/ext/net/lib.rs
@@ -13,6 +13,7 @@ use deno_core::error::AnyError;
use deno_core::OpState;
use deno_tls::rustls::RootCertStore;
use deno_tls::RootCertStoreProvider;
+use std::borrow::Cow;
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
@@ -22,12 +23,27 @@ pub const UNSTABLE_FEATURE_NAME: &str = "net";
pub trait NetPermissions {
fn check_net<T: AsRef<str>>(
&mut self,
- _host: &(T, Option<u16>),
- _api_name: &str,
+ host: &(T, Option<u16>),
+ api_name: &str,
) -> Result<(), AnyError>;
- fn check_read(&mut self, _p: &Path, _api_name: &str) -> Result<(), AnyError>;
- fn check_write(&mut self, _p: &Path, _api_name: &str)
- -> Result<(), AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
+ fn check_read(
+ &mut self,
+ p: &str,
+ api_name: &str,
+ ) -> Result<PathBuf, AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
+ fn check_write(
+ &mut self,
+ p: &str,
+ api_name: &str,
+ ) -> Result<PathBuf, AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
+ fn check_write_path<'a>(
+ &mut self,
+ p: &'a Path,
+ api_name: &str,
+ ) -> Result<Cow<'a, Path>, AnyError>;
}
impl NetPermissions for deno_permissions::PermissionsContainer {
@@ -43,20 +59,31 @@ impl NetPermissions for deno_permissions::PermissionsContainer {
#[inline(always)]
fn check_read(
&mut self,
- path: &Path,
+ path: &str,
api_name: &str,
- ) -> Result<(), AnyError> {
+ ) -> Result<PathBuf, AnyError> {
deno_permissions::PermissionsContainer::check_read(self, path, api_name)
}
#[inline(always)]
fn check_write(
&mut self,
- path: &Path,
+ path: &str,
api_name: &str,
- ) -> Result<(), AnyError> {
+ ) -> Result<PathBuf, AnyError> {
deno_permissions::PermissionsContainer::check_write(self, path, api_name)
}
+
+ #[inline(always)]
+ fn check_write_path<'a>(
+ &mut self,
+ path: &'a Path,
+ api_name: &str,
+ ) -> Result<Cow<'a, Path>, AnyError> {
+ deno_permissions::PermissionsContainer::check_write_path(
+ self, path, api_name,
+ )
+ }
}
/// Helper for checking unstable features. Used for sync ops.
diff --git a/ext/net/ops.rs b/ext/net/ops.rs
index b74dc8d75..67d32fe03 100644
--- a/ext/net/ops.rs
+++ b/ext/net/ops.rs
@@ -784,6 +784,7 @@ mod tests {
use std::net::Ipv6Addr;
use std::net::ToSocketAddrs;
use std::path::Path;
+ use std::path::PathBuf;
use std::sync::Arc;
use std::sync::Mutex;
use trust_dns_proto::rr::rdata::a::A;
@@ -991,18 +992,26 @@ mod tests {
fn check_read(
&mut self,
- _p: &Path,
+ p: &str,
_api_name: &str,
- ) -> Result<(), AnyError> {
- Ok(())
+ ) -> Result<PathBuf, AnyError> {
+ Ok(PathBuf::from(p))
}
fn check_write(
&mut self,
- _p: &Path,
+ p: &str,
_api_name: &str,
- ) -> Result<(), AnyError> {
- Ok(())
+ ) -> Result<PathBuf, AnyError> {
+ Ok(PathBuf::from(p))
+ }
+
+ fn check_write_path<'a>(
+ &mut self,
+ p: &'a Path,
+ _api_name: &str,
+ ) -> Result<Cow<'a, Path>, AnyError> {
+ Ok(Cow::Borrowed(p))
}
}
diff --git a/ext/net/ops_tls.rs b/ext/net/ops_tls.rs
index 3ca5adbbe..064da5818 100644
--- a/ext/net/ops_tls.rs
+++ b/ext/net/ops_tls.rs
@@ -52,7 +52,6 @@ use std::io::ErrorKind;
use std::io::Read;
use std::net::SocketAddr;
use std::num::NonZeroUsize;
-use std::path::Path;
use std::rc::Rc;
use std::sync::Arc;
use tokio::io::AsyncReadExt;
@@ -356,15 +355,17 @@ where
.try_borrow::<UnsafelyIgnoreCertificateErrors>()
.and_then(|it| it.0.clone());
- {
+ let cert_file = {
let mut s = state.borrow_mut();
let permissions = s.borrow_mut::<NP>();
permissions
.check_net(&(&addr.hostname, Some(addr.port)), "Deno.connectTls()")?;
if let Some(path) = cert_file {
- permissions.check_read(Path::new(path), "Deno.connectTls()")?;
+ Some(permissions.check_read(path, "Deno.connectTls()")?)
+ } else {
+ None
}
- }
+ };
let mut ca_certs = args
.ca_certs
diff --git a/ext/net/ops_unix.rs b/ext/net/ops_unix.rs
index 7d2f6af3c..95293284f 100644
--- a/ext/net/ops_unix.rs
+++ b/ext/net/ops_unix.rs
@@ -94,22 +94,22 @@ pub async fn op_net_accept_unix(
#[serde]
pub async fn op_net_connect_unix<NP>(
state: Rc<RefCell<OpState>>,
- #[string] path: String,
+ #[string] address_path: String,
) -> Result<(ResourceId, Option<String>, Option<String>), AnyError>
where
NP: NetPermissions + 'static,
{
- let address_path = Path::new(&path);
- {
+ let address_path = {
let mut state_ = state.borrow_mut();
- state_
+ let address_path = state_
.borrow_mut::<NP>()
- .check_read(address_path, "Deno.connect()")?;
- state_
+ .check_read(&address_path, "Deno.connect()")?;
+ _ = state_
.borrow_mut::<NP>()
- .check_write(address_path, "Deno.connect()")?;
- }
- let unix_stream = UnixStream::connect(Path::new(&path)).await?;
+ .check_write_path(&address_path, "Deno.connect()")?;
+ address_path
+ };
+ let unix_stream = UnixStream::connect(&address_path).await?;
let local_addr = unix_stream.local_addr()?;
let remote_addr = unix_stream.peer_addr()?;
let local_addr_path = local_addr.as_pathname().map(pathstring).transpose()?;
@@ -148,18 +148,17 @@ pub async fn op_net_recv_unixpacket(
pub async fn op_net_send_unixpacket<NP>(
state: Rc<RefCell<OpState>>,
#[smi] rid: ResourceId,
- #[string] path: String,
+ #[string] address_path: String,
#[buffer] zero_copy: JsBuffer,
) -> Result<usize, AnyError>
where
NP: NetPermissions + 'static,
{
- let address_path = Path::new(&path);
- {
+ let address_path = {
let mut s = state.borrow_mut();
s.borrow_mut::<NP>()
- .check_write(address_path, "Deno.DatagramConn.send()")?;
- }
+ .check_write(&address_path, "Deno.DatagramConn.send()")?
+ };
let resource = state
.borrow()
@@ -178,17 +177,16 @@ where
#[serde]
pub fn op_net_listen_unix<NP>(
state: &mut OpState,
- #[string] path: String,
+ #[string] address_path: String,
#[string] api_name: String,
) -> Result<(ResourceId, Option<String>), AnyError>
where
NP: NetPermissions + 'static,
{
- let address_path = Path::new(&path);
let permissions = state.borrow_mut::<NP>();
let api_call_expr = format!("{}()", api_name);
- permissions.check_read(address_path, &api_call_expr)?;
- permissions.check_write(address_path, &api_call_expr)?;
+ let address_path = permissions.check_read(&address_path, &api_call_expr)?;
+ _ = permissions.check_write_path(&address_path, &api_call_expr)?;
let listener = UnixListener::bind(address_path)?;
let local_addr = listener.local_addr()?;
let pathname = local_addr.as_pathname().map(pathstring).transpose()?;
@@ -199,15 +197,15 @@ where
pub fn net_listen_unixpacket<NP>(
state: &mut OpState,
- path: String,
+ address_path: String,
) -> Result<(ResourceId, Option<String>), AnyError>
where
NP: NetPermissions + 'static,
{
- let address_path = Path::new(&path);
let permissions = state.borrow_mut::<NP>();
- permissions.check_read(address_path, "Deno.listenDatagram()")?;
- permissions.check_write(address_path, "Deno.listenDatagram()")?;
+ let address_path =
+ permissions.check_read(&address_path, "Deno.listenDatagram()")?;
+ _ = permissions.check_write_path(&address_path, "Deno.listenDatagram()")?;
let socket = UnixDatagram::bind(address_path)?;
let local_addr = socket.local_addr()?;
let pathname = local_addr.as_pathname().map(pathstring).transpose()?;
diff --git a/ext/node/lib.rs b/ext/node/lib.rs
index 75581c128..fd9d4f9af 100644
--- a/ext/node/lib.rs
+++ b/ext/node/lib.rs
@@ -3,8 +3,10 @@
#![deny(clippy::print_stderr)]
#![deny(clippy::print_stdout)]
+use std::borrow::Cow;
use std::collections::HashSet;
use std::path::Path;
+use std::path::PathBuf;
use deno_core::error::AnyError;
use deno_core::located_script_name;
@@ -49,21 +51,29 @@ pub trait NodePermissions {
url: &Url,
api_name: &str,
) -> Result<(), AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
#[inline(always)]
- fn check_read(&mut self, path: &Path) -> Result<(), AnyError> {
+ fn check_read(&mut self, path: &str) -> Result<PathBuf, AnyError> {
self.check_read_with_api_name(path, None)
}
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
fn check_read_with_api_name(
&mut self,
- path: &Path,
+ path: &str,
api_name: Option<&str>,
- ) -> Result<(), AnyError>;
+ ) -> Result<PathBuf, AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
+ fn check_read_path<'a>(
+ &mut self,
+ path: &'a Path,
+ ) -> Result<Cow<'a, Path>, AnyError>;
fn check_sys(&mut self, kind: &str, api_name: &str) -> Result<(), AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
fn check_write_with_api_name(
&mut self,
- path: &Path,
+ path: &str,
api_name: Option<&str>,
- ) -> Result<(), AnyError>;
+ ) -> Result<PathBuf, AnyError>;
}
impl NodePermissions for deno_permissions::PermissionsContainer {
@@ -79,20 +89,27 @@ impl NodePermissions for deno_permissions::PermissionsContainer {
#[inline(always)]
fn check_read_with_api_name(
&mut self,
- path: &Path,
+ path: &str,
api_name: Option<&str>,
- ) -> Result<(), AnyError> {
+ ) -> Result<PathBuf, AnyError> {
deno_permissions::PermissionsContainer::check_read_with_api_name(
self, path, api_name,
)
}
+ fn check_read_path<'a>(
+ &mut self,
+ path: &'a Path,
+ ) -> Result<Cow<'a, Path>, AnyError> {
+ deno_permissions::PermissionsContainer::check_read_path(self, path, None)
+ }
+
#[inline(always)]
fn check_write_with_api_name(
&mut self,
- path: &Path,
+ path: &str,
api_name: Option<&str>,
- ) -> Result<(), AnyError> {
+ ) -> Result<PathBuf, AnyError> {
deno_permissions::PermissionsContainer::check_write_with_api_name(
self, path, api_name,
)
diff --git a/ext/node/ops/fs.rs b/ext/node/ops/fs.rs
index 687903325..6253f32d0 100644
--- a/ext/node/ops/fs.rs
+++ b/ext/node/ops/fs.rs
@@ -1,8 +1,6 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::cell::RefCell;
-use std::path::Path;
-use std::path::PathBuf;
use std::rc::Rc;
use deno_core::error::AnyError;
@@ -21,8 +19,7 @@ pub fn op_node_fs_exists_sync<P>(
where
P: NodePermissions + 'static,
{
- let path = PathBuf::from(path);
- state
+ let path = state
.borrow_mut::<P>()
.check_read_with_api_name(&path, Some("node:fs.existsSync()"))?;
let fs = state.borrow::<FileSystemRc>();
@@ -37,14 +34,12 @@ pub async fn op_node_fs_exists<P>(
where
P: NodePermissions + 'static,
{
- let path = PathBuf::from(path);
-
- let fs = {
+ let (fs, path) = {
let mut state = state.borrow_mut();
- state
+ let path = state
.borrow_mut::<P>()
.check_read_with_api_name(&path, Some("node:fs.exists()"))?;
- state.borrow::<FileSystemRc>().clone()
+ (state.borrow::<FileSystemRc>().clone(), path)
};
Ok(fs.exists_async(path).await?)
@@ -59,18 +54,15 @@ pub fn op_node_cp_sync<P>(
where
P: NodePermissions + 'static,
{
- let path = Path::new(path);
- let new_path = Path::new(new_path);
-
- state
+ let path = state
.borrow_mut::<P>()
.check_read_with_api_name(path, Some("node:fs.cpSync"))?;
- state
+ let new_path = state
.borrow_mut::<P>()
.check_write_with_api_name(new_path, Some("node:fs.cpSync"))?;
let fs = state.borrow::<FileSystemRc>();
- fs.cp_sync(path, new_path)?;
+ fs.cp_sync(&path, &new_path)?;
Ok(())
}
@@ -83,18 +75,15 @@ pub async fn op_node_cp<P>(
where
P: NodePermissions + 'static,
{
- let path = PathBuf::from(path);
- let new_path = PathBuf::from(new_path);
-
- let fs = {
+ let (fs, path, new_path) = {
let mut state = state.borrow_mut();
- state
+ let path = state
.borrow_mut::<P>()
.check_read_with_api_name(&path, Some("node:fs.cpSync"))?;
- state
+ let new_path = state
.borrow_mut::<P>()
.check_write_with_api_name(&new_path, Some("node:fs.cpSync"))?;
- state.borrow::<FileSystemRc>().clone()
+ (state.borrow::<FileSystemRc>().clone(), path, new_path)
};
fs.cp_async(path, new_path).await?;
@@ -123,21 +112,21 @@ pub fn op_node_statfs<P>(
where
P: NodePermissions + 'static,
{
- {
+ let path = {
let mut state = state.borrow_mut();
- state
+ let path = state
.borrow_mut::<P>()
- .check_read_with_api_name(Path::new(&path), Some("node:fs.statfs"))?;
+ .check_read_with_api_name(&path, Some("node:fs.statfs"))?;
state
.borrow_mut::<P>()
.check_sys("statfs", "node:fs.statfs")?;
- }
+ path
+ };
#[cfg(unix)]
{
- use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt;
- let path = OsStr::new(&path);
+ let path = path.as_os_str();
let mut cpath = path.as_bytes().to_vec();
cpath.push(0);
if bigint {
@@ -196,7 +185,7 @@ where
// Using a vfs here doesn't make sense, it won't align with the windows API
// call below.
#[allow(clippy::disallowed_methods)]
- let path = Path::new(&path).canonicalize()?;
+ let path = path.canonicalize()?;
let root = path
.ancestors()
.last()
@@ -256,14 +245,12 @@ pub fn op_node_lutimes_sync<P>(
where
P: NodePermissions + 'static,
{
- let path = Path::new(path);
-
- state
+ let path = state
.borrow_mut::<P>()
.check_write_with_api_name(path, Some("node:fs.lutimes"))?;
let fs = state.borrow::<FileSystemRc>();
- fs.lutime_sync(path, atime_secs, atime_nanos, mtime_secs, mtime_nanos)?;
+ fs.lutime_sync(&path, atime_secs, atime_nanos, mtime_secs, mtime_nanos)?;
Ok(())
}
@@ -279,14 +266,12 @@ pub async fn op_node_lutimes<P>(
where
P: NodePermissions + 'static,
{
- let path = PathBuf::from(path);
-
- let fs = {
+ let (fs, path) = {
let mut state = state.borrow_mut();
- state
+ let path = state
.borrow_mut::<P>()
.check_write_with_api_name(&path, Some("node:fs.lutimesSync"))?;
- state.borrow::<FileSystemRc>().clone()
+ (state.borrow::<FileSystemRc>().clone(), path)
};
fs.lutime_async(path, atime_secs, atime_nanos, mtime_secs, mtime_nanos)
@@ -305,8 +290,7 @@ pub fn op_node_lchown_sync<P>(
where
P: NodePermissions + 'static,
{
- let path = PathBuf::from(path);
- state
+ let path = state
.borrow_mut::<P>()
.check_write_with_api_name(&path, Some("node:fs.lchownSync"))?;
let fs = state.borrow::<FileSystemRc>();
@@ -324,13 +308,12 @@ pub async fn op_node_lchown<P>(
where
P: NodePermissions + 'static,
{
- let path = PathBuf::from(path);
- let fs = {
+ let (fs, path) = {
let mut state = state.borrow_mut();
- state
+ let path = state
.borrow_mut::<P>()
.check_write_with_api_name(&path, Some("node:fs.lchown"))?;
- state.borrow::<FileSystemRc>().clone()
+ (state.borrow::<FileSystemRc>().clone(), path)
};
fs.lchown_async(path, uid, gid).await?;
Ok(())