diff --git a/README.md b/README.md index c22b519..a857d18 100644 --- a/README.md +++ b/README.md @@ -636,37 +636,39 @@ This is useful for: - Checking if a credential is still active before rotation - Validating secrets from external sources (password managers, ticketing systems, etc.) +> **Note:** The `kingfisher.` prefix is optional for built-in rules. You can use `--rule aws` instead of `--rule kingfisher.aws`. + ```bash # Validate an OpsGenie API key (using rule prefix matching) -kingfisher validate --rule kingfisher.opsgenie "12345678-9abc-def0-1234-56789abcdef0" +kingfisher validate --rule opsgenie "12345678-9abc-def0-1234-56789abcdef0" # Validate from stdin -echo "ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" | kingfisher validate --rule kingfisher.github - +echo "ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" | kingfisher validate --rule github - # JSON output for scripting -kingfisher validate --rule kingfisher.slack "xoxb-..." --format json +kingfisher validate --rule slack "xoxb-..." --format json # AWS credentials - use --arg to auto-assign additional values -kingfisher validate --rule kingfisher.aws --arg AKIAIOSFODNN7EXAMPLE \ +kingfisher validate --rule aws --arg AKIAIOSFODNN7EXAMPLE \ "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" -# Or use --var if you know the variable name +# Or use --var if you know the variable name (explicit rule ID still works) kingfisher validate --rule kingfisher.aws.2 --var AKID=AKIAIOSFODNN7EXAMPLE \ "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" # GCP service account (pass JSON as secret) -kingfisher validate --rule kingfisher.gcp "$(cat service-account.json)" +kingfisher validate --rule gcp "$(cat service-account.json)" # MongoDB connection string -kingfisher validate --rule kingfisher.mongodb.3 \ +kingfisher validate --rule mongodb.3 \ "mongodb+srv://user:password@cluster.mongodb.net/db" # PostgreSQL connection -kingfisher validate --rule kingfisher.postgres \ +kingfisher validate --rule postgres \ "postgres://admin:password@db.example.com:5432/mydb" # JWT token -kingfisher validate --rule kingfisher.jwt \ +kingfisher validate --rule jwt \ "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." ``` @@ -683,16 +685,16 @@ Some validators need more than just the secret. For example, AWS needs both an a ```bash # --arg auto-assigns to AKID (the only non-TOKEN variable for AWS) -kingfisher validate --rule kingfisher.aws --arg AKIAEXAMPLE "secret_key" +kingfisher validate --rule aws --arg AKIAEXAMPLE "secret_key" # --var for explicit assignment -kingfisher validate --rule kingfisher.aws --var AKID=AKIAEXAMPLE "secret_key" +kingfisher validate --rule aws --var AKID=AKIAEXAMPLE "secret_key" ``` -**Rule prefix matching:** Use partial rule IDs like `kingfisher.opsgenie` instead of the full `kingfisher.opsgenie.1`. If the prefix matches multiple rules, **all matching rules with compatible variables are tried**: +**Rule prefix matching:** Use partial rule IDs like `opsgenie` instead of the full `kingfisher.opsgenie.1`. If the prefix matches multiple rules, **all matching rules with compatible variables are tried**: ```bash -$ kingfisher validate --rule kingfisher.aws --arg AKIAEXAMPLE "secret_key" +$ kingfisher validate --rule aws --arg AKIAEXAMPLE "secret_key" Rule: AWS Secret Access Key (kingfisher.aws.2) Result: ✓ VALID Response: arn:aws:iam::123456789012:user/example diff --git a/src/cli/commands/validate.rs b/src/cli/commands/validate.rs index e8c1bbf..e1ee48a 100644 --- a/src/cli/commands/validate.rs +++ b/src/cli/commands/validate.rs @@ -4,7 +4,8 @@ use std::path::PathBuf; /// Directly validate a known secret against a rule's validator #[derive(Args, Debug, Clone)] pub struct ValidateArgs { - /// Rule ID or prefix to use for validation (e.g., kingfisher.opsgenie.1 or kingfisher.opsgenie) + /// Rule ID or prefix to use for validation (e.g., aws, opsgenie, or kingfisher.aws.2). + /// The `kingfisher.` prefix is optional for built-in rules. #[arg(long, required = true)] pub rule: String, diff --git a/src/direct_validate.rs b/src/direct_validate.rs index 71cae0c..acf445d 100644 --- a/src/direct_validate.rs +++ b/src/direct_validate.rs @@ -64,12 +64,30 @@ fn find_rules_by_selector<'a>( ) -> Result> { let mut matches: Vec<&Rule> = Vec::new(); - for (id, rule) in rules { - // Exact match OR "selector." is a prefix of id - if id == selector - || (id.starts_with(selector) && id.as_bytes().get(selector.len()) == Some(&b'.')) - { - matches.push(rule); + // Try the selector as-is first, then with "kingfisher." prefix as fallback. + // This allows users to pass `--rule aws` instead of `--rule kingfisher.aws`. + let selectors_to_try: Vec> = if selector.starts_with("kingfisher.") { + vec![std::borrow::Cow::Borrowed(selector)] + } else { + vec![ + std::borrow::Cow::Borrowed(selector), + std::borrow::Cow::Owned(format!("kingfisher.{}", selector)), + ] + }; + + for try_selector in &selectors_to_try { + for (id, rule) in rules { + // Exact match OR "selector." is a prefix of id + if id == try_selector.as_ref() + || (id.starts_with(try_selector.as_ref()) + && id.as_bytes().get(try_selector.len()) == Some(&b'.')) + { + matches.push(rule); + } + } + // If we matched with this selector, no need to try the fallback + if !matches.is_empty() { + break; } } diff --git a/src/rule_loader.rs b/src/rule_loader.rs index 43375af..78e2e69 100644 --- a/src/rule_loader.rs +++ b/src/rule_loader.rs @@ -139,7 +139,7 @@ impl LoadedRules { let mut matched_any = false; for (id, rule) in &self.id_to_rule { - // Exact match OR “selector.” is a prefix of id + // Exact match OR "selector." is a prefix of id if id == selector || (id.starts_with(selector) && id.as_bytes().get(selector.len()) == Some(&b'.'))