Private
Public Access
1
0

fix: CIDR suffix in agent URLs, agent client CIDR strip, and IP SAN fixes
Some checks failed
CI Pipeline / Rust Format Check (push) Failing after 5s
CI Pipeline / Clippy Lints (push) Successful in 45s
CI Pipeline / Rust Unit Tests (push) Successful in 1m1s
CI Pipeline / Security Audit (push) Successful in 5s
CI Pipeline / Frontend Lint & Type Check (push) Successful in 12s
CI Pipeline / Build .deb & Release (push) Has been skipped

BUG-10: PostgreSQL inet type includes CIDR suffix (/32) when cast to text,
causing malformed agent URLs like https://127.0.0.1/32:12443.
Fixed by using host(ip_address)::text in all SQL queries across
pm-worker and pm-web modules, and adding a Rust-side safety
strip of CIDR notation in pm-agent-client.

Files changed:
- crates/pm-agent-client/src/client.rs: strip CIDR suffix from IP
- crates/pm-worker/src/health_poller.rs: host(ip_address)::text
- crates/pm-worker/src/patch_poller.rs: host(ip_address)::text
- crates/pm-worker/src/refresh_listener.rs: host(ip_address)::text
- crates/pm-worker/src/job_executor.rs: host(ip_address)::text (2 places)
- crates/pm-worker/src/ws_relay.rs: host(h.ip_address)::text
- crates/pm-web/src/routes/discovery.rs: host(ip_address)::text (2 places)
- crates/pm-web/src/routes/hosts.rs: host(ip_address)::text (3 places)
- docs/linux_patch_api_research.md: added research notes
This commit is contained in:
2026-04-29 00:58:43 +00:00
parent 9a8e9bfa38
commit b552a13619
9 changed files with 533 additions and 13 deletions

View File

@ -51,7 +51,7 @@ pub async fn run_health_poller(pool: PgPool, config: Arc<AppConfig>) {
// Fetch all hosts.
let hosts: Vec<HostRow> = match sqlx::query_as(
"SELECT id, ip_address::text AS ip_address, agent_port FROM hosts ORDER BY id",
"SELECT id, host(ip_address)::text AS ip_address, agent_port FROM hosts ORDER BY id",
)
.fetch_all(&pool)
.await

View File

@ -344,7 +344,7 @@ async fn execute_host_job(
// ── 1. Fetch host connection details ─────────────────────────────────────
let host: HostRow = match sqlx::query_as(
"SELECT ip_address::text AS ip_address, agent_port FROM hosts WHERE id = $1",
"SELECT host(ip_address)::text AS ip_address, agent_port FROM hosts WHERE id = $1",
)
.bind(host_id)
.fetch_optional(&pool)
@ -480,7 +480,7 @@ pub async fn poll_running_jobs(pool: PgPool, config: Arc<AppConfig>) {
SELECT pjh.id,
pjh.agent_job_id,
pjh.job_id,
h.ip_address::text AS ip_address,
host(h.ip_address)::text AS ip_address,
h.agent_port
FROM patch_job_hosts pjh
JOIN hosts h ON h.id = pjh.host_id

View File

@ -49,7 +49,7 @@ pub async fn run_patch_poller(pool: PgPool, config: Arc<AppConfig>) {
let ca_cert = Arc::new(certs.ca_cert);
let hosts: Vec<HostRow> = match sqlx::query_as(
"SELECT id, ip_address::text AS ip_address, agent_port FROM hosts ORDER BY id",
"SELECT id, host(ip_address)::text AS ip_address, agent_port FROM hosts ORDER BY id",
)
.fetch_all(&pool)
.await

View File

@ -69,7 +69,7 @@ async fn listen_loop(pool: &PgPool, config: &AppConfig) -> anyhow::Result<()> {
// Fetch the host from the database.
let host: Option<HostRow> = sqlx::query_as(
"SELECT id, ip_address::text AS ip_address, agent_port FROM hosts WHERE id = $1",
"SELECT id, host(ip_address)::text AS ip_address, agent_port FROM hosts WHERE id = $1",
)
.bind(host_id)
.fetch_optional(pool)

View File

@ -138,7 +138,7 @@ async fn query_running_jobs(pool: &PgPool) -> anyhow::Result<Vec<RunningHostJob>
pjh.job_id,
pjh.host_id,
pjh.agent_job_id,
COALESCE(h.fqdn, h.ip_address::text) AS host_address
COALESCE(h.fqdn, host(h.ip_address)::text) AS host_address
FROM patch_job_hosts pjh
JOIN hosts h ON h.id = pjh.host_id
WHERE pjh.status = 'running'::job_status