Populate the finding path from git blob metadata so history-derived secrets display their file location instead of an empty path

This commit is contained in:
Mick Grove 2025-09-24 10:06:47 -07:00
commit 645bfa2e01
6 changed files with 42 additions and 48 deletions

View file

@ -20,6 +20,7 @@ if "%VCINSTALLDIR%"=="" (
echo VCINSTALLDIR not set - attempting auto-detection…
for %%P in (
"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC"
"C:\Program Files\Microsoft Visual Studio\2022\Community\VC"
"C:\Program Files\Microsoft Visual Studio\2022\Professional\VC"
"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC"
"C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC"

View file

@ -1,37 +0,0 @@
rules:
- name: OpenWeather Map API Key
id: kingfisher.openweather.1
pattern: |
(?xi)
(?:pyowm|openweather|\bowm\b)
(?:.|[\n\r]){0,64}?
\b
(
(?:
[a-z0-9]{32}
)
\b
|APPID=
(?:
[a-z0-9]{32}
)
)
\b
min_entropy: 3.5
examples:
- pyowm = '3k144a5af729351d0fc58bdrj9a21mkr'
- owm = '3k144a5af729351d0fc58bdrj9a21mkr'
- openweatherapikey=cd2b1d12d01ae2deffecfebafcc3c31d
- apikey=openweather:cd2b1d12d01ae2deffecfebafcc3c31d
validation:
type: Http
content:
request:
method: GET
response_matcher:
- report_response: true
- match_all_status: true
status:
- 200
type: StatusMatch
url: https://api.openweathermap.org/geo/1.0/reverse?lat=0&lon=0&limit=1&appid={{ TOKEN }}

View file

@ -32,7 +32,7 @@ rules:
- type: StatusMatch
status: [200]
- name: Travis CI Encrypted Variable
id: kingfisher.travisci.1
id: kingfisher.travisci.2
pattern: |
(?xis)
\b

View file

@ -64,8 +64,8 @@ impl TryFrom<Url> for GitUrl {
type Error = &'static str;
fn try_from(url: Url) -> Result<Self, Self::Error> {
// if url.scheme() != "https"
if url.host().is_none()
if (url.scheme() != "https" && url.scheme() != "http")
|| url.host().is_none()
|| !url.username().is_empty()
|| url.password().is_some()
|| url.query().is_some()
@ -104,11 +104,6 @@ mod test {
assert!(GitUrl::from_str("ssh://example.com/repo.git").is_err());
}
#[test]
fn bad_scheme_04() {
assert!(GitUrl::from_str("http://example.com/repo.git").is_err());
}
#[test]
fn bad_query_params() {
assert!(GitUrl::from_str("https://example.com/repo.git?admin=1").is_err());

View file

@ -428,10 +428,10 @@ impl DetailsReporter {
})
.next();
let file_path = rm
let mut file_path = rm
.origin
.iter()
.find_map(|origin| match origin {
.filter_map(|origin| match origin {
Origin::File(e) => {
if let Some(url) = self.repo_artifact_url(&e.path) {
Some(url)
@ -452,6 +452,7 @@ impl DetailsReporter {
Origin::GitRepo(e) => e.first_commit.as_ref().map(|c| c.blob_path.clone()),
Origin::Extended(e) => e.path().map(|p| p.display().to_string()),
})
.find(|path| !path.trim().is_empty())
.unwrap_or_else(|| {
rm.origin
.iter()
@ -459,6 +460,31 @@ impl DetailsReporter {
.unwrap_or_default()
});
// If the file path is still empty, and we have git blob metadata,
// try to reconstruct the path from the git object ID.
if file_path.is_empty() {
let blob_hex = rm.blob_metadata.id.hex();
if let Some(repo_origin) = rm.origin.iter().find_map(|origin| match origin {
Origin::GitRepo(e) => Some(e),
_ => None,
}) {
let (prefix, suffix) = blob_hex.split_at(2);
let repo_path = repo_origin.repo_path.as_ref();
let git_dir_objects = repo_path.join(".git").join("objects");
let objects_dir = if git_dir_objects.is_dir() {
git_dir_objects
} else {
repo_path.join("objects")
};
let fallback_path = objects_dir.join(prefix).join(suffix);
file_path = fallback_path.display().to_string();
}
if file_path.is_empty() {
file_path = format!("blob:{blob_hex}");
}
}
FindingReporterRecord {
rule: RuleMetadata {
name: rm.m.rule.name().to_string(),
@ -632,10 +658,12 @@ mod tests {
cli::commands::scan::{ConfidenceLevel, ScanArgs},
cli::commands::{
bitbucket::{BitbucketAuthArgs, BitbucketRepoType},
gitea::GiteaRepoType,
github::{GitCloneMode, GitHistoryMode, GitHubRepoType},
gitlab::GitLabRepoType,
rules::RuleSpecifierArgs,
},
git_commit_metadata::CommitMetadata,
location::{Location, OffsetSpan, SourcePoint, SourceSpan},
matcher::{SerializableCapture, SerializableCaptures},
origin::OriginSet,
@ -737,6 +765,12 @@ mod tests {
gitlab_api_url: Url::parse("https://gitlab.com/").unwrap(),
gitlab_repo_type: GitLabRepoType::All,
gitlab_include_subgroups: false,
gitea_user: Vec::new(),
gitea_organization: Vec::new(),
gitea_exclude: Vec::new(),
all_gitea_organizations: false,
gitea_api_url: Url::parse("https://gitea.com/api/v1/").unwrap(),
gitea_repo_type: GiteaRepoType::Source,
bitbucket_user: Vec::new(),
bitbucket_workspace: Vec::new(),
bitbucket_project: Vec::new(),
@ -779,6 +813,7 @@ mod tests {
rule_stats: false,
no_dedup: false,
redact: false,
no_base64: false,
git_repo_timeout: 1_800,
output_args: OutputArgs { output: None, format: ReportOutputFormat::Pretty },
baseline_file: None,

View file

@ -102,7 +102,7 @@ pub fn check_for_update(global_args: &GlobalArgs, base_url: Option<&str>) -> Opt
// ───────────── Case 1: running == latest ─────────────
if release.version == running_v {
let plain = format!("Kingfisher {running_v} is up to date");
info!("{}", styled_heading(&styles, plain.as_str()));
info!("{}", plain.as_str());
return Some(plain);
}