fix(pusher): improve welist response logging, error handling, and add request timeout

- Always log HTTP status code and response body from welist (info level)
- Log send_stats_report errors at call site instead of silently discarding
- Add 10s timeout to reqwest client to prevent indefinite hangs
- Apply clippy fixes (is_empty, if-let chains, dead_code, etc.)
This commit is contained in:
2026-07-18 22:58:42 -04:00
parent 8ce3f6a445
commit cd24eda111
9 changed files with 111 additions and 186 deletions

View File

@@ -1,7 +1,6 @@
use bal_server::db::open_db;
use sqlite::State;
use std::fs;
use std::path::Path;
#[test]
fn test_open_db_blocks_traversal() {
@@ -74,7 +73,7 @@ fn test_open_db_rejects_symlink() {
let _ = fs::remove_file(real);
let _ = fs::remove_file(link);
fs::File::create(real).unwrap();
fs::soft_link(real, link).unwrap();
std::os::unix::fs::symlink(real, link).unwrap();
let res = open_db(link);
assert!(res.is_err(), "Symlink DB path should be rejected");

View File

@@ -36,11 +36,8 @@ fn test_db_null_unwrap_or() {
let mut found_value = None;
let _ = db.iterate("SELECT * FROM test_stats;", |pairs| {
let row: HashMap<_, _> = pairs
.into_iter()
.map(|(k, v)| (k.to_string(), v.map(|s| s)))
.collect();
let totals = row["totals"].clone().unwrap_or("0").to_string();
let row: HashMap<_, _> = pairs.iter().map(|(k, v)| (k.to_string(), *v)).collect();
let totals = row["totals"].unwrap_or("0").to_string();
found_value = Some(totals);
true
});

View File

@@ -1,5 +1,4 @@
use std::fs;
use std::path::Path;
#[test]
fn test_gitignore_protection_env() {
@@ -23,30 +22,20 @@ fn test_gitignore_protection_env() {
];
for pattern in required_patterns {
let has_exact = gitignore.contains(&pattern);
let has_wildcard = gitignore.contains(&format!("*.env.local"))
|| gitignore.contains(&format!(".env.local"));
let has_wildcard = gitignore.contains("*.env.local") || gitignore.contains(".env.local");
let has_env = gitignore.contains("*.env") || gitignore.contains(".env");
// For .env.local, either .env.local or *.env.local is acceptable
let is_env_local = pattern == "*.env.local" || pattern == ".env.local";
if is_env_local {
assert!(
has_wildcard,
".gitignore must contain pattern '*.env.local' or '.env.local' to protect secrets",
);
} else if pattern == ".env.production" || pattern == ".env.secret" {
assert!(
gitignore.contains(pattern),
".gitignore must contain pattern '{}' to protect secrets",
pattern
);
} else if pattern == "*.env" {
assert!(
has_env,
".gitignore must contain pattern '*.env' or '.env' to protect secrets",
);
} else if pattern == ".env" {
} else if pattern == ".env.production"
|| pattern == ".env.secret"
|| pattern == "*.env"
|| pattern == ".env"
{
assert!(
has_env,
".gitignore must contain pattern '*.env' or '.env' to protect secrets",
@@ -65,7 +54,6 @@ fn test_gitignore_protection_env() {
#[test]
fn test_no_private_key_in_git() {
// Check that .gitignore includes private_key.pem
let gitignore = match fs::read_to_string(".gitignore") {
Ok(c) => c,
Err(e) => {
@@ -88,20 +76,17 @@ fn test_no_private_key_in_git() {
".gitignore must block chiave_privata.key"
);
// Check that no private key files are tracked by git
let output = std::process::Command::new("git")
.args(&["ls-files", "*.pem", "*.key"])
.args(["ls-files", "*.pem", "*.key"])
.output()
.expect("Failed to run git ls-files");
let tracked_keys = String::from_utf8(output.stdout).unwrap();
let tracked_keys: Vec<&str> = tracked_keys.lines().collect();
// Only non-empty entries and only public_key.pem should be tracked
for tracked in tracked_keys.iter().filter(|s| !s.is_empty()) {
if !tracked.contains("public_key.pem") {
assert!(
false,
panic!(
"Private key file is tracked by git: {}. Remove it with git rm --cached",
tracked
);
@@ -113,47 +98,41 @@ fn test_no_private_key_in_git() {
#[test]
fn test_no_token_in_source_files() {
// Scan source files for hardcoded tokens
let mut found_issues = Vec::new();
// Scan .sh files for hardcoded 40-char hex strings
for entry in fs::read_dir(".").unwrap().filter_map(|e| e.ok()) {
let path = entry.path();
if !path.is_file() {
continue;
}
if let Some(ext) = path.extension() {
if ext == "sh" {
let content = fs::read_to_string(&path).unwrap();
for (line_num, line) in content.lines().enumerate() {
// Skip comments and example/template files
if line.trim().starts_with("#")
|| line.to_lowercase().contains("example")
|| line.to_lowercase().contains("template")
if let Some(ext) = path.extension()
&& ext == "sh"
{
let content = fs::read_to_string(&path).unwrap();
for (line_num, line) in content.lines().enumerate() {
if line.trim().starts_with('#')
|| line.to_lowercase().contains("example")
|| line.to_lowercase().contains("template")
{
continue;
}
if line.trim().len() >= 40 {
let hex_chars: Vec<_> = line
.trim()
.chars()
.filter(|c| c.is_ascii_hexdigit())
.collect();
if (40..=64).contains(&hex_chars.len())
&& (line.to_lowercase().contains("token")
|| line.to_lowercase().contains("api")
|| line.to_lowercase().contains("secret"))
{
continue;
}
// Check for 40-64 hex chars that could be API tokens (not in .env.example comments)
if line.trim().len() >= 40 {
let hex_chars = line
.trim()
.chars()
.filter(|c| c.is_ascii_hexdigit())
.collect::<Vec<_>>();
if hex_chars.len() >= 40 && hex_chars.len() <= 64 {
// Check if it looks like it's part of a TOKEN assignment
if line.to_lowercase().contains("token")
|| line.to_lowercase().contains("api")
|| line.to_lowercase().contains("secret")
{
found_issues.push(format!(
"Potential hardcoded token in {}: line {}: {}",
path.display(),
line_num + 1,
line.trim()
));
}
}
found_issues.push(format!(
"Potential hardcoded token in {}: line {}: {}",
path.display(),
line_num + 1,
line.trim()
));
}
}
}
@@ -165,8 +144,7 @@ fn test_no_token_in_source_files() {
for issue in &found_issues {
println!(" {}", issue);
}
assert!(
false,
panic!(
"Found potential hardcoded tokens in shell scripts: {:?}",
found_issues
);