Ensuring temp files are cleaned up. Applying visual style to the update check output

This commit is contained in:
Mick Grove 2025-06-26 09:45:14 -07:00
commit d5f9d40027
5 changed files with 44 additions and 92 deletions

View file

@ -2,6 +2,10 @@
All notable changes to this project will be documented in this file.
## [1.15.0]
- Ensuring temp files are cleaned up
- Applying visual style to the update check output
## [1.14.0]
- Fixed several malformed rules
- Now validating that response_matcher is present in validation section of all rules

View file

@ -190,6 +190,8 @@ async fn async_main(args: CommandLineArgs) -> Result<()> {
let rules_db = Arc::new(load_and_record_rules(&scan_args, &datastore)?);
run_scan(&args.global_args, &scan_args, &rules_db, Arc::clone(&datastore)).await?;
let exit_code = determine_exit_code(&datastore);
temp_dir.close()?;
std::process::exit(exit_code);
}
Command::Rules(ref rule_args) => match &rule_args.command {

View file

@ -1,25 +1,32 @@
use std::{fs, io::ErrorKind, path::PathBuf};
use std::{
fs,
io::{ErrorKind, IsTerminal},
path::PathBuf,
};
use self_update::{backends::github::Update, cargo_crate_version, errors::Error as UpdError};
use tracing::{error, info, warn};
use crate::cli::global::GlobalArgs;
use crate::reporter::styles::Styles;
/// Return `true` when the canonical executable path lives inside a Homebrew Cellar.
/// Works for Intel macOS (/usr/local/Cellar), Apple-Silicon macOS (/opt/homebrew/Cellar)
/// Works for Intel macOS (/usr/local/Cellar), AppleSilicon macOS (/opt/homebrew/Cellar)
/// and Linuxbrew (~/.linuxbrew/Cellar).
fn installed_via_homebrew() -> bool {
fn canonical_exe() -> Option<PathBuf> {
std::env::current_exe().ok().and_then(|p| fs::canonicalize(p).ok())
}
canonical_exe().map(|p| p.components().any(|c| c.as_os_str() == "Cellar")).unwrap_or(false)
canonical_exe()
.map(|p| p.components().any(|c| c.as_os_str() == "Cellar"))
.unwrap_or(false)
}
/// Check GitHub for a newer Kingfisher release.
///
/// * `base_url` lets tests point at a mock server.
/// * Self-update is performed unless the user disabled it **or** the binary is a Homebrew install.
/// * Selfupdate is performed unless the user disabled it **or** the binary is a Homebrew install.
pub fn check_for_update(global_args: &GlobalArgs, base_url: Option<&str>) -> Option<String> {
if global_args.no_update_check {
return None;
@ -27,11 +34,19 @@ pub fn check_for_update(global_args: &GlobalArgs, base_url: Option<&str>) -> Opt
let is_brew = installed_via_homebrew();
if is_brew {
info!("Homebrew install detected will notify about updates but not self-update");
info!(
"Homebrew install detected will notify about updates but not selfupdate"
);
}
info!("Checking for updates…");
// -------------------------------------------------------------
// Prepare colour/style helper so every message looks consistent
// -------------------------------------------------------------
let use_color = std::io::stderr().is_terminal() && !global_args.quiet;
let styles = Styles::new(use_color);
let mut builder = Update::configure();
builder
.repo_owner("mongodb")
@ -54,15 +69,22 @@ pub fn check_for_update(global_args: &GlobalArgs, base_url: Option<&str>) -> Opt
return None;
};
// ----------------------------
// Already on the latest version
// ----------------------------
if release.version == cargo_crate_version!() {
let msg = format!("Kingfisher {} is up to date", release.version);
info!("{msg}");
return Some(msg);
let plain = format!("Kingfisher {} is up to date", release.version);
let styled = styles.style_finding_active_heading.apply_to(&plain);
info!("{}", styled);
return Some(plain);
}
// There is a newer release.
let msg = format!("New Kingfisher release {} available", release.version);
info!("{msg}");
// ----------------------------
// A newer version is available
// ----------------------------
let plain = format!("New Kingfisher release {} available", release.version);
let styled = styles.style_finding_active_heading.apply_to(&plain);
info!("{}", styled);
// Decide whether to perform the update in place.
if global_args.self_update && !is_brew {
@ -73,7 +95,7 @@ pub fn check_for_update(global_args: &GlobalArgs, base_url: Option<&str>) -> Opt
warn!(
"Cannot replace the current binary permission denied.\n\
If you installed via a package manager, run its upgrade command.\n\
Otherwise reinstall to a user-writable directory or re-run with sudo."
Otherwise reinstall to a userwritable directory or rerun with sudo."
);
}
_ => error!("Failed to update: {e}"),
@ -83,5 +105,5 @@ pub fn check_for_update(global_args: &GlobalArgs, base_url: Option<&str>) -> Opt
info!("Run `brew upgrade kingfisher` to install the new version.");
}
Some(msg)
Some(plain)
}

View file

@ -1,76 +0,0 @@
package main
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strconv"
regexp "github.com/wasilibs/go-re2"
)
func main() {
// fmt.Println(">> [*] Testing 'kingfisher local-git' functionality against owasp/wrongsecrets repo.")
// Remove the existing /tmp/wrongsecrets directory
if err := os.RemoveAll("/tmp/wrongsecrets"); err != nil {
fmt.Printf("Error removing /tmp/wrongsecrets: %s\n", err)
return
}
// Clone the owasp/wrongsecrets repository
gitCloneCmd := exec.Command("git", "clone", "https://github.com/OWASP/wrongsecrets.git", "/tmp/wrongsecrets", "--depth", "1")
if err := gitCloneCmd.Run(); err != nil {
fmt.Printf("Error cloning repository: %s\n", err)
return
}
defer os.RemoveAll("/tmp/wrongsecrets")
// Get the current working directory
cwd, err := os.Getwd()
if err != nil {
fmt.Printf("Error getting current directory: %s\n", err)
return
}
// Construct the path to main.go
mainGoPath := filepath.Join(cwd, "main.go")
// Run the main.go with local-git command
mainGoCmd := exec.Command("go", "run", mainGoPath, "local-git", "--path", "/tmp/wrongsecrets", "--silent", "--debug", "--confidence", "low")
outputBytes, err := mainGoCmd.CombinedOutput()
if err != nil {
fmt.Printf("Error running main.go: %s\nOutput: %s\n", err, string(outputBytes))
return
}
output := string(outputBytes)
// Print output
// fmt.Println(output)
// Extract the number of files processed
re := regexp.MustCompile(`Files Read\.*?: (\d+)`)
matches := re.FindStringSubmatch(output)
if len(matches) < 2 {
fmt.Println("Error: Could not find files count")
os.Exit(1)
return
}
filesCount, err := strconv.Atoi(matches[1])
if err != nil {
fmt.Printf("Error parsing files count: %s\n", err)
os.Exit(1)
return
}
// Check if the files count is greater than 10
if filesCount <= 10 {
fmt.Printf("Error: Files count (%d) is not greater than 10\n", filesCount)
os.Exit(1)
return
}
fmt.Println("Test completed successfully.")
}

View file

@ -16,9 +16,9 @@ async fn detects_new_release() {
let server = MockServer::start().await;
let body = serde_json::json!({
"tag_name": "v1.99.0",
"tag_name": "v99.999.0",
"created_at": "2025-01-01T00:00:00Z",
"name": "Kingfisher 1.99.0",
"name": "Kingfisher 99.999.0",
"body": "",
"assets": [{"url": "http://example.com/bin", "name": "bin"}]
});
@ -42,5 +42,5 @@ async fn detects_new_release() {
.expect("blocking task panicked")
.expect("update checker returned None");
assert!(msg.contains("1.99.0"));
assert!(msg.contains("99.999.0"));
}