fix: implement 22 security pentest remediation fixes
Server-side session management with SHA-256 hashed tokens and HttpOnly cookies. Auth middleware gating all RPC/WS/proxy routes with method allowlist. Login rate limiting (5/60s per IP). CORS restricted to config origin. Docker registry allowlist. App ID and path validation. P2P message sanitization (HTML + log injection). Onion address and known-peer validation. Nginx security headers (CSP, X-Frame-Options, etc.) and AIUI proxy auth. Systemd hardening (non-root, NoNewPrivileges, ProtectSystem). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,7 @@ impl RpcHandler {
|
||||
.get("id")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing package id"))?;
|
||||
validate_app_id(package_id)?;
|
||||
|
||||
let docker_image = params
|
||||
.get("dockerImage")
|
||||
@@ -565,6 +566,7 @@ impl RpcHandler {
|
||||
.get("id")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing package id"))?;
|
||||
validate_app_id(package_id)?;
|
||||
let preserve_data = params
|
||||
.get("preserve_data")
|
||||
.and_then(|v| v.as_bool())
|
||||
@@ -711,6 +713,7 @@ impl RpcHandler {
|
||||
|
||||
/// Get all container names for an app (handles multi-container apps like mempool)
|
||||
async fn get_containers_for_app(package_id: &str) -> Result<Vec<String>> {
|
||||
validate_app_id(package_id)?;
|
||||
let output = tokio::process::Command::new("sudo")
|
||||
.args(["podman", "ps", "-a", "--format", "{{.Names}}"])
|
||||
.output()
|
||||
@@ -759,7 +762,8 @@ async fn get_containers_for_app(package_id: &str) -> Result<Vec<String>> {
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Get data directories to clean for an app
|
||||
/// Get data directories to clean for an app.
|
||||
/// Caller must validate package_id before calling.
|
||||
fn get_data_dirs_for_app(package_id: &str) -> Vec<String> {
|
||||
let base = "/var/lib/archipelago";
|
||||
match package_id {
|
||||
@@ -781,20 +785,39 @@ fn get_data_dirs_for_app(package_id: &str) -> Vec<String> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Validate Docker image name format
|
||||
/// Prevents command injection via malicious image names
|
||||
/// Trusted Docker registries. Only images from these sources are allowed.
|
||||
const TRUSTED_REGISTRIES: &[&str] = &[
|
||||
"docker.io/",
|
||||
"ghcr.io/",
|
||||
"localhost/",
|
||||
];
|
||||
|
||||
/// Validate Docker image against trusted registry allowlist.
|
||||
fn is_valid_docker_image(image: &str) -> bool {
|
||||
if image.is_empty() || image.len() > 256 {
|
||||
return false;
|
||||
}
|
||||
// Reject shell metacharacters
|
||||
let dangerous_chars = ['&', '|', ';', '`', '$', '(', ')', '<', '>', '\n', '\r'];
|
||||
if image.chars().any(|c| dangerous_chars.contains(&c)) {
|
||||
return false;
|
||||
}
|
||||
if !image.chars().any(|c| c.is_alphanumeric()) {
|
||||
return false;
|
||||
// Must come from a trusted registry
|
||||
TRUSTED_REGISTRIES.iter().any(|r| image.starts_with(r))
|
||||
}
|
||||
|
||||
/// Validate that a package/app ID is safe (lowercase alphanumeric + hyphens, 1-64 chars).
|
||||
fn validate_app_id(id: &str) -> Result<()> {
|
||||
if id.is_empty() || id.len() > 64 {
|
||||
anyhow::bail!("Invalid app id: must be 1-64 characters");
|
||||
}
|
||||
if image.len() > 256 {
|
||||
return false;
|
||||
if !id.bytes().all(|b| b.is_ascii_lowercase() || b.is_ascii_digit() || b == b'-') {
|
||||
anyhow::bail!("Invalid app id: only lowercase letters, digits, and hyphens allowed");
|
||||
}
|
||||
true
|
||||
if id.starts_with('-') {
|
||||
anyhow::bail!("Invalid app id: must not start with a hyphen");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Per-app Linux capabilities needed beyond the default cap-drop=ALL.
|
||||
|
||||
Reference in New Issue
Block a user