Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sanitize asset paths before passing them to the loader #223

Merged
merged 2 commits into from
Jun 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions rmf_site_editor/src/site/drawing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,19 @@ pub fn add_drawing_visuals(
)),
_ => source.clone(),
};
let texture_handle: Handle<Image> = asset_server.load(&String::from(&asset_source));
let asset_path = match String::try_from(&asset_source) {
Ok(asset_path) => asset_path,
Err(err) => {
error!(
"Invalid syntax while creating asset path for a drawing: {err}. \
Check that your asset information was input correctly. \
Current value:\n{:?}",
asset_source,
);
continue;
}
};
let texture_handle: Handle<Image> = asset_server.load(asset_path);
commands.entity(e).insert(LoadingDrawing(texture_handle));
}
}
Expand Down Expand Up @@ -194,7 +206,7 @@ pub fn handle_loaded_drawing(
.remove::<LoadingDrawing>();
}
LoadState::Failed => {
error!("Failed loading drawing {:?}", String::from(source));
error!("Failed loading drawing {:?}", source);
commands.entity(entity).remove::<LoadingDrawing>();
}
_ => {}
Expand Down
34 changes: 30 additions & 4 deletions rmf_site_editor/src/site/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,19 @@ pub fn update_model_scenes(
}
_ => source.clone(),
};
let handle = asset_server.load_untyped(&String::from(&asset_source));
let asset_path = match String::try_from(&asset_source) {
Ok(asset_path) => asset_path,
Err(err) => {
error!(
"Invalid syntax while creating asset path for a model: {err}. \
Check that your asset information was input correctly. \
Current value:\n{:?}",
asset_source,
);
return;
}
};
let handle = asset_server.load_untyped(asset_path);
commands
.insert(PreventDeletion::because(
"Waiting for model to spawn".to_string(),
Expand Down Expand Up @@ -305,8 +317,19 @@ pub fn update_model_tentative_formats(
continue;
}
}
let path = String::from(source);
let model_ext = path
let asset_path = match String::try_from(source) {
Ok(asset_path) => asset_path,
Err(err) => {
error!(
"Invalid syntax while creating asset path to load a model: {err}. \
Check that your asset information was input correctly. \
Current value:\n{:?}",
source,
);
continue;
}
};
let model_ext = asset_path
.rsplit_once('.')
.map(|s| s.1.to_owned())
.unwrap_or_else(|| tentative_format.to_string(""));
Expand All @@ -323,7 +346,10 @@ pub fn update_model_tentative_formats(
_ => "Failed parsing file".to_owned(),
}
};
warn!("Failed loading Model with source {}: {}", path, reason);
warn!(
"Failed loading Model with source {}: {}",
asset_path, reason
);
cmd.remove::<TentativeModelFormat>();
}
}
Expand Down
17 changes: 15 additions & 2 deletions rmf_site_editor/src/site/texture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,23 @@ pub fn fetch_image_for_texture(
asset_server: Res<AssetServer>,
) {
for (e, image, texture) in &mut changed_textures {
let asset_path = match String::try_from(&texture.source) {
Ok(asset_path) => asset_path,
Err(err) => {
error!(
"Invalid syntax while creating asset path: {err}. \
Check that your asset information was input correctly. \
Current value:\n{:?}",
texture.source,
);
continue;
}
};

if let Some(mut image) = image {
*image = asset_server.load(String::from(&texture.source));
*image = asset_server.load(asset_path);
} else {
let image: Handle<Image> = asset_server.load(String::from(&texture.source));
let image: Handle<Image> = asset_server.load(asset_path);
commands.entity(e).insert(image);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ fn convert_and_copy_meshes(
fn get_path_to_asset_file(asset_source: &AssetSource) -> Result<PathBuf, Box<dyn Error>> {
match asset_source {
AssetSource::Package(_) => Ok(urdf_rs::utils::expand_package_path(
&(String::from(asset_source)),
&(String::try_from(asset_source)?),
None,
)
.to_string()
Expand Down
37 changes: 28 additions & 9 deletions rmf_site_format/src/asset_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@

use crate::*;
#[cfg(feature = "bevy")]
use bevy::prelude::{Component, Reflect, ReflectComponent};
use bevy::{
asset::{AssetPath, ParseAssetPathError},
prelude::{Component, Reflect, ReflectComponent},
};
use pathdiff::diff_paths;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
Expand Down Expand Up @@ -75,6 +78,17 @@ impl AssetSource {

Ok(())
}

/// Convert the asset source into an asset path without attempting to validate
/// whether the asset path has valid syntax.
pub unsafe fn as_unvalidated_asset_path(&self) -> String {
match self {
AssetSource::Remote(uri) => String::from("rmf-server://") + uri,
AssetSource::Local(filename) => String::from("file://") + filename,
AssetSource::Search(name) => String::from("search://") + name,
AssetSource::Package(path) => String::from("package://") + path,
}
}
}

impl Default for AssetSource {
Expand All @@ -83,14 +97,19 @@ impl Default for AssetSource {
}
}

impl From<&AssetSource> for String {
fn from(asset_source: &AssetSource) -> String {
match asset_source {
AssetSource::Remote(uri) => String::from("rmf-server://") + uri,
AssetSource::Local(filename) => String::from("file://") + filename,
AssetSource::Search(name) => String::from("search://") + name,
AssetSource::Package(path) => String::from("package://") + path,
}
#[cfg(feature = "bevy")]
impl TryFrom<&AssetSource> for String {
type Error = ParseAssetPathError;
fn try_from(asset_source: &AssetSource) -> Result<String, ParseAssetPathError> {
// SAFETY: After we get this string, we immediately validate it before
// returning it.
let result = unsafe { asset_source.as_unvalidated_asset_path() };

// Verify that the string can be parsed as an asset path before we
// return it.
AssetPath::try_parse(&result)
.map(|_| ()) // drop the borrowing of result
.map(|_| result)
}
}

Expand Down
6 changes: 5 additions & 1 deletion rmf_site_format/src/legacy/building_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -630,7 +630,11 @@ impl BuildingMap {
let textures = textures
.into_iter()
.map(|(id, texture)| {
let name: String = (&texture.source).into();
// SAFETY: We're picking the string apart to automatically generate
// a name for the texture. We don't need to validate the syntax
// because what we produce here will only exist to be viewed by
// humans.
let name: String = unsafe { (&texture.source).as_unvalidated_asset_path() };
let name = Path::new(&name)
.file_stem()
.map(|s| s.to_str().map(|s| s.to_owned()))
Expand Down
5 changes: 4 additions & 1 deletion rmf_site_format/src/workcell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,10 @@ impl From<Geometry> for urdf_rs::Geometry {
fn from(geometry: Geometry) -> Self {
match geometry {
Geometry::Mesh { source, scale } => urdf_rs::Geometry::Mesh {
filename: (&source).into(),
// SAFETY: We don't need to validate the syntax of the asset
// path because that will be done later when we attempt to load
// this as an asset.
filename: unsafe { (&source).as_unvalidated_asset_path() },
scale: scale.map(|v| urdf_rs::Vec3([v.x as f64, v.y as f64, v.z as f64])),
},
Geometry::Primitive(PrimitiveShape::Box { size: [x, y, z] }) => {
Expand Down
Loading