forked from bitcoinafterlife/bal-server
fix: Docker support, WAL race condition, pusher panic fixes
- Add multi-stage Dockerfile with tini, non-root user, healthcheck - Fix SQLite WAL mode race between bal-server and bal-pusher (busy_timeout + retry) - Fix tbl_stats missing UNIQUE index for ON CONFLICT clause - Replace unwrap() panics in bal-pusher with graceful error handling - Add docker/entrypoint.sh with BAL_PUSHER_NETWORK support - cargo fmt across all files
This commit is contained in:
@@ -6,32 +6,31 @@ use std::path::Path;
|
||||
#[test]
|
||||
fn test_open_db_blocks_traversal() {
|
||||
let res = open_db("../etc/passwd");
|
||||
assert!(
|
||||
res.is_err(),
|
||||
"Path with '..' should be rejected"
|
||||
);
|
||||
assert!(res.is_err(), "Path with '..' should be rejected");
|
||||
let err = match res {
|
||||
Err(e) => e,
|
||||
Ok(_) => panic!("Expected error for traversal path"),
|
||||
};
|
||||
assert!(err.contains("'..'"), "Error should mention directory traversal: {}", err);
|
||||
assert!(
|
||||
err.contains("'..'"),
|
||||
"Error should mention directory traversal: {}",
|
||||
err
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_open_db_blocks_forbidden_absolute() {
|
||||
for path in ["/etc/passwd", "/proc/self/mem", "/dev/null", "/usr/bin/ls"] {
|
||||
let res = open_db(path);
|
||||
assert!(
|
||||
res.is_err(),
|
||||
"Absolute path {} should be rejected", path
|
||||
);
|
||||
assert!(res.is_err(), "Absolute path {} should be rejected", path);
|
||||
let err = match res {
|
||||
Err(e) => e,
|
||||
Ok(_) => panic!("Expected error for forbidden path {}", path),
|
||||
};
|
||||
assert!(
|
||||
err.contains("forbidden"),
|
||||
"Error should mention forbidden prefix: {}", err
|
||||
"Error should mention forbidden prefix: {}",
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -41,10 +40,7 @@ fn test_open_db_allows_relative() {
|
||||
let test_path = "tmp_test_bal.db";
|
||||
let _ = fs::remove_file(test_path);
|
||||
let res = open_db(test_path);
|
||||
assert!(
|
||||
res.is_ok(),
|
||||
"Valid relative path should be allowed"
|
||||
);
|
||||
assert!(res.is_ok(), "Valid relative path should be allowed");
|
||||
let db = res.unwrap();
|
||||
drop(db);
|
||||
let _ = fs::remove_file(test_path);
|
||||
@@ -61,10 +57,7 @@ fn test_open_db_wal_pragmas_set() {
|
||||
let mut stmt = db.prepare("PRAGMA journal_mode;").unwrap();
|
||||
if let Ok(State::Row) = stmt.next() {
|
||||
let mode: String = stmt.read(0).unwrap();
|
||||
assert_eq!(
|
||||
mode, "wal",
|
||||
"SQLite journal mode should be WAL"
|
||||
);
|
||||
assert_eq!(mode, "wal", "SQLite journal mode should be WAL");
|
||||
} else {
|
||||
panic!("Could not read journal_mode pragma");
|
||||
}
|
||||
@@ -84,17 +77,15 @@ fn test_open_db_rejects_symlink() {
|
||||
fs::soft_link(real, link).unwrap();
|
||||
|
||||
let res = open_db(link);
|
||||
assert!(
|
||||
res.is_err(),
|
||||
"Symlink DB path should be rejected"
|
||||
);
|
||||
assert!(res.is_err(), "Symlink DB path should be rejected");
|
||||
let err = match res {
|
||||
Err(e) => e,
|
||||
Ok(_) => panic!("Expected error for symlink"),
|
||||
};
|
||||
assert!(
|
||||
err.contains("symlink"),
|
||||
"Error should mention symlink: {}", err
|
||||
"Error should mention symlink: {}",
|
||||
err
|
||||
);
|
||||
|
||||
let _ = fs::remove_file(real);
|
||||
|
||||
@@ -10,15 +10,21 @@ fn setup_db_with_xpub() -> sqlite::Connection {
|
||||
"CREATE TABLE tbl_address (address TEXT PRIMARY KEY, path TEXT, xpub INTEGER, remote_address TEXT);"
|
||||
);
|
||||
// Insert test xpub
|
||||
let mut stmt = db.prepare("INSERT INTO tbl_xpub(id, network, xpub) VALUES(?, ?, ?);").unwrap();
|
||||
let mut stmt = db
|
||||
.prepare("INSERT INTO tbl_xpub(id, network, xpub) VALUES(?, ?, ?);")
|
||||
.unwrap();
|
||||
stmt.bind((1, Value::Integer(1))).unwrap();
|
||||
stmt.bind((2, Value::String("testnet".to_string()))).unwrap();
|
||||
stmt.bind((3, Value::String("tpub_test".to_string()))).unwrap();
|
||||
stmt.bind((2, Value::String("testnet".to_string())))
|
||||
.unwrap();
|
||||
stmt.bind((3, Value::String("tpub_test".to_string())))
|
||||
.unwrap();
|
||||
let _ = stmt.next();
|
||||
drop(stmt);
|
||||
// Insert test addresses
|
||||
for addr in ["addr1", "addr2", "addr3"] {
|
||||
let mut stmt = db.prepare("INSERT INTO tbl_address(address, path, xpub) VALUES(?, ?, ?);").unwrap();
|
||||
let mut stmt = db
|
||||
.prepare("INSERT INTO tbl_address(address, path, xpub) VALUES(?, ?, ?);")
|
||||
.unwrap();
|
||||
stmt.bind((1, Value::String(addr.to_string()))).unwrap();
|
||||
stmt.bind((2, Value::String("m/0/1".to_string()))).unwrap();
|
||||
stmt.bind((3, Value::Integer(1))).unwrap();
|
||||
@@ -53,7 +59,10 @@ fn test_network_unknown_returns_404() {
|
||||
for n in networks {
|
||||
assert!(networks.contains(&n), "{} should be a valid network", n);
|
||||
}
|
||||
assert!(!networks.contains(&"attacker"), "attacker should not be a valid network");
|
||||
assert!(
|
||||
!networks.contains(&"attacker"),
|
||||
"attacker should not be a valid network"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user