diff --git a/dsc/tests/dsc_discovery.tests.ps1 b/dsc/tests/dsc_discovery.tests.ps1 index 8c1c4897f..6e4c49af4 100644 --- a/dsc/tests/dsc_discovery.tests.ps1 +++ b/dsc/tests/dsc_discovery.tests.ps1 @@ -67,4 +67,45 @@ Describe 'tests for resource discovery' { $resources = dsc resource list | ConvertFrom-Json $resources.Count | Should -Be 0 } + + It 'warns on invalid semver' { + $manifest = @' + { + "$schema": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/bundled/resource/manifest.json", + "type": "Test/Echo", + "version": "1.1.0..1", + "get": { + "executable": "dsctest", + "args": [ + "echo", + "--input", + "{json}" + ], + "input": { + "arg": "{json}" + } + }, + "schema": { + "command": { + "executable": "dsctest", + "args": [ + "schema", + "-s", + "echo" + ] + } + } + } +'@ + $oldPath = $env:DSC_RESOURCE_PATH + try { + $env:DSC_RESOURCE_PATH = $testdrive + Set-Content -Path "$testdrive/test.dsc.resource.json" -Value $manifest + $out = dsc resource list 2>&1 + $out | Should -Match 'WARN.*?Validation.*?Invalid manifest.*?version' + } + finally { + $env:DSC_RESOURCE_PATH = $oldPath + } + } } diff --git a/dsc_lib/Cargo.toml b/dsc_lib/Cargo.toml index 8bb98bfec..ed37b49af 100644 --- a/dsc_lib/Cargo.toml +++ b/dsc_lib/Cargo.toml @@ -18,6 +18,7 @@ serde_json = { version = "1.0", features = ["preserve_order"] } serde_yaml = { version = "0.9.3" } thiserror = "1.0" security_context_lib = { path = "../security_context_lib" } +semver = "1.0" tracing = "0.1.37" tracing-indicatif = { version = "0.3.6" } tree-sitter = "0.22" diff --git a/dsc_lib/src/discovery/command_discovery.rs b/dsc_lib/src/discovery/command_discovery.rs index 2535fe07b..5adf163a3 100644 --- a/dsc_lib/src/discovery/command_discovery.rs +++ b/dsc_lib/src/discovery/command_discovery.rs @@ -4,7 +4,7 @@ use crate::discovery::discovery_trait::ResourceDiscovery; use crate::discovery::convert_wildcard_to_regex; use crate::dscresources::dscresource::{Capability, DscResource, ImplementedAs}; -use crate::dscresources::resource_manifest::{import_manifest, Kind, ResourceManifest}; +use crate::dscresources::resource_manifest::{import_manifest, validate_semver, Kind, ResourceManifest}; use crate::dscresources::command_resource::invoke_command; use crate::dscresources::command_resource::log_resource_traces; use crate::dscerror::DscError; @@ -380,6 +380,10 @@ fn load_manifest(path: &Path) -> Result { } }; + if let Err(err) = validate_semver(&manifest.version) { + return Err(DscError::Validation(format!("Invalid manifest {path:?} version value: {err}"))); + } + let kind = if let Some(kind) = manifest.kind.clone() { kind } else if manifest.adapter.is_some() { diff --git a/dsc_lib/src/dscresources/resource_manifest.rs b/dsc_lib/src/dscresources/resource_manifest.rs index 81a245dc6..99d4ce931 100644 --- a/dsc_lib/src/dscresources/resource_manifest.rs +++ b/dsc_lib/src/dscresources/resource_manifest.rs @@ -2,6 +2,7 @@ // Licensed under the MIT License. use schemars::JsonSchema; +use semver::Version; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::collections::HashMap; @@ -232,11 +233,29 @@ pub struct ListMethod { /// /// * `DscError` - The JSON value is invalid or the schema version is not supported. pub fn import_manifest(manifest: Value) -> Result { + // TODO: enable schema version validation, if not provided, use the latest // const MANIFEST_SCHEMA_VERSION: &str = "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/bundled/resource/manifest.json"; let manifest = serde_json::from_value::(manifest)?; // if !manifest.schema_version.eq(MANIFEST_SCHEMA_VERSION) { // return Err(DscError::InvalidManifestSchemaVersion(manifest.schema_version, MANIFEST_SCHEMA_VERSION.to_string())); // } - Ok(manifest) } + +/// Validate a semantic version string. +/// +/// # Arguments +/// +/// * `version` - The semantic version string to validate. +/// +/// # Returns +/// +/// * `Result<(), Error>` - The result of the validation. +/// +/// # Errors +/// +/// * `Error` - The version string is not a valid semantic version. +pub fn validate_semver(version: &str) -> Result<(), semver::Error> { + Version::parse(version)?; + Ok(()) +}