diff --git a/changelog.d/http_client_vrl_compilation_errors.fix.md b/changelog.d/http_client_vrl_compilation_errors.fix.md new file mode 100644 index 0000000000000..45a9aaf2063bc --- /dev/null +++ b/changelog.d/http_client_vrl_compilation_errors.fix.md @@ -0,0 +1,5 @@ +The `http_client` source now fails to start if VRL compilation errors occur in `query` parameters when +type is set to `vrl`, instead of silently logging a warning and continuing with invalid expressions. +This prevents unexpected behavior where malformed VRL would be sent as literal strings in HTTP requests. + +authors: thomasqueirozb diff --git a/src/sources/http_client/client.rs b/src/sources/http_client/client.rs index 548cbc795e3ab..34c2ee09dbe2a 100644 --- a/src/sources/http_client/client.rs +++ b/src/sources/http_client/client.rs @@ -214,17 +214,19 @@ pub struct Query { } impl Query { - pub fn new(params: &HashMap) -> Self { + pub fn new(params: &HashMap) -> Result { let functions = vrl::stdlib::all() .into_iter() .chain(vector_lib::enrichment::vrl_functions()) .chain(vector_vrl_functions::all()) .collect::>(); - let compiled: HashMap = params - .iter() - .map(|(k, v)| (k.clone(), Self::compile_param(v, &functions))) - .collect(); + let mut compiled: HashMap = HashMap::new(); + + for (k, v) in params.iter() { + let compiled_param = Self::compile_param(v, &functions)?; + compiled.insert(k.clone(), compiled_param); + } let has_vrl = compiled.values().any(|compiled| match compiled { CompiledQueryParameterValue::SingleParam(param) => param.program.is_some(), @@ -233,14 +235,17 @@ impl Query { } }); - Query { + Ok(Query { original: params.clone(), compiled, has_vrl, - } + }) } - fn compile_value(param: &ParameterValue, functions: &[Box]) -> CompiledParam { + fn compile_value( + param: &ParameterValue, + functions: &[Box], + ) -> Result { let program = if param.is_vrl() { let state = TypeState::default(); let config = CompileConfig::default(); @@ -256,34 +261,37 @@ impl Query { } Err(diagnostics) => { let error = format_vrl_diagnostics(param.value(), diagnostics); - warn!(message = "VRL compilation failed.", %error); - None + return Err(sources::BuildError::VrlCompilationError { + message: format!("VRL compilation failed: {}", error), + }); } } } else { None }; - CompiledParam { + Ok(CompiledParam { value: param.value().to_string(), program, - } + }) } fn compile_param( value: &QueryParameterValue, functions: &[Box], - ) -> CompiledQueryParameterValue { + ) -> Result { match value { - QueryParameterValue::SingleParam(param) => CompiledQueryParameterValue::SingleParam( - Box::new(Self::compile_value(param, functions)), - ), + QueryParameterValue::SingleParam(param) => { + Ok(CompiledQueryParameterValue::SingleParam(Box::new( + Self::compile_value(param, functions)?, + ))) + } QueryParameterValue::MultiParams(params) => { let compiled = params .iter() .map(|p| Self::compile_value(p, functions)) - .collect(); - CompiledQueryParameterValue::MultiParams(compiled) + .collect::, _>>()?; + Ok(CompiledQueryParameterValue::MultiParams(compiled)) } } } @@ -293,7 +301,7 @@ impl Query { #[typetag::serde(name = "http_client")] impl SourceConfig for HttpClientConfig { async fn build(&self, cx: SourceContext) -> crate::Result { - let query = Query::new(&self.query.clone()); + let query = Query::new(&self.query.clone())?; // Build the base URLs let endpoints = [self.endpoint.clone()]; diff --git a/src/sources/mod.rs b/src/sources/mod.rs index f7b2b6bb534e8..77258bdb77a46 100644 --- a/src/sources/mod.rs +++ b/src/sources/mod.rs @@ -102,7 +102,9 @@ pub use vector_lib::source::Source; #[allow(dead_code)] // Easier than listing out all the features that use this /// Common build errors #[derive(Debug, Snafu)] -enum BuildError { +pub enum BuildError { #[snafu(display("URI parse error: {}", source))] UriParseError { source: ::http::uri::InvalidUri }, + #[snafu(display("VRL compilation error: {}", message))] + VrlCompilationError { message: String }, }