Skip to content

Commit 16367dd

Browse files
chore: validate trigger.pipeline fields for newlines (#184)
* Initial plan * chore: validate trigger.pipeline fields for newlines Agent-Logs-Url: https://github.com/githubnext/ado-aw/sessions/b3ff3ea2-2fe8-43fe-bb23-a3eda6330acb Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
1 parent 2da49cd commit 16367dd

1 file changed

Lines changed: 89 additions & 1 deletion

File tree

src/compile/common.rs

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,35 @@ pub fn validate_front_matter_identity(front_matter: &FrontMatter) -> Result<()>
184184
);
185185
}
186186
}
187+
188+
// Validate trigger.pipeline fields for newlines
189+
if let Some(trigger_config) = &front_matter.triggers {
190+
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+
);
196+
}
197+
if let Some(project) = &pipeline.project {
198+
if project.contains('\n') || project.contains('\r') {
199+
anyhow::bail!(
200+
"Front matter 'triggers.pipeline.project' must be a single line (no newlines). \
201+
Multi-line values could inject YAML structure into the generated pipeline.",
202+
);
203+
}
204+
}
205+
for branch in &pipeline.branches {
206+
if branch.contains('\n') || branch.contains('\r') {
207+
anyhow::bail!(
208+
"Front matter 'triggers.pipeline.branches' entries must be single line (no newlines). \
209+
Multi-line values could inject YAML structure into the generated pipeline.",
210+
);
211+
}
212+
}
213+
}
214+
}
215+
187216
Ok(())
188217
}
189218

@@ -2325,14 +2354,73 @@ mod tests {
23252354
}
23262355

23272356
#[test]
2328-
fn test_validate_front_matter_identity_allows_valid_values() {
2357+
fn test_validate_front_matter_identity_rejects_newline_in_trigger_pipeline_name() {
2358+
let mut fm = minimal_front_matter();
2359+
fm.triggers = Some(TriggerConfig {
2360+
pipeline: Some(crate::compile::types::PipelineTrigger {
2361+
name: "Build\ninjected: true".to_string(),
2362+
project: None,
2363+
branches: vec![],
2364+
}),
2365+
});
2366+
let result = validate_front_matter_identity(&fm);
2367+
assert!(result.is_err());
2368+
assert!(result.unwrap_err().to_string().contains("triggers.pipeline.name"));
2369+
}
2370+
2371+
#[test]
2372+
fn test_validate_front_matter_identity_rejects_newline_in_trigger_pipeline_project() {
2373+
let mut fm = minimal_front_matter();
2374+
fm.triggers = Some(TriggerConfig {
2375+
pipeline: Some(crate::compile::types::PipelineTrigger {
2376+
name: "Build Pipeline".to_string(),
2377+
project: Some("OtherProject\ninjected: true".to_string()),
2378+
branches: vec![],
2379+
}),
2380+
});
2381+
let result = validate_front_matter_identity(&fm);
2382+
assert!(result.is_err());
2383+
assert!(result.unwrap_err().to_string().contains("triggers.pipeline.project"));
2384+
}
2385+
2386+
#[test]
2387+
fn test_validate_front_matter_identity_rejects_newline_in_trigger_pipeline_branch() {
2388+
let mut fm = minimal_front_matter();
2389+
fm.triggers = Some(TriggerConfig {
2390+
pipeline: Some(crate::compile::types::PipelineTrigger {
2391+
name: "Build Pipeline".to_string(),
2392+
project: None,
2393+
branches: vec!["main\ninjected: true".to_string()],
2394+
}),
2395+
});
2396+
let result = validate_front_matter_identity(&fm);
2397+
assert!(result.is_err());
2398+
assert!(result.unwrap_err().to_string().contains("triggers.pipeline.branches"));
2399+
}
2400+
2401+
#[test]
2402+
fn test_validate_front_matter_identity_allows_valid_name_and_description() {
23292403
let mut fm = minimal_front_matter();
23302404
fm.name = "Daily Code Review Agent".to_string();
23312405
fm.description = "Reviews code daily for quality issues".to_string();
23322406
let result = validate_front_matter_identity(&fm);
23332407
assert!(result.is_ok());
23342408
}
23352409

2410+
#[test]
2411+
fn test_validate_front_matter_identity_allows_valid_trigger_pipeline_fields() {
2412+
let mut fm = minimal_front_matter();
2413+
fm.triggers = Some(TriggerConfig {
2414+
pipeline: Some(crate::compile::types::PipelineTrigger {
2415+
name: "Build Pipeline".to_string(),
2416+
project: Some("OtherProject".to_string()),
2417+
branches: vec!["main".to_string(), "release/*".to_string()],
2418+
}),
2419+
});
2420+
let result = validate_front_matter_identity(&fm);
2421+
assert!(result.is_ok());
2422+
}
2423+
23362424
#[test]
23372425
fn test_validate_front_matter_identity_rejects_runtime_expression() {
23382426
let mut fm = minimal_front_matter();

0 commit comments

Comments
 (0)