#![allow(clippy::unwrap_used)]
#![allow(clippy::expect_used)]
use super::config::*;
use super::discovery::*;
use super::rules::*;
use super::runner;
use super::scoring::*;
use std::path::PathBuf;
#[test]
fn test_comments_not_flagged() {
let content = "#!/bin/sh\n# $RANDOM is used for demo\necho hello\n";
let artifact = Artifact::new(
PathBuf::from("test.sh"),
Scope::Project,
ArtifactKind::ShellScript,
);
let result = check_rule(RuleId::Determinism, content, &artifact);
assert!(result.passed, "Comments should not be flagged");
}
#[test]
fn test_scope_display() {
assert_eq!(format!("{}", Scope::Project), "project");
assert_eq!(format!("{}", Scope::User), "user");
assert_eq!(format!("{}", Scope::System), "system");
}
#[test]
fn test_shellcheck_backtick_detection() {
let content = "#!/bin/sh\nresult=`ls -la`\n";
let artifact = Artifact::new(
PathBuf::from("test.sh"),
Scope::Project,
ArtifactKind::ShellScript,
);
let result = check_rule(RuleId::ShellCheck, content, &artifact);
assert!(!result.passed, "Backticks should be flagged as SC2006");
}
#[test]
fn test_clean_script_passes_all() {
let content = "#!/bin/sh\necho \"hello world\"\nmkdir -p /tmp/test\n";
let artifact = Artifact::new(
PathBuf::from("clean.sh"),
Scope::Project,
ArtifactKind::ShellScript,
);
for rule in RuleId::applicable_rules(ArtifactKind::ShellScript) {
let result = check_rule(rule, content, &artifact);
assert!(result.passed, "{} should pass on clean script", rule.code());
}
}
#[test]
fn test_gh134_makefile_dollar_escape_not_flagged() {
let content = "coverage-check:\n\t@cargo llvm-cov 2>&1 | awk '{print $$10}'\n";
let artifact = Artifact::new(
PathBuf::from("Makefile"),
Scope::Project,
ArtifactKind::Makefile,
);
let result = check_rule(RuleId::Determinism, content, &artifact);
assert!(
result.passed,
"Makefile $$ should not be flagged as COMPLY-002: {:?}",
result.violations
);
}
#[test]
fn test_gh134_makefile_dollar_escape_in_sed() {
let content = "clean:\n\t@sed 's/$$HOME/\\/root/g' input.txt\n";
let artifact = Artifact::new(
PathBuf::from("Makefile"),
Scope::Project,
ArtifactKind::Makefile,
);
let result = check_rule(RuleId::Determinism, content, &artifact);
assert!(
result.passed,
"Makefile $$ in sed should not be flagged: {:?}",
result.violations
);
}
#[test]
fn test_gh134_makefile_dollar_escape_loop_var() {
let content = "process:\n\t@for f in *.txt; do echo $$f; done\n";
let artifact = Artifact::new(
PathBuf::from("Makefile"),
Scope::Project,
ArtifactKind::Makefile,
);
let result = check_rule(RuleId::Determinism, content, &artifact);
assert!(
result.passed,
"Makefile $$ loop variable should not be flagged: {:?}",
result.violations
);
}
#[test]
fn test_gh134_shell_script_pid_still_flagged() {
let content = "#!/bin/sh\ntmpfile=/tmp/foo.$$\n";
let artifact = Artifact::new(
PathBuf::from("script.sh"),
Scope::Project,
ArtifactKind::ShellScript,
);
let result = check_rule(RuleId::Determinism, content, &artifact);
assert!(
!result.passed,
"Shell script $$ should still be flagged as PID usage"
);
}
#[test]
fn test_gh134_makefile_random_still_flagged() {
let content = "generate:\n\t@echo $RANDOM\n";
let artifact = Artifact::new(
PathBuf::from("Makefile"),
Scope::Project,
ArtifactKind::Makefile,
);
let result = check_rule(RuleId::Determinism, content, &artifact);
assert!(
!result.passed,
"$RANDOM should still be flagged in Makefiles"
);
}
#[test]
fn test_gh135_yq_eval_not_flagged() {
let content = "#!/bin/sh\nyq eval '.' \"$f\" > /dev/null\n";
let artifact = Artifact::new(
PathBuf::from("validate.sh"),
Scope::Project,
ArtifactKind::ShellScript,
);
let result = check_rule(RuleId::Security, content, &artifact);
assert!(
result.passed,
"yq eval should not be flagged as SEC001: {:?}",
result.violations
);
}
#[test]
fn test_gh135_jq_eval_not_flagged() {
let content = "#!/bin/sh\njq eval \"$expr\" input.json\n";
let artifact = Artifact::new(
PathBuf::from("process.sh"),
Scope::Project,
ArtifactKind::ShellScript,
);
let result = check_rule(RuleId::Security, content, &artifact);
assert!(
result.passed,
"jq eval should not be flagged as SEC001: {:?}",
result.violations
);
}
#[test]
fn test_gh135_helm_eval_not_flagged() {
let content = "#!/bin/sh\nhelm eval \"$template\" --values values.yaml\n";
let artifact = Artifact::new(
PathBuf::from("deploy.sh"),
Scope::Project,
ArtifactKind::ShellScript,
);
let result = check_rule(RuleId::Security, content, &artifact);
assert!(
result.passed,
"helm eval should not be flagged as SEC001: {:?}",
result.violations
);
}
#[test]
fn test_gh135_bare_eval_still_flagged() {
let content = "#!/bin/sh\neval \"$USER_INPUT\"\n";
let artifact = Artifact::new(
PathBuf::from("test.sh"),
Scope::Project,
ArtifactKind::ShellScript,
);
let result = check_rule(RuleId::Security, content, &artifact);
assert!(
!result.passed,
"Bare eval with variable should still be flagged"
);
}
#[test]
fn test_gh135_eval_after_semicolon_still_flagged() {
let content = "#!/bin/sh\ncd /tmp; eval \"$CMD\"\n";
let artifact = Artifact::new(
PathBuf::from("test.sh"),
Scope::Project,
ArtifactKind::ShellScript,
);
let result = check_rule(RuleId::Security, content, &artifact);
assert!(!result.passed, "eval after ; should still be flagged");
}
#[test]
fn test_gh135_eval_after_and_still_flagged() {
let content = "#!/bin/sh\ntest -f config && eval \"$CONFIG\"\n";
let artifact = Artifact::new(
PathBuf::from("test.sh"),
Scope::Project,
ArtifactKind::ShellScript,
);
let result = check_rule(RuleId::Security, content, &artifact);
assert!(!result.passed, "eval after && should still be flagged");
}
#[test]
fn test_gh135_workflow_yq_eval_not_flagged() {
let content = " - name: Validate\n run: |\n for f in playbooks/**/*.yaml; do\n yq eval '.' \"$f\" > /dev/null\n done\n";
let artifact = Artifact::new(
PathBuf::from(".github/workflows/ci.yml"),
Scope::Project,
ArtifactKind::Workflow,
);
let result = check_rule(RuleId::Security, content, &artifact);
assert!(
result.passed,
"yq eval in workflow should not be flagged: {:?}",
result.violations
);
}
#[test]
fn test_gh135_eval_after_or_still_flagged() {
let content = "#!/bin/sh\ntest -f config || eval \"$FALLBACK\"\n";
let artifact = Artifact::new(
PathBuf::from("test.sh"),
Scope::Project,
ArtifactKind::ShellScript,
);
let result = check_rule(RuleId::Security, content, &artifact);
assert!(!result.passed, "eval after || should still be flagged");
}
#[test]
fn test_config_save_load_roundtrip() {
let dir = tempfile::tempdir().unwrap();
let config = ComplyConfig::new_default("7.1.0");
config.save(dir.path()).unwrap();
let loaded = ComplyConfig::load(dir.path()).expect("Failed to load saved config");
assert_eq!(loaded.comply.version, "1.0.0");
assert_eq!(loaded.comply.bashrs_version, "7.1.0");
assert!(loaded.scopes.project);
assert!(!loaded.scopes.user);
assert!(!loaded.scopes.system);
assert!(loaded.rules.posix);
assert!(loaded.rules.security);
assert_eq!(loaded.thresholds.min_score, 80);
}
#[test]
fn test_config_save_creates_directory() {
let dir = tempfile::tempdir().unwrap();
let bashrs_dir = dir.path().join(".bashrs");
assert!(!bashrs_dir.exists());
let config = ComplyConfig::new_default("7.1.0");
config.save(dir.path()).unwrap();
assert!(bashrs_dir.exists());
assert!(bashrs_dir.join("comply.toml").exists());
}
#[test]
fn test_config_exists_false_when_no_file() {
let dir = tempfile::tempdir().unwrap();
assert!(!ComplyConfig::exists(dir.path()));
}
#[test]
fn test_config_exists_true_after_save() {
let dir = tempfile::tempdir().unwrap();
let config = ComplyConfig::new_default("7.1.0");
config.save(dir.path()).unwrap();
assert!(ComplyConfig::exists(dir.path()));
}
#[test]
fn test_config_load_returns_none_when_missing() {
let dir = tempfile::tempdir().unwrap();
assert!(ComplyConfig::load(dir.path()).is_none());
}
#[test]
include!("tests_s2_tests_config_load.rs");
include!("tests_s2_rules.rs");
include!("tests_s2_security.rs");
include!("tests_s2_posix.rs");
include!("tests_s2_docker.rs");
include!("tests_s2_suppression.rs");