use std::str::FromStr;
pub const VALID_PRESETS: &[&str] = &["opensource", "enterprise", "strict"];
#[allow(dead_code)]
pub fn is_valid_preset(name: &str) -> bool {
VALID_PRESETS.contains(&name.to_lowercase().as_str()) || Preset::from_name(name).is_some()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Preset {
OpenSource,
Enterprise,
Strict,
}
impl FromStr for Preset {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::from_name(s).ok_or_else(|| format!("Invalid preset: {}", s))
}
}
impl Preset {
pub fn from_name(name: &str) -> Option<Self> {
match name.to_lowercase().as_str() {
"opensource" | "open-source" | "oss" => Some(Self::OpenSource),
"enterprise" | "ent" | "internal" => Some(Self::Enterprise),
"strict" | "secure" | "compliance" => Some(Self::Strict),
_ => None,
}
}
pub fn name(&self) -> &'static str {
match self {
Self::OpenSource => "opensource",
Self::Enterprise => "enterprise",
Self::Strict => "strict",
}
}
#[allow(dead_code)]
pub fn description(&self) -> &'static str {
match self {
Self::OpenSource => "Prepare repository for public open source release",
Self::Enterprise => "Apply internal company standards and policies",
Self::Strict => "Maximum security and compliance checks",
}
}
#[allow(dead_code)]
pub fn enabled_rules(&self) -> Vec<&'static str> {
match self {
Self::OpenSource => vec![
"secrets/hardcoded",
"secrets/files",
"secrets/env",
"docs/readme",
"docs/license",
"docs/contributing",
"docs/code-of-conduct",
"docs/security",
"docs/changelog",
"files/sensitive",
"files/large",
"files/gitignore",
"security/dependencies",
"security/branch-protection",
"security/vulnerability-alerts",
"security/dependabot-updates",
"workflows/secrets",
"workflows/permissions",
"workflows/linters-in-ci",
"docker/dockerfile-presence",
"docker/dockerignore",
"docker/from-pinning",
"docker/user",
"git/large-binaries",
"git/gitattributes",
"git/sensitive-files",
"dependencies/lock-files",
"codeowners/presence",
"codeowners/releases",
],
Self::Enterprise => vec![
"secrets/hardcoded",
"secrets/files",
"secrets/env",
"docs/readme",
"docs/security",
"files/sensitive",
"files/large",
"files/gitignore",
"security/dependencies",
"security/codeowners",
"security/signed-commits",
"security/branch-protection",
"security/vulnerability-alerts",
"security/dependabot-updates",
"security/secret-scanning",
"security/push-protection",
"security/actions-permissions",
"security/workflow-permissions",
"security/fork-pr-approval",
"security/access-control",
"security/infrastructure",
"workflows/secrets",
"workflows/permissions",
"workflows/timeout",
"workflows/pull-request-target",
"docker/dockerfile-presence",
"docker/dockerignore",
"docker/from-pinning",
"docker/user",
"docker/secrets-in-env",
"docker/healthcheck",
"quality/coverage",
"git/large-binaries",
"git/gitattributes",
"git/sensitive-files",
"dependencies/lock-files",
"codeowners/presence",
"codeowners/syntax",
"codeowners/valid-owners",
"codeowners/releases",
],
Self::Strict => vec![
"secrets/hardcoded",
"secrets/files",
"secrets/env",
"secrets/history",
"docs/readme",
"docs/license",
"docs/contributing",
"docs/code-of-conduct",
"docs/security",
"docs/changelog",
"docs/changelog-format",
"docs/changelog-unreleased",
"files/sensitive",
"files/large",
"files/gitignore",
"files/editorconfig",
"security/dependencies",
"security/codeowners",
"security/signed-commits",
"security/branch-protection",
"security/vulnerability-alerts",
"security/dependabot-updates",
"security/secret-scanning",
"security/push-protection",
"security/actions-permissions",
"security/workflow-permissions",
"security/fork-pr-approval",
"security/access-control",
"security/infrastructure",
"workflows/secrets",
"workflows/permissions",
"workflows/pinned-actions",
"workflows/timeout",
"workflows/concurrency",
"workflows/reusable-workflows",
"workflows/artifacts-retention",
"workflows/pull-request-target",
"workflows/linters-in-ci",
"docker/dockerfile-presence",
"docker/dockerignore",
"docker/from-pinning",
"docker/user",
"docker/healthcheck",
"docker/multistage",
"docker/secrets-in-env",
"docker/copy-all",
"quality/tests",
"quality/linting",
"quality/coverage",
"quality/api-docs",
"quality/complexity",
"quality/dead-code",
"quality/naming-conventions",
"git/large-binaries",
"git/gitattributes",
"git/sensitive-files",
"dependencies/lock-files",
"codeowners/presence",
"codeowners/syntax",
"codeowners/valid-owners",
"codeowners/releases",
"codeowners/signed-tags",
],
}
}
#[allow(dead_code)]
pub fn critical_rules(&self) -> Vec<&'static str> {
match self {
Self::OpenSource => vec!["secrets/hardcoded", "secrets/files", "docs/license"],
Self::Enterprise => vec!["secrets/hardcoded", "secrets/files", "security/codeowners"],
Self::Strict => vec![
"secrets/hardcoded",
"secrets/files",
"secrets/history",
"docs/license",
"security/codeowners",
"security/signed-commits",
"docker/from-pinning",
"git/large-binaries",
],
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_preset_from_name() {
assert_eq!(Preset::from_name("opensource").unwrap(), Preset::OpenSource);
assert_eq!(Preset::from_name("oss").unwrap(), Preset::OpenSource);
assert_eq!(Preset::from_name("enterprise").unwrap(), Preset::Enterprise);
assert_eq!(Preset::from_name("strict").unwrap(), Preset::Strict);
assert!(Preset::from_name("invalid").is_none());
}
#[test]
fn test_preset_name() {
assert_eq!(Preset::OpenSource.name(), "opensource");
assert_eq!(Preset::Enterprise.name(), "enterprise");
assert_eq!(Preset::Strict.name(), "strict");
}
#[test]
fn test_preset_from_name_aliases() {
assert_eq!(
Preset::from_name("open-source").unwrap(),
Preset::OpenSource
);
assert_eq!(Preset::from_name("OPENSOURCE").unwrap(), Preset::OpenSource);
assert_eq!(Preset::from_name("ent").unwrap(), Preset::Enterprise);
assert_eq!(Preset::from_name("internal").unwrap(), Preset::Enterprise);
assert_eq!(Preset::from_name("ENTERPRISE").unwrap(), Preset::Enterprise);
assert_eq!(Preset::from_name("secure").unwrap(), Preset::Strict);
assert_eq!(Preset::from_name("compliance").unwrap(), Preset::Strict);
assert_eq!(Preset::from_name("STRICT").unwrap(), Preset::Strict);
}
#[test]
fn test_preset_description() {
assert!(Preset::OpenSource.description().contains("open source"));
assert!(Preset::Enterprise.description().contains("internal"));
assert!(Preset::Strict.description().contains("security"));
}
#[test]
fn test_preset_enabled_rules_opensource() {
let rules = Preset::OpenSource.enabled_rules();
assert!(rules.contains(&"secrets/hardcoded"));
assert!(rules.contains(&"docs/readme"));
assert!(rules.contains(&"docs/license"));
assert!(rules.contains(&"docs/contributing"));
assert!(rules.contains(&"docs/code-of-conduct"));
}
#[test]
fn test_preset_enabled_rules_enterprise() {
let rules = Preset::Enterprise.enabled_rules();
assert!(rules.contains(&"secrets/hardcoded"));
assert!(rules.contains(&"security/codeowners"));
assert!(rules.contains(&"security/signed-commits"));
assert!(!rules.contains(&"docs/license"));
}
#[test]
fn test_preset_enabled_rules_strict() {
let rules = Preset::Strict.enabled_rules();
assert!(rules.contains(&"secrets/hardcoded"));
assert!(rules.contains(&"secrets/history"));
assert!(rules.contains(&"quality/tests"));
assert!(rules.contains(&"quality/linting"));
assert!(rules.contains(&"workflows/pinned-actions"));
assert!(rules.contains(&"docker/from-pinning"));
assert!(rules.contains(&"workflows/timeout"));
assert!(rules.contains(&"quality/coverage"));
assert!(rules.contains(&"docs/changelog-format"));
}
#[test]
fn test_preset_enabled_rules_opensource_new() {
let rules = Preset::OpenSource.enabled_rules();
assert!(rules.contains(&"docs/changelog"));
assert!(rules.contains(&"workflows/linters-in-ci"));
assert!(rules.contains(&"docker/dockerfile-presence"));
}
#[test]
fn test_preset_enabled_rules_enterprise_new() {
let rules = Preset::Enterprise.enabled_rules();
assert!(rules.contains(&"workflows/timeout"));
assert!(rules.contains(&"workflows/pull-request-target"));
assert!(rules.contains(&"docker/from-pinning"));
assert!(rules.contains(&"quality/coverage"));
}
#[test]
fn test_preset_critical_rules_strict_docker() {
let rules = Preset::Strict.critical_rules();
assert!(rules.contains(&"docker/from-pinning"));
}
#[test]
fn test_preset_critical_rules_opensource() {
let rules = Preset::OpenSource.critical_rules();
assert!(rules.contains(&"secrets/hardcoded"));
assert!(rules.contains(&"secrets/files"));
assert!(rules.contains(&"docs/license"));
}
#[test]
fn test_preset_critical_rules_enterprise() {
let rules = Preset::Enterprise.critical_rules();
assert!(rules.contains(&"secrets/hardcoded"));
assert!(rules.contains(&"security/codeowners"));
}
#[test]
fn test_preset_critical_rules_strict() {
let rules = Preset::Strict.critical_rules();
assert!(rules.contains(&"secrets/hardcoded"));
assert!(rules.contains(&"secrets/history"));
assert!(rules.contains(&"security/signed-commits"));
}
#[test]
fn test_preset_equality() {
assert_eq!(Preset::OpenSource, Preset::OpenSource);
assert_ne!(Preset::OpenSource, Preset::Enterprise);
assert_ne!(Preset::Enterprise, Preset::Strict);
}
#[test]
fn test_preset_copy() {
let preset = Preset::OpenSource;
let copied = preset;
assert_eq!(preset, copied);
}
#[test]
fn test_valid_presets_constant() {
assert_eq!(VALID_PRESETS.len(), 3);
assert!(VALID_PRESETS.contains(&"opensource"));
assert!(VALID_PRESETS.contains(&"enterprise"));
assert!(VALID_PRESETS.contains(&"strict"));
}
#[test]
fn test_is_valid_preset_canonical_names() {
assert!(is_valid_preset("opensource"));
assert!(is_valid_preset("enterprise"));
assert!(is_valid_preset("strict"));
}
#[test]
fn test_is_valid_preset_aliases() {
assert!(is_valid_preset("open-source"));
assert!(is_valid_preset("oss"));
assert!(is_valid_preset("ent"));
assert!(is_valid_preset("internal"));
assert!(is_valid_preset("secure"));
assert!(is_valid_preset("compliance"));
}
#[test]
fn test_is_valid_preset_case_insensitive() {
assert!(is_valid_preset("OPENSOURCE"));
assert!(is_valid_preset("OpenSource"));
assert!(is_valid_preset("ENTERPRISE"));
assert!(is_valid_preset("Enterprise"));
assert!(is_valid_preset("STRICT"));
assert!(is_valid_preset("Strict"));
}
#[test]
fn test_is_valid_preset_invalid() {
assert!(!is_valid_preset("invalid"));
assert!(!is_valid_preset("unknown"));
assert!(!is_valid_preset(""));
assert!(!is_valid_preset("foo"));
}
#[test]
fn test_preset_opensource_has_vulnerability_alerts() {
let rules = Preset::OpenSource.enabled_rules();
assert!(rules.contains(&"security/vulnerability-alerts"));
assert!(rules.contains(&"security/dependabot-updates"));
}
#[test]
fn test_preset_enterprise_has_all_security_rules() {
let rules = Preset::Enterprise.enabled_rules();
assert!(rules.contains(&"security/vulnerability-alerts"));
assert!(rules.contains(&"security/dependabot-updates"));
assert!(rules.contains(&"security/secret-scanning"));
assert!(rules.contains(&"security/push-protection"));
assert!(rules.contains(&"security/actions-permissions"));
assert!(rules.contains(&"security/workflow-permissions"));
assert!(rules.contains(&"security/fork-pr-approval"));
}
#[test]
fn test_preset_strict_has_all_security_rules() {
let rules = Preset::Strict.enabled_rules();
assert!(rules.contains(&"security/vulnerability-alerts"));
assert!(rules.contains(&"security/dependabot-updates"));
assert!(rules.contains(&"security/secret-scanning"));
assert!(rules.contains(&"security/push-protection"));
assert!(rules.contains(&"security/actions-permissions"));
assert!(rules.contains(&"security/workflow-permissions"));
assert!(rules.contains(&"security/fork-pr-approval"));
}
#[test]
fn test_preset_enterprise_has_access_control() {
let rules = Preset::Enterprise.enabled_rules();
assert!(rules.contains(&"security/access-control"));
assert!(rules.contains(&"security/infrastructure"));
}
#[test]
fn test_preset_strict_has_access_control() {
let rules = Preset::Strict.enabled_rules();
assert!(rules.contains(&"security/access-control"));
assert!(rules.contains(&"security/infrastructure"));
}
#[test]
fn test_preset_opensource_no_access_control() {
let rules = Preset::OpenSource.enabled_rules();
assert!(!rules.contains(&"security/access-control"));
assert!(!rules.contains(&"security/infrastructure"));
}
}