@@ -79,6 +79,14 @@ fn main() {
7979 println ! ( "\n 📋 Gate 9: Cargo About Integration" ) ;
8080 gates. push ( run_cargo_about_gate ( ) ) ;
8181
82+ // Gate 10: Source Validation - Registry and VCS source restrictions
83+ println ! ( "\n 🔐 Gate 10: Source Validation" ) ;
84+ gates. push ( run_source_validation_gate ( ) ) ;
85+
86+ // Gate 11: MSRV and Edition Enforcement - Workspace consistency
87+ println ! ( "\n 📋 Gate 11: MSRV and Edition Enforcement" ) ;
88+ gates. push ( run_msrv_edition_gate ( ) ) ;
89+
8290 // Summary
8391 println ! ( "\n 📊 CI Supply Chain Security Gate Summary" ) ;
8492 println ! ( "=======================================" ) ;
@@ -1077,6 +1085,209 @@ fn run_cargo_about_gate() -> GateResult {
10771085 }
10781086}
10791087
1088+ fn run_source_validation_gate ( ) -> GateResult {
1089+ println ! ( " Validating registry and VCS source restrictions..." ) ;
1090+
1091+ // Run cargo deny check sources to validate source restrictions
1092+ let output = Command :: new ( "cargo" )
1093+ . args ( & [ "deny" , "--locked" , "--version" , "0.14.23" , "check" , "sources" , "--format" , "json" ] )
1094+ . output ( ) ;
1095+
1096+ match output {
1097+ Ok ( result) => {
1098+ let exit_code = result. status . code ( ) . unwrap_or ( -1 ) ;
1099+ let stdout = String :: from_utf8_lossy ( & result. stdout ) ;
1100+ let stderr = String :: from_utf8_lossy ( & result. stderr ) ;
1101+
1102+ // Save raw output as artifact
1103+ let artifacts = save_gate_artifacts ( "cargo-deny-sources" , & stdout, & stderr, exit_code) ;
1104+
1105+ // Always check exit code first
1106+ if exit_code != 0 {
1107+ // Parse JSON for detailed error information
1108+ let error_details = if let Ok ( report) = parse_deny_json ( & stdout) {
1109+ let error_count = report. diagnostics . iter ( )
1110+ . filter ( |d| d. severity == "error" )
1111+ . count ( ) ;
1112+ let warning_count = report. diagnostics . iter ( )
1113+ . filter ( |d| d. severity == "warn" )
1114+ . count ( ) ;
1115+
1116+ // Check for specific source violations
1117+ let git_violations = report. diagnostics . iter ( )
1118+ . filter ( |d| d. message . contains ( "git" ) || d. message . contains ( "unknown-git" ) )
1119+ . count ( ) ;
1120+ let registry_violations = report. diagnostics . iter ( )
1121+ . filter ( |d| d. message . contains ( "registry" ) || d. message . contains ( "unknown-registry" ) )
1122+ . count ( ) ;
1123+
1124+ format ! ( "{} errors, {} warnings (git: {}, registry: {})" ,
1125+ error_count, warning_count, git_violations, registry_violations)
1126+ } else {
1127+ // Fallback to stderr parsing
1128+ let error_lines: Vec < & str > = stderr. lines ( )
1129+ . filter ( |line| line. contains ( "error:" ) || line. contains ( "denied:" ) )
1130+ . collect ( ) ;
1131+ format ! ( "{} source violations detected" , error_lines. len( ) )
1132+ } ;
1133+
1134+ return GateResult {
1135+ name : "Source Validation" . to_string ( ) ,
1136+ passed : false ,
1137+ message : format ! ( "Source restrictions violated: {}" , error_details) ,
1138+ artifacts,
1139+ } ;
1140+ }
1141+
1142+ // Additional validation: Check for git dependencies using cargo tree
1143+ println ! ( " Performing additional git dependency validation..." ) ;
1144+ let tree_output = Command :: new ( "cargo" )
1145+ . args ( & [ "tree" , "--format" , "{p} {r}" ] )
1146+ . output ( ) ;
1147+
1148+ match tree_output {
1149+ Ok ( tree_result) if tree_result. status . success ( ) => {
1150+ let tree_stdout = String :: from_utf8_lossy ( & tree_result. stdout ) ;
1151+ let git_deps: Vec < & str > = tree_stdout. lines ( )
1152+ . filter ( |line| line. contains ( "git+" ) )
1153+ . collect ( ) ;
1154+
1155+ if !git_deps. is_empty ( ) {
1156+ return GateResult {
1157+ name : "Source Validation" . to_string ( ) ,
1158+ passed : false ,
1159+ message : format ! ( "Found {} git dependencies: {}" ,
1160+ git_deps. len( ) ,
1161+ git_deps. iter( ) . take( 3 ) . map( |s| s. split_whitespace( ) . next( ) . unwrap_or( "" ) ) . collect:: <Vec <_>>( ) . join( ", " ) ) ,
1162+ artifacts,
1163+ } ;
1164+ }
1165+ }
1166+ _ => {
1167+ // Tree command failed, but deny passed, so continue
1168+ println ! ( " Warning: Could not validate git dependencies with cargo tree" ) ;
1169+ }
1170+ }
1171+
1172+ // Parse JSON output for warnings
1173+ if let Ok ( report) = parse_deny_json ( & stdout) {
1174+ let warnings: Vec < & Diagnostic > = report. diagnostics . iter ( )
1175+ . filter ( |d| d. severity == "warn" )
1176+ . collect ( ) ;
1177+
1178+ let message = if warnings. is_empty ( ) {
1179+ "All source restrictions validated - only crates.io registry allowed" . to_string ( )
1180+ } else {
1181+ format ! ( "Source validation passed with {} warnings" , warnings. len( ) )
1182+ } ;
1183+
1184+ GateResult {
1185+ name : "Source Validation" . to_string ( ) ,
1186+ passed : true ,
1187+ message,
1188+ artifacts,
1189+ }
1190+ } else {
1191+ GateResult {
1192+ name : "Source Validation" . to_string ( ) ,
1193+ passed : true ,
1194+ message : "Source restrictions validated (JSON parse failed)" . to_string ( ) ,
1195+ artifacts,
1196+ }
1197+ }
1198+ }
1199+ Err ( e) => {
1200+ GateResult {
1201+ name : "Source Validation" . to_string ( ) ,
1202+ passed : false ,
1203+ message : format ! ( "Failed to run cargo deny sources: {}" , e) ,
1204+ artifacts : Vec :: new ( ) ,
1205+ }
1206+ }
1207+ }
1208+ }
1209+
1210+ fn run_msrv_edition_gate ( ) -> GateResult {
1211+ println ! ( " Validating MSRV and edition consistency across workspace..." ) ;
1212+
1213+ // Run the MSRV/edition validation script
1214+ let output = Command :: new ( "cargo" )
1215+ . args ( & [ "+nightly" , "-Zscript" , "scripts/validate_msrv_edition.rs" ] )
1216+ . output ( ) ;
1217+
1218+ match output {
1219+ Ok ( result) => {
1220+ let exit_code = result. status . code ( ) . unwrap_or ( -1 ) ;
1221+ let stdout = String :: from_utf8_lossy ( & result. stdout ) ;
1222+ let stderr = String :: from_utf8_lossy ( & result. stderr ) ;
1223+
1224+ // Save output as artifact
1225+ let artifacts = save_gate_artifacts ( "msrv-edition-validation" , & stdout, & stderr, exit_code) ;
1226+
1227+ if exit_code == 0 {
1228+ // Parse success message for details
1229+ let message = if stdout. contains ( "All" ) && stdout. contains ( "workspace crates comply" ) {
1230+ // Extract the number of crates from the output
1231+ if let Some ( line) = stdout. lines ( ) . find ( |l| l. contains ( "workspace crates comply" ) ) {
1232+ line. trim_start_matches ( "✅ " ) . to_string ( )
1233+ } else {
1234+ "All workspace crates comply with MSRV and edition requirements" . to_string ( )
1235+ }
1236+ } else {
1237+ "MSRV and edition validation passed" . to_string ( )
1238+ } ;
1239+
1240+ GateResult {
1241+ name : "MSRV and Edition Enforcement" . to_string ( ) ,
1242+ passed : true ,
1243+ message,
1244+ artifacts,
1245+ }
1246+ } else {
1247+ // Parse failure details from output
1248+ let mut violations = Vec :: new ( ) ;
1249+ let mut in_violations = false ;
1250+
1251+ for line in stdout. lines ( ) {
1252+ if line. starts_with ( "❌" ) {
1253+ in_violations = true ;
1254+ continue ;
1255+ }
1256+ if in_violations && line. starts_with ( " - " ) {
1257+ violations. push ( line. trim_start_matches ( " - " ) . to_string ( ) ) ;
1258+ }
1259+ if in_violations && line. starts_with ( "💡" ) {
1260+ break ;
1261+ }
1262+ }
1263+
1264+ let violation_summary = if violations. is_empty ( ) {
1265+ "MSRV/edition violations detected" . to_string ( )
1266+ } else {
1267+ format ! ( "{} violations: {}" ,
1268+ violations. len( ) ,
1269+ violations. iter( ) . take( 3 ) . cloned( ) . collect:: <Vec <_>>( ) . join( "; " ) )
1270+ } ;
1271+
1272+ GateResult {
1273+ name : "MSRV and Edition Enforcement" . to_string ( ) ,
1274+ passed : false ,
1275+ message : violation_summary,
1276+ artifacts,
1277+ }
1278+ }
1279+ }
1280+ Err ( e) => {
1281+ GateResult {
1282+ name : "MSRV and Edition Enforcement" . to_string ( ) ,
1283+ passed : false ,
1284+ message : format ! ( "Failed to run MSRV/edition validation: {}" , e) ,
1285+ artifacts : Vec :: new ( ) ,
1286+ }
1287+ }
1288+ }
1289+ }
1290+
10801291fn save_comprehensive_artifacts ( gate_name : & str , stdout : & str , stderr : & str , exit_code : i32 ) -> Vec < String > {
10811292 use std:: time:: { SystemTime , UNIX_EPOCH } ;
10821293
0 commit comments