diff --git a/CHANGELOG.md b/CHANGELOG.md index 43e129e34..a5c7820cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -517,8 +517,13 @@ ([evipepota](https://github.com/evipepota) and ([Louis Pilfold](https://github.com/lpil)) -- Restrict publication tarball creation so they cannot contain files from - outside the project root. +- Stricter deserialisation rules for files internal the build directory to + reject corrupted data. ([Abdelrahman Ahmed Aboelkasem](https://github.com/0x2face), ([Aly](https://github.com/spect3r1), and ([Louis Pilfold](https://github.com/lpil)) + +- Restrict publication tarball creation so they cannot contain files from + outside the project root. + ([Abdelrahman Ahmed Aboelkasem](https://github.com/0x2face), + ([Aly](https://github.com/spect3r1), and ([Louis Pilfold](https://github.com/lpil)) diff --git a/compiler-cli/src/dependencies.rs b/compiler-cli/src/dependencies.rs index 4f448ce40..30d13383d 100644 --- a/compiler-cli/src/dependencies.rs +++ b/compiler-cli/src/dependencies.rs @@ -571,13 +571,14 @@ fn write_manifest_to_disc(paths: &ProjectPaths, manifest: &Manifest) -> Result<( // the `project/build/packages` directory. // For descriptions of packages provided by paths and git deps, see the ProvidedPackage struct. // The same package may appear in both at different times. -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)] struct LocalPackages { - packages: HashMap, + #[serde(deserialize_with = "gleam_core::config::map_with_package_name_keys::deserialize")] + packages: HashMap, } impl LocalPackages { - pub fn extra_local_packages(&self, manifest: &Manifest) -> Vec<(String, Version)> { + pub fn extra_local_packages(&self, manifest: &Manifest) -> Vec<(EcoString, Version)> { let manifest_packages: HashSet<_> = manifest .packages .iter() @@ -634,12 +635,43 @@ impl LocalPackages { packages: manifest .packages .iter() - .map(|p| (p.name.to_string(), p.version.clone())) + .map(|p| (p.name.clone(), p.version.clone())) .collect(), } } } +#[test] +fn local_packages_deserialise_ok() { + let toml = r#" +[packages] +gleam_stdlib = "1.0.0" +gleam_otp = "1.1.0" +"#; + let packages: LocalPackages = toml::from_str(toml).unwrap(); + assert_eq!( + packages, + LocalPackages { + packages: HashMap::from_iter([ + ("gleam_stdlib".into(), Version::new(1, 0, 0)), + ("gleam_otp".into(), Version::new(1, 1, 0)), + ]) + } + ) +} + +#[test] +fn local_packages_deserialise_invalid_name() { + let toml = r#" +[packages] +gleam_stdlib = "1.0.0" +"../../stuff" = "1.1.0" +"#; + let error = toml::from_str::(toml) + .expect_err("should fail to deserialise because of invalid name"); + insta::assert_snapshot!(insta::internals::AutoName, error.to_string()); +} + fn is_same_requirements( requirements1: &HashMap, requirements2: &HashMap, diff --git a/compiler-cli/src/snapshots/gleam_cli__dependencies__local_packages_deserialise_invalid_name.snap b/compiler-cli/src/snapshots/gleam_cli__dependencies__local_packages_deserialise_invalid_name.snap new file mode 100644 index 000000000..7e889fff6 --- /dev/null +++ b/compiler-cli/src/snapshots/gleam_cli__dependencies__local_packages_deserialise_invalid_name.snap @@ -0,0 +1,9 @@ +--- +source: compiler-cli/src/dependencies.rs +expression: error.to_string() +--- +TOML parse error at line 2, column 1 + | +2 | [packages] + | ^^^^^^^^^^ +invalid value: string "../../stuff", expected a package name containing only lowercase letter, numbers, and underscores diff --git a/compiler-core/src/config.rs b/compiler-core/src/config.rs index 314ee8e6d..77205bb86 100644 --- a/compiler-core/src/config.rs +++ b/compiler-core/src/config.rs @@ -34,8 +34,6 @@ fn default_javascript_runtime() -> Runtime { Runtime::NodeJs } -pub type Dependencies = HashMap; - #[derive(Clone, Debug, PartialEq, Eq)] pub struct SpdxLicense { pub licence: String, @@ -166,16 +164,16 @@ pub struct PackageConfig { #[serde( default, serialize_with = "ordered_map", - deserialize_with = "dependencies_map::deserialize" + deserialize_with = "map_with_package_name_keys ::deserialize" )] - pub dependencies: Dependencies, + pub dependencies: HashMap, #[serde( default, alias = "dev-dependencies", serialize_with = "ordered_map", - deserialize_with = "dependencies_map::deserialize" + deserialize_with = "map_with_package_name_keys ::deserialize" )] - pub dev_dependencies: Dependencies, + pub dev_dependencies: HashMap, #[serde(default)] pub repository: Option, #[serde(default)] @@ -217,7 +215,7 @@ where } impl PackageConfig { - pub fn dependencies_for(&self, mode: Mode) -> Result { + pub fn dependencies_for(&self, mode: Mode) -> Result> { match mode { Mode::Dev | Mode::Lsp => self.all_direct_dependencies(), Mode::Prod => Ok(self.dependencies.clone()), @@ -226,7 +224,7 @@ impl PackageConfig { // Return all the dependencies listed in the configuration, that is, all the // direct dependencies, both in the `dependencies` and `dev_dependencies`. - pub fn all_direct_dependencies(&self) -> Result { + pub fn all_direct_dependencies(&self) -> Result> { let mut deps = HashMap::with_capacity(self.dependencies.len() + self.dev_dependencies.len()); for (name, requirement) in self.dependencies.iter().chain(&self.dev_dependencies) { @@ -1207,7 +1205,7 @@ pub(crate) mod package_name { } } -pub(crate) mod dependencies_map { +pub mod map_with_package_name_keys { use ecow::EcoString; use serde::{Deserialize, Deserializer, de}; use std::collections::HashMap; diff --git a/compiler-core/src/manifest.rs b/compiler-core/src/manifest.rs index 08b952c6a..b16fafc38 100644 --- a/compiler-core/src/manifest.rs +++ b/compiler-core/src/manifest.rs @@ -13,7 +13,7 @@ use itertools::Itertools; pub struct Manifest { #[serde( serialize_with = "ordered_map", - deserialize_with = "super::config::dependencies_map::deserialize" + deserialize_with = "super::config::map_with_package_name_keys ::deserialize" )] pub requirements: HashMap, #[serde(serialize_with = "sorted_vec")]