fix: throw error when asset is empty (#3379)
* fix: throw error when asset is empty * Use std::path::absolute to fix windows unc problems * fix: don't use canonicalize at all on windows
This commit is contained in:
1 parent
fa48016d0c
commit
94b86477b5
1 file changed
+47
-22
@@ -19,19 +19,37 @@ fn resolve_path(raw: &str) -> Result<PathBuf, AssetParseError> {
|
|||||||
// /users/dioxus/dev/app/assets/blah.css
|
// /users/dioxus/dev/app/assets/blah.css
|
||||||
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR")
|
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR")
|
||||||
.map(PathBuf::from)
|
.map(PathBuf::from)
|
||||||
|
.unwrap()
|
||||||
|
.canonicalize()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// 1. the input file should be a pathbuf
|
// 1. the input file should be a pathbuf
|
||||||
let input = PathBuf::from(raw);
|
let input = PathBuf::from(raw);
|
||||||
|
|
||||||
// 2. absolute path to the asset
|
// 2. absolute path to the asset
|
||||||
manifest_dir
|
let Ok(path) = std::path::absolute(manifest_dir.join(raw.trim_start_matches('/'))) else {
|
||||||
.join(raw.trim_start_matches('/'))
|
return Err(AssetParseError::InvalidPath {
|
||||||
.canonicalize()
|
|
||||||
.map_err(|err| AssetParseError::AssetDoesntExist {
|
|
||||||
err,
|
|
||||||
path: input.clone(),
|
path: input.clone(),
|
||||||
})
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 3. Ensure the path exists
|
||||||
|
let Ok(path) = path.canonicalize() else {
|
||||||
|
return Err(AssetParseError::AssetDoesntExist {
|
||||||
|
path: input.clone(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 4. Ensure the path doesn't escape the crate dir
|
||||||
|
//
|
||||||
|
// - Note: since we called canonicalize on both paths, we can safely compare the parent dirs.
|
||||||
|
// On windows, we can only compare the prefix if both paths are canonicalized (not just absolute)
|
||||||
|
// https://github.com/rust-lang/rust/issues/42869
|
||||||
|
if path == manifest_dir || !path.starts_with(manifest_dir) {
|
||||||
|
return Err(AssetParseError::InvalidPath { path });
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hash_file_contents(file_path: &Path) -> Result<u64, AssetParseError> {
|
fn hash_file_contents(file_path: &Path) -> Result<u64, AssetParseError> {
|
||||||
@@ -40,11 +58,10 @@ fn hash_file_contents(file_path: &Path) -> Result<u64, AssetParseError> {
|
|||||||
|
|
||||||
// If this is a folder, hash the folder contents
|
// If this is a folder, hash the folder contents
|
||||||
if file_path.is_dir() {
|
if file_path.is_dir() {
|
||||||
let files =
|
let files = std::fs::read_dir(file_path).map_err(|err| AssetParseError::IoError {
|
||||||
std::fs::read_dir(file_path).map_err(|err| AssetParseError::AssetDoesntExist {
|
err,
|
||||||
err,
|
path: file_path.to_path_buf(),
|
||||||
path: file_path.to_path_buf(),
|
})?;
|
||||||
})?;
|
|
||||||
for file in files.flatten() {
|
for file in files.flatten() {
|
||||||
let path = file.path();
|
let path = file.path();
|
||||||
hash_file_contents(&path)?;
|
hash_file_contents(&path)?;
|
||||||
@@ -53,11 +70,10 @@ fn hash_file_contents(file_path: &Path) -> Result<u64, AssetParseError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, open the file to get its contents
|
// Otherwise, open the file to get its contents
|
||||||
let mut file =
|
let mut file = std::fs::File::open(file_path).map_err(|err| AssetParseError::IoError {
|
||||||
std::fs::File::open(file_path).map_err(|err| AssetParseError::AssetDoesntExist {
|
err,
|
||||||
err,
|
path: file_path.to_path_buf(),
|
||||||
path: file_path.to_path_buf(),
|
})?;
|
||||||
})?;
|
|
||||||
|
|
||||||
// We add a hash to the end of the file so it is invalidated when the bundled version of the file changes
|
// We add a hash to the end of the file so it is invalidated when the bundled version of the file changes
|
||||||
// The hash includes the file contents, the options, and the version of manganis. From the macro, we just
|
// The hash includes the file contents, the options, and the version of manganis. From the macro, we just
|
||||||
@@ -78,18 +94,27 @@ fn hash_file_contents(file_path: &Path) -> Result<u64, AssetParseError> {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum AssetParseError {
|
pub(crate) enum AssetParseError {
|
||||||
AssetDoesntExist {
|
AssetDoesntExist { path: PathBuf },
|
||||||
err: std::io::Error,
|
IoError { err: std::io::Error, path: PathBuf },
|
||||||
path: std::path::PathBuf,
|
InvalidPath { path: PathBuf },
|
||||||
},
|
|
||||||
FailedToReadAsset(std::io::Error),
|
FailedToReadAsset(std::io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for AssetParseError {
|
impl std::fmt::Display for AssetParseError {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
AssetParseError::AssetDoesntExist { err, path } => {
|
AssetParseError::AssetDoesntExist { path } => {
|
||||||
write!(f, "Asset at {} doesn't exist: {}", path.display(), err)
|
write!(f, "Asset at {} doesn't exist", path.display())
|
||||||
|
}
|
||||||
|
AssetParseError::IoError { path, err } => {
|
||||||
|
write!(f, "Failed to read file: {}; {}", path.display(), err)
|
||||||
|
}
|
||||||
|
AssetParseError::InvalidPath { path } => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"Asset path {} is invalid. Make sure the asset exists within this crate.",
|
||||||
|
path.display()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
AssetParseError::FailedToReadAsset(err) => write!(f, "Failed to read asset: {}", err),
|
AssetParseError::FailedToReadAsset(err) => write!(f, "Failed to read asset: {}", err),
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user