Merge pull request #88 from mongodb/development

v1.41.0
This commit is contained in:
Mick Grove 2025-08-14 11:19:01 -07:00 committed by GitHub
commit 87a97b2811
24 changed files with 89 additions and 19 deletions

View file

@ -2,6 +2,10 @@
All notable changes to this project will be documented in this file.
## [1.41.0]
- Added support for scanning gitlab subgroups, with `kingfisher scan --gitlab-group my-group --gitlab-include-subgroups`
- Added rule for Vercel
## [1.40.0]
- Dropped the “prevalidated” flag from rule definitions and validation logic so every finding now flows through the standard active/inactive/unknown pipeline, simplifying rule configuration and preventing specialcase bypasses
- Improved Tailscale api key detectors

View file

@ -10,7 +10,7 @@ publish = false
[package]
name = "kingfisher"
version = "1.40.0"
version = "1.41.0"
description = "MongoDB's blazingly fast secret scanning and validation tool"
edition.workspace = true
rust-version.workspace = true

View file

@ -384,6 +384,8 @@ KF_GITHUB_TOKEN="ghp_…" kingfisher scan --git-url https://github.com/org/priva
```bash
kingfisher scan --gitlab-group my-group
# include repositories from all nested subgroups
kingfisher scan --gitlab-group my-group --gitlab-include-subgroups
```
### Scan GitLab user
@ -402,6 +404,8 @@ kingfisher scan --git-url https://gitlab.com/group/project.git
```bash
kingfisher gitlab repos list --group my-group
# include repositories from all nested subgroups
kingfisher gitlab repos list --group my-group --include-subgroups
```
## Scanning Jira

View file

@ -5,9 +5,7 @@ rules:
(?xi)
\b
airbrake
(?:.|[\n\r]){0,16}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,16}?
(?:.|[\n\r]){0,32}?
(
[A-Z0-9-]{40}
)

View file

@ -2,11 +2,9 @@ rules:
- name: Aiven API Key
id: kingfisher.aiven.1
pattern: |
(?xi)
(?xi)
aiven
(?:.|[\n\r]){0,32}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,32}?
\b
(
[a-z0-9/+=]{372}

View file

@ -6,8 +6,6 @@ rules:
\b
asana
(?:.|[\n\r]){0,32}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,32}?
\b
(
[0-9]{16}

View file

@ -6,8 +6,6 @@ rules:
\b
atlassian
(?:.|[\n\r]){0,32}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,32}?
\b
(
[a-z0-9]{24}

View file

@ -6,8 +6,6 @@ rules:
\b
baremetrics
(?:.|[\n\r]){0,32}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,32}?
\b
(
[a-z0-9_-]{25}

View file

@ -9,7 +9,9 @@ rules:
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,32}?
\b
([a-z0-9_-]{32})
(
[a-z0-9_-]{32}
)
\b
min_entropy: 3.5
confidence: medium

View file

@ -2,10 +2,9 @@ rules:
- name: Heroku API Key
id: kingfisher.heroku.1
pattern: |
(?xi)
(?xi)
\b
heroku
(?:.|[\n\r]){0,32}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,32}?
\b
(

39
data/rules/vercel.yml Normal file
View file

@ -0,0 +1,39 @@
rules:
- name: Vercel API Token
id: kingfisher.vercel.1
pattern: |
(?xi)
\b
vercel
(?:.|[\n\r]){0,32}?
\b
(
[a-zA-Z0-9]{24}
)
\b
confidence: medium
min_entropy: 3.5
validation:
type: Http
content:
request:
method: GET
url: https://api.vercel.com/v2/user
headers:
Authorization: "Bearer {{TOKEN}}"
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: WordMatch
words:
- '"user":'
- '"email":'
match_all_words: true
references:
- https://vercel.com/docs/rest-api#authentication
examples:
- "vercel-key = DdZV6ZDZW6Vpl7n7JqtrCE5i"
- "vercel_token = zyMBA1qVEMAf4UNNZtCAbg6u"
- "vercel_api_key = MTg0AW799OY1HmyDdn84or3C"
- "vercel_secret = A7n9Xfp3tBz7D0XpOTMWpiOM"

View file

@ -56,6 +56,10 @@ pub struct GitLabRepoSpecifiers {
/// Filter by repository type
#[arg(long, default_value_t = GitLabRepoType::All, alias = "gitlab-repo-type")]
pub repo_type: GitLabRepoType,
/// Include repositories from subgroups of the specified groups
#[arg(long, alias = "gitlab-include-subgroups")]
pub include_subgroups: bool,
}
impl GitLabRepoSpecifiers {

View file

@ -89,6 +89,10 @@ pub struct InputSpecifierArgs {
#[arg(long, default_value_t = GitLabRepoType::All)]
pub gitlab_repo_type: GitLabRepoType,
/// Include projects from GitLab subgroups when scanning groups
#[arg(long, alias = "include-subgroups")]
pub gitlab_include_subgroups: bool,
/// Jira base URL (e.g. https://jira.example.com)
#[arg(long, value_hint = ValueHint::Url, requires = "jql")]
pub jira_url: Option<Url>,

View file

@ -42,6 +42,7 @@ pub struct RepoSpecifiers {
pub user: Vec<String>,
pub group: Vec<String>,
pub all_groups: bool,
pub include_subgroups: bool,
pub repo_filter: RepoType,
}
@ -137,6 +138,9 @@ pub async fn enumerate_repo_urls(
if matches!(repo_specifiers.repo_filter, RepoType::Owner) {
gp_builder.owned(true);
}
if repo_specifiers.include_subgroups {
gp_builder.include_subgroups(true);
}
let gp_ep = gp_builder.build()?;
let projects: Vec<SimpleProject> = gp_ep.query(&client)?;
@ -162,10 +166,16 @@ pub async fn list_repositories(
users: &[String],
groups: &[String],
all_groups: bool,
include_subgroups: bool,
repo_filter: RepoType,
) -> Result<()> {
let repo_specifiers =
RepoSpecifiers { user: users.to_vec(), group: groups.to_vec(), all_groups, repo_filter };
let repo_specifiers = RepoSpecifiers {
user: users.to_vec(),
group: groups.to_vec(),
all_groups,
include_subgroups,
repo_filter,
};
// Create a progress bar for displaying status
let mut progress = if progress_enabled {

View file

@ -245,6 +245,7 @@ async fn async_main(args: CommandLineArgs) -> Result<()> {
&list_args.repo_specifiers.user,
&list_args.repo_specifiers.group,
list_args.repo_specifiers.all_groups,
list_args.repo_specifiers.include_subgroups,
list_args.repo_specifiers.repo_type.into(),
)
.await?;
@ -282,6 +283,7 @@ fn create_default_scan_args() -> cli::commands::scan::ScanArgs {
all_gitlab_groups: false,
gitlab_api_url: Url::parse("https://gitlab.com/").unwrap(),
gitlab_repo_type: GitLabRepoType::All,
gitlab_include_subgroups: false,
jira_url: None,
jql: None,

View file

@ -84,6 +84,7 @@ mod tests {
all_gitlab_groups: false,
gitlab_api_url: Url::parse("https://gitlab.com/").unwrap(),
gitlab_repo_type: GitLabRepoType::All,
gitlab_include_subgroups: false,
// Jira options
jira_url: None,
jql: None,

View file

@ -182,6 +182,7 @@ pub async fn enumerate_gitlab_repos(
user: args.input_specifier_args.gitlab_user.clone(),
group: args.input_specifier_args.gitlab_group.clone(),
all_groups: args.input_specifier_args.all_gitlab_groups,
include_subgroups: args.input_specifier_args.gitlab_include_subgroups,
repo_filter: args.input_specifier_args.gitlab_repo_type.into(),
};

View file

@ -78,6 +78,7 @@ rules:
all_gitlab_groups: false,
gitlab_api_url: Url::parse("https://gitlab.com/").unwrap(),
gitlab_repo_type: GitLabRepoType::Owner,
gitlab_include_subgroups: false,
jira_url: None,
jql: None,

View file

@ -65,6 +65,7 @@ fn test_github_remote_scan() -> Result<()> {
all_gitlab_groups: false,
gitlab_api_url: Url::parse("https://gitlab.com/").unwrap(),
gitlab_repo_type: GitLabRepoType::Owner,
gitlab_include_subgroups: false,
jira_url: None,
jql: None,

View file

@ -64,6 +64,7 @@ fn test_gitlab_remote_scan() -> Result<()> {
all_gitlab_groups: false,
gitlab_api_url: Url::parse("https://gitlab.com/")?,
gitlab_repo_type: GitLabRepoType::Owner,
gitlab_include_subgroups: false,
jira_url: None,
jql: None,
@ -169,6 +170,7 @@ fn test_gitlab_remote_scan_no_history() -> Result<()> {
all_gitlab_groups: false,
gitlab_api_url: Url::parse("https://gitlab.com/")?,
gitlab_repo_type: GitLabRepoType::Owner,
gitlab_include_subgroups: false,
jira_url: None,
jql: None,

View file

@ -49,6 +49,7 @@ async fn test_redact_hashes_finding_values() -> Result<()> {
all_gitlab_groups: false,
gitlab_api_url: Url::parse("https://gitlab.com/").unwrap(),
gitlab_repo_type: GitLabRepoType::Owner,
gitlab_include_subgroups: false,
jira_url: None,
jql: None,
confluence_url: None,

View file

@ -55,6 +55,7 @@ impl TestContext {
all_gitlab_groups: false,
gitlab_api_url: Url::parse("https://gitlab.com/").unwrap(),
gitlab_repo_type: GitLabRepoType::Owner,
gitlab_include_subgroups: false,
jira_url: None,
jql: None,
confluence_url: None,
@ -147,6 +148,7 @@ async fn test_scan_slack_messages() -> Result<()> {
all_gitlab_groups: false,
gitlab_api_url: Url::parse("https://gitlab.com/").unwrap(),
gitlab_repo_type: GitLabRepoType::Owner,
gitlab_include_subgroups: false,
jira_url: None,
jql: None,
confluence_url: None,

View file

@ -121,6 +121,7 @@ async fn test_validation_cache_and_depvars() -> Result<()> {
all_gitlab_groups: false,
gitlab_api_url: Url::parse("https://gitlab.com/").unwrap(),
gitlab_repo_type: GitLabRepoType::Owner,
gitlab_include_subgroups: false,
jira_url: None,
jql: None,

View file

@ -64,6 +64,7 @@ impl TestContext {
all_gitlab_groups: false,
gitlab_api_url: Url::parse("https://gitlab.com/").unwrap(),
gitlab_repo_type: GitLabRepoType::Owner,
gitlab_include_subgroups: false,
jira_url: None,
jql: None,
@ -142,6 +143,7 @@ impl TestContext {
all_gitlab_groups: false,
gitlab_api_url: Url::parse("https://gitlab.com/").unwrap(),
gitlab_repo_type: GitLabRepoType::Owner,
gitlab_include_subgroups: false,
jira_url: None,
jql: None,