@@ -185,16 +185,34 @@ pub fn validate_front_matter_identity(front_matter: &FrontMatter) -> Result<()>
185185 }
186186 }
187187
188- // Validate trigger.pipeline fields for newlines
188+ // Validate trigger.pipeline fields for newlines and ADO expressions
189189 if let Some ( trigger_config) = & front_matter. triggers {
190190 if let Some ( pipeline) = & trigger_config. pipeline {
191- if pipeline. name . contains ( '\n' ) || pipeline. name . contains ( '\r' ) {
192- anyhow:: bail!(
193- "Front matter 'triggers.pipeline.name' must be a single line (no newlines). \
194- Multi-line values could inject YAML structure into the generated pipeline.",
195- ) ;
191+ for ( field, value) in [ ( "triggers.pipeline.name" , pipeline. name . as_str ( ) ) ] {
192+ if value. contains ( "${{" ) || value. contains ( "$(" ) || value. contains ( "$[" ) {
193+ anyhow:: bail!(
194+ "Front matter '{}' contains an ADO expression ('${{{{', '$(', or '$[') which is not allowed. \
195+ Use literal values only. Found: '{}'",
196+ field,
197+ value,
198+ ) ;
199+ }
200+ if value. contains ( '\n' ) || value. contains ( '\r' ) {
201+ anyhow:: bail!(
202+ "Front matter '{}' must be a single line (no newlines). \
203+ Multi-line values could inject YAML structure into the generated pipeline.",
204+ field,
205+ ) ;
206+ }
196207 }
197208 if let Some ( project) = & pipeline. project {
209+ if project. contains ( "${{" ) || project. contains ( "$(" ) || project. contains ( "$[" ) {
210+ anyhow:: bail!(
211+ "Front matter 'triggers.pipeline.project' contains an ADO expression ('${{{{', '$(', or '$[') which is not allowed. \
212+ Use literal values only. Found: '{}'",
213+ project,
214+ ) ;
215+ }
198216 if project. contains ( '\n' ) || project. contains ( '\r' ) {
199217 anyhow:: bail!(
200218 "Front matter 'triggers.pipeline.project' must be a single line (no newlines). \
@@ -203,10 +221,18 @@ pub fn validate_front_matter_identity(front_matter: &FrontMatter) -> Result<()>
203221 }
204222 }
205223 for branch in & pipeline. branches {
224+ if branch. contains ( "${{" ) || branch. contains ( "$(" ) || branch. contains ( "$[" ) {
225+ anyhow:: bail!(
226+ "Front matter 'triggers.pipeline.branches' entry {:?} contains an ADO expression ('${{{{', '$(', or '$[') \
227+ which is not allowed. Use literal values only.",
228+ branch,
229+ ) ;
230+ }
206231 if branch. contains ( '\n' ) || branch. contains ( '\r' ) {
207232 anyhow:: bail!(
208- "Front matter 'triggers.pipeline.branches' entries must be single line (no newlines). \
233+ "Front matter 'triggers.pipeline.branches' entry {:?} must be single line (no newlines). \
209234 Multi-line values could inject YAML structure into the generated pipeline.",
235+ branch,
210236 ) ;
211237 }
212238 }
@@ -2430,6 +2456,51 @@ mod tests {
24302456 assert ! ( result. unwrap_err( ) . to_string( ) . contains( "ADO expression" ) ) ;
24312457 }
24322458
2459+ #[ test]
2460+ fn test_validate_front_matter_identity_rejects_ado_expression_in_trigger_pipeline_name ( ) {
2461+ let mut fm = minimal_front_matter ( ) ;
2462+ fm. triggers = Some ( TriggerConfig {
2463+ pipeline : Some ( crate :: compile:: types:: PipelineTrigger {
2464+ name : "Build $(System.AccessToken)" . to_string ( ) ,
2465+ project : None ,
2466+ branches : vec ! [ ] ,
2467+ } ) ,
2468+ } ) ;
2469+ let result = validate_front_matter_identity ( & fm) ;
2470+ assert ! ( result. is_err( ) ) ;
2471+ assert ! ( result. unwrap_err( ) . to_string( ) . contains( "ADO expression" ) ) ;
2472+ }
2473+
2474+ #[ test]
2475+ fn test_validate_front_matter_identity_rejects_ado_expression_in_trigger_pipeline_project ( ) {
2476+ let mut fm = minimal_front_matter ( ) ;
2477+ fm. triggers = Some ( TriggerConfig {
2478+ pipeline : Some ( crate :: compile:: types:: PipelineTrigger {
2479+ name : "Build Pipeline" . to_string ( ) ,
2480+ project : Some ( "$(System.AccessToken)" . to_string ( ) ) ,
2481+ branches : vec ! [ ] ,
2482+ } ) ,
2483+ } ) ;
2484+ let result = validate_front_matter_identity ( & fm) ;
2485+ assert ! ( result. is_err( ) ) ;
2486+ assert ! ( result. unwrap_err( ) . to_string( ) . contains( "ADO expression" ) ) ;
2487+ }
2488+
2489+ #[ test]
2490+ fn test_validate_front_matter_identity_rejects_ado_expression_in_trigger_pipeline_branch ( ) {
2491+ let mut fm = minimal_front_matter ( ) ;
2492+ fm. triggers = Some ( TriggerConfig {
2493+ pipeline : Some ( crate :: compile:: types:: PipelineTrigger {
2494+ name : "Build Pipeline" . to_string ( ) ,
2495+ project : None ,
2496+ branches : vec ! [ "$[variables['token']]" . to_string( ) ] ,
2497+ } ) ,
2498+ } ) ;
2499+ let result = validate_front_matter_identity ( & fm) ;
2500+ assert ! ( result. is_err( ) ) ;
2501+ assert ! ( result. unwrap_err( ) . to_string( ) . contains( "ADO expression" ) ) ;
2502+ }
2503+
24332504 #[ test]
24342505 fn test_pipeline_resources_escapes_single_quotes ( ) {
24352506 let triggers = Some ( TriggerConfig {
0 commit comments