feat(dtmt): Validate path values in dtmt.cfg

Closes #34.
This commit is contained in:
Lucas Schwiderski 2023-02-25 10:12:23 +01:00
parent 79729cad02
commit 55a1fc9723
Signed by: lucas
GPG key ID: AA12679AAA6DF4D8
6 changed files with 91 additions and 15 deletions

7
Cargo.lock generated
View file

@ -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"

View file

@ -163,22 +163,22 @@ fn build_mod_data_lua(state: Arc<State>) -> 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("\")");
}

View file

@ -61,23 +61,23 @@ impl PackageInfo {
}
}
#[derive(Clone, Data, Debug)]
#[derive(Clone, Debug)]
pub(crate) struct ModResourceInfo {
init: String,
data: Option<String>,
localization: Option<String>,
init: PathBuf,
data: Option<PathBuf>,
localization: Option<PathBuf>,
}
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<String>,
enabled: bool,
#[lens(ignore)]
#[data(ignore)]
packages: Vector<PackageInfo>,
#[lens(ignore)]
#[data(ignore)]
resources: ModResourceInfo,
}

View file

@ -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"

View file

@ -164,6 +164,23 @@ where
.wrap_err("failed to build bundle")
}
fn normalize_file_path<P: AsRef<Path>>(path: P) -> Result<PathBuf> {
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::<PathBuf>("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 = {

View file

@ -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<String>,
pub data: Option<PathBuf>,
#[serde(default)]
pub localization: Option<String>,
pub localization: Option<PathBuf>,
}
#[derive(Clone, Debug, Default, serde::Deserialize)]