From e4cd6dd164318038dd4722420c0c962b59caf0de Mon Sep 17 00:00:00 2001 From: Mick Grove Date: Fri, 17 Apr 2026 16:53:21 -0700 Subject: [PATCH] performance improvements and rule improvements --- crates/kingfisher-core/src/blob.rs | 2 +- crates/kingfisher-core/src/content_type.rs | 6 +- .../src/git_commit_metadata.rs | 2 +- crates/kingfisher-core/src/origin.rs | 2 +- crates/kingfisher-rules/data/rules/convex.yml | 53 ++++++- .../kingfisher-rules/data/rules/crossmint.yml | 2 + crates/kingfisher-rules/src/defaults.rs | 2 +- crates/kingfisher-rules/src/lib.rs | 8 +- crates/kingfisher-rules/src/liquid_filters.rs | 14 +- crates/kingfisher-rules/src/rule.rs | 7 +- crates/kingfisher-rules/src/rules.rs | 4 +- crates/kingfisher-rules/src/rules_database.rs | 4 +- crates/kingfisher-scanner/src/lib.rs | 2 +- crates/kingfisher-scanner/src/primitives.rs | 2 +- crates/kingfisher-scanner/src/scanner.rs | 8 +- .../kingfisher-scanner/src/validation/aws.rs | 41 +++--- .../src/validation/azure.rs | 10 +- .../src/validation/coinbase.rs | 132 +++++++++--------- .../kingfisher-scanner/src/validation/gcp.rs | 6 +- .../src/validation/http_validation.rs | 7 +- .../kingfisher-scanner/src/validation/jdbc.rs | 2 +- .../kingfisher-scanner/src/validation/jwt.rs | 16 +-- .../kingfisher-scanner/src/validation/mod.rs | 10 +- .../src/validation/mongodb.rs | 2 +- .../src/validation/mysql.rs | 4 +- .../src/validation/postgres.rs | 12 +- .../kingfisher-scanner/src/validation/raw.rs | 14 +- .../src/validation/validation_body.rs | 8 +- src/access_map/airtable.rs | 8 +- src/access_map/algolia.rs | 8 +- src/access_map/alibaba.rs | 12 +- src/access_map/anthropic.rs | 14 +- src/access_map/artifactory.rs | 12 +- src/access_map/auth0.rs | 14 +- src/access_map/aws.rs | 8 +- src/access_map/azure.rs | 12 +- src/access_map/azure_devops.rs | 32 +++-- src/access_map/bitbucket.rs | 8 +- src/access_map/buildkite.rs | 8 +- src/access_map/circleci.rs | 8 +- src/access_map/digitalocean.rs | 8 +- src/access_map/fastly.rs | 8 +- src/access_map/gcp.rs | 28 ++-- src/access_map/gitea.rs | 8 +- src/access_map/github.rs | 8 +- src/access_map/gitlab.rs | 8 +- src/access_map/harness.rs | 10 +- src/access_map/hubspot.rs | 8 +- src/access_map/huggingface.rs | 14 +- src/access_map/ibm_cloud.rs | 8 +- src/access_map/jira.rs | 11 +- src/access_map/microsoft_teams.rs | 6 +- src/access_map/mongodb.rs | 10 +- src/access_map/mysql.rs | 6 +- src/access_map/openai.rs | 10 +- src/access_map/paypal.rs | 8 +- src/access_map/plaid.rs | 8 +- src/access_map/postgres.rs | 12 +- src/access_map/report.rs | 4 +- src/access_map/salesforce.rs | 8 +- src/access_map/sendgrid.rs | 8 +- src/access_map/sendinblue.rs | 8 +- src/access_map/shopify.rs | 8 +- src/access_map/slack.rs | 6 +- src/access_map/square.rs | 8 +- src/access_map/stripe.rs | 8 +- src/access_map/terraform.rs | 8 +- src/access_map/weightsandbiases.rs | 8 +- src/access_map/xray.rs | 8 +- src/access_map/zendesk.rs | 8 +- src/azure.rs | 10 +- src/binary.rs | 2 +- src/bstring_table.rs | 2 +- src/cli/commands/view.rs | 14 +- src/confluence.rs | 4 +- src/decompress.rs | 12 +- src/defaults.rs | 2 +- src/direct_revoke.rs | 18 +-- src/direct_validate.rs | 14 +- src/findings_store.rs | 6 +- src/gcs.rs | 2 +- src/git_binary.rs | 6 +- src/git_metadata_graph.rs | 18 +-- src/git_repo_enumerator.rs | 4 +- src/gitea.rs | 2 +- src/github.rs | 14 +- src/gitlab.rs | 2 +- src/grpc_validation.rs | 4 +- src/huggingface.rs | 14 +- src/inline_ignore.rs | 4 +- src/jira.rs | 24 ++-- src/lib.rs | 2 +- src/main.rs | 15 +- src/matcher/base64_decode.rs | 2 +- src/matcher/captures.rs | 2 +- src/matcher/conversion.rs | 4 +- src/matcher/filter.rs | 2 +- src/matcher/mod.rs | 32 ++--- src/origin.rs | 2 +- src/parser/css.rs | 4 +- src/parser/html.rs | 2 +- src/parser/lexer.rs | 6 +- src/pyc.rs | 6 +- src/reporter.rs | 34 ++--- src/reporter/toon_format.rs | 6 +- src/rule_loader.rs | 4 +- src/rules.rs | 4 +- src/rules_database.rs | 2 +- src/s3.rs | 4 +- src/safe_list.rs | 4 +- src/scanner/docker.rs | 6 +- src/scanner/enumerate.rs | 26 ++-- src/scanner/mod.rs | 2 +- src/scanner/processing.rs | 4 +- src/scanner/repos.rs | 4 +- src/scanner/runner.rs | 37 ++--- src/scanner/summary.rs | 6 +- src/scanner/validation.rs | 8 +- src/snippet.rs | 4 +- src/sqlite.rs | 2 +- src/teams.rs | 6 +- src/util.rs | 8 +- src/validation.rs | 15 +- src/validation_body.rs | 8 +- src/validation_rate_limit.rs | 4 +- tests/fingerprint_dedup.rs | 4 +- tests/int_allowlist.rs | 2 +- tests/int_bitbucket.rs | 2 +- tests/int_context_verification.rs | 6 +- tests/int_dedup.rs | 2 +- tests/int_github.rs | 2 +- tests/int_gitlab.rs | 2 +- tests/int_slack.rs | 4 +- tests/int_teams.rs | 4 +- tests/int_validation_cache.rs | 6 +- tests/int_vulnerable_files.rs | 2 +- tests/jdbc_rule.rs | 2 +- tests/live_db_validation.rs | 4 +- tests/pre_commit_installer.rs | 2 +- tests/smoke_archive.rs | 2 +- tests/smoke_branch.rs | 4 +- tests/smoke_update.rs | 14 +- 142 files changed, 633 insertions(+), 681 deletions(-) diff --git a/crates/kingfisher-core/src/blob.rs b/crates/kingfisher-core/src/blob.rs index 0a62790..66e462b 100644 --- a/crates/kingfisher-core/src/blob.rs +++ b/crates/kingfisher-core/src/blob.rs @@ -14,8 +14,8 @@ use std::{ io::{Read, Write}, path::Path, sync::{ - atomic::{AtomicU64, Ordering}, Arc, OnceLock, + atomic::{AtomicU64, Ordering}, }, }; diff --git a/crates/kingfisher-core/src/content_type.rs b/crates/kingfisher-core/src/content_type.rs index c4ca22d..e678877 100644 --- a/crates/kingfisher-core/src/content_type.rs +++ b/crates/kingfisher-core/src/content_type.rs @@ -62,11 +62,7 @@ impl ContentInspector { let controls = bytes.iter().filter(|&&b| b < 32 && !matches!(b, b'\n' | b'\r' | b'\t')).count(); let ratio = if bytes.is_empty() { 0.0 } else { controls as f64 / bytes.len() as f64 }; - if ratio > self.max_control_ratio { - ContentType::BINARY - } else { - ContentType::TEXT - } + if ratio > self.max_control_ratio { ContentType::BINARY } else { ContentType::TEXT } } /// Guess MIME type from `path` extension. diff --git a/crates/kingfisher-core/src/git_commit_metadata.rs b/crates/kingfisher-core/src/git_commit_metadata.rs index e4cbee7..5475a3d 100644 --- a/crates/kingfisher-core/src/git_commit_metadata.rs +++ b/crates/kingfisher-core/src/git_commit_metadata.rs @@ -3,7 +3,7 @@ //! This module provides types for tracking commit information associated //! with blobs found in git history. -use gix::{date::Time, ObjectId}; +use gix::{ObjectId, date::Time}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; diff --git a/crates/kingfisher-core/src/origin.rs b/crates/kingfisher-core/src/origin.rs index af8a02f..ee3d3ed 100644 --- a/crates/kingfisher-core/src/origin.rs +++ b/crates/kingfisher-core/src/origin.rs @@ -14,7 +14,7 @@ use std::{ use dashmap::DashMap; use rustc_hash::FxHashSet; use schemars::JsonSchema; -use serde::{ser::SerializeSeq, Deserialize, Serialize}; +use serde::{Deserialize, Serialize, ser::SerializeSeq}; use smallvec::SmallVec; use crate::git_commit_metadata::CommitMetadata; diff --git a/crates/kingfisher-rules/data/rules/convex.yml b/crates/kingfisher-rules/data/rules/convex.yml index e9cff92..bef84a2 100644 --- a/crates/kingfisher-rules/data/rules/convex.yml +++ b/crates/kingfisher-rules/data/rules/convex.yml @@ -4,15 +4,16 @@ rules: pattern: | (?x) \b + (?i:convex|CONVEX_DEPLOY_KEY) + (?:.|[\n\r]){0,32}? ( (?: (?:prod|dev):[a-z]+-[a-z]+-\d+ | (?:preview|project):[a-z0-9-]+:[a-z0-9-]+ ) - \|eyJ[A-Za-z0-9._=-]{20,500} + \|eyJ[A-Za-z0-9\/._=-]{20,64} ) - (?:\b|$) min_entropy: 3.5 confidence: medium examples: @@ -21,3 +22,51 @@ rules: - 'CONVEX_DEPLOY_KEY=project:my-team:my-project|eyJ2ZXIiOiIxIiwiYWxnIjoiSFMyNTYifQ.eyJpc3MiOiJjb252ZXgifQ.dGVzdA==' references: - https://docs.convex.dev/cli/deploy-key-types + + - name: Convex Management Access Token + id: kingfisher.convex.2 + pattern: | + (?x) + \b + (?i:convex|CONVEX_TEAM_ACCESS_TOKEN|CONVEX_PAT) + (?:.|[\n\r]){0,32}? + \b + ( + eyJ2MiI6I[A-Za-z0-9+\/]{46}= + ) + min_entropy: 3.5 + confidence: medium + categories: [api, key] + examples: + - 'CONVEX_TEAM_ACCESS_TOKEN=eyJ2MiI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIn0=' + - 'CONVEX_PAT=eyJ2MiI6IjE1MDVkZTcxMTVjMzRlMWM5Yjg3Y2VlMGQ2NzgxNGUzIn0=' + references: + - https://docs.convex.dev/platform-apis + - https://docs.convex.dev/management-api + - https://docs.convex.dev/management-api/get-token-details + - https://docs.convex.dev/management-api/delete-personal-access-token + validation: + type: Http + content: + request: + method: GET + url: https://api.convex.dev/v1/token_details + headers: + Authorization: "Bearer {{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200, 403] + - type: JsonValid + - type: WordMatch + words: + - '"AuthenticationFailed"' + negative: true + - type: WordMatch + words: + - '"AccessTokenInvalid"' + negative: true + # Convex PATs can be deleted with /delete_personal_access_token by passing the + # encoded token secret, but team access tokens share the same observed format + # and are not revocable through that endpoint, so no generic revocation is added. diff --git a/crates/kingfisher-rules/data/rules/crossmint.yml b/crates/kingfisher-rules/data/rules/crossmint.yml index a4b1bfe..d239909 100644 --- a/crates/kingfisher-rules/data/rules/crossmint.yml +++ b/crates/kingfisher-rules/data/rules/crossmint.yml @@ -3,6 +3,8 @@ rules: id: kingfisher.crossmint.1 pattern: | (?x) + (?i:crossmint) + (?:.|[\n\r]){0,32}? \b ( sk_(?:staging|production)_[A-Za-z0-9]{24,48} diff --git a/crates/kingfisher-rules/src/defaults.rs b/crates/kingfisher-rules/src/defaults.rs index 8ce6a38..e8c4115 100644 --- a/crates/kingfisher-rules/src/defaults.rs +++ b/crates/kingfisher-rules/src/defaults.rs @@ -3,7 +3,7 @@ use std::path::Path; use anyhow::Result; -use include_dir::{include_dir, Dir, DirEntry}; +use include_dir::{Dir, DirEntry, include_dir}; use crate::rule::Confidence; use crate::rules::Rules; diff --git a/crates/kingfisher-rules/src/lib.rs b/crates/kingfisher-rules/src/lib.rs index 8455999..ac7e102 100644 --- a/crates/kingfisher-rules/src/lib.rs +++ b/crates/kingfisher-rules/src/lib.rs @@ -18,9 +18,9 @@ pub mod rules_database; pub use rule::{ ChecksumActual, ChecksumRequirement, Confidence, DependsOnRule, GrpcRequest, GrpcValidation, HttpMultiStepRevocation, HttpRequest, HttpValidation, MultipartConfig, MultipartPart, - PatternRequirementContext, PatternRequirements, PatternValidationResult, ReportResponseData, - ResponseExtractor, ResponseMatcher, Revocation, RevocationStep, Rule, RuleSyntax, TlsMode, - Validation, RULE_COMMENTS_PATTERN, + PatternRequirementContext, PatternRequirements, PatternValidationResult, RULE_COMMENTS_PATTERN, + ReportResponseData, ResponseExtractor, ResponseMatcher, Revocation, RevocationStep, Rule, + RuleSyntax, TlsMode, Validation, }; // Re-export Rules collection @@ -28,7 +28,7 @@ pub use rules::{Rules, RulesError}; // Re-export RulesDatabase pub use rules_database::{ - format_regex_pattern, RuleDetectionProfileKind, RuleMatchProfile, RulesDatabase, + RuleDetectionProfileKind, RuleMatchProfile, RulesDatabase, format_regex_pattern, }; // Re-export defaults diff --git a/crates/kingfisher-rules/src/liquid_filters.rs b/crates/kingfisher-rules/src/liquid_filters.rs index 1fd7920..d24e337 100644 --- a/crates/kingfisher-rules/src/liquid_filters.rs +++ b/crates/kingfisher-rules/src/liquid_filters.rs @@ -1,6 +1,6 @@ //! Collection of small Liquid filters that make HTTP validations & API-signing templates easy -use base64::{engine::general_purpose, Engine}; +use base64::{Engine, engine::general_purpose}; use crc32fast::Hasher; use hmac::{Hmac, Mac}; use liquid_core::{ @@ -8,13 +8,13 @@ use liquid_core::{ FromFilterParameters, ParseFilter, Result, Runtime, Value, ValueView, }; -use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; -use rand::{distr::Alphanumeric, RngExt}; +use percent_encoding::{NON_ALPHANUMERIC, utf8_percent_encode}; +use rand::{RngExt, distr::Alphanumeric}; use sha1::Sha1; use sha2::{Digest, Sha256, Sha384}; use time::{ - format_description::well_known::{Iso8601, Rfc2822}, OffsetDateTime, + format_description::well_known::{Iso8601, Rfc2822}, }; use uuid::Uuid; @@ -1075,10 +1075,10 @@ pub fn register_all(builder: liquid::ParserBuilder) -> liquid::ParserBuilder { #[cfg(test)] mod tests { - use base64::{engine::general_purpose, Engine as _}; + use base64::{Engine as _, engine::general_purpose}; use hmac::{Hmac, Mac}; - use liquid::{object, ParserBuilder}; - use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; + use liquid::{ParserBuilder, object}; + use percent_encoding::{NON_ALPHANUMERIC, utf8_percent_encode}; use regex::Regex; use sha1::Sha1; use sha2::{Digest, Sha256, Sha384}; diff --git a/crates/kingfisher-rules/src/rule.rs b/crates/kingfisher-rules/src/rule.rs index f42a534..74078e0 100644 --- a/crates/kingfisher-rules/src/rule.rs +++ b/crates/kingfisher-rules/src/rule.rs @@ -9,16 +9,17 @@ use std::{ sync::LazyLock, }; -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result, anyhow}; use liquid::{ + ParserBuilder, model::{KString, Value}, - object, ParserBuilder, + object, }; use regex::Regex; use schemars::{ + JsonSchema, r#gen::SchemaGenerator, schema::{Schema, SchemaObject}, - JsonSchema, }; use serde::{Deserialize, Serialize}; use tracing::debug; diff --git a/crates/kingfisher-rules/src/rules.rs b/crates/kingfisher-rules/src/rules.rs index 4b7defa..bc4a559 100644 --- a/crates/kingfisher-rules/src/rules.rs +++ b/crates/kingfisher-rules/src/rules.rs @@ -1,7 +1,7 @@ //! Rule collection and loading utilities. -use anyhow::{bail, Context, Result}; -use ignore::{types::TypesBuilder, WalkBuilder}; +use anyhow::{Context, Result, bail}; +use ignore::{WalkBuilder, types::TypesBuilder}; use serde::Deserialize; use thiserror::Error; use tracing::{debug, debug_span, error}; diff --git a/crates/kingfisher-rules/src/rules_database.rs b/crates/kingfisher-rules/src/rules_database.rs index 5ba76ea..4a1c523 100644 --- a/crates/kingfisher-rules/src/rules_database.rs +++ b/crates/kingfisher-rules/src/rules_database.rs @@ -1,11 +1,11 @@ use std::{sync::Arc, time::Instant}; -use anyhow::{anyhow, bail, Result}; +use anyhow::{Result, anyhow, bail}; use regex::bytes::Regex; use tracing::{debug, debug_span, error}; use vectorscan_rs::{BlockDatabase, Flag, Pattern}; -use crate::rule::{Rule, RULE_COMMENTS_PATTERN}; +use crate::rule::{RULE_COMMENTS_PATTERN, Rule}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum RuleDetectionProfileKind { diff --git a/crates/kingfisher-scanner/src/lib.rs b/crates/kingfisher-scanner/src/lib.rs index fc3abe8..3e10a87 100644 --- a/crates/kingfisher-scanner/src/lib.rs +++ b/crates/kingfisher-scanner/src/lib.rs @@ -71,7 +71,7 @@ mod scanner_pool; ))] pub mod validation; -pub use finding::{intern, Finding, FindingLocation, SerializableCapture, SerializableCaptures}; +pub use finding::{Finding, FindingLocation, SerializableCapture, SerializableCaptures, intern}; pub use scanner::{Scanner, ScannerConfig}; pub use scanner_pool::ScannerPool; diff --git a/crates/kingfisher-scanner/src/primitives.rs b/crates/kingfisher-scanner/src/primitives.rs index a9e11fe..ee54537 100644 --- a/crates/kingfisher-scanner/src/primitives.rs +++ b/crates/kingfisher-scanner/src/primitives.rs @@ -6,7 +6,7 @@ use std::hash::{Hash, Hasher}; -use base64::{engine::general_purpose, Engine}; +use base64::{Engine, engine::general_purpose}; use kingfisher_core::OffsetSpan; use rustc_hash::{FxHashMap, FxHasher}; use xxhash_rust::xxh3::xxh3_64; diff --git a/crates/kingfisher-scanner/src/scanner.rs b/crates/kingfisher-scanner/src/scanner.rs index cca0ecb..09bb0f3 100644 --- a/crates/kingfisher-scanner/src/scanner.rs +++ b/crates/kingfisher-scanner/src/scanner.rs @@ -5,7 +5,7 @@ use std::path::Path; use std::sync::Arc; use anyhow::Result; -use kingfisher_core::{calculate_shannon_entropy, Blob, BlobIdMap, LocationMapping, OffsetSpan}; +use kingfisher_core::{Blob, BlobIdMap, LocationMapping, OffsetSpan, calculate_shannon_entropy}; use kingfisher_rules::RulesDatabase; use rustc_hash::{FxHashMap, FxHashSet}; use tracing::debug; @@ -297,11 +297,7 @@ impl Scanner { fn redact(&self, bytes: &[u8]) -> String { let s = String::from_utf8_lossy(bytes); - if s.len() <= 8 { - "*".repeat(s.len()) - } else { - format!("{}...{}", &s[..4], "*".repeat(4)) - } + if s.len() <= 8 { "*".repeat(s.len()) } else { format!("{}...{}", &s[..4], "*".repeat(4)) } } fn scan_base64_content( diff --git a/crates/kingfisher-scanner/src/validation/aws.rs b/crates/kingfisher-scanner/src/validation/aws.rs index ce03ee0..f313a92 100644 --- a/crates/kingfisher-scanner/src/validation/aws.rs +++ b/crates/kingfisher-scanner/src/validation/aws.rs @@ -9,25 +9,25 @@ use std::{ time::Duration, }; -use anyhow::{anyhow, Result}; -use aws_config::{retry::RetryConfig, BehaviorVersion, SdkConfig}; +use anyhow::{Result, anyhow}; +use aws_config::{BehaviorVersion, SdkConfig, retry::RetryConfig}; use aws_credential_types::Credentials; use aws_sdk_iam::{ - config::Builder as IamConfigBuilder, error::SdkError as IamSdkError, - operation::update_access_key::UpdateAccessKeyError, types::StatusType, Client as IamClient, + Client as IamClient, config::Builder as IamConfigBuilder, error::SdkError as IamSdkError, + operation::update_access_key::UpdateAccessKeyError, types::StatusType, }; use aws_sdk_sts::{ - config::Builder as StsConfigBuilder, error::SdkError, - operation::get_caller_identity::GetCallerIdentityError, Client as StsClient, + Client as StsClient, config::Builder as StsConfigBuilder, error::SdkError, + operation::get_caller_identity::GetCallerIdentityError, }; use aws_smithy_http_client::{ - proxy::ProxyConfig, tls, Builder as HttpClientBuilder, ConnectorBuilder, + Builder as HttpClientBuilder, ConnectorBuilder, proxy::ProxyConfig, tls, }; use aws_smithy_runtime_api::{ box_error::BoxError, client::{ http::SharedHttpClient, - interceptors::{context::BeforeTransmitInterceptorContextMut, Intercept}, + interceptors::{Intercept, context::BeforeTransmitInterceptorContextMut}, runtime_components::RuntimeComponents, }, }; @@ -36,10 +36,10 @@ use aws_types::region::Region; use base32::Alphabet; use byteorder::{BigEndian, ByteOrder}; use http::{ - header::{HeaderValue, USER_AGENT}, StatusCode, + header::{HeaderValue, USER_AGENT}, }; -use rand::{rng, RngExt}; +use rand::{RngExt, rng}; use regex::Regex; use tokio::{ sync::Semaphore, @@ -100,7 +100,8 @@ fn extract_account_id(input: &str) -> Option { return Some(trimmed.to_string()); } - static ACCOUNT_ID_RE: LazyLock = LazyLock::new(|| Regex::new(r"(\d{12})").expect("valid regex")); + static ACCOUNT_ID_RE: LazyLock = + LazyLock::new(|| Regex::new(r"(\d{12})").expect("valid regex")); ACCOUNT_ID_RE.captures(trimmed).and_then(|caps| caps.get(1)).map(|m| m.as_str().to_string()) } @@ -152,11 +153,7 @@ pub fn should_skip_aws_validation(access_key_id: &str) -> Option { } let account = aws_key_to_account_number(access_key_id).ok()?; - if guard.contains(&account) { - Some(account) - } else { - None - } + if guard.contains(&account) { Some(account) } else { None } } #[derive(Debug)] @@ -438,11 +435,13 @@ mod tests { #[test] fn test_validate_credentials_format() { - assert!(validate_aws_credentials_input( - "AKIAIOSFODNN7EXAMPLE", - "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" - ) - .is_ok()); + assert!( + validate_aws_credentials_input( + "AKIAIOSFODNN7EXAMPLE", + "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" + ) + .is_ok() + ); assert!(validate_aws_credentials_input("short", "secret").is_err()); assert!(validate_aws_credentials_input("AKIAIOSFODNN7EXAMPLE", "short").is_err()); } diff --git a/crates/kingfisher-scanner/src/validation/azure.rs b/crates/kingfisher-scanner/src/validation/azure.rs index f330811..8b34270 100644 --- a/crates/kingfisher-scanner/src/validation/azure.rs +++ b/crates/kingfisher-scanner/src/validation/azure.rs @@ -1,17 +1,17 @@ use std::time::Duration; -use anyhow::{anyhow, Result}; -use base64::{engine::general_purpose::STANDARD as b64, Engine as _}; +use anyhow::{Result, anyhow}; +use base64::{Engine as _, engine::general_purpose::STANDARD as b64}; use chrono::Utc; use hmac::{Hmac, Mac}; use http::StatusCode; -use quick_xml::{events::Event, Reader}; -use reqwest::{header::HeaderValue, Client}; +use quick_xml::{Reader, events::Event}; +use reqwest::{Client, header::HeaderValue}; use serde_json::Value as JsonValue; use sha2::Sha256; use super::{ - validation_body, Cache, CachedResponse, ValidationResponseBody, VALIDATION_CACHE_SECONDS, + Cache, CachedResponse, VALIDATION_CACHE_SECONDS, ValidationResponseBody, validation_body, }; pub fn generate_azure_cache_key(azure_json: &str) -> String { diff --git a/crates/kingfisher-scanner/src/validation/coinbase.rs b/crates/kingfisher-scanner/src/validation/coinbase.rs index ec4951f..6b6a945 100644 --- a/crates/kingfisher-scanner/src/validation/coinbase.rs +++ b/crates/kingfisher-scanner/src/validation/coinbase.rs @@ -1,21 +1,21 @@ use std::collections::BTreeMap; use std::time::Duration; -use anyhow::{anyhow, Result}; -use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _}; +use anyhow::{Result, anyhow}; +use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD}; use chrono::Utc; use ed25519_dalek::SigningKey as Ed25519Key; use p256::{ - ecdsa::{signature::Signer as _, SigningKey}, - pkcs8::DecodePrivateKey, SecretKey, + ecdsa::{SigningKey, signature::Signer as _}, + pkcs8::DecodePrivateKey, }; use reqwest::{Client, StatusCode, Url}; use sha1::{Digest, Sha1}; use super::http_validation as httpvalidation; use super::{ - validation_body, Cache, CachedResponse, ValidationResponseBody, VALIDATION_CACHE_SECONDS, + Cache, CachedResponse, VALIDATION_CACHE_SECONDS, ValidationResponseBody, validation_body, }; pub fn generate_coinbase_cache_key(cred_name: &str, private_key: &str) -> String { @@ -82,70 +82,72 @@ fn build_jwt( let nonce: [u8; 16] = rand::random(); - match SecretKey::from_sec1_pem(&pem).or_else(|_| SecretKey::from_pkcs8_pem(&pem)) - { Ok(secret_key) => { - let signing_key = SigningKey::from(secret_key); - let header = serde_json::json!({ - "typ": "JWT", - "alg": "ES256", - "kid": key_name, - "nonce": hex::encode(nonce), - }); - let header_b64 = URL_SAFE_NO_PAD.encode(header.to_string()); + match SecretKey::from_sec1_pem(&pem).or_else(|_| SecretKey::from_pkcs8_pem(&pem)) { + Ok(secret_key) => { + let signing_key = SigningKey::from(secret_key); + let header = serde_json::json!({ + "typ": "JWT", + "alg": "ES256", + "kid": key_name, + "nonce": hex::encode(nonce), + }); + let header_b64 = URL_SAFE_NO_PAD.encode(header.to_string()); - let now = Utc::now().timestamp(); - let claims = serde_json::json!({ - "sub": key_name, - "iss": "cdp", - "nbf": now, - "exp": now + 120, - "uri": format!("{} {}{}", method, host, endpoint), - }); - let claims_b64 = URL_SAFE_NO_PAD.encode(claims.to_string()); + let now = Utc::now().timestamp(); + let claims = serde_json::json!({ + "sub": key_name, + "iss": "cdp", + "nbf": now, + "exp": now + 120, + "uri": format!("{} {}{}", method, host, endpoint), + }); + let claims_b64 = URL_SAFE_NO_PAD.encode(claims.to_string()); - let signing_input = format!("{header_b64}.{claims_b64}"); - let sig: p256::ecdsa::Signature = signing_key.sign(signing_input.as_bytes()); - let sig_b64 = URL_SAFE_NO_PAD.encode(sig.to_bytes()); + let signing_input = format!("{header_b64}.{claims_b64}"); + let sig: p256::ecdsa::Signature = signing_key.sign(signing_input.as_bytes()); + let sig_b64 = URL_SAFE_NO_PAD.encode(sig.to_bytes()); - return Ok(format!("{signing_input}.{sig_b64}")); - } _ => { - let key_bytes = base64::engine::general_purpose::STANDARD - .decode(pem.as_bytes()) - .map_err(|e| anyhow!("invalid base64 key: {e}"))?; - let signing_key = match key_bytes.len() { - 32 => { - let arr: [u8; 32] = key_bytes[..32].try_into().unwrap(); - Ed25519Key::from_bytes(&arr) - } - 64 => { - let arr: [u8; 64] = key_bytes[..64].try_into().unwrap(); - Ed25519Key::from_keypair_bytes(&arr) - .map_err(|e| anyhow!("invalid Ed25519 key: {e}"))? - } - _ => return Err(anyhow!("invalid Ed25519 key length")), - }; + return Ok(format!("{signing_input}.{sig_b64}")); + } + _ => { + let key_bytes = base64::engine::general_purpose::STANDARD + .decode(pem.as_bytes()) + .map_err(|e| anyhow!("invalid base64 key: {e}"))?; + let signing_key = match key_bytes.len() { + 32 => { + let arr: [u8; 32] = key_bytes[..32].try_into().unwrap(); + Ed25519Key::from_bytes(&arr) + } + 64 => { + let arr: [u8; 64] = key_bytes[..64].try_into().unwrap(); + Ed25519Key::from_keypair_bytes(&arr) + .map_err(|e| anyhow!("invalid Ed25519 key: {e}"))? + } + _ => return Err(anyhow!("invalid Ed25519 key length")), + }; - let header = serde_json::json!({ - "typ": "JWT", - "alg": "EdDSA", - "kid": key_name, - "nonce": hex::encode(nonce), - }); - let header_b64 = URL_SAFE_NO_PAD.encode(header.to_string()); + let header = serde_json::json!({ + "typ": "JWT", + "alg": "EdDSA", + "kid": key_name, + "nonce": hex::encode(nonce), + }); + let header_b64 = URL_SAFE_NO_PAD.encode(header.to_string()); - let now = Utc::now().timestamp(); - let claims = serde_json::json!({ - "sub": key_name, - "iss": "cdp", - "nbf": now, - "exp": now + 120, - "uri": format!("{} {}{}", method, host, endpoint), - }); - let claims_b64 = URL_SAFE_NO_PAD.encode(claims.to_string()); + let now = Utc::now().timestamp(); + let claims = serde_json::json!({ + "sub": key_name, + "iss": "cdp", + "nbf": now, + "exp": now + 120, + "uri": format!("{} {}{}", method, host, endpoint), + }); + let claims_b64 = URL_SAFE_NO_PAD.encode(claims.to_string()); - let signing_input = format!("{header_b64}.{claims_b64}"); - let sig: ed25519_dalek::Signature = signing_key.sign(signing_input.as_bytes()); - let sig_b64 = URL_SAFE_NO_PAD.encode(sig.to_bytes()); - return Ok(format!("{signing_input}.{sig_b64}")); - }} + let signing_input = format!("{header_b64}.{claims_b64}"); + let sig: ed25519_dalek::Signature = signing_key.sign(signing_input.as_bytes()); + let sig_b64 = URL_SAFE_NO_PAD.encode(sig.to_bytes()); + return Ok(format!("{signing_input}.{sig_b64}")); + } + } } diff --git a/crates/kingfisher-scanner/src/validation/gcp.rs b/crates/kingfisher-scanner/src/validation/gcp.rs index d7dd63b..079fa50 100644 --- a/crates/kingfisher-scanner/src/validation/gcp.rs +++ b/crates/kingfisher-scanner/src/validation/gcp.rs @@ -1,10 +1,10 @@ use std::sync::{Arc, OnceLock}; -use anyhow::{anyhow, Result}; -use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _}; +use anyhow::{Result, anyhow}; +use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD}; use chrono::{Duration as ChronoDuration, Utc}; use pem::parse; -use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; +use percent_encoding::{NON_ALPHANUMERIC, utf8_percent_encode}; use reqwest::{Client, Proxy}; use ring::{rand, signature}; use serde_json::Value as JsonValue; diff --git a/crates/kingfisher-scanner/src/validation/http_validation.rs b/crates/kingfisher-scanner/src/validation/http_validation.rs index 255edd3..35a863e 100644 --- a/crates/kingfisher-scanner/src/validation/http_validation.rs +++ b/crates/kingfisher-scanner/src/validation/http_validation.rs @@ -1,18 +1,17 @@ use std::{collections::BTreeMap, future::Future, net::IpAddr, str::FromStr, time::Duration}; -use anyhow::{anyhow, Error, Result}; +use anyhow::{Error, Result, anyhow}; use http::StatusCode; use liquid::Object; use liquid_core::Value; use quick_xml::de::from_str as xml_from_str; use reqwest::{ - header, + Client, Method, RequestBuilder, Response, Url, header, header::{HeaderMap, HeaderName, HeaderValue}, - Client, Method, RequestBuilder, Response, Url, }; use serde::de::IgnoredAny; use sha1::{Digest, Sha1}; -use time::{format_description::well_known::Rfc2822, OffsetDateTime}; +use time::{OffsetDateTime, format_description::well_known::Rfc2822}; use tokio::{net::lookup_host, time::sleep}; use tracing::debug; diff --git a/crates/kingfisher-scanner/src/validation/jdbc.rs b/crates/kingfisher-scanner/src/validation/jdbc.rs index 2bc35f1..e73a8a5 100644 --- a/crates/kingfisher-scanner/src/validation/jdbc.rs +++ b/crates/kingfisher-scanner/src/validation/jdbc.rs @@ -1,4 +1,4 @@ -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result, anyhow}; use http::StatusCode; use tracing::debug; use url::Url; diff --git a/crates/kingfisher-scanner/src/validation/jwt.rs b/crates/kingfisher-scanner/src/validation/jwt.rs index 19d03cf..4bdac7c 100644 --- a/crates/kingfisher-scanner/src/validation/jwt.rs +++ b/crates/kingfisher-scanner/src/validation/jwt.rs @@ -1,11 +1,11 @@ -use super::http_validation::{check_url_resolvable, SsrfBlockedError}; -use anyhow::{anyhow, Result}; -use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _}; +use super::http_validation::{SsrfBlockedError, check_url_resolvable}; +use anyhow::{Result, anyhow}; +use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD}; use chrono::Utc; use jsonwebtoken::{ - decode, decode_header, jwk::JwkSet, Algorithm, DecodingKey, Validation as JwtValidation, + Algorithm, DecodingKey, Validation as JwtValidation, decode, decode_header, jwk::JwkSet, }; -use reqwest::{redirect::Policy, Client, Url}; +use reqwest::{Client, Url, redirect::Policy}; use serde::Deserialize; use std::sync::LazyLock; @@ -28,11 +28,7 @@ static LAX_CLIENT: LazyLock = LazyLock::new(|| { }); fn get_client(lax_tls: bool) -> &'static Client { - if lax_tls { - &LAX_CLIENT - } else { - &STRICT_CLIENT - } + if lax_tls { &LAX_CLIENT } else { &STRICT_CLIENT } } #[derive(Debug, Deserialize)] diff --git a/crates/kingfisher-scanner/src/validation/mod.rs b/crates/kingfisher-scanner/src/validation/mod.rs index b1d36e9..030f2d2 100644 --- a/crates/kingfisher-scanner/src/validation/mod.rs +++ b/crates/kingfisher-scanner/src/validation/mod.rs @@ -61,17 +61,17 @@ pub mod raw; // Re-exports pub use utils::{find_closest_variable, process_captures}; -pub use validation_body::{as_str, clone_as_string, from_string, ValidationResponseBody}; +pub use validation_body::{ValidationResponseBody, as_str, clone_as_string, from_string}; #[cfg(feature = "validation-http")] pub use http_validation::{ - build_request_builder, check_url_resolvable, generate_http_cache_key_parts, is_ssrf_safe_ip, - parse_http_method, process_headers, retry_multipart_request, retry_request, validate_response, - with_request_template_globals, SsrfBlockedError, + SsrfBlockedError, build_request_builder, check_url_resolvable, generate_http_cache_key_parts, + is_ssrf_safe_ip, parse_http_method, process_headers, retry_multipart_request, retry_request, + validate_response, with_request_template_globals, }; #[cfg(feature = "validation-raw")] -pub use raw::{required_vars as raw_required_vars, validate_raw, RawValidationOutcome}; +pub use raw::{RawValidationOutcome, required_vars as raw_required_vars, validate_raw}; #[cfg(feature = "validation-http")] #[expect(deprecated)] diff --git a/crates/kingfisher-scanner/src/validation/mongodb.rs b/crates/kingfisher-scanner/src/validation/mongodb.rs index d092405..63bfe0a 100644 --- a/crates/kingfisher-scanner/src/validation/mongodb.rs +++ b/crates/kingfisher-scanner/src/validation/mongodb.rs @@ -2,10 +2,10 @@ use std::{net::IpAddr, time::Duration}; use anyhow::Result; use mongodb::{ + Client, bson::doc, error::ErrorKind, options::{ClientOptions, Tls, TlsOptions}, - Client, }; use tokio::time::timeout; use tracing::debug; diff --git a/crates/kingfisher-scanner/src/validation/mysql.rs b/crates/kingfisher-scanner/src/validation/mysql.rs index 31e9d48..7be1e1c 100644 --- a/crates/kingfisher-scanner/src/validation/mysql.rs +++ b/crates/kingfisher-scanner/src/validation/mysql.rs @@ -1,7 +1,7 @@ use std::{net::IpAddr, time::Duration}; -use anyhow::{anyhow, Result}; -use mysql_async::{prelude::Queryable, Conn, Opts, OptsBuilder, SslOpts}; +use anyhow::{Result, anyhow}; +use mysql_async::{Conn, Opts, OptsBuilder, SslOpts, prelude::Queryable}; use tokio::time::{error::Elapsed, timeout}; use tracing::debug; use url::Url; diff --git a/crates/kingfisher-scanner/src/validation/postgres.rs b/crates/kingfisher-scanner/src/validation/postgres.rs index c79f763..2d204e5 100644 --- a/crates/kingfisher-scanner/src/validation/postgres.rs +++ b/crates/kingfisher-scanner/src/validation/postgres.rs @@ -4,18 +4,18 @@ use std::{ time::Duration, }; -use anyhow::{anyhow, Result}; +use anyhow::{Result, anyhow}; use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}; -use rustls::crypto::{ring, verify_tls12_signature, verify_tls13_signature, CryptoProvider}; +use rustls::crypto::{CryptoProvider, ring, verify_tls12_signature, verify_tls13_signature}; use rustls::pki_types::{CertificateDer, ServerName, UnixTime}; -use rustls::{client::ClientConfig, DigitallySignedStruct, RootCertStore, SignatureScheme}; -use rustls_native_certs::{load_native_certs, CertificateResult}; +use rustls::{DigitallySignedStruct, RootCertStore, SignatureScheme, client::ClientConfig}; +use rustls_native_certs::{CertificateResult, load_native_certs}; use sha1::{Digest, Sha1}; use tokio::time::{error::Elapsed, timeout}; use tokio_postgres::{ + Config, Error, config::{Host, SslMode}, tls::NoTls, - Config, Error, }; use tokio_postgres_rustls::MakeRustlsConnect; use tracing::debug; @@ -226,7 +226,7 @@ async fn check_postgres_db_connection( Ok(Err(e)) => return Err(anyhow!("Postgres connection failed: {e}")), Err(_) => { - return Err(anyhow!("Postgres connection timed out after {CONNECT_TIMEOUT:?}")) + return Err(anyhow!("Postgres connection timed out after {CONNECT_TIMEOUT:?}")); } } } diff --git a/crates/kingfisher-scanner/src/validation/raw.rs b/crates/kingfisher-scanner/src/validation/raw.rs index aaf33d4..6357593 100644 --- a/crates/kingfisher-scanner/src/validation/raw.rs +++ b/crates/kingfisher-scanner/src/validation/raw.rs @@ -6,9 +6,9 @@ use std::{ time::{Duration, SystemTime, UNIX_EPOCH}, }; -use anyhow::{anyhow, Context, Result}; -use base64::{engine::general_purpose::STANDARD as B64, Engine}; -use hmac::{digest::KeyInit, Hmac, Mac}; +use anyhow::{Context, Result, anyhow}; +use base64::{Engine, engine::general_purpose::STANDARD as B64}; +use hmac::{Hmac, Mac, digest::KeyInit}; use http::StatusCode; use ldap3::LdapConnSettings; use liquid::Object; @@ -16,7 +16,7 @@ use liquid_core::ValueView; use percent_encoding::percent_decode_str; use reqwest::Client; use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}; -use rustls::crypto::{ring, verify_tls12_signature, verify_tls13_signature, CryptoProvider}; +use rustls::crypto::{CryptoProvider, ring, verify_tls12_signature, verify_tls13_signature}; use rustls::pki_types::{CertificateDer, ServerName, UnixTime}; use rustls::{ClientConfig, DigitallySignedStruct, RootCertStore, SignatureScheme}; use sha2::{Digest, Sha256, Sha512}; @@ -225,11 +225,7 @@ async fn connect_from_url( let host = url.host_str().ok_or_else(|| anyhow!("URL is missing host"))?; let tls = matches!(url.scheme(), "ftps" | "amqps" | "rediss" | "ldaps"); let port = url.port().unwrap_or(if tls { tls_default_port } else { plain_default_port }); - if tls { - connect_tls(host, port, use_lax_tls).await - } else { - connect_plain(host, port).await - } + if tls { connect_tls(host, port, use_lax_tls).await } else { connect_plain(host, port).await } } async fn validate_azure_batch(globals: &Object, client: &Client) -> Result { diff --git a/crates/kingfisher-scanner/src/validation/validation_body.rs b/crates/kingfisher-scanner/src/validation/validation_body.rs index 022869d..f6118fb 100644 --- a/crates/kingfisher-scanner/src/validation/validation_body.rs +++ b/crates/kingfisher-scanner/src/validation/validation_body.rs @@ -2,7 +2,7 @@ #![allow(dead_code)] // Public API for serde attributes in downstream crates -use schemars::{r#gen::SchemaGenerator, schema::Schema, JsonSchema}; +use schemars::{JsonSchema, r#gen::SchemaGenerator, schema::Schema}; use serde::{Deserialize, Deserializer, Serializer}; use std::borrow::Cow; @@ -14,11 +14,7 @@ pub type ValidationResponseBody = Option>; #[inline] pub fn from_string(body: impl Into) -> ValidationResponseBody { let body = body.into(); - if body.is_empty() { - None - } else { - Some(body.into_boxed_str()) - } + if body.is_empty() { None } else { Some(body.into_boxed_str()) } } /// Get the response body as a string slice. diff --git a/src/access_map/airtable.rs b/src/access_map/airtable.rs index 27a5bbe..781e1da 100644 --- a/src/access_map/airtable.rs +++ b/src/access_map/airtable.rs @@ -1,13 +1,13 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, header}; use serde::Deserialize; use tracing::warn; use crate::{cli::commands::access_map::AccessMapArgs, validation::GLOBAL_USER_AGENT}; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; const AIRTABLE_API: &str = "https://api.airtable.com"; diff --git a/src/access_map/algolia.rs b/src/access_map/algolia.rs index 2a70e56..51dc660 100644 --- a/src/access_map/algolia.rs +++ b/src/access_map/algolia.rs @@ -1,5 +1,5 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, header}; use serde::Deserialize; use tracing::warn; @@ -7,8 +7,8 @@ use crate::cli::commands::access_map::AccessMapArgs; use crate::validation::GLOBAL_USER_AGENT; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; // --------------------------------------------------------------------------- diff --git a/src/access_map/alibaba.rs b/src/access_map/alibaba.rs index 364e109..7fd7dd4 100644 --- a/src/access_map/alibaba.rs +++ b/src/access_map/alibaba.rs @@ -1,10 +1,10 @@ use std::collections::BTreeMap; -use anyhow::{anyhow, Context, Result}; -use base64::{engine::general_purpose::STANDARD as b64, Engine as _}; +use anyhow::{Context, Result, anyhow}; +use base64::{Engine as _, engine::general_purpose::STANDARD as b64}; use chrono::Utc; -use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS}; -use reqwest::{header, Client, Url}; +use percent_encoding::{AsciiSet, CONTROLS, utf8_percent_encode}; +use reqwest::{Client, Url, header}; use serde::Deserialize; use serde_json::Value; use sha1::{Digest, Sha1}; @@ -13,8 +13,8 @@ use uuid::Uuid; use crate::{cli::commands::access_map::AccessMapArgs, validation::GLOBAL_USER_AGENT}; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; const STS_API: &str = "https://sts.aliyuncs.com/"; diff --git a/src/access_map/anthropic.rs b/src/access_map/anthropic.rs index 5beb19f..0fdb97c 100644 --- a/src/access_map/anthropic.rs +++ b/src/access_map/anthropic.rs @@ -1,13 +1,13 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, header}; use serde::Deserialize; use tracing::warn; use crate::{cli::commands::access_map::AccessMapArgs, validation::GLOBAL_USER_AGENT}; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; const ANTHROPIC_API: &str = "https://api.anthropic.com/v1"; @@ -355,11 +355,7 @@ async fn fetch_permissions_from_endpoint( .await .with_context(|| format!("Anthropic access-map: invalid API key JSON from {url}"))?; - if body.permissions.is_empty() { - Ok(None) - } else { - Ok(Some(body)) - } + if body.permissions.is_empty() { Ok(None) } else { Ok(Some(body)) } } fn derive_severity(permissions: &PermissionSummary) -> Severity { diff --git a/src/access_map/artifactory.rs b/src/access_map/artifactory.rs index 86ca74a..1d48718 100644 --- a/src/access_map/artifactory.rs +++ b/src/access_map/artifactory.rs @@ -1,5 +1,5 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, header}; use serde::Deserialize; use serde_json::Value; use tracing::warn; @@ -7,8 +7,8 @@ use tracing::warn; use crate::{cli::commands::access_map::AccessMapArgs, validation::GLOBAL_USER_AGENT}; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; const MAX_REPO_RESOURCES: usize = 100; @@ -37,7 +37,9 @@ struct ArtifactoryRepo { /// Entry point when invoked via the CLI `access-map jfrog-art` subcommand. pub async fn map_access(args: &AccessMapArgs) -> Result { let path = args.credential_path.as_deref().ok_or_else(|| { - anyhow!("Artifactory access-map requires a credential file with token (and optionally base_url)") + anyhow!( + "Artifactory access-map requires a credential file with token (and optionally base_url)" + ) })?; let raw = std::fs::read_to_string(path).with_context(|| { format!("Failed to read Artifactory credential file from {}", path.display()) diff --git a/src/access_map/auth0.rs b/src/access_map/auth0.rs index 736dab3..c951edf 100644 --- a/src/access_map/auth0.rs +++ b/src/access_map/auth0.rs @@ -1,5 +1,5 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, header}; use serde::Deserialize; use tracing::warn; @@ -7,8 +7,8 @@ use crate::cli::commands::access_map::AccessMapArgs; use crate::validation::GLOBAL_USER_AGENT; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; // --------------------------------------------------------------------------- @@ -65,11 +65,7 @@ fn classify_scope(scope: &str) -> ScopeCategory { return ScopeCategory::Read; } // Default: treat unknown read: as read, unknown others as risky - if scope.starts_with("read:") { - ScopeCategory::Read - } else { - ScopeCategory::Risky - } + if scope.starts_with("read:") { ScopeCategory::Read } else { ScopeCategory::Risky } } enum ScopeCategory { diff --git a/src/access_map/aws.rs b/src/access_map/aws.rs index 49c2d2e..878b9af 100644 --- a/src/access_map/aws.rs +++ b/src/access_map/aws.rs @@ -1,13 +1,13 @@ use std::collections::BTreeSet; use std::path::Path; -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result, anyhow}; use aws_config::{BehaviorVersion, SdkConfig}; use aws_credential_types::Credentials; use aws_sdk_dynamodb::Client as DynamoClient; use aws_sdk_ec2::Client as Ec2Client; use aws_sdk_ecr::Client as EcrClient; -use aws_sdk_iam::{error::SdkError, Client as IamClient}; +use aws_sdk_iam::{Client as IamClient, error::SdkError}; use aws_sdk_kms::Client as KmsClient; use aws_sdk_lambda::Client as LambdaClient; use aws_sdk_rds::Client as RdsClient; @@ -24,8 +24,8 @@ use tracing::warn; use crate::cli::commands::access_map::AccessMapArgs; use super::{ - build_default_account_resource, build_recommendations, AccessMapResult, AccessSummary, - AccessTokenDetails, PermissionSummary, ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_default_account_resource, build_recommendations, }; pub async fn map_access(args: &AccessMapArgs) -> Result { diff --git a/src/access_map/azure.rs b/src/access_map/azure.rs index d2f9c6a..cbace7d 100644 --- a/src/access_map/azure.rs +++ b/src/access_map/azure.rs @@ -1,17 +1,17 @@ -use anyhow::{anyhow, Context, Result}; -use base64::{engine::general_purpose::STANDARD as b64, Engine as _}; +use anyhow::{Context, Result, anyhow}; +use base64::{Engine as _, engine::general_purpose::STANDARD as b64}; use chrono::Utc; use hmac::{Hmac, KeyInit, Mac}; -use quick_xml::{events::Event, Reader}; -use reqwest::{header::HeaderValue, Client}; +use quick_xml::{Reader, events::Event}; +use reqwest::{Client, header::HeaderValue}; use serde_json::Value as JsonValue; use sha2::Sha256; use crate::cli::commands::access_map::AccessMapArgs; use super::{ - build_recommendations, AccessMapResult, AccessSummary, PermissionSummary, ResourceExposure, - RoleBinding, Severity, + AccessMapResult, AccessSummary, PermissionSummary, ResourceExposure, RoleBinding, Severity, + build_recommendations, }; #[derive(Clone, Copy)] diff --git a/src/access_map/azure_devops.rs b/src/access_map/azure_devops.rs index 8ff4464..cd9b94a 100644 --- a/src/access_map/azure_devops.rs +++ b/src/access_map/azure_devops.rs @@ -1,14 +1,14 @@ -use anyhow::{anyhow, Context, Result}; -use base64::{engine::general_purpose::STANDARD as b64, Engine as _}; -use reqwest::{header, Client, Url}; +use anyhow::{Context, Result, anyhow}; +use base64::{Engine as _, engine::general_purpose::STANDARD as b64}; +use reqwest::{Client, Url, header}; use serde::Deserialize; use tracing::warn; use crate::validation::GLOBAL_USER_AGENT; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; const AZURE_DEVOPS_PROFILE_API: &str = @@ -524,15 +524,19 @@ async fn list_repositories( continue; } - let mut project_repos = - list_project_repositories(client, organization, project_name, auth_header.clone()) - .await - .unwrap_or_else(|err| { - warn!( - "Azure DevOps access-map: project repo enumeration failed for {project_name}: {err}" - ); - Vec::new() - }); + let mut project_repos = list_project_repositories( + client, + organization, + project_name, + auth_header.clone(), + ) + .await + .unwrap_or_else(|err| { + warn!( + "Azure DevOps access-map: project repo enumeration failed for {project_name}: {err}" + ); + Vec::new() + }); repos.append(&mut project_repos); } diff --git a/src/access_map/bitbucket.rs b/src/access_map/bitbucket.rs index 0ed5a1b..ab5bddb 100644 --- a/src/access_map/bitbucket.rs +++ b/src/access_map/bitbucket.rs @@ -1,13 +1,13 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, header}; use serde::Deserialize; use tracing::warn; use crate::{cli::commands::access_map::AccessMapArgs, validation::GLOBAL_USER_AGENT}; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; const BITBUCKET_API: &str = "https://api.bitbucket.org/2.0"; diff --git a/src/access_map/buildkite.rs b/src/access_map/buildkite.rs index 0a67d9d..91e7e66 100644 --- a/src/access_map/buildkite.rs +++ b/src/access_map/buildkite.rs @@ -1,13 +1,13 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, header}; use serde::Deserialize; use tracing::warn; use crate::{cli::commands::access_map::AccessMapArgs, validation::GLOBAL_USER_AGENT}; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; const BUILDKITE_API: &str = "https://api.buildkite.com/v2"; diff --git a/src/access_map/circleci.rs b/src/access_map/circleci.rs index ed89c59..4ae1d38 100644 --- a/src/access_map/circleci.rs +++ b/src/access_map/circleci.rs @@ -1,13 +1,13 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, header}; use serde::Deserialize; use tracing::warn; use crate::{cli::commands::access_map::AccessMapArgs, validation::GLOBAL_USER_AGENT}; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; const CIRCLECI_API: &str = "https://circleci.com"; diff --git a/src/access_map/digitalocean.rs b/src/access_map/digitalocean.rs index 679bc7a..d897121 100644 --- a/src/access_map/digitalocean.rs +++ b/src/access_map/digitalocean.rs @@ -1,13 +1,13 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, header}; use serde::Deserialize; use tracing::warn; use crate::{cli::commands::access_map::AccessMapArgs, validation::GLOBAL_USER_AGENT}; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; const DIGITALOCEAN_API: &str = "https://api.digitalocean.com"; diff --git a/src/access_map/fastly.rs b/src/access_map/fastly.rs index cc4b626..8ca0b2f 100644 --- a/src/access_map/fastly.rs +++ b/src/access_map/fastly.rs @@ -1,13 +1,13 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, header}; use serde::Deserialize; use tracing::warn; use crate::{cli::commands::access_map::AccessMapArgs, validation::GLOBAL_USER_AGENT}; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; const FASTLY_API: &str = "https://api.fastly.com"; diff --git a/src/access_map/gcp.rs b/src/access_map/gcp.rs index 4e72acc..ab7ce80 100644 --- a/src/access_map/gcp.rs +++ b/src/access_map/gcp.rs @@ -1,8 +1,8 @@ use std::collections::HashSet; use std::path::Path; -use anyhow::{anyhow, Context, Result}; -use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; +use anyhow::{Context, Result, anyhow}; +use percent_encoding::{NON_ALPHANUMERIC, utf8_percent_encode}; use reqwest::{Client, StatusCode}; use serde_json::Value; use tracing::warn; @@ -24,8 +24,8 @@ struct Ancestor { } use super::{ - build_default_resource, build_recommendations, AccessMapResult, AccessSummary, - AccessTokenDetails, PermissionSummary, ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_default_resource, build_recommendations, }; pub async fn map_access(credential_path: Option<&Path>) -> Result { @@ -258,7 +258,9 @@ async fn fetch_project_policy( } if let Some(disabled) = service_disabled_message(&body_v1)? { - verbose_warn!("GCP access-map: Cloud Resource Manager API disabled for project {project}: {disabled}"); + verbose_warn!( + "GCP access-map: Cloud Resource Manager API disabled for project {project}: {disabled}" + ); return Ok(None); } @@ -298,7 +300,9 @@ async fn fetch_project_ancestry( let body = resp.bytes().await?; if let Some(disabled) = service_disabled_message(&body)? { - verbose_warn!("GCP access-map: Cloud Resource Manager API disabled for project {project_id}: {disabled}"); + verbose_warn!( + "GCP access-map: Cloud Resource Manager API disabled for project {project_id}: {disabled}" + ); return Ok(Vec::new()); } @@ -437,7 +441,9 @@ async fn fetch_service_account_metadata( let body = resp.bytes().await?; if let Some(disabled) = service_disabled_message(&body)? { - verbose_warn!("GCP access-map: IAM API disabled when fetching metadata for {client_email}: {disabled}"); + verbose_warn!( + "GCP access-map: IAM API disabled when fetching metadata for {client_email}: {disabled}" + ); return Ok(ServiceAccountMetadata::default()); } @@ -953,7 +959,9 @@ async fn enumerate_resources( let body = resp.bytes().await?; if let Some(disabled) = service_disabled_message(&body)? { - verbose_warn!("GCP access-map: Artifact Registry API disabled for project {project_id}: {disabled}"); + verbose_warn!( + "GCP access-map: Artifact Registry API disabled for project {project_id}: {disabled}" + ); } else if status.is_success() { let json: Value = serde_json::from_slice(&body)?; if let Some(items) = json.get("repositories").and_then(|i| i.as_array()) { @@ -999,7 +1007,9 @@ async fn enumerate_resources( let body = resp.bytes().await?; if let Some(disabled) = service_disabled_message(&body)? { - verbose_warn!("GCP access-map: Kubernetes Engine API disabled for project {project_id}: {disabled}"); + verbose_warn!( + "GCP access-map: Kubernetes Engine API disabled for project {project_id}: {disabled}" + ); } else if status.is_success() { let json: Value = serde_json::from_slice(&body)?; if let Some(items) = json.get("clusters").and_then(|i| i.as_array()) { diff --git a/src/access_map/gitea.rs b/src/access_map/gitea.rs index 10c706d..1c52d75 100644 --- a/src/access_map/gitea.rs +++ b/src/access_map/gitea.rs @@ -1,13 +1,13 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client, Url}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, Url, header}; use serde::Deserialize; use tracing::warn; use crate::{cli::commands::access_map::AccessMapArgs, validation::GLOBAL_USER_AGENT}; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; const DEFAULT_GITEA_API: &str = "https://gitea.com/api/v1/"; diff --git a/src/access_map/github.rs b/src/access_map/github.rs index 664505d..48dacf2 100644 --- a/src/access_map/github.rs +++ b/src/access_map/github.rs @@ -1,15 +1,15 @@ use std::collections::{BTreeMap, BTreeSet}; -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client, Url}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, Url, header}; use serde::Deserialize; use tracing::warn; use crate::{cli::commands::access_map::AccessMapArgs, validation::GLOBAL_USER_AGENT}; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; const DEFAULT_GITHUB_API: &str = "https://api.github.com"; diff --git a/src/access_map/gitlab.rs b/src/access_map/gitlab.rs index ba2c605..7edaee8 100644 --- a/src/access_map/gitlab.rs +++ b/src/access_map/gitlab.rs @@ -1,13 +1,13 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client, Url}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, Url, header}; use serde::Deserialize; use tracing::warn; use crate::{cli::commands::access_map::AccessMapArgs, validation::GLOBAL_USER_AGENT}; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ProviderMetadata, ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ProviderMetadata, + ResourceExposure, RoleBinding, Severity, build_recommendations, }; const DEFAULT_GITLAB_API: &str = "https://gitlab.com/api/v4/"; diff --git a/src/access_map/harness.rs b/src/access_map/harness.rs index 4775bd5..3820f97 100644 --- a/src/access_map/harness.rs +++ b/src/access_map/harness.rs @@ -1,14 +1,14 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client, StatusCode}; -use serde::{de::DeserializeOwned, Deserialize}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, StatusCode, header}; +use serde::{Deserialize, de::DeserializeOwned}; use serde_json::Value; use tracing::warn; use crate::{cli::commands::access_map::AccessMapArgs, validation::GLOBAL_USER_AGENT}; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; const HARNESS_API: &str = "https://app.harness.io"; diff --git a/src/access_map/hubspot.rs b/src/access_map/hubspot.rs index 5c56d1c..f00ec18 100644 --- a/src/access_map/hubspot.rs +++ b/src/access_map/hubspot.rs @@ -1,13 +1,13 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, header}; use serde::Deserialize; use tracing::warn; use crate::{cli::commands::access_map::AccessMapArgs, validation::GLOBAL_USER_AGENT}; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; const HUBSPOT_API: &str = "https://api.hubapi.com"; diff --git a/src/access_map/huggingface.rs b/src/access_map/huggingface.rs index af47633..104e732 100644 --- a/src/access_map/huggingface.rs +++ b/src/access_map/huggingface.rs @@ -1,5 +1,5 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, header}; use serde::Deserialize; use std::collections::BTreeSet; use tracing::warn; @@ -7,8 +7,8 @@ use tracing::warn; use crate::{cli::commands::access_map::AccessMapArgs, validation::GLOBAL_USER_AGENT}; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; const HUGGINGFACE_API: &str = "https://huggingface.co/api"; @@ -453,11 +453,7 @@ fn derive_severity( } } - if has_private_assets { - Severity::Medium - } else { - Severity::Low - } + if has_private_assets { Severity::Medium } else { Severity::Low } } fn severity_to_str(severity: Severity) -> &'static str { diff --git a/src/access_map/ibm_cloud.rs b/src/access_map/ibm_cloud.rs index 045adc8..90b5947 100644 --- a/src/access_map/ibm_cloud.rs +++ b/src/access_map/ibm_cloud.rs @@ -1,13 +1,13 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, header}; use serde::Deserialize; use tracing::warn; use crate::{cli::commands::access_map::AccessMapArgs, validation::GLOBAL_USER_AGENT}; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; const IBM_IAM_API: &str = "https://iam.cloud.ibm.com"; diff --git a/src/access_map/jira.rs b/src/access_map/jira.rs index a90a0c7..6e0465a 100644 --- a/src/access_map/jira.rs +++ b/src/access_map/jira.rs @@ -1,5 +1,5 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, header}; use serde::Deserialize; use tracing::warn; @@ -7,8 +7,8 @@ use crate::cli::commands::access_map::AccessMapArgs; use crate::validation::GLOBAL_USER_AGENT; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; // ─── API response types ───────────────────────────────────────────────────── @@ -55,8 +55,7 @@ const RISKY_PERMISSIONS: &[&str] = &["DELETE_ISSUES", "EDIT_ISSUES", "CREATE_ISSUES", "MANAGE_WATCHERS"]; const READ_PERMISSIONS: &[&str] = &["BROWSE_PROJECTS"]; -const CHECKED_PERMISSIONS: &str = - "BROWSE_PROJECTS,CREATE_ISSUES,EDIT_ISSUES,DELETE_ISSUES,MANAGE_WATCHERS,ADMINISTER_PROJECTS,SYSTEM_ADMIN"; +const CHECKED_PERMISSIONS: &str = "BROWSE_PROJECTS,CREATE_ISSUES,EDIT_ISSUES,DELETE_ISSUES,MANAGE_WATCHERS,ADMINISTER_PROJECTS,SYSTEM_ADMIN"; // ─── Public entry points ──────────────────────────────────────────────────── diff --git a/src/access_map/microsoft_teams.rs b/src/access_map/microsoft_teams.rs index 50c58d0..a432f2e 100644 --- a/src/access_map/microsoft_teams.rs +++ b/src/access_map/microsoft_teams.rs @@ -1,11 +1,11 @@ -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result, anyhow}; use reqwest::Client; use crate::cli::commands::access_map::AccessMapArgs; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; pub async fn map_access(args: &AccessMapArgs) -> Result { diff --git a/src/access_map/mongodb.rs b/src/access_map/mongodb.rs index eb019fc..0259c53 100644 --- a/src/access_map/mongodb.rs +++ b/src/access_map/mongodb.rs @@ -1,18 +1,18 @@ use std::time::Duration; -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result, anyhow}; use mongodb::{ - bson::{doc, Document}, - options::{ClientOptions, Tls, TlsOptions}, Client, + bson::{Document, doc}, + options::{ClientOptions, Tls, TlsOptions}, }; use tracing::{debug, warn}; use crate::cli::commands::access_map::AccessMapArgs; use super::{ - build_recommendations, AccessMapResult, AccessSummary, PermissionSummary, ProviderMetadata, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, PermissionSummary, ProviderMetadata, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; const CONNECT_TIMEOUT_MS: u64 = 5_000; diff --git a/src/access_map/mysql.rs b/src/access_map/mysql.rs index 9e62491..b076ef0 100644 --- a/src/access_map/mysql.rs +++ b/src/access_map/mysql.rs @@ -1,6 +1,6 @@ use std::time::Duration; -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result, anyhow}; use mysql_async::prelude::*; use mysql_async::{Opts, Pool}; use tokio::time::timeout; @@ -9,8 +9,8 @@ use tracing::warn; use crate::cli::commands::access_map::AccessMapArgs; use super::{ - build_recommendations, AccessMapResult, AccessSummary, PermissionSummary, ResourceExposure, - RoleBinding, Severity, + AccessMapResult, AccessSummary, PermissionSummary, ResourceExposure, RoleBinding, Severity, + build_recommendations, }; const CONNECT_TIMEOUT: Duration = Duration::from_secs(8); diff --git a/src/access_map/openai.rs b/src/access_map/openai.rs index 33f0694..e7f65be 100644 --- a/src/access_map/openai.rs +++ b/src/access_map/openai.rs @@ -1,5 +1,5 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client, Method, StatusCode}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, Method, StatusCode, header}; use serde::Deserialize; use serde_json::json; use tracing::warn; @@ -7,8 +7,8 @@ use tracing::warn; use crate::{cli::commands::access_map::AccessMapArgs, validation::GLOBAL_USER_AGENT}; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; const OPENAI_API: &str = "https://api.openai.com/v1"; @@ -968,7 +968,7 @@ fn severity_to_str(severity: Severity) -> &'static str { #[cfg(test)] mod tests { - use super::{scope_has_read_access, scope_has_write_access, ScopeResult}; + use super::{ScopeResult, scope_has_read_access, scope_has_write_access}; #[test] fn scope_helpers_track_read_and_write_access_independently() { diff --git a/src/access_map/paypal.rs b/src/access_map/paypal.rs index 6b5e064..4078d39 100644 --- a/src/access_map/paypal.rs +++ b/src/access_map/paypal.rs @@ -1,5 +1,5 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, header}; use serde::Deserialize; use tracing::warn; @@ -7,8 +7,8 @@ use crate::cli::commands::access_map::AccessMapArgs; use crate::validation::GLOBAL_USER_AGENT; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; // --------------------------------------------------------------------------- diff --git a/src/access_map/plaid.rs b/src/access_map/plaid.rs index 2ee55cb..1e9d56c 100644 --- a/src/access_map/plaid.rs +++ b/src/access_map/plaid.rs @@ -1,5 +1,5 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client, StatusCode}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, StatusCode, header}; use serde::Deserialize; use tracing::warn; @@ -7,8 +7,8 @@ use crate::cli::commands::access_map::AccessMapArgs; use crate::validation::GLOBAL_USER_AGENT; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; // --------------------------------------------------------------------------- diff --git a/src/access_map/postgres.rs b/src/access_map/postgres.rs index b433cd6..e4028e4 100644 --- a/src/access_map/postgres.rs +++ b/src/access_map/postgres.rs @@ -3,11 +3,11 @@ use std::time::Duration; use std::sync::OnceLock; -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result, anyhow}; use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}; -use rustls::crypto::{ring, verify_tls12_signature, verify_tls13_signature, CryptoProvider}; +use rustls::crypto::{CryptoProvider, ring, verify_tls12_signature, verify_tls13_signature}; use rustls::pki_types::{CertificateDer, ServerName, UnixTime}; -use rustls::{client::ClientConfig, DigitallySignedStruct, SignatureScheme}; +use rustls::{DigitallySignedStruct, SignatureScheme, client::ClientConfig}; use tokio::time::timeout; use tokio_postgres::config::SslMode; use tokio_postgres::tls::NoTls; @@ -18,8 +18,8 @@ use tracing::{debug, warn}; use crate::cli::commands::access_map::AccessMapArgs; use super::{ - build_recommendations, AccessMapResult, AccessSummary, PermissionSummary, ResourceExposure, - RoleBinding, Severity, + AccessMapResult, AccessSummary, PermissionSummary, ResourceExposure, RoleBinding, Severity, + build_recommendations, }; const CONNECT_TIMEOUT: Duration = Duration::from_secs(8); @@ -367,7 +367,7 @@ async fn connect(pg_url: &str) -> Result { } Ok(Err(e)) => return Err(anyhow!("Postgres connection failed: {e}")), Err(_) => { - return Err(anyhow!("Postgres connection timed out after {CONNECT_TIMEOUT:?}")) + return Err(anyhow!("Postgres connection timed out after {CONNECT_TIMEOUT:?}")); } } } diff --git a/src/access_map/report.rs b/src/access_map/report.rs index be5b6ef..9ae2c1e 100644 --- a/src/access_map/report.rs +++ b/src/access_map/report.rs @@ -1,9 +1,9 @@ use std::path::Path; use anyhow::Result; -use base64::engine::general_purpose::STANDARD as BASE64_STANDARD; use base64::Engine; -use flate2::{write::GzEncoder, Compression}; +use base64::engine::general_purpose::STANDARD as BASE64_STANDARD; +use flate2::{Compression, write::GzEncoder}; use std::io::Write; use super::AccessMapResult; diff --git a/src/access_map/salesforce.rs b/src/access_map/salesforce.rs index bb338ab..208902e 100644 --- a/src/access_map/salesforce.rs +++ b/src/access_map/salesforce.rs @@ -1,6 +1,6 @@ -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result, anyhow}; use regex::Regex; -use reqwest::{header, Client, StatusCode}; +use reqwest::{Client, StatusCode, header}; use serde_json::Value; use std::sync::LazyLock; use tracing::warn; @@ -8,8 +8,8 @@ use tracing::warn; use crate::{cli::commands::access_map::AccessMapArgs, validation::GLOBAL_USER_AGENT}; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; const SALESFORCE_API_VERSION: &str = "v60.0"; diff --git a/src/access_map/sendgrid.rs b/src/access_map/sendgrid.rs index 42766ee..cb9b6ca 100644 --- a/src/access_map/sendgrid.rs +++ b/src/access_map/sendgrid.rs @@ -1,13 +1,13 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, header}; use serde::Deserialize; use tracing::warn; use crate::{cli::commands::access_map::AccessMapArgs, validation::GLOBAL_USER_AGENT}; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; const SENDGRID_API: &str = "https://api.sendgrid.com"; diff --git a/src/access_map/sendinblue.rs b/src/access_map/sendinblue.rs index a45b957..b71dbe6 100644 --- a/src/access_map/sendinblue.rs +++ b/src/access_map/sendinblue.rs @@ -1,13 +1,13 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, header}; use serde::Deserialize; use tracing::warn; use crate::{cli::commands::access_map::AccessMapArgs, validation::GLOBAL_USER_AGENT}; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; const BREVO_API: &str = "https://api.brevo.com"; diff --git a/src/access_map/shopify.rs b/src/access_map/shopify.rs index fef807a..fce2f48 100644 --- a/src/access_map/shopify.rs +++ b/src/access_map/shopify.rs @@ -1,5 +1,5 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, header}; use serde::Deserialize; use serde_json::Value; use tracing::warn; @@ -7,8 +7,8 @@ use tracing::warn; use crate::{cli::commands::access_map::AccessMapArgs, validation::GLOBAL_USER_AGENT}; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; const SHOPIFY_API_VERSION: &str = "2024-10"; diff --git a/src/access_map/slack.rs b/src/access_map/slack.rs index ff4d2e6..272ca72 100644 --- a/src/access_map/slack.rs +++ b/src/access_map/slack.rs @@ -1,10 +1,10 @@ -use anyhow::{anyhow, Result}; +use anyhow::{Result, anyhow}; use reqwest::header::AUTHORIZATION; use serde::Deserialize; use super::{ - build_recommendations, AccessMapArgs, AccessMapResult, AccessSummary, AccessTokenDetails, - PermissionSummary, ProviderMetadata, ResourceExposure, RoleBinding, Severity, + AccessMapArgs, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, + ProviderMetadata, ResourceExposure, RoleBinding, Severity, build_recommendations, }; pub async fn map_access(args: &AccessMapArgs) -> Result { diff --git a/src/access_map/square.rs b/src/access_map/square.rs index 128bacd..cb6422e 100644 --- a/src/access_map/square.rs +++ b/src/access_map/square.rs @@ -1,13 +1,13 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, header}; use serde::Deserialize; use tracing::warn; use crate::{cli::commands::access_map::AccessMapArgs, validation::GLOBAL_USER_AGENT}; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; const SQUARE_API: &str = "https://connect.squareup.com"; diff --git a/src/access_map/stripe.rs b/src/access_map/stripe.rs index 989d94c..8d20823 100644 --- a/src/access_map/stripe.rs +++ b/src/access_map/stripe.rs @@ -1,13 +1,13 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, header}; use serde::Deserialize; use tracing::warn; use crate::{cli::commands::access_map::AccessMapArgs, validation::GLOBAL_USER_AGENT}; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; const STRIPE_API: &str = "https://api.stripe.com"; diff --git a/src/access_map/terraform.rs b/src/access_map/terraform.rs index 6272df7..9ce5cc1 100644 --- a/src/access_map/terraform.rs +++ b/src/access_map/terraform.rs @@ -1,13 +1,13 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, header}; use serde::Deserialize; use tracing::warn; use crate::{cli::commands::access_map::AccessMapArgs, validation::GLOBAL_USER_AGENT}; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; const TERRAFORM_API: &str = "https://app.terraform.io"; diff --git a/src/access_map/weightsandbiases.rs b/src/access_map/weightsandbiases.rs index 95c2afe..273e499 100644 --- a/src/access_map/weightsandbiases.rs +++ b/src/access_map/weightsandbiases.rs @@ -1,13 +1,13 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, header}; use serde::Deserialize; use serde_json::json; use crate::{cli::commands::access_map::AccessMapArgs, validation::GLOBAL_USER_AGENT}; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; const WANDB_API: &str = "https://api.wandb.ai/graphql"; diff --git a/src/access_map/xray.rs b/src/access_map/xray.rs index 5660d58..f842de6 100644 --- a/src/access_map/xray.rs +++ b/src/access_map/xray.rs @@ -1,5 +1,5 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, header}; use serde::Deserialize; use serde_json::Value; use tracing::warn; @@ -7,8 +7,8 @@ use tracing::warn; use crate::{cli::commands::access_map::AccessMapArgs, validation::GLOBAL_USER_AGENT}; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; const MAX_REPO_RESOURCES: usize = 100; diff --git a/src/access_map/zendesk.rs b/src/access_map/zendesk.rs index 1ce6924..9ab71e8 100644 --- a/src/access_map/zendesk.rs +++ b/src/access_map/zendesk.rs @@ -1,5 +1,5 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{header, Client}; +use anyhow::{Context, Result, anyhow}; +use reqwest::{Client, header}; use serde::Deserialize; use serde_json::Value; use tracing::warn; @@ -7,8 +7,8 @@ use tracing::warn; use crate::{cli::commands::access_map::AccessMapArgs, validation::GLOBAL_USER_AGENT}; use super::{ - build_recommendations, AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, - ResourceExposure, RoleBinding, Severity, + AccessMapResult, AccessSummary, AccessTokenDetails, PermissionSummary, ResourceExposure, + RoleBinding, Severity, build_recommendations, }; #[derive(Deserialize)] diff --git a/src/azure.rs b/src/azure.rs index 9f2a752..4b434b6 100644 --- a/src/azure.rs +++ b/src/azure.rs @@ -12,11 +12,11 @@ use std::{ // preview API surfaces we rely on, while the raw requests keep the binary lean and // let us opt into newer API versions as Microsoft rolls them out. -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result, anyhow}; use indicatif::{ProgressBar, ProgressStyle}; use serde::Deserialize; use tracing::warn; -use url::{form_urlencoded, Url}; +use url::{Url, form_urlencoded}; use crate::{findings_store, git_host, git_url::GitUrl}; @@ -73,11 +73,7 @@ fn normalize_repo_identifier(parts: &[String]) -> Option { normalized.retain(|s| !s.is_empty()); normalized.push(project.to_lowercase()); normalized.push(repo.trim_end_matches(".git").to_lowercase()); - if normalized.is_empty() { - None - } else { - Some(normalized.join("/")) - } + if normalized.is_empty() { None } else { Some(normalized.join("/")) } } fn parse_repo_identifier_from_path(path: &str) -> Option { diff --git a/src/binary.rs b/src/binary.rs index ac3d9fe..0760b03 100644 --- a/src/binary.rs +++ b/src/binary.rs @@ -1,7 +1,7 @@ use std::{fs, io::Read, path::Path}; use anyhow::Result; -use content_inspector::{inspect, ContentType}; +use content_inspector::{ContentType, inspect}; use crate::util::is_safe_path; const MAX_PEEK_SIZE: usize = 1024; diff --git a/src/bstring_table.rs b/src/bstring_table.rs index c7ea6eb..fcaa3d1 100644 --- a/src/bstring_table.rs +++ b/src/bstring_table.rs @@ -1,5 +1,5 @@ use std::{ - collections::{hash_map::Entry, HashMap}, + collections::{HashMap, hash_map::Entry}, ops::Range, }; diff --git a/src/cli/commands/view.rs b/src/cli/commands/view.rs index 5fdd145..475e6f2 100644 --- a/src/cli/commands/view.rs +++ b/src/cli/commands/view.rs @@ -5,16 +5,16 @@ use std::{ sync::Arc, }; -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result, anyhow}; use axum::{ + Router, body::Body, extract::State, - http::{header, HeaderValue, StatusCode, Uri}, + http::{HeaderValue, StatusCode, Uri, header}, response::Response, routing::get, - Router, }; -use include_dir::{include_dir, Dir}; +use include_dir::{Dir, include_dir}; use tokio::net::TcpListener; use tracing::{info, warn}; @@ -159,11 +159,7 @@ pub async fn run(args: ViewArgs) -> Result<()> { None } else { let combined = load_and_combine_reports(&paths).await?; - if combined.is_empty() { - None - } else { - Some(combined) - } + if combined.is_empty() { None } else { Some(combined) } } } else { None diff --git a/src/confluence.rs b/src/confluence.rs index a7f61c7..edf8b09 100644 --- a/src/confluence.rs +++ b/src/confluence.rs @@ -1,5 +1,5 @@ -use anyhow::{bail, Context, Result}; -use reqwest::{header, Client}; +use anyhow::{Context, Result, bail}; +use reqwest::{Client, header}; use serde::{Deserialize, Serialize}; use std::path::PathBuf; use url::Url; diff --git a/src/decompress.rs b/src/decompress.rs index 94cbc1f..5545581 100644 --- a/src/decompress.rs +++ b/src/decompress.rs @@ -11,7 +11,7 @@ use flate2::read::{GzDecoder, ZlibDecoder}; use lzma_rs::xz_decompress; use memmap2::Mmap; use tar::Archive; -use tempfile::{tempdir, TempDir}; +use tempfile::{TempDir, tempdir}; use uuid::Uuid; use zip::ZipArchive; @@ -350,12 +350,12 @@ pub fn decompress_file_to_temp(path: &Path) -> Result<(CompressedContent, TempDi mod tests { use std::{fs::File, io::Write}; - use flate2::{write::GzEncoder, Compression}; + use flate2::{Compression, write::GzEncoder}; use tar::Builder; use tempfile::tempdir; - use zip::{write::SimpleFileOptions, CompressionMethod, ZipWriter}; + use zip::{CompressionMethod, ZipWriter, write::SimpleFileOptions}; - use super::{decompress_once, CompressedContent}; + use super::{CompressedContent, decompress_once}; /// 1) Fully unpack: /// - 1st decompress `.gz` -- get a `.tar` file @@ -498,11 +498,11 @@ mod tests { fn smoke_decompress_nested_tar_gz_archives() -> anyhow::Result<()> { use std::{fs::File, io::Read, path::PathBuf}; - use flate2::{write::GzEncoder, Compression}; + use flate2::{Compression, write::GzEncoder}; use tar::Builder; use tempfile::tempdir; - use super::{decompress_once, CompressedContent}; + use super::{CompressedContent, decompress_once}; let tmp = tempdir()?; diff --git a/src/defaults.rs b/src/defaults.rs index 4291656..f9d3cd5 100644 --- a/src/defaults.rs +++ b/src/defaults.rs @@ -2,4 +2,4 @@ //! //! This module re-exports from [`kingfisher_rules::defaults`]. -pub use kingfisher_rules::defaults::{get_builtin_rules, DEFAULT_RULES_DIR}; +pub use kingfisher_rules::defaults::{DEFAULT_RULES_DIR, get_builtin_rules}; diff --git a/src/direct_revoke.rs b/src/direct_revoke.rs index 646c2cb..e2ae76f 100644 --- a/src/direct_revoke.rs +++ b/src/direct_revoke.rs @@ -9,7 +9,7 @@ use std::{ time::Duration, }; -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{Context, Result, anyhow, bail}; use liquid::Object; use liquid_core::{Value, ValueView}; use regex::Regex; @@ -22,10 +22,10 @@ use crate::{ liquid_filters::register_all, rule_loader::RuleLoader, template_vars::extract_template_vars, + validation::GLOBAL_USER_AGENT, validation::aws::{revoke_aws_access_key, validate_aws_credentials_input}, validation::gcp::revoke_gcp_service_account_key, validation::httpvalidation::{build_request_builder, retry_request, validate_response}, - validation::GLOBAL_USER_AGENT, }; use kingfisher_rules::{ @@ -238,11 +238,7 @@ fn render_extractor( fn truncate_with_ellipsis(input: &str, max_chars: usize) -> String { let truncated: String = input.chars().take(max_chars).collect(); - if input.chars().count() > max_chars { - format!("{}...", truncated) - } else { - input.to_string() - } + if input.chars().count() > max_chars { format!("{}...", truncated) } else { input.to_string() } } /// Extract a value from an HTTP response using the specified extractor. @@ -733,11 +729,7 @@ pub fn print_results(results: &[DirectRevocationResult], format: &str, use_color } let revoked_str = if result.revoked { - if use_color { - "\x1b[32m✓ REVOKED\x1b[0m" - } else { - "REVOKED" - } + if use_color { "\x1b[32m✓ REVOKED\x1b[0m" } else { "REVOKED" } } else if use_color { "\x1b[31m✗ FAILED\x1b[0m" } else { @@ -766,8 +758,8 @@ pub fn any_revoked(results: &[DirectRevocationResult]) -> bool { mod tests { use super::*; use kingfisher_rules::{HttpValidation, ResponseExtractor, Revocation}; - use reqwest::header::{HeaderMap, HeaderValue}; use reqwest::StatusCode; + use reqwest::header::{HeaderMap, HeaderValue}; use std::collections::{BTreeMap, BTreeSet}; // ---- extract_value_from_response: JsonPath ---- diff --git a/src/direct_validate.rs b/src/direct_validate.rs index a57c850..f68caff 100644 --- a/src/direct_validate.rs +++ b/src/direct_validate.rs @@ -10,7 +10,7 @@ use std::{ time::Duration, }; -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{Context, Result, anyhow, bail}; use crossbeam_skiplist::SkipMap; use liquid::Object; use liquid_core::{Value, ValueView}; @@ -22,9 +22,10 @@ use crate::{ cli::{commands::validate::ValidateArgs, global::GlobalArgs}, liquid_filters::register_all, rule_loader::RuleLoader, - rules::{rule::Rule, HttpValidation, Validation}, + rules::{HttpValidation, Validation, rule::Rule}, template_vars::extract_template_vars, validation::{ + GLOBAL_USER_AGENT, aws::validate_aws_credentials, azure::validate_azure_storage_credentials, coinbase::validate_cdp_api_key, @@ -37,10 +38,9 @@ use crate::{ mongodb::validate_mongodb, mysql::validate_mysql, postgres::validate_postgres, - GLOBAL_USER_AGENT, }, validation_body, - validation_rate_limit::{should_rate_limit_validation, ValidationRateLimiter}, + validation_rate_limit::{ValidationRateLimiter, should_rate_limit_validation}, }; use crate::grpc_validation; @@ -1049,11 +1049,7 @@ pub fn print_results(results: &[DirectValidationResult], format: &str, use_color } let valid_str = if result.is_valid { - if use_color { - "\x1b[32m✓ VALID\x1b[0m" - } else { - "VALID" - } + if use_color { "\x1b[32m✓ VALID\x1b[0m" } else { "VALID" } } else if use_color { "\x1b[31m✗ INVALID\x1b[0m" } else { diff --git a/src/findings_store.rs b/src/findings_store.rs index 9d3417a..e315991 100644 --- a/src/findings_store.rs +++ b/src/findings_store.rs @@ -45,11 +45,7 @@ fn origin_fp(os: &OriginSet) -> u64 { } fn dedup_origin_kind(origin: &OriginSet) -> &'static str { - if origin.iter().any(|o| matches!(o, Origin::Extended(_))) { - "ext" - } else { - "file_git" - } + if origin.iter().any(|o| matches!(o, Origin::Extended(_))) { "ext" } else { "file_git" } } const DEDUP_BLOOM_FP_RATE: f64 = 0.001; diff --git a/src/gcs.rs b/src/gcs.rs index dc346fd..042f3d8 100644 --- a/src/gcs.rs +++ b/src/gcs.rs @@ -2,7 +2,7 @@ use std::path::Path; use anyhow::{Context, Result}; use gcloud_storage::{ - client::{google_cloud_auth::credentials::CredentialsFile, Client, ClientConfig}, + client::{Client, ClientConfig, google_cloud_auth::credentials::CredentialsFile}, http::objects::{ download::Range, get::GetObjectRequest, diff --git a/src/git_binary.rs b/src/git_binary.rs index a475613..fe3d25a 100644 --- a/src/git_binary.rs +++ b/src/git_binary.rs @@ -81,11 +81,7 @@ fn format_git_error_summary(stdout: &[u8], stderr: &[u8]) -> String { if let Some(line) = summarize_output(stdout) { messages.push(line); } - if messages.is_empty() { - String::new() - } else { - format!(": {}", messages.join(" | ")) - } + if messages.is_empty() { String::new() } else { format!(": {}", messages.join(" | ")) } } fn summarize_output(output: &[u8]) -> Option { diff --git a/src/git_metadata_graph.rs b/src/git_metadata_graph.rs index fb2c1c6..8189c23 100644 --- a/src/git_metadata_graph.rs +++ b/src/git_metadata_graph.rs @@ -1,14 +1,14 @@ use std::{collections::BinaryHeap, time::Instant}; -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result, bail}; use bstr::{BString, ByteSlice}; use fixedbitset::FixedBitSet; use gix::{ - hashtable::{hash_map, HashMap}, + ObjectId, OdbHandle, + hashtable::{HashMap, hash_map}, object::Kind, objs::tree::EntryKind, prelude::*, - ObjectId, OdbHandle, }; use globset::GlobSet; @@ -502,7 +502,7 @@ fn visit_tree( mod tests { use std::{fs, path::Path, sync::Arc}; - use anyhow::{bail, Result}; + use anyhow::{Result, bail}; use bstr::ByteSlice; use git2::{Repository as Git2Repository, Signature}; use gix::{open::Options, open_opts}; @@ -573,10 +573,12 @@ mod tests { .collect(); assert_eq!(matching.len(), 1); - assert!(matching[0] - .first_seen - .iter() - .all(|appearance| appearance.path.to_str_lossy() != "excluded/secret.txt")); + assert!( + matching[0] + .first_seen + .iter() + .all(|appearance| appearance.path.to_str_lossy() != "excluded/secret.txt") + ); Ok(()) } diff --git a/src/git_repo_enumerator.rs b/src/git_repo_enumerator.rs index 6c3df67..532afac 100644 --- a/src/git_repo_enumerator.rs +++ b/src/git_repo_enumerator.rs @@ -7,10 +7,10 @@ use std::{ use anyhow::Result; use bstr::ByteSlice; use gix::{ - date::{parse as parse_time, Time}, + ObjectId, Repository, + date::{Time, parse as parse_time}, hashtable::HashMap, prelude::FindExt, - ObjectId, Repository, }; use smallvec::SmallVec; use tracing::{debug, debug_span}; diff --git a/src/gitea.rs b/src/gitea.rs index d748685..95c214a 100644 --- a/src/gitea.rs +++ b/src/gitea.rs @@ -1,6 +1,6 @@ use std::{collections::HashSet, env, str::FromStr, time::Duration}; -use anyhow::{anyhow, Result}; +use anyhow::{Result, anyhow}; use indicatif::{ProgressBar, ProgressStyle}; use reqwest::StatusCode; use serde::Deserialize; diff --git a/src/github.rs b/src/github.rs index a4614da..1b25e9b 100644 --- a/src/github.rs +++ b/src/github.rs @@ -9,9 +9,9 @@ use std::{ use anyhow::{Context, Result}; use indicatif::{ProgressBar, ProgressStyle}; use octorust::{ + Client, auth::Credentials, types::{Order, ReposListOrgSort, ReposListOrgType, ReposListUserType}, - Client, }; use reqwest::StatusCode; use serde::Deserialize; @@ -358,11 +358,7 @@ pub async fn enumerate_repo_urls( .await?; repo_urls.extend(repos.body.into_iter().filter_map(|repo| { let clone_url = repo.clone_url; - if should_exclude_repo(&clone_url, &exclude_set) { - None - } else { - Some(clone_url) - } + if should_exclude_repo(&clone_url, &exclude_set) { None } else { Some(clone_url) } })); if let Some(progress) = progress.as_mut() { progress.inc(1); @@ -388,11 +384,7 @@ pub async fn enumerate_repo_urls( .await?; repo_urls.extend(repos.body.into_iter().filter_map(|repo| { let clone_url = repo.clone_url; - if should_exclude_repo(&clone_url, &exclude_set) { - None - } else { - Some(clone_url) - } + if should_exclude_repo(&clone_url, &exclude_set) { None } else { Some(clone_url) } })); if let Some(progress) = progress.as_mut() { progress.inc(1); diff --git a/src/gitlab.rs b/src/gitlab.rs index ce6014c..1e24c13 100644 --- a/src/gitlab.rs +++ b/src/gitlab.rs @@ -12,7 +12,7 @@ use reqwest::StatusCode; use serde::Deserialize; use serde_json::Value; use tracing::{info, warn}; -use url::{form_urlencoded, Url}; +use url::{Url, form_urlencoded}; use crate::{findings_store, git_host, git_url::GitUrl, validation::GLOBAL_USER_AGENT}; use std::str::FromStr; diff --git a/src/grpc_validation.rs b/src/grpc_validation.rs index bdd4468..9d6346e 100644 --- a/src/grpc_validation.rs +++ b/src/grpc_validation.rs @@ -1,9 +1,9 @@ use std::{collections::BTreeMap, sync::Arc, time::Duration}; -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result, anyhow}; use bytes::Bytes; use h2::client; -use http::{header::HeaderName, HeaderMap, HeaderValue, Request, Uri}; +use http::{HeaderMap, HeaderValue, Request, Uri, header::HeaderName}; use std::sync::OnceLock; use liquid::Object; diff --git a/src/huggingface.rs b/src/huggingface.rs index 42bbf03..1d26824 100644 --- a/src/huggingface.rs +++ b/src/huggingface.rs @@ -1,8 +1,8 @@ use std::{collections::HashSet, env, time::Duration}; -use anyhow::{anyhow, Result}; +use anyhow::{Result, anyhow}; use indicatif::{ProgressBar, ProgressStyle}; -use reqwest::{header::LINK, StatusCode, Url}; +use reqwest::{StatusCode, Url, header::LINK}; use serde::Deserialize; use serde_json::Value; use tracing::{debug, warn}; @@ -55,11 +55,7 @@ impl AuthConfig { } fn apply(&self, request: reqwest::RequestBuilder) -> reqwest::RequestBuilder { - if let Some(token) = &self.token { - request.bearer_auth(token) - } else { - request - } + if let Some(token) = &self.token { request.bearer_auth(token) } else { request } } fn has_token(&self) -> bool { @@ -182,7 +178,9 @@ impl ExcludeSet { slug.to_lowercase() )); } else { - warn!("Ignoring invalid Hugging Face exclusion '{raw}' (expected owner/name)"); + warn!( + "Ignoring invalid Hugging Face exclusion '{raw}' (expected owner/name)" + ); } } None => warn!("Ignoring invalid Hugging Face exclusion '{raw}' (unknown type)"), diff --git a/src/inline_ignore.rs b/src/inline_ignore.rs index e42f6c9..a7dda6f 100644 --- a/src/inline_ignore.rs +++ b/src/inline_ignore.rs @@ -270,8 +270,8 @@ fn line_has_directive(line: &[u8], tokens: &[Vec]) -> bool { #[cfg(test)] mod tests { use super::{ - line_bounds, line_has_directive, should_skip_for_directive_search, trim_ascii_whitespace, - InlineIgnoreConfig, + InlineIgnoreConfig, line_bounds, line_has_directive, should_skip_for_directive_search, + trim_ascii_whitespace, }; use crate::location::OffsetSpan; diff --git a/src/jira.rs b/src/jira.rs index 5051a4c..1f37d38 100644 --- a/src/jira.rs +++ b/src/jira.rs @@ -1,5 +1,5 @@ use anyhow::{Context, Result}; -use gouqi::{r#async::Jira, Credentials, SearchOptions}; +use gouqi::{Credentials, SearchOptions, r#async::Jira}; use reqwest::Client; use std::path::PathBuf; use url::Url; @@ -219,11 +219,7 @@ fn flatten_adf_fields(issue_value: &mut serde_json::Value) { if let Some(arr) = comments.as_array_mut() { for comment in arr.iter_mut() { let plain_text = comment.get("body").and_then(|body| { - if is_adf(body) { - Some(extract_adf_text(body)) - } else { - None - } + if is_adf(body) { Some(extract_adf_text(body)) } else { None } }); if let Some(plain_text) = plain_text { if let Some(comment_obj) = comment.as_object_mut() { @@ -242,13 +238,9 @@ fn flatten_adf_fields(issue_value: &mut serde_json::Value) { fn flatten_comment_bodies(comments: &mut [serde_json::Value]) { for comment in comments { - let plain_text = comment.get("body").and_then(|body| { - if is_adf(body) { - Some(extract_adf_text(body)) - } else { - None - } - }); + let plain_text = comment + .get("body") + .and_then(|body| if is_adf(body) { Some(extract_adf_text(body)) } else { None }); if let Some(plain_text) = plain_text { if let Some(comment_obj) = comment.as_object_mut() { comment_obj.insert( @@ -449,14 +441,14 @@ pub async fn download_issues_to_dir( #[cfg(test)] mod tests { use super::{ - extract_adf_text, extract_embedded_comments, fetch_comments, flatten_adf_fields, - flatten_comment_bodies, is_adf, JIRA_COMMENTS_PAGE_SIZE, + JIRA_COMMENTS_PAGE_SIZE, extract_adf_text, extract_embedded_comments, fetch_comments, + flatten_adf_fields, flatten_comment_bodies, is_adf, }; use serde_json::json; use url::Url; use wiremock::{ - matchers::{method, path, query_param}, Mock, MockServer, ResponseTemplate, + matchers::{method, path, query_param}, }; #[test] diff --git a/src/lib.rs b/src/lib.rs index 917770a..14452d6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,7 +64,7 @@ pub mod validation_rate_limit; use std::path::{Path, PathBuf}; -use anyhow::{bail, Result}; +use anyhow::{Result, bail}; use crossbeam_channel::Sender; pub use git_repo_enumerator::{ GitBlobSource, GitRepoEnumerator, GitRepoResult, GitRepoWithMetadataEnumerator, diff --git a/src/main.rs b/src/main.rs index 5b7a4e1..b626e4a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -60,7 +60,7 @@ use console::Term; use kingfisher::{ access_map, azure, bitbucket, cli::{ - self, + self, CommandLineArgs, GlobalArgs, commands::{ github::{GitCloneMode, GitHistoryMode, GitHubRepoType}, inputs::{ContentFilteringArgs, InputSpecifierArgs}, @@ -71,12 +71,11 @@ use kingfisher::{ }, }, global::Command, - CommandLineArgs, GlobalArgs, }, direct_revoke, direct_validate, findings_store, findings_store::FindingsStore, gitea, github, huggingface, - reporter::{styles::Styles, DetailsReporter, ScanAuditContext}, + reporter::{DetailsReporter, ScanAuditContext, styles::Styles}, rule_loader::RuleLoader, rules_database::RulesDatabase, scanner::{load_and_record_rules, run_scan}, @@ -181,7 +180,7 @@ fn setup_logging(global_args: &GlobalArgs) { tracing_subscriber::filter::Targets::new() .with_default(LevelFilter::ERROR) // Default for all modules .with_target("kingfisher", level) // Replace `kingfisher` with your - // crate's name + // crate's name }; // Configure the formatter layer let fmt_layer = fmt::layer() @@ -189,7 +188,7 @@ fn setup_logging(global_args: &GlobalArgs) { .with_target(true) // Enable target filtering .with_ansi(std::io::stderr().is_terminal()) // Emit ANSI colours when stderr is a TTY .without_time(); // Remove timestamps - // Build and initialize the registry + // Build and initialize the registry registry() .with(fmt_layer) // Attach the formatter layer .with(filter) // Attach the filter @@ -750,7 +749,9 @@ pub fn run_rules_check(args: &RulesCheckArgs) -> Result<()> { " [!] Pattern requirements not met for example: {}", example ); - println!(" The match does not satisfy the character requirements (min_digits, min_uppercase, etc.)"); + println!( + " The match does not satisfy the character requirements (min_digits, min_uppercase, etc.)" + ); num_errors += 1; } PatternValidationResult::FailedChecksum { actual_len, expected_len } => { @@ -822,7 +823,7 @@ pub fn run_rules_list(args: &RulesListArgs) -> Result<()> { .max() .unwrap_or(0) .max(10); // "Confidence" header - // Calculate pattern width based on terminal width + // Calculate pattern width based on terminal width let reserved_width = max_name_width + max_id_width + max_conf_width + 10; let pattern_width = term_width.saturating_sub(reserved_width); // Format pattern on a single line diff --git a/src/matcher/base64_decode.rs b/src/matcher/base64_decode.rs index 0ee1f26..8145155 100644 --- a/src/matcher/base64_decode.rs +++ b/src/matcher/base64_decode.rs @@ -1,2 +1,2 @@ // Re-export from the canonical implementation in kingfisher-scanner. -pub use kingfisher_scanner::primitives::{get_base64_strings, DecodedData}; +pub use kingfisher_scanner::primitives::{DecodedData, get_base64_strings}; diff --git a/src/matcher/captures.rs b/src/matcher/captures.rs index 3e4d102..d9279be 100644 --- a/src/matcher/captures.rs +++ b/src/matcher/captures.rs @@ -1,9 +1,9 @@ use bstr::BString; use regex::bytes::Regex; use schemars::{ + JsonSchema, r#gen::SchemaGenerator, schema::{ArrayValidation, InstanceType, Schema}, - JsonSchema, }; use serde::{Deserialize, Serialize}; use smallvec::SmallVec; diff --git a/src/matcher/conversion.rs b/src/matcher/conversion.rs index 551119c..a82409f 100644 --- a/src/matcher/conversion.rs +++ b/src/matcher/conversion.rs @@ -12,7 +12,7 @@ use crate::{ validation_body::{self, ValidationResponseBody}, }; -use super::{captures::SerializableCaptures, BlobMatch}; +use super::{BlobMatch, captures::SerializableCaptures}; use kingfisher_scanner::primitives::compute_finding_fingerprint; @@ -224,7 +224,7 @@ impl Match { let mut num = xxh3_64(&buffer); // Ensure the number is positive and within i64 range num &= 0x7FFF_FFFF_FFFF_FFFF; // Clear the sign bit to make it positive - // Convert to string + // Convert to string num.to_string() } } diff --git a/src/matcher/filter.rs b/src/matcher/filter.rs index 7964ccc..66d2eac 100644 --- a/src/matcher/filter.rs +++ b/src/matcher/filter.rs @@ -18,9 +18,9 @@ use crate::{ }; use super::{ + BlobMatch, captures::SerializableCaptures, dedup::{compute_match_key, record_match}, - BlobMatch, }; // Re-use the canonical secret capture selection from kingfisher-scanner. diff --git a/src/matcher/mod.rs b/src/matcher/mod.rs index c23a5dd..30146da 100644 --- a/src/matcher/mod.rs +++ b/src/matcher/mod.rs @@ -6,7 +6,7 @@ mod filter; mod fingerprint; // Re-export public API -pub use base64_decode::{get_base64_strings, DecodedData}; +pub use base64_decode::{DecodedData, get_base64_strings}; pub use captures::{Group, Groups, SerializableCapture, SerializableCaptures}; pub use conversion::{Match, MatcherStats, OwnedBlobMatch}; pub use fingerprint::compute_finding_fingerprint; @@ -39,10 +39,10 @@ const MAX_CHUNK_SIZE: usize = 1 << 30; // 1 GiB per scan segment const CHUNK_OVERLAP: usize = 64 * 1024; // 64 KiB overlap to catch boundary matches const RAW_MATCH_LOOKBACK: usize = 4 * 1024; // Re-scan a bounded suffix ending at the raw match. const BASE64_SCAN_LIMIT: usize = 64 * 1024 * 1024; // skip expensive Base64 pass on huge blobs - // The old tree-sitter limit was 128 KiB due to full-AST parsing cost. - // The lightweight regex-based lexer is O(n) line-by-line, so we can afford - // a much higher ceiling. We still cap it to avoid spending time on huge - // generated/minified blobs where context verification adds little value. +// The old tree-sitter limit was 128 KiB due to full-AST parsing cost. +// The lightweight regex-based lexer is O(n) line-by-line, so we can afford +// a much higher ceiling. We still cap it to avoid spending time on huge +// generated/minified blobs where context verification adds little value. const CONTEXT_VERIFIER_MAX_LIMIT: usize = 2 * 1024 * 1024; // verify code context on blobs <= 2 MiB const CONTEXT_VERIFIER_MIN_LIMIT: usize = 0; // allow context verification starting at 0 bytes @@ -186,11 +186,7 @@ impl<'a> Matcher<'a> { let raw_matches_scratch = Vec::new(); let user_data = UserData { raw_matches_scratch, input_len: 0 }; let profiler = shared_profiler.or_else(|| { - if enable_profiling { - Some(Arc::new(ConcurrentRuleProfiler::new())) - } else { - None - } + if enable_profiling { Some(Arc::new(ConcurrentRuleProfiler::new())) } else { None } }); Ok(Matcher { scanner_pool, @@ -757,10 +753,14 @@ mod test { let matches = match matcher.scan_blob(&blob, &origin, None, false, false, false)? { ScanResult::New(matches) => matches, ScanResult::SeenWithMatches => { - panic!("unexpected scan result: blob should not be considered previously seen with matches") + panic!( + "unexpected scan result: blob should not be considered previously seen with matches" + ) } ScanResult::SeenSansMatches => { - panic!("unexpected scan result: blob should not be considered previously seen without matches") + panic!( + "unexpected scan result: blob should not be considered previously seen without matches" + ) } }; @@ -1223,8 +1223,8 @@ line2 } #[test] - fn assignment_style_context_rule_survives_when_context_verification_is_unavailable( - ) -> Result<()> { + fn assignment_style_context_rule_survives_when_context_verification_is_unavailable() + -> Result<()> { let token = "xcexacEQFtULkSTDCXejdWy5ew8NyU9QJoip5a97TE7A"; let rule = Rule::new(RuleSyntax { id: "kingfisher.livekit.2".into(), @@ -1265,8 +1265,8 @@ line2 } #[test] - fn depends_on_assignment_style_rule_survives_when_context_verification_is_unavailable( - ) -> Result<()> { + fn depends_on_assignment_style_rule_survives_when_context_verification_is_unavailable() + -> Result<()> { use crate::rules::rule::DependsOnRule; let token = "xcexacEQFtULkSTDCXejdWy5ew8NyU9QJoip5a97TE7A"; diff --git a/src/origin.rs b/src/origin.rs index b898f81..688cc1a 100644 --- a/src/origin.rs +++ b/src/origin.rs @@ -3,5 +3,5 @@ //! This module re-exports types from [`kingfisher_core::origin`]. pub use kingfisher_core::origin::{ - get_repo_url, CommitOrigin, ExtendedOrigin, FileOrigin, GitRepoOrigin, Origin, OriginSet, + CommitOrigin, ExtendedOrigin, FileOrigin, GitRepoOrigin, Origin, OriginSet, get_repo_url, }; diff --git a/src/parser/css.rs b/src/parser/css.rs index 34245d5..dd0616b 100644 --- a/src/parser/css.rs +++ b/src/parser/css.rs @@ -2,8 +2,8 @@ use std::cell::Cell; use anyhow::Result; use cssparser::{ - parse_important, AtRuleParser, CowRcStr, DeclarationParser, ParseError, Parser, ParserInput, - ParserState, RuleBodyItemParser, RuleBodyParser, StyleSheetParser, ToCss, Token, + AtRuleParser, CowRcStr, DeclarationParser, ParseError, Parser, ParserInput, ParserState, + RuleBodyItemParser, RuleBodyParser, StyleSheetParser, ToCss, Token, parse_important, }; pub(super) fn stream_context_candidates(source: &[u8], sink: &mut F) -> Result<()> diff --git a/src/parser/html.rs b/src/parser/html.rs index f0ad094..0b3cab3 100644 --- a/src/parser/html.rs +++ b/src/parser/html.rs @@ -1,7 +1,7 @@ use anyhow::Result; use tl::{HTMLTag, Node, Parser, ParserOptions}; -use super::{css, lexer, Language}; +use super::{Language, css, lexer}; pub(super) fn stream_context_candidates(source: &[u8], sink: &mut F) -> Result<()> where diff --git a/src/parser/lexer.rs b/src/parser/lexer.rs index 5568b45..1106323 100644 --- a/src/parser/lexer.rs +++ b/src/parser/lexer.rs @@ -528,11 +528,7 @@ where return Flow::Continue; } let candidate = format!("{key} = {value}"); - if sink(&candidate) { - Flow::Continue - } else { - Flow::Break - } + if sink(&candidate) { Flow::Continue } else { Flow::Break } } fn normalize_key(key: &str, keep_full_key: bool) -> String { diff --git a/src/pyc.rs b/src/pyc.rs index 116e615..1b25a93 100644 --- a/src/pyc.rs +++ b/src/pyc.rs @@ -1,7 +1,7 @@ use std::io::{self, Cursor, Read}; use std::path::Path; -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result, bail}; use tracing::debug; const MAX_RECURSION_DEPTH: usize = 256; @@ -503,7 +503,7 @@ mod tests { buf.extend_from_slice(&marshal_small_tuple(&[])); // cellvars buf.extend_from_slice(&marshal_short_ascii("")); // filename buf.extend_from_slice(&marshal_short_ascii("")); // name - // firstlineno + // firstlineno buf.extend_from_slice(&1i32.to_le_bytes()); // 1 trailing object: lnotab buf.extend_from_slice(&marshal_string(b"")); @@ -722,7 +722,7 @@ mod tests { buf.extend_from_slice(&marshal_short_ascii("")); // filename buf.extend_from_slice(&marshal_short_ascii("")); // name buf.extend_from_slice(&marshal_short_ascii("")); // qualname - // firstlineno + // firstlineno buf.extend_from_slice(&1i32.to_le_bytes()); // 2 trailing objects: linetable, exceptiontable buf.extend_from_slice(&marshal_string(b"")); diff --git a/src/reporter.rs b/src/reporter.rs index 16d1671..b76c0ed 100644 --- a/src/reporter.rs +++ b/src/reporter.rs @@ -7,7 +7,7 @@ use std::{ use anyhow::Result; use chrono::{Local, Utc}; use http::StatusCode; -use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS}; +use percent_encoding::{AsciiSet, CONTROLS, utf8_percent_encode}; use schemars::JsonSchema; use serde::Serialize; use url::Url; @@ -21,10 +21,10 @@ use crate::{ cli, cli::global::GlobalArgs, finding_data, findings_store, - matcher::{compute_finding_fingerprint, Match}, + matcher::{Match, compute_finding_fingerprint}, origin::{Origin, OriginSet}, - rules::rule::Confidence, rules::Revocation, + rules::rule::Confidence, template_vars::extract_template_vars, validation_body::{self, ValidationResponseBody}, }; @@ -42,7 +42,7 @@ use styles::{StyledObject, Styles}; use crate::{ cli::commands::output::ReportOutputFormat, location::SourceSpan, - origin::{get_repo_url, GitRepoOrigin}, + origin::{GitRepoOrigin, get_repo_url}, }; /// Shell-escape a string for safe command-line usage using single quotes. @@ -244,11 +244,7 @@ fn build_var_args( } } - if var_args.is_empty() { - String::new() - } else { - format!("{} ", var_args.join(" ")) - } + if var_args.is_empty() { String::new() } else { format!("{} ", var_args.join(" ")) } } /// Generate a kingfisher revoke command for an active credential if the rule supports revocation. @@ -763,11 +759,7 @@ impl DetailsReporter { } } else { // When not filtering by only_valid, use visibility if desired. - if filter_visible { - match_item.visible - } else { - true - } + if filter_visible { match_item.visible } else { true } } }) .map(|msg| { @@ -1105,11 +1097,7 @@ impl DetailsReporter { } fn non_empty_string(value: String) -> Option { - if value.trim().is_empty() { - None - } else { - Some(value) - } + if value.trim().is_empty() { None } else { Some(value) } } pub fn build_finding_records( @@ -1323,11 +1311,7 @@ fn format_resource(resource: &ResourceExposure) -> String { } let resource_type = resource.resource_type.trim(); - if resource_type.is_empty() { - name.to_string() - } else { - format!("{}:{}", resource_type, name) - } + if resource_type.is_empty() { name.to_string() } else { format!("{}:{}", resource_type, name) } } /// A trait for things that can be output as a document. /// @@ -1579,7 +1563,7 @@ mod tests { origin::{Origin, OriginSet}, rules::rule::{Confidence, Rule, RuleSyntax}, }; - use gix::{date::Time, ObjectId}; + use gix::{ObjectId, date::Time}; use smallvec::SmallVec; use std::collections::BTreeMap; use std::path::PathBuf; diff --git a/src/reporter/toon_format.rs b/src/reporter/toon_format.rs index 42951e7..d69a09f 100644 --- a/src/reporter/toon_format.rs +++ b/src/reporter/toon_format.rs @@ -85,11 +85,7 @@ fn json_string(value: Option<&serde_json::Value>, path: &[&str]) -> Option Option { let trimmed = value.trim(); - if trimmed.is_empty() { - None - } else { - Some(trimmed.to_string()) - } + if trimmed.is_empty() { None } else { Some(trimmed.to_string()) } } impl DetailsReporter { diff --git a/src/rule_loader.rs b/src/rule_loader.rs index 78e2e69..7cbce8c 100644 --- a/src/rule_loader.rs +++ b/src/rule_loader.rs @@ -3,7 +3,7 @@ use std::{ path::{Path, PathBuf}, }; -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result, bail}; use thiserror::Error; use tracing::{debug, error, info, trace}; @@ -12,8 +12,8 @@ use crate::{ cli::commands::rules::RuleSpecifierArgs, defaults::get_builtin_rules, rules::{ - rule::{Confidence, Rule}, Rules, + rule::{Confidence, Rule}, }, util::Counted, }; diff --git a/src/rules.rs b/src/rules.rs index 85ff0f4..de39ef9 100644 --- a/src/rules.rs +++ b/src/rules.rs @@ -13,6 +13,6 @@ pub use kingfisher_rules::rules::{Rules, RulesError}; pub use kingfisher_rules::{ ChecksumActual, ChecksumRequirement, Confidence, DependsOnRule, GrpcRequest, GrpcValidation, HttpRequest, HttpValidation, MultipartConfig, MultipartPart, PatternRequirementContext, - PatternRequirements, PatternValidationResult, ReportResponseData, ResponseMatcher, Rule, - RuleSyntax, Validation, RULE_COMMENTS_PATTERN, + PatternRequirements, PatternValidationResult, RULE_COMMENTS_PATTERN, ReportResponseData, + ResponseMatcher, Rule, RuleSyntax, Validation, }; diff --git a/src/rules_database.rs b/src/rules_database.rs index c37b0f4..73db985 100644 --- a/src/rules_database.rs +++ b/src/rules_database.rs @@ -3,5 +3,5 @@ //! This module re-exports types from [`kingfisher_rules::rules_database`]. pub use kingfisher_rules::rules_database::{ - format_regex_pattern, RuleDetectionProfileKind, RuleMatchProfile, RulesDatabase, + RuleDetectionProfileKind, RuleMatchProfile, RulesDatabase, format_regex_pattern, }; diff --git a/src/s3.rs b/src/s3.rs index 39ff060..4b934c9 100644 --- a/src/s3.rs +++ b/src/s3.rs @@ -1,10 +1,10 @@ use anyhow::{Context, Result}; -use aws_config::{defaults, meta::region::RegionProviderChain, BehaviorVersion}; +use aws_config::{BehaviorVersion, defaults, meta::region::RegionProviderChain}; use aws_credential_types::Credentials; use aws_sdk_s3::{ + Client, error::ProvideErrorMetadata, // for .code() operation::list_objects_v2::ListObjectsV2Error, // modeled service error - Client, }; use aws_types::region::Region; use reqwest; // HTTP client for HEAD fallback diff --git a/src/safe_list.rs b/src/safe_list.rs index ee6986b..d9e41d1 100644 --- a/src/safe_list.rs +++ b/src/safe_list.rs @@ -122,7 +122,9 @@ static SAFE_LIST_FILTER_RULES: LazyLock> = LazyLock::new(|| { }, SafeRule { description: "URL with basic auth to host ending in example/test (placeholder)", - regex: compile(r"(?i)\b((?:https?:)?//[^:@]{3,50}:[^:@]{3,50}@[\w.]{0,16}(?:example|test))"), + regex: compile( + r"(?i)\b((?:https?:)?//[^:@]{3,50}:[^:@]{3,50}@[\w.]{0,16}(?:example|test))", + ), }, SafeRule { description: "Assignment ending with SECRETMANAGER (explicit placeholder)", diff --git a/src/scanner/docker.rs b/src/scanner/docker.rs index 43d47f0..278996f 100644 --- a/src/scanner/docker.rs +++ b/src/scanner/docker.rs @@ -5,12 +5,12 @@ use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; use std::time::Duration; -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result, anyhow}; use base64::Engine; use indicatif::{ProgressBar, ProgressStyle}; -use oci_client::client::{linux_amd64_resolver, Client, ClientConfig}; -use oci_client::secrets::RegistryAuth; use oci_client::Reference; +use oci_client::client::{Client, ClientConfig, linux_amd64_resolver}; +use oci_client::secrets::RegistryAuth; use serde_json::Value; use sha2::{Digest, Sha256}; use tracing::debug; diff --git a/src/scanner/enumerate.rs b/src/scanner/enumerate.rs index feb3959..c9aafc8 100644 --- a/src/scanner/enumerate.rs +++ b/src/scanner/enumerate.rs @@ -3,16 +3,16 @@ use std::{ path::Path, process::Command, sync::{ - atomic::{AtomicBool, Ordering}, Arc, Mutex, + atomic::{AtomicBool, Ordering}, }, time::{Duration, Instant as StdInstant, Instant}, }; -use anyhow::{anyhow, bail, Context, Result}; -use base64::{engine::general_purpose::STANDARD, Engine}; +use anyhow::{Context, Result, anyhow, bail}; +use base64::{Engine, engine::general_purpose::STANDARD}; use bstr::{BString, ByteSlice}; -use gix::{object::tree::diff::ChangeDetached, object::tree::EntryKind, Repository as GixRepo}; +use gix::{Repository as GixRepo, object::tree::EntryKind, object::tree::diff::ChangeDetached}; use indicatif::{ProgressBar, ProgressStyle}; use rayon::{ iter::plumbing::Folder, @@ -24,10 +24,13 @@ use tracing::{debug, error}; use smallvec::smallvec; use crate::{ + DirectoryResult, EnumeratorConfig, EnumeratorFileResult, FileResult, FilesystemEnumerator, + FoundInput, GitDiffConfig, GitRepoEnumerator, GitRepoResult, GitRepoWithMetadataEnumerator, + PathBuf, binary::is_binary, blob::{Blob, BlobAppearance, BlobId, BlobIdMap}, cli::commands::{github::GitHistoryMode, scan}, - decompress::{decompress_file_to_temp, CompressedContent}, + decompress::{CompressedContent, decompress_file_to_temp}, findings_store, git_commit_metadata::CommitMetadata, git_repo_enumerator::{GitBlobMetadata, GitBlobSource, MIN_SCANNABLE_BLOB_SIZE}, @@ -44,9 +47,6 @@ use crate::{ }, scanner_pool::ScannerPool, sqlite::extract_sqlite_contents, - DirectoryResult, EnumeratorConfig, EnumeratorFileResult, FileResult, FilesystemEnumerator, - FoundInput, GitDiffConfig, GitRepoEnumerator, GitRepoResult, GitRepoWithMetadataEnumerator, - PathBuf, }; type OwnedBlob = Blob<'static>; @@ -1081,11 +1081,7 @@ fn run_git_command(path: &Path, args: &[&str], bubble_up_error: bool) -> Result< } let stdout = String::from_utf8_lossy(&output.stdout).trim().to_string(); - if stdout.is_empty() { - Ok(None) - } else { - Ok(Some(stdout)) - } + if stdout.is_empty() { Ok(None) } else { Ok(Some(stdout)) } } fn resolve_diff_ref<'repo>( @@ -1173,8 +1169,8 @@ mod tests { use std::path::Path; use super::{ - enumerate_git_diff_repo, reference_candidates, FileResult, GitBlobSource, GitDiffConfig, - ParallelBlobIterator, + FileResult, GitBlobSource, GitDiffConfig, ParallelBlobIterator, enumerate_git_diff_repo, + reference_candidates, }; use anyhow::Result; use bstr::ByteSlice; diff --git a/src/scanner/mod.rs b/src/scanner/mod.rs index 4a819f1..918136e 100644 --- a/src/scanner/mod.rs +++ b/src/scanner/mod.rs @@ -6,7 +6,7 @@ pub(crate) use repos::{ enumerate_github_repos, enumerate_huggingface_repos, }; pub use runner::{load_and_record_rules, run_async_scan, run_scan}; -pub(crate) use validation::{run_secret_validation, AccessMapCollector}; +pub(crate) use validation::{AccessMapCollector, run_secret_validation}; mod docker; mod enumerate; diff --git a/src/scanner/processing.rs b/src/scanner/processing.rs index 7b572e7..19b1c5e 100644 --- a/src/scanner/processing.rs +++ b/src/scanner/processing.rs @@ -3,13 +3,13 @@ use tokio::time::Instant; use tracing::{debug_span, trace}; use crate::{ + Path, blob::{Blob, BlobMetadata}, content_type::ContentInspector, location::LocationMapping, - matcher::{should_attempt_context_verification, Match, Matcher, OwnedBlobMatch, ScanResult}, + matcher::{Match, Matcher, OwnedBlobMatch, ScanResult, should_attempt_context_verification}, origin::{Origin, OriginSet}, scanner::repos::DatastoreMessage, - Path, }; const LOCATION_LIMIT_BYTES: usize = 256 * 1024 * 1024; diff --git a/src/scanner/repos.rs b/src/scanner/repos.rs index e975914..d58e050 100644 --- a/src/scanner/repos.rs +++ b/src/scanner/repos.rs @@ -13,7 +13,7 @@ use url::Url; use crate::blob::BlobIdMap; use crate::{ - azure, bitbucket, + PathBuf, azure, bitbucket, blob::BlobMetadata, cli::{ commands::{github::GitCloneMode, github::GitHistoryMode, scan}, @@ -29,7 +29,7 @@ use crate::{ s3, scanner::processing::BlobProcessor, scanner_pool::ScannerPool, - slack, teams, PathBuf, + slack, teams, }; pub type DatastoreMessage = (OriginSet, BlobMetadata, Vec<(Option, Match)>); diff --git a/src/scanner/runner.rs b/src/scanner/runner.rs index 65b229a..4fde808 100644 --- a/src/scanner/runner.rs +++ b/src/scanner/runner.rs @@ -2,12 +2,12 @@ use std::{ fs, path::PathBuf, sync::{ - atomic::{AtomicBool, Ordering}, Arc, Mutex, + atomic::{AtomicBool, Ordering}, }, }; -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result, bail}; use crossbeam_channel; use crossbeam_skiplist::SkipMap; use indicatif::ProgressBar; @@ -30,8 +30,9 @@ use crate::{ rules_database::RulesDatabase, safe_list, scanner::{ - clone_or_update_git_repos_streaming, enumerate_azure_repos, enumerate_bitbucket_repos, - enumerate_filesystem_inputs, enumerate_github_repos, enumerate_huggingface_repos, + AccessMapCollector, clone_or_update_git_repos_streaming, enumerate_azure_repos, + enumerate_bitbucket_repos, enumerate_filesystem_inputs, enumerate_github_repos, + enumerate_huggingface_repos, repos::{ enumerate_gitea_repos, enumerate_gitlab_repos, fetch_confluence_pages, fetch_gcs_objects, fetch_git_host_artifacts, fetch_jira_issues, fetch_s3_objects, @@ -39,7 +40,6 @@ use crate::{ }, run_secret_validation, save_docker_images, summary::{compute_scan_totals, print_scan_summary}, - AccessMapCollector, }, util::set_redaction_enabled, validation::CachedResponse, @@ -504,11 +504,7 @@ fn apply_baseline_if_configured( } fn effective_max_validation_body_len(args: &scan::ScanArgs) -> usize { - if args.full_validation_response { - 0 - } else { - args.max_validation_response_length - } + if args.full_validation_response { 0 } else { args.max_validation_response_length } } /// Runs the validation phase on matches in the datastore. @@ -883,11 +879,14 @@ async fn run_parallel_scan( aggregate_summary, ); - match access_map_collector.take() { Some(collector) => { - finalize_access_map(datastore, collector, args).await?; - } _ => { - maybe_hint_access_map(datastore, args); - }} + match access_map_collector.take() { + Some(collector) => { + finalize_access_map(datastore, collector, args).await?; + } + _ => { + maybe_hint_access_map(datastore, args); + } + } Ok(()) } @@ -903,7 +902,9 @@ async fn finalize_access_map( let requests = collector.into_requests(); if requests.is_empty() { - debug!("access-map enabled but no validated AWS, GCP, or Azure credentials were collected; skipping report output"); + debug!( + "access-map enabled but no validated AWS, GCP, or Azure credentials were collected; skipping report output" + ); let mut ds = datastore.lock().unwrap(); ds.set_access_map_results(Vec::new()); return Ok(()); @@ -976,8 +977,8 @@ fn maybe_hint_access_map(datastore: &Arc>, args: &scan::Sca if has_mappable_identities { info!( - "Access map not requested. Rerun with --access-map to include resource-level permissions, if authorized." - ); + "Access map not requested. Rerun with --access-map to include resource-level permissions, if authorized." + ); } } diff --git a/src/scanner/summary.rs b/src/scanner/summary.rs index d02ca02..8d4c164 100644 --- a/src/scanner/summary.rs +++ b/src/scanner/summary.rs @@ -76,11 +76,7 @@ pub fn compute_scan_totals( let total_findings = if args.no_dedup { all_matches.iter().fold(0, |count, msg| { let (origin_set, _, match_item) = &**msg; - if match_item.validation_success { - count + origin_set.len() - } else { - count + 1 - } + if match_item.validation_success { count + origin_set.len() } else { count + 1 } }) } else { ds.get_num_matches() diff --git a/src/scanner/validation.rs b/src/scanner/validation.rs index 74b9c66..0e5d135 100644 --- a/src/scanner/validation.rs +++ b/src/scanner/validation.rs @@ -1,7 +1,7 @@ use std::{ sync::{ - atomic::{AtomicUsize, Ordering}, Arc, Mutex, + atomic::{AtomicUsize, Ordering}, }, time::{Duration, Instant}, }; @@ -9,7 +9,7 @@ use std::{ use anyhow::Result; use crossbeam_skiplist::SkipMap; use dashmap::DashMap; -use futures::{stream, FutureExt, StreamExt}; +use futures::{FutureExt, StreamExt, stream}; use indicatif::{ProgressBar, ProgressStyle}; use liquid::Parser; use reqwest::StatusCode; @@ -25,7 +25,7 @@ use crate::{ matcher::OwnedBlobMatch, rules::rule::Validation, validation::{ - collect_variables_and_dependencies, utils, validate_single_match, CachedResponse, + CachedResponse, collect_variables_and_dependencies, utils, validate_single_match, }, validation_body, validation_rate_limit::ValidationRateLimiter, @@ -861,7 +861,7 @@ async fn validate_single( let first = in_progress.insert(cache_key.clone(), ()).is_none(); if !first { notify.notified().await; // suspend with zero polling - // cached result now present + // cached result now present if let Some(cached) = cache.get(&cache_key) { om.validation_success = cached.is_valid; om.validation_response_body = cached.body.clone(); diff --git a/src/snippet.rs b/src/snippet.rs index b76f742..f84de08 100644 --- a/src/snippet.rs +++ b/src/snippet.rs @@ -3,9 +3,9 @@ use std::{ fmt::{Display, Formatter}, }; -use base64::{engine::general_purpose, Engine as _}; +use base64::{Engine as _, engine::general_purpose}; use bstr::{BString, ByteSlice}; -use schemars::{r#gen::SchemaGenerator, schema::Schema, JsonSchema}; +use schemars::{JsonSchema, r#gen::SchemaGenerator, schema::Schema}; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] pub struct Base64BString(#[serde(with = "Base64BString")] pub BString); diff --git a/src/sqlite.rs b/src/sqlite.rs index ddcaf01..a300ea8 100644 --- a/src/sqlite.rs +++ b/src/sqlite.rs @@ -1,7 +1,7 @@ use std::fmt::Write as FmtWrite; use std::path::Path; -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result, bail}; use rusqlite::{Connection, OpenFlags}; use tracing::debug; diff --git a/src/teams.rs b/src/teams.rs index 4b9d222..891c025 100644 --- a/src/teams.rs +++ b/src/teams.rs @@ -71,11 +71,7 @@ fn sanitize_filename_component(value: &str) -> String { .collect(); let trimmed = sanitized.trim_matches([' ', '.']); - if trimmed.is_empty() { - "unknown".to_string() - } else { - trimmed.to_string() - } + if trimmed.is_empty() { "unknown".to_string() } else { trimmed.to_string() } } pub async fn search_messages( diff --git a/src/util.rs b/src/util.rs index fe86cf6..7c8e233 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,7 +1,7 @@ use std::{ borrow::Cow, fs::File, - io::{stdin, stdout, BufReader, BufWriter}, + io::{BufReader, BufWriter, stdin, stdout}, path::Path, sync::atomic::{AtomicBool, Ordering}, }; @@ -59,11 +59,7 @@ pub fn redaction_enabled() -> bool { /// Returns either the original value or a redacted placeholder depending on /// the current redaction setting. pub fn display_value(value: &'static str) -> Cow<'static, str> { - if redaction_enabled() { - Cow::Owned(redact_value(value)) - } else { - Cow::Borrowed(value) - } + if redaction_enabled() { Cow::Owned(redact_value(value)) } else { Cow::Borrowed(value) } } // Generate a random salt (16-character alphanumeric string) fn generate_salt() -> String { diff --git a/src/validation.rs b/src/validation.rs index dbe536f..2418663 100644 --- a/src/validation.rs +++ b/src/validation.rs @@ -14,7 +14,7 @@ use futures::FutureExt; use http::StatusCode; use liquid::Object; use liquid_core::{Value, ValueView}; -use reqwest::{header, header::HeaderValue, multipart, Client, Url}; +use reqwest::{Client, Url, header, header::HeaderValue, multipart}; use rustc_hash::FxHashMap; use tokio::{sync::Notify, time}; use tracing::{debug, trace}; @@ -33,11 +33,11 @@ use crate::validation_rate_limit::should_rate_limit_validation; // Re-export TlsMode from kingfisher_rules for use in client_for_rule pub use kingfisher_rules::TlsMode as RuleTlsMode; +pub use kingfisher_scanner::validation::CachedResponse; pub use kingfisher_scanner::validation::aws; pub use kingfisher_scanner::validation::http_validation as httpvalidation; pub use kingfisher_scanner::validation::mysql::validate_mysql; pub use kingfisher_scanner::validation::postgres::validate_postgres; -pub use kingfisher_scanner::validation::CachedResponse; pub use kingfisher_scanner::validation::{ azure, coinbase, gcp, jdbc, jwt, mongodb, mysql, postgres, }; @@ -232,11 +232,7 @@ impl ValidationClients { TlsMode::Lax => { // Convert rule's TlsMode to CLI TlsMode for comparison let rule_wants_lax = matches!(rule_tls_mode, Some(kingfisher_rules::TlsMode::Lax)); - if rule_wants_lax { - &self.lax - } else { - &self.strict - } + if rule_wants_lax { &self.lax } else { &self.strict } } TlsMode::Strict => &self.strict, } @@ -841,7 +837,10 @@ async fn validate_http( ) { let std_headers = [ (header::USER_AGENT, GLOBAL_USER_AGENT.as_str()), - (header::ACCEPT , "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"), + ( + header::ACCEPT, + "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + ), (header::ACCEPT_LANGUAGE, "en-US,en;q=0.5"), (header::ACCEPT_ENCODING, "gzip, deflate, br"), (header::CONNECTION, "keep-alive"), diff --git a/src/validation_body.rs b/src/validation_body.rs index e7fcf0e..a7617d1 100644 --- a/src/validation_body.rs +++ b/src/validation_body.rs @@ -1,4 +1,4 @@ -use schemars::{r#gen::SchemaGenerator, schema::Schema, JsonSchema}; +use schemars::{JsonSchema, r#gen::SchemaGenerator, schema::Schema}; use serde::{Deserialize, Deserializer, Serializer}; use std::borrow::Cow; @@ -9,11 +9,7 @@ pub type ValidationResponseBody = Option>; #[inline] pub fn from_string(body: impl Into) -> ValidationResponseBody { let body = body.into(); - if body.is_empty() { - None - } else { - Some(body.into_boxed_str()) - } + if body.is_empty() { None } else { Some(body.into_boxed_str()) } } #[inline] diff --git a/src/validation_rate_limit.rs b/src/validation_rate_limit.rs index e7413cb..5e4478e 100644 --- a/src/validation_rate_limit.rs +++ b/src/validation_rate_limit.rs @@ -1,10 +1,10 @@ use std::{sync::Arc, time::Duration}; -use anyhow::{bail, Result}; +use anyhow::{Result, bail}; use dashmap::DashMap; use tokio::{ sync::Mutex, - time::{sleep_until, Instant}, + time::{Instant, sleep_until}, }; use crate::rules::rule::Validation; diff --git a/tests/fingerprint_dedup.rs b/tests/fingerprint_dedup.rs index ce123cf..f10557a 100644 --- a/tests/fingerprint_dedup.rs +++ b/tests/fingerprint_dedup.rs @@ -5,7 +5,7 @@ use std::{ }; use anyhow::Result; -use gix::{date, ObjectId}; +use gix::{ObjectId, date}; use kingfisher::{ blob::{BlobId, BlobMetadata}, findings_store::FindingsStore, @@ -13,7 +13,7 @@ use kingfisher::{ location::{Location, OffsetSpan, SourcePoint, SourceSpan}, matcher::{Match, SerializableCapture, SerializableCaptures}, origin::{Origin, OriginSet}, - reporter::{styles::Styles, DetailsReporter, ReportMatch}, + reporter::{DetailsReporter, ReportMatch, styles::Styles}, rules::rule::{Confidence, Rule, RuleSyntax}, util::intern, }; diff --git a/tests/int_allowlist.rs b/tests/int_allowlist.rs index f40ce8e..e4fcd6b 100644 --- a/tests/int_allowlist.rs +++ b/tests/int_allowlist.rs @@ -6,6 +6,7 @@ use std::{ use anyhow::Result; use kingfisher::{ cli::{ + GlobalArgs, commands::{ azure::AzureRepoType, bitbucket::{BitbucketAuthArgs, BitbucketRepoType}, @@ -18,7 +19,6 @@ use kingfisher::{ scan::{ConfidenceLevel, ScanArgs}, }, global::{Mode, TlsMode}, - GlobalArgs, }, findings_store::FindingsStore, rule_loader::RuleLoader, diff --git a/tests/int_bitbucket.rs b/tests/int_bitbucket.rs index 8fa00c0..e7133bf 100644 --- a/tests/int_bitbucket.rs +++ b/tests/int_bitbucket.rs @@ -6,6 +6,7 @@ use std::{ use anyhow::{Context, Result}; use kingfisher::{ cli::{ + GlobalArgs, commands::{ azure::AzureRepoType, bitbucket::{BitbucketAuthArgs, BitbucketRepoType}, @@ -18,7 +19,6 @@ use kingfisher::{ scan::{ConfidenceLevel, ScanArgs}, }, global::{Mode, TlsMode}, - GlobalArgs, }, findings_store::FindingsStore, git_url::GitUrl, diff --git a/tests/int_context_verification.rs b/tests/int_context_verification.rs index b1d8c3e..a26d484 100644 --- a/tests/int_context_verification.rs +++ b/tests/int_context_verification.rs @@ -113,9 +113,9 @@ fn scan_inputs_exclude_parser_fixture_directory() -> Result<()> { let inputs = scan_inputs_without_parser_fixtures()?; assert!(inputs.iter().all(|path| Path::new(path) != Path::new("testdata/parsers"))); - assert!(inputs - .iter() - .any(|path| Path::new(path) == Path::new("testdata/python_vulnerable.py"))); + assert!( + inputs.iter().any(|path| Path::new(path) == Path::new("testdata/python_vulnerable.py")) + ); Ok(()) } diff --git a/tests/int_dedup.rs b/tests/int_dedup.rs index 48ef032..32b0955 100644 --- a/tests/int_dedup.rs +++ b/tests/int_dedup.rs @@ -10,6 +10,7 @@ use std::{ use anyhow::Result; use kingfisher::{ cli::{ + GlobalArgs, commands::{ azure::AzureRepoType, bitbucket::{BitbucketAuthArgs, BitbucketRepoType}, @@ -22,7 +23,6 @@ use kingfisher::{ scan::{ConfidenceLevel, ScanArgs}, }, global::{Mode, TlsMode}, - GlobalArgs, }, findings_store::FindingsStore, rule_loader::RuleLoader, diff --git a/tests/int_github.rs b/tests/int_github.rs index 90f7883..b85c617 100644 --- a/tests/int_github.rs +++ b/tests/int_github.rs @@ -7,6 +7,7 @@ use std::{ use anyhow::{Context, Result}; use kingfisher::{ cli::{ + GlobalArgs, commands::{ azure::AzureRepoType, bitbucket::{BitbucketAuthArgs, BitbucketRepoType}, @@ -19,7 +20,6 @@ use kingfisher::{ scan::{ConfidenceLevel, ScanArgs}, }, global::{Mode, TlsMode}, - GlobalArgs, }, findings_store::FindingsStore, git_url::GitUrl, diff --git a/tests/int_gitlab.rs b/tests/int_gitlab.rs index 19e3f46..b916cb5 100644 --- a/tests/int_gitlab.rs +++ b/tests/int_gitlab.rs @@ -7,6 +7,7 @@ use std::{ use anyhow::{Context, Result}; use kingfisher::{ cli::{ + GlobalArgs, commands::{ azure::AzureRepoType, bitbucket::{BitbucketAuthArgs, BitbucketRepoType}, @@ -19,7 +20,6 @@ use kingfisher::{ scan::{ConfidenceLevel, ScanArgs}, }, global::{Mode, TlsMode}, - GlobalArgs, }, findings_store::FindingsStore, git_url::GitUrl, diff --git a/tests/int_slack.rs b/tests/int_slack.rs index 78aaacf..4aa0f4e 100644 --- a/tests/int_slack.rs +++ b/tests/int_slack.rs @@ -3,6 +3,7 @@ use std::sync::{Arc, Mutex}; use anyhow::Result; use kingfisher::{ cli::{ + GlobalArgs, commands::{ azure::AzureRepoType, bitbucket::{BitbucketAuthArgs, BitbucketRepoType}, @@ -15,7 +16,6 @@ use kingfisher::{ scan::{ConfidenceLevel, ScanArgs}, }, global::{Mode, TlsMode}, - GlobalArgs, }, findings_store::FindingsStore, rule_loader::RuleLoader, @@ -26,8 +26,8 @@ use kingfisher::{ use tempfile::TempDir; use url::Url; use wiremock::{ - matchers::{method, path}, Mock, MockServer, ResponseTemplate, + matchers::{method, path}, }; struct TestContext { diff --git a/tests/int_teams.rs b/tests/int_teams.rs index 667449b..625ede8 100644 --- a/tests/int_teams.rs +++ b/tests/int_teams.rs @@ -3,6 +3,7 @@ use std::sync::{Arc, Mutex}; use anyhow::Result; use kingfisher::{ cli::{ + GlobalArgs, commands::{ azure::AzureRepoType, bitbucket::{BitbucketAuthArgs, BitbucketRepoType}, @@ -15,7 +16,6 @@ use kingfisher::{ scan::{ConfidenceLevel, ScanArgs}, }, global::{Mode, TlsMode}, - GlobalArgs, }, findings_store::FindingsStore, rule_loader::RuleLoader, @@ -26,8 +26,8 @@ use kingfisher::{ use tempfile::TempDir; use url::Url; use wiremock::{ - matchers::{method, path}, Mock, MockServer, ResponseTemplate, + matchers::{method, path}, }; #[tokio::test] diff --git a/tests/int_validation_cache.rs b/tests/int_validation_cache.rs index 5edad66..e4ade67 100644 --- a/tests/int_validation_cache.rs +++ b/tests/int_validation_cache.rs @@ -2,14 +2,15 @@ use std::{ fs, sync::{ - atomic::{AtomicUsize, Ordering}, Arc, Mutex, + atomic::{AtomicUsize, Ordering}, }, }; use anyhow::Result; use kingfisher::{ cli::{ + GlobalArgs, commands::{ azure::AzureRepoType, bitbucket::{BitbucketAuthArgs, BitbucketRepoType}, @@ -22,7 +23,6 @@ use kingfisher::{ scan::{ConfidenceLevel, ScanArgs}, }, global::{Mode, TlsMode}, - GlobalArgs, }, findings_store::FindingsStore, rule_loader::RuleLoader, @@ -33,8 +33,8 @@ use kingfisher::{ use tempfile::TempDir; use url::Url; use wiremock::{ - matchers::{method, path}, Mock, MockServer, Request, ResponseTemplate, + matchers::{method, path}, }; #[tokio::test] diff --git a/tests/int_vulnerable_files.rs b/tests/int_vulnerable_files.rs index 72ca410..f37eaf7 100644 --- a/tests/int_vulnerable_files.rs +++ b/tests/int_vulnerable_files.rs @@ -8,6 +8,7 @@ use std::{ use anyhow::{Context, Result}; use kingfisher::{ cli::{ + GlobalArgs, commands::{ azure::AzureRepoType, bitbucket::{BitbucketAuthArgs, BitbucketRepoType}, @@ -20,7 +21,6 @@ use kingfisher::{ scan::{ConfidenceLevel, ScanArgs}, }, global::{Mode, TlsMode}, - GlobalArgs, }, findings_store::FindingsStore, rule_loader::RuleLoader, diff --git a/tests/jdbc_rule.rs b/tests/jdbc_rule.rs index bb4fad0..8852413 100644 --- a/tests/jdbc_rule.rs +++ b/tests/jdbc_rule.rs @@ -1,6 +1,6 @@ use std::collections::BTreeSet; -use anyhow::{anyhow, Result}; +use anyhow::{Result, anyhow}; use kingfisher::{rules::rule::RuleSyntax, safe_list}; fn load_jdbc_rule() -> Result { diff --git a/tests/live_db_validation.rs b/tests/live_db_validation.rs index 78801cd..a83c727 100644 --- a/tests/live_db_validation.rs +++ b/tests/live_db_validation.rs @@ -6,12 +6,12 @@ use std::time::{Duration, Instant}; -use anyhow::{anyhow, Result}; +use anyhow::{Result, anyhow}; use kingfisher::validation::{validate_mysql, validate_postgres}; use testcontainers::{ + GenericImage, ImageExt, core::{IntoContainerPort, WaitFor}, runners::AsyncRunner, - GenericImage, ImageExt, }; use tokio::{net::TcpStream, time::sleep}; diff --git a/tests/pre_commit_installer.rs b/tests/pre_commit_installer.rs index 0474b9f..358920e 100644 --- a/tests/pre_commit_installer.rs +++ b/tests/pre_commit_installer.rs @@ -1,5 +1,5 @@ -use assert_cmd::assert::OutputAssertExt; use assert_cmd::Command; +use assert_cmd::assert::OutputAssertExt; use predicates::str::contains; use std::fs; use std::path::{Path, PathBuf}; diff --git a/tests/smoke_archive.rs b/tests/smoke_archive.rs index ebd524b..4d4f8c6 100644 --- a/tests/smoke_archive.rs +++ b/tests/smoke_archive.rs @@ -13,7 +13,7 @@ fn smoke_scan_tar_gz_archive() -> anyhow::Result<()> { { use std::fs::File; - use flate2::{write::GzEncoder, Compression}; + use flate2::{Compression, write::GzEncoder}; use tar::Builder; let f = File::create(&tar_gz)?; diff --git a/tests/smoke_branch.rs b/tests/smoke_branch.rs index 4634bfa..89bcf6e 100644 --- a/tests/smoke_branch.rs +++ b/tests/smoke_branch.rs @@ -10,9 +10,9 @@ use std::path::Path; use anyhow::Result; use assert_cmd::Command; -use git2::{build::CheckoutBuilder, BranchType, Repository, Signature}; +use git2::{BranchType, Repository, Signature, build::CheckoutBuilder}; use predicates::{prelude::PredicateBooleanExt, str::contains}; -use tempfile::{tempdir, TempDir}; +use tempfile::{TempDir, tempdir}; const AWS_SECRET_VALUE: &str = "UpUbsQANRHLf2uuQ7QOlNXPbbtV5fmseW/GgTs5D"; const GCP_PRIVATE_KEY_VALUE: &str = "c4c474d61701fd6fd4191883b8fea9a8411bf771"; diff --git a/tests/smoke_update.rs b/tests/smoke_update.rs index 74b8b31..af542d7 100644 --- a/tests/smoke_update.rs +++ b/tests/smoke_update.rs @@ -1,8 +1,8 @@ use kingfisher::{cli::global::GlobalArgs, update::check_for_update}; use tokio; use wiremock::{ - matchers::{method, path}, Mock, MockServer, ResponseTemplate, + matchers::{method, path}, }; #[tokio::test] @@ -44,9 +44,11 @@ async fn detects_new_release() { .expect("blocking task panicked"); assert!(status.is_outdated); - assert!(status - .message - .as_deref() - .expect("update check should return a message") - .contains("99.999.0")); + assert!( + status + .message + .as_deref() + .expect("update check should return a message") + .contains("99.999.0") + ); }