ci: tighten supply-chain fallback policy
This commit is contained in:
@@ -11,7 +11,7 @@ S0-ARCH-003 covered cargo xtask policy rejects platform/render adapter dependenc
|
|||||||
S0-ARCH-004 covered cargo xtask policy scans workspace-owned Rust/TOML for unsafe constructs and workspace lints forbid unsafe_code
|
S0-ARCH-004 covered cargo xtask policy scans workspace-owned Rust/TOML for unsafe constructs and workspace lints forbid unsafe_code
|
||||||
S0-ARCH-005 covered cargo xtask policy rejects Python source files, Python shebangs, and Python CI workflow steps while allowing docs requirements.txt
|
S0-ARCH-005 covered cargo xtask policy rejects Python source files, Python shebangs, and Python CI workflow steps while allowing docs requirements.txt
|
||||||
S0-ARCH-006 covered cargo xtask policy rejects non-fparkan package directories under crates/
|
S0-ARCH-006 covered cargo xtask policy rejects non-fparkan package directories under crates/
|
||||||
S0-ARCH-007 covered cargo xtask ci runs fmt, policy, workspace test, clippy, rustdoc warnings, cargo-deny or built-in supply-chain fallback, and strict acceptance audit
|
S0-ARCH-007 covered cargo xtask ci runs fmt, policy, workspace test, clippy, rustdoc warnings, cargo-deny with reviewed deny.toml, and strict acceptance audit; built-in supply-chain fallback is opt-in local-only and forbidden when CI is set
|
||||||
S0-ARCH-008 covered cargo xtask policy rejects moving Rust toolchains and workspace rust-version drift
|
S0-ARCH-008 covered cargo xtask policy rejects moving Rust toolchains and workspace rust-version drift
|
||||||
S0-ARCH-009 covered .github/workflows/ci.yml runs a pinned MSRV backend-neutral crate job
|
S0-ARCH-009 covered .github/workflows/ci.yml runs a pinned MSRV backend-neutral crate job
|
||||||
S0-ARCH-010 covered cargo xtask acceptance audit emits commit_sha, rust_toolchain, and msrv metadata into the JSON artifact
|
S0-ARCH-010 covered cargo xtask acceptance audit emits commit_sha, rust_toolchain, and msrv metadata into the JSON artifact
|
||||||
|
|||||||
|
+60
-1
@@ -38,6 +38,7 @@ const CI_ACCEPTANCE_ROADMAP: &str = "fixtures/acceptance/stage_0_roadmap.md";
|
|||||||
const CI_ACCEPTANCE_COVERAGE: &str = "fixtures/acceptance/coverage.tsv";
|
const CI_ACCEPTANCE_COVERAGE: &str = "fixtures/acceptance/coverage.tsv";
|
||||||
const CI_ACCEPTANCE_REPORT: &str = "target/fparkan/acceptance/stage-0-audit.json";
|
const CI_ACCEPTANCE_REPORT: &str = "target/fparkan/acceptance/stage-0-audit.json";
|
||||||
const STAGE_PACKAGE_MANIFEST: &str = "fixtures/acceptance/stage_packages.toml";
|
const STAGE_PACKAGE_MANIFEST: &str = "fixtures/acceptance/stage_packages.toml";
|
||||||
|
const SUPPLY_CHAIN_POLICY_CONFIG: &str = "deny.toml";
|
||||||
const REQUIRED_NATIVE_SMOKE_PLATFORMS: &[&str] = &["macos"];
|
const REQUIRED_NATIVE_SMOKE_PLATFORMS: &[&str] = &["macos"];
|
||||||
const APPROVED_REGISTRY_SOURCE: &str = "registry+https://github.com/rust-lang/crates.io-index";
|
const APPROVED_REGISTRY_SOURCE: &str = "registry+https://github.com/rust-lang/crates.io-index";
|
||||||
const SUPPLY_CHAIN_BANNED_PACKAGES: &[&str] = &["native-tls", "openssl", "openssl-sys"];
|
const SUPPLY_CHAIN_BANNED_PACKAGES: &[&str] = &["native-tls", "openssl", "openssl-sys"];
|
||||||
@@ -192,6 +193,7 @@ fn run_cargo_fmt_check() -> Result<(), String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn run_cargo_deny() -> Result<(), String> {
|
fn run_cargo_deny() -> Result<(), String> {
|
||||||
|
validate_supply_chain_policy_config(&workspace_relative_path(SUPPLY_CHAIN_POLICY_CONFIG))?;
|
||||||
let cargo_deny = std::env::var_os("CARGO_DENY").unwrap_or_else(|| "cargo-deny".into());
|
let cargo_deny = std::env::var_os("CARGO_DENY").unwrap_or_else(|| "cargo-deny".into());
|
||||||
let version_output = match Command::new(&cargo_deny).arg("--version").output() {
|
let version_output = match Command::new(&cargo_deny).arg("--version").output() {
|
||||||
Ok(output) => output,
|
Ok(output) => output,
|
||||||
@@ -238,11 +240,15 @@ fn run_cargo_deny() -> Result<(), String> {
|
|||||||
const PINNED_CARGO_DENY_VERSION: &str = "0.19.9";
|
const PINNED_CARGO_DENY_VERSION: &str = "0.19.9";
|
||||||
|
|
||||||
fn handle_cargo_deny_fallback(reason: &str) -> Result<(), String> {
|
fn handle_cargo_deny_fallback(reason: &str) -> Result<(), String> {
|
||||||
if std::env::var_os(ALLOW_SUPPLY_CHAIN_FALLBACK_ENV).is_some() {
|
if allow_supply_chain_fallback() {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"{reason}; running built-in supply-chain policy fallback because {ALLOW_SUPPLY_CHAIN_FALLBACK_ENV} is set"
|
"{reason}; running built-in supply-chain policy fallback because {ALLOW_SUPPLY_CHAIN_FALLBACK_ENV} is set"
|
||||||
);
|
);
|
||||||
run_builtin_supply_chain_policy(Path::new("."))
|
run_builtin_supply_chain_policy(Path::new("."))
|
||||||
|
} else if std::env::var_os(ALLOW_SUPPLY_CHAIN_FALLBACK_ENV).is_some() && ci_env_active() {
|
||||||
|
Err(format!(
|
||||||
|
"{reason}; {ALLOW_SUPPLY_CHAIN_FALLBACK_ENV} is for local developer convenience only and is forbidden when CI is set"
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
Err(format!(
|
Err(format!(
|
||||||
"{reason}; install cargo-deny {PINNED_CARGO_DENY_VERSION} or explicitly opt into the fallback with {ALLOW_SUPPLY_CHAIN_FALLBACK_ENV}=1"
|
"{reason}; install cargo-deny {PINNED_CARGO_DENY_VERSION} or explicitly opt into the fallback with {ALLOW_SUPPLY_CHAIN_FALLBACK_ENV}=1"
|
||||||
@@ -250,6 +256,32 @@ fn handle_cargo_deny_fallback(reason: &str) -> Result<(), String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn validate_supply_chain_policy_config(path: &Path) -> Result<(), String> {
|
||||||
|
if path.is_file() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(format!(
|
||||||
|
"reviewed supply-chain policy config is missing: {}",
|
||||||
|
path.display()
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn allow_supply_chain_fallback() -> bool {
|
||||||
|
std::env::var_os(ALLOW_SUPPLY_CHAIN_FALLBACK_ENV).is_some() && !ci_env_active()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ci_env_active() -> bool {
|
||||||
|
ci_env_value_is_active(std::env::var("CI").ok().as_deref())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ci_env_value_is_active(value: Option<&str>) -> bool {
|
||||||
|
value.is_some_and(|value| {
|
||||||
|
let trimmed = value.trim();
|
||||||
|
!trimmed.is_empty() && trimmed != "0" && !trimmed.eq_ignore_ascii_case("false")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn run_builtin_supply_chain_policy(root: &Path) -> Result<(), String> {
|
fn run_builtin_supply_chain_policy(root: &Path) -> Result<(), String> {
|
||||||
let mut failures = Vec::new();
|
let mut failures = Vec::new();
|
||||||
validate_workspace_license(root, &mut failures)?;
|
validate_workspace_license(root, &mut failures)?;
|
||||||
@@ -2747,6 +2779,33 @@ source = "git+https://example.invalid/repo"
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn supply_chain_policy_config_must_exist() -> Result<(), String> {
|
||||||
|
let root = temp_dir("supply-chain-config");
|
||||||
|
fs::create_dir_all(&root).map_err(|err| err.to_string())?;
|
||||||
|
|
||||||
|
let missing = root.join("deny.toml");
|
||||||
|
assert!(validate_supply_chain_policy_config(&missing).is_err());
|
||||||
|
|
||||||
|
fs::write(&missing, "[graph]\nall-features = true\n").map_err(|err| err.to_string())?;
|
||||||
|
assert_eq!(validate_supply_chain_policy_config(&missing), Ok(()));
|
||||||
|
|
||||||
|
fs::remove_dir_all(root).map_err(|err| err.to_string())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ci_env_truthy_values_are_detected() {
|
||||||
|
assert!(ci_env_value_is_active(Some("true")));
|
||||||
|
assert!(ci_env_value_is_active(Some("1")));
|
||||||
|
assert!(ci_env_value_is_active(Some("yes")));
|
||||||
|
assert!(!ci_env_value_is_active(None));
|
||||||
|
assert!(!ci_env_value_is_active(Some("")));
|
||||||
|
assert!(!ci_env_value_is_active(Some("0")));
|
||||||
|
assert!(!ci_env_value_is_active(Some("false")));
|
||||||
|
assert!(!ci_env_value_is_active(Some(" FALSE ")));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn detects_forbidden_domain_dependencies() {
|
fn detects_forbidden_domain_dependencies() {
|
||||||
assert!(!is_forbidden_domain_dependency("fparkan-render-vulkan"));
|
assert!(!is_forbidden_domain_dependency("fparkan-render-vulkan"));
|
||||||
|
|||||||
Reference in New Issue
Block a user