feat: standardise front matter sanitization via SanitizeConfig/SanitizeContent traits#210
Conversation
…g structs Add #[derive(SanitizeConfig)] from the ado_aw_derive crate to Config structs across 9 safe-output modules: - CreateWorkItemConfig (with #[sanitize_config(sanitize_keys)] on custom_fields and #[sanitize_config(nested)] on artifact_link) - ArtifactLinkConfig - CreateWikiPageConfig - UpdateWikiPageConfig - UpdateWorkItemConfig - CreatePrConfig - CommentOnWorkItemConfig - AddPrCommentConfig - QueueBuildConfig - LinkWorkItemsConfig Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…choke-point - Add SanitizeConfig impls for all FrontMatter sub-types (manual impls for enums: PoolConfig, ScheduleConfig, EngineConfig, McpConfig, CacheMemoryToolConfig, AzureDevOpsToolConfig; derive for structs) - Add manual SanitizeConfig impl for FrontMatter delegating to all nested types (skips opaque serde_yaml::Value fields) - Add SanitizeConfig bound to ExecutionContext::get_tool_config<T>() as a compile-time forcing function for config sanitization - Call front_matter.sanitize_config_fields() in compile_pipeline() and check_pipeline() before template substitution - Add Option<Vec<String>> support to both derive macros Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add SanitizeConfig trait impls for LeanRuntimeConfig (enum, manual), LeanOptions (derive), and RuntimesConfig (manual). Update FrontMatter impl to sanitize the new runtimes field. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
🔍 Rust PR ReviewSummary: Looks good overall — the architecture is clean and the compile-time forcing function via trait bounds is a solid pattern. One genuine footgun to fix, and one minor inconsistency. Findings🐛 Bugs / Logic Issues
|
- Remove hardcoded 'name' field skip from SanitizeContent derive — the skip was a silent footgun for future structs with user-controlled 'name' fields. Tool result structs use manual SanitizeContent impls that already explicitly list fields, so this skip was redundant. - Replace manual EngineOptions SanitizeConfig impl with derive — the manual impl only touched model (Option<String>) which the derive handles automatically, and risked missing future String fields. - Add doc comment to ado-aw-derive explaining the crate::sanitize:: path coupling constraint for maintainability. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
🔍 Rust PR ReviewSummary: Well-designed systematic improvement — a few maintenance footguns in the derive macro worth being aware of. Findings
|
Summary
Replaces ad-hoc, per-field sanitization of front matter values with two dedicated, derivable traits that systematically cover all textual fields across the codebase.
Problem
Every time a new textual field was added to a front matter struct or safe-output config, developers had to remember to manually add sanitization — this was error-prone and led to repeated issues with unsanitized config values flowing into YAML templates and ADO API calls.
Solution
Two traits with distinct purposes
SanitizeContent(renamed fromSanitize)SanitizeConfig(new)##vso[) + content limitsDerive macros (
ado-aw-derivecrate)Both traits are derivable via proc macros that auto-handle
String,Option<String>,Option<Vec<String>>,Vec<String>, andHashMap<String, String>fields. Supported attributes:#[sanitize_config(skip)]/#[sanitize_content(skip)]— skip a field#[sanitize_config(nested)]/#[sanitize_content(nested)]— recurse into nested structs#[sanitize_config(sanitize_keys)]— sanitize HashMap keys (config only)#[sanitize_content(light)]— control char removal only (content only, e.g. wiki paths)Compile-time enforcement
get_tool_config<T: SanitizeConfig>()— adding a new config struct without implementing the trait causes a compile errorcompile_pipeline()— callsfront_matter.sanitize_config_fields()before any template substitutionChanges
ado-aw-derive/— proc macro crate with#[derive(SanitizeConfig)]and#[derive(SanitizeContent)]src/sanitize.rs: NewSanitizeConfigtrait +sanitize_config()+sanitize_light()functions; renamedSanitize→SanitizeContentsrc/compile/types.rs:SanitizeConfigon all front matter types (derive on structs, manual impls for 6 enums)src/compile/mod.rs: Sanitization call in compile pipelinesrc/safeoutputs/result.rs:SanitizeConfigbound onget_tool_config()src/safeoutputs/*.rsfiles:Sanitize→SanitizeContentrename +SanitizeConfigderive on all Config structsTesting
All 819 existing tests pass with no regressions. New unit tests added for
sanitize_config()andsanitize_light().