From 55a1fc9723f0b646a887166e64798fefbaf350ef Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sat, 25 Feb 2023 10:12:23 +0100 Subject: [PATCH] feat(dtmt): Validate path values in dtmt.cfg Closes #34. --- Cargo.lock | 7 ++++ crates/dtmm/src/engine.rs | 8 ++--- crates/dtmm/src/state.rs | 16 +++++---- crates/dtmt/Cargo.toml | 1 + crates/dtmt/src/cmd/build.rs | 66 +++++++++++++++++++++++++++++++++++- lib/dtmt-shared/src/lib.rs | 8 +++-- 6 files changed, 91 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9781122..db80a10 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -702,6 +702,7 @@ dependencies = [ "libloading", "nanorand", "oodle-sys", + "path-clean", "pin-project-lite", "promptly", "sdk", @@ -1698,6 +1699,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "path-clean" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17359afc20d7ab31fdb42bb844c8b3bb1dabd7dcf7e68428492da7f16966fcef" + [[package]] name = "pbkdf2" version = "0.11.0" diff --git a/crates/dtmm/src/engine.rs b/crates/dtmm/src/engine.rs index 7b61361..a0e07a2 100644 --- a/crates/dtmm/src/engine.rs +++ b/crates/dtmm/src/engine.rs @@ -163,22 +163,22 @@ fn build_mod_data_lua(state: Arc) -> String { lua.push_str(" new_mod(\""); lua.push_str(mod_info.get_id()); lua.push_str("\", {\n init = \""); - lua.push_str(resources.get_init()); + lua.push_str(&resources.get_init().to_string_lossy()); if let Some(data) = resources.get_data() { lua.push_str("\",\n data = \""); - lua.push_str(data); + lua.push_str(&data.to_string_lossy()); } if let Some(localization) = resources.get_localization() { lua.push_str("\",\n localization = \""); - lua.push_str(localization); + lua.push_str(&localization.to_string_lossy()); } lua.push_str("\",\n })\n"); } else { lua.push_str(" return dofile(\""); - lua.push_str(resources.get_init()); + lua.push_str(&resources.get_init().to_string_lossy()); lua.push_str("\")"); } diff --git a/crates/dtmm/src/state.rs b/crates/dtmm/src/state.rs index 315cd2e..e320b0e 100644 --- a/crates/dtmm/src/state.rs +++ b/crates/dtmm/src/state.rs @@ -61,23 +61,23 @@ impl PackageInfo { } } -#[derive(Clone, Data, Debug)] +#[derive(Clone, Debug)] pub(crate) struct ModResourceInfo { - init: String, - data: Option, - localization: Option, + init: PathBuf, + data: Option, + localization: Option, } impl ModResourceInfo { - pub(crate) fn get_init(&self) -> &String { + pub(crate) fn get_init(&self) -> &PathBuf { &self.init } - pub(crate) fn get_data(&self) -> Option<&String> { + pub(crate) fn get_data(&self) -> Option<&PathBuf> { self.data.as_ref() } - pub(crate) fn get_localization(&self) -> Option<&String> { + pub(crate) fn get_localization(&self) -> Option<&PathBuf> { self.localization.as_ref() } } @@ -89,8 +89,10 @@ pub(crate) struct ModInfo { description: Arc, enabled: bool, #[lens(ignore)] + #[data(ignore)] packages: Vector, #[lens(ignore)] + #[data(ignore)] resources: ModResourceInfo, } diff --git a/crates/dtmt/Cargo.toml b/crates/dtmt/Cargo.toml index 3b8cb36..83015db 100644 --- a/crates/dtmt/Cargo.toml +++ b/crates/dtmt/Cargo.toml @@ -28,6 +28,7 @@ tracing-error = "0.2.0" tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } tracing = { version = "0.1.37", features = ["async-await"] } zip = "0.6.3" +path-clean = "1.0.1" [dev-dependencies] tempfile = "3.3.0" diff --git a/crates/dtmt/src/cmd/build.rs b/crates/dtmt/src/cmd/build.rs index f470d97..46462e1 100644 --- a/crates/dtmt/src/cmd/build.rs +++ b/crates/dtmt/src/cmd/build.rs @@ -164,6 +164,23 @@ where .wrap_err("failed to build bundle") } +fn normalize_file_path>(path: P) -> Result { + let path = path.as_ref(); + + if path.is_absolute() || path.has_root() { + let err = eyre::eyre!("path is absolute: {}", path.display()); + return Err(err).with_suggestion(|| "Specify a relative file path.".to_string()); + } + + let path = path_clean::clean(path); + + if path.starts_with("..") { + eyre::bail!("path starts with a parent component: {}", path.display()); + } + + Ok(path) +} + #[tracing::instrument(skip_all)] pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { unsafe { @@ -172,7 +189,54 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> let cfg = { let dir = matches.get_one::("directory").cloned(); - find_project_config(dir).await? + let mut cfg = find_project_config(dir).await?; + + cfg.resources.init = normalize_file_path(cfg.resources.init) + .wrap_err("invalid config field 'resources.init'") + .with_suggestion(|| { + "Specify a file path relative to and child path of the \ + directory where 'dtmt.cfg' is." + .to_string() + }) + .with_suggestion(|| { + "Use 'dtmt new' in a separate directory to generate \ + a valid mod template." + .to_string() + })?; + + if let Some(path) = cfg.resources.data { + let path = normalize_file_path(path) + .wrap_err("invalid config field 'resources.data'") + .with_suggestion(|| { + "Specify a file path relative to and child path of the \ + directory where 'dtmt.cfg' is." + .to_string() + }) + .with_suggestion(|| { + "Use 'dtmt new' in a separate directory to generate \ + a valid mod template." + .to_string() + })?; + cfg.resources.data = Some(path); + } + + if let Some(path) = cfg.resources.localization { + let path = normalize_file_path(path) + .wrap_err("invalid config field 'resources.localization'") + .with_suggestion(|| { + "Specify a file path relative to and child path of the \ + directory where 'dtmt.cfg' is." + .to_string() + }) + .with_suggestion(|| { + "Use 'dtmt new' in a separate directory to generate \ + a valid mod template." + .to_string() + })?; + cfg.resources.localization = Some(path); + } + + cfg }; let dest = { diff --git a/lib/dtmt-shared/src/lib.rs b/lib/dtmt-shared/src/lib.rs index 6625c5b..3c8690d 100644 --- a/lib/dtmt-shared/src/lib.rs +++ b/lib/dtmt-shared/src/lib.rs @@ -1,14 +1,16 @@ mod log; +use std::path::PathBuf; + pub use log::*; #[derive(Clone, Debug, Default, serde::Deserialize)] pub struct ModConfigResources { - pub init: String, + pub init: PathBuf, #[serde(default)] - pub data: Option, + pub data: Option, #[serde(default)] - pub localization: Option, + pub localization: Option, } #[derive(Clone, Debug, Default, serde::Deserialize)]