From dd45e6fb405442cd3474b1e53a03e37399eb5f2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Sat, 20 Jul 2024 13:22:27 +0200 Subject: [PATCH] Fix #1540, due to the URL-nature a get with an empty name is processed as a list call. kube then fails to parse the result from the server. Other HTTP METHODS, like "DELETE" are also affected and e.g. cause the deletion of ALL objects. See the issue for more info. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Märtens --- kube-core/src/request.rs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/kube-core/src/request.rs b/kube-core/src/request.rs index bd358bef0..4234041c0 100644 --- a/kube-core/src/request.rs +++ b/kube-core/src/request.rs @@ -80,6 +80,7 @@ impl Request { /// Get a single instance pub fn get(&self, name: &str, gp: &GetParams) -> Result>, Error> { + validate_name(name)?; let urlstr = if let Some(rv) = &gp.resource_version { let target = format!("{}/{}?", self.url_path, name); form_urlencoded::Serializer::new(target) @@ -106,6 +107,7 @@ impl Request { /// Delete an instance of a resource pub fn delete(&self, name: &str, dp: &DeleteParams) -> Result>, Error> { + validate_name(name)?; let target = format!("{}/{}?", self.url_path, name); let mut qp = form_urlencoded::Serializer::new(target); let urlstr = qp.finish(); @@ -149,6 +151,7 @@ impl Request { pp: &PatchParams, patch: &Patch

, ) -> Result>, Error> { + validate_name(name)?; pp.validate(patch)?; let target = format!("{}/{}?", self.url_path, name); let mut qp = form_urlencoded::Serializer::new(target); @@ -171,6 +174,7 @@ impl Request { pp: &PostParams, data: Vec, ) -> Result>, Error> { + validate_name(name)?; let target = format!("{}/{}?", self.url_path, name); let mut qp = form_urlencoded::Serializer::new(target); pp.populate_qp(&mut qp); @@ -188,6 +192,7 @@ impl Request { subresource_name: &str, name: &str, ) -> Result>, Error> { + validate_name(name)?; let target = format!("{}/{}/{}", self.url_path, name, subresource_name); let mut qp = form_urlencoded::Serializer::new(target); let urlstr = qp.finish(); @@ -203,6 +208,7 @@ impl Request { pp: &PostParams, data: Vec, ) -> Result>, Error> { + validate_name(name)?; let target = format!("{}/{}/{}?", self.url_path, name, subresource_name); let mut qp = form_urlencoded::Serializer::new(target); pp.populate_qp(&mut qp); @@ -219,6 +225,7 @@ impl Request { pp: &PatchParams, patch: &Patch

, ) -> Result>, Error> { + validate_name(name)?; pp.validate(patch)?; let target = format!("{}/{}/{}?", self.url_path, name, subresource_name); let mut qp = form_urlencoded::Serializer::new(target); @@ -240,6 +247,7 @@ impl Request { pp: &PostParams, data: Vec, ) -> Result>, Error> { + validate_name(name)?; let target = format!("{}/{}/{}?", self.url_path, name, subresource_name); let mut qp = form_urlencoded::Serializer::new(target); pp.populate_qp(&mut qp); @@ -256,6 +264,7 @@ impl Request { impl Request { /// Get a single metadata instance for a named resource pub fn get_metadata(&self, name: &str, gp: &GetParams) -> Result>, Error> { + validate_name(name)?; let urlstr = if let Some(rv) = &gp.resource_version { let target = format!("{}/{}?", self.url_path, name); form_urlencoded::Serializer::new(target) @@ -310,6 +319,7 @@ impl Request { pp: &PatchParams, patch: &Patch

, ) -> Result>, Error> { + validate_name(name)?; pp.validate(patch)?; let target = format!("{}/{}?", self.url_path, name); let mut qp = form_urlencoded::Serializer::new(target); @@ -324,6 +334,14 @@ impl Request { } } +/// Names must not be empty as otherwise API server would interpret a `get` as `list`, or a `delete` as `delete_collection` +fn validate_name(name: &str) -> Result<(), Error> { + if name.is_empty() { + return Err(Error::Validation("A non-empty name is required".into())); + } + Ok(()) +} + /// Extensive tests for Request of k8s_openapi::Resource structs /// /// Cheap sanity check to ensure type maps work as expected @@ -331,7 +349,7 @@ impl Request { mod test { use crate::{ params::{GetParams, PostParams, VersionMatch, WatchParams}, - request::Request, + request::{Error, Request}, resource::Resource, }; use http::header; @@ -490,6 +508,13 @@ mod test { ); } + #[test] + fn get_empty_name() { + let url = appsv1::Deployment::url_path(&(), Some("ns")); + let req = Request::new(url).get("", &GetParams::any()); + assert!(matches!(req, Err(Error::Validation(_)))); + } + #[test] fn list_path() { let url = appsv1::Deployment::url_path(&(), Some("ns"));