config visibility, PR review fixes, XSS hardening
Config visibility:
- startup banner shows config path, data dir, services path
- config search: ./numa.toml → ~/.config/numa/ → /usr/local/var/numa/
- /stats API exposes config_path and data_dir, dashboard footer renders them
- GET /ca.pem endpoint serves CA cert for cross-device TLS trust
- load_config returns ConfigLoad with found flag, warns on not-found
- ServerCtx stores PathBuf for config_dir/data_dir, string conversion at boundaries
PR review fixes:
- add explicit parens in resolve_route operator precedence (service_store.rs)
- hostname portability: drop -s flag, trim domain with split('.') (lan.rs)
- serve_ca uses spawn_blocking instead of sync fs::read in async handler
- load_config: remove TOCTOU exists() check, read directly and handle NotFound
XSS hardening:
- HTML-escape all user-controlled interpolations in dashboard (service names,
route paths, ports, URLs, block check domain/reason)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
23
src/api.rs
23
src/api.rs
@@ -49,6 +49,7 @@ pub fn router(ctx: Arc<ServerCtx>) -> Router {
|
||||
.route("/services/{name}/routes", get(list_routes))
|
||||
.route("/services/{name}/routes", post(add_route))
|
||||
.route("/services/{name}/routes", delete(remove_route))
|
||||
.route("/ca.pem", get(serve_ca))
|
||||
.with_state(ctx)
|
||||
}
|
||||
|
||||
@@ -130,6 +131,8 @@ struct QueryLogResponse {
|
||||
struct StatsResponse {
|
||||
uptime_secs: u64,
|
||||
upstream: String,
|
||||
config_path: String,
|
||||
data_dir: String,
|
||||
queries: QueriesStats,
|
||||
cache: CacheStats,
|
||||
overrides: OverrideStats,
|
||||
@@ -451,6 +454,8 @@ async fn stats(State(ctx): State<Arc<ServerCtx>>) -> Json<StatsResponse> {
|
||||
Json(StatsResponse {
|
||||
uptime_secs: snap.uptime_secs,
|
||||
upstream,
|
||||
config_path: ctx.config_path.clone(),
|
||||
data_dir: ctx.data_dir.to_string_lossy().to_string(),
|
||||
queries: QueriesStats {
|
||||
total: snap.total,
|
||||
forwarded: snap.forwarded,
|
||||
@@ -810,6 +815,24 @@ async fn remove_route(
|
||||
}
|
||||
}
|
||||
|
||||
async fn serve_ca(State(ctx): State<Arc<ServerCtx>>) -> Result<impl IntoResponse, StatusCode> {
|
||||
let ca_path = ctx.data_dir.join("ca.pem");
|
||||
let bytes = tokio::task::spawn_blocking(move || std::fs::read(ca_path))
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
|
||||
.map_err(|_| StatusCode::NOT_FOUND)?;
|
||||
Ok((
|
||||
[
|
||||
(header::CONTENT_TYPE, "application/x-pem-file"),
|
||||
(
|
||||
header::CONTENT_DISPOSITION,
|
||||
"attachment; filename=\"numa-ca.pem\"",
|
||||
),
|
||||
],
|
||||
bytes,
|
||||
))
|
||||
}
|
||||
|
||||
async fn check_tcp(addr: std::net::SocketAddr) -> bool {
|
||||
tokio::time::timeout(
|
||||
std::time::Duration::from_millis(100),
|
||||
|
||||
Reference in New Issue
Block a user