Skip to content

Commit 61cb012

Browse files
8
1 parent b00a65b commit 61cb012

5 files changed

Lines changed: 533 additions & 7 deletions

File tree

.kiro/specs/supply-chain-fixes/tasks.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,15 +213,24 @@
213213
- Set clap default-features = false with features = ["derive"]
214214
- _Requirements: SC-05.4_
215215

216-
- [ ] 8. Implement security policy enforcement
217-
- [ ] 8.1 Configure comprehensive registry and VCS source restrictions
216+
- [x] 8. Implement security policy enforcement
217+
218+
219+
220+
221+
222+
- [x] 8.1 Configure comprehensive registry and VCS source restrictions
223+
224+
218225
- Update deny.toml [sources] with unknown-registry = "deny" and unknown-git = "deny"
219226
- Set allow-registry = ["https://github.com/rust-lang/crates.io-index"]
220227
- Add empty allow-git template with per-crate exception structure
221228
- Create CI gate to fail on git dependencies or unknown registries
222229
- _Requirements: NFR-C_
223230

224-
- [ ] 8.2 Add MSRV and edition enforcement across workspace
231+
- [x] 8.2 Add MSRV and edition enforcement across workspace
232+
233+
225234
- Create CI job to validate edition = "2024" in all workspace package Cargo.toml files
226235
- Verify rust-version = "1.89.0" consistency across all crates
227236
- Add automated check that fails CI if any crate deviates from workspace standards

THIRD_PARTY_LICENSES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Generated on:
88

99
### MIT License
1010

11-
**Dependencies using this license (271 total):**
11+
**Dependencies using this license (259 total):**
1212

1313

1414
**License Text:**

examples/Cargo.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
[package]
22
name = "flight-hub-examples"
33
version = "0.1.0"
4-
edition = "2024"
5-
license = "MIT OR Apache-2.0"
6-
authors = ["Flight Hub Team"]
4+
edition.workspace = true
5+
rust-version.workspace = true
6+
license.workspace = true
7+
authors.workspace = true
78
publish = false
89

910
# SPDX License Identifier

scripts/ci_supply_chain_gate.rs

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
10801291
fn 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

Comments
 (0)