Skip to content

Commit 25c763e

Browse files
committed
fix(hu-monitor): build request and --json output with serde_json
Interpolating node/topic/service/namespace/type names directly into JSON strings produced invalid JSON (or injection) when a name contained a quote or backslash. Build the GetLoggerLevels request and the --json graph snapshot via serde_json so all string fields are escaped.
1 parent 79ae5bf commit 25c763e

3 files changed

Lines changed: 33 additions & 29 deletions

File tree

crates/hiroz-union/plugins/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/hiroz-union/plugins/hu-monitor/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ crate-type = ["cdylib"]
99

1010
[dependencies]
1111
wit-bindgen = { workspace = true }
12+
serde_json = { workspace = true }

crates/hiroz-union/plugins/hu-monitor/src/lib.rs

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ impl HuMonitor {
244244
let mut failed = false;
245245
match ros::connect_service(&svc_name, "rcl_interfaces/srv/GetLoggerLevels") {
246246
Ok(client) => {
247-
let req = format!(r#"{{"names": ["{node_name}"]}}"#);
247+
let req = serde_json::json!({ "names": [node_name] }).to_string();
248248
match client.call(&req, 5000) {
249249
Ok(resp) => render::println(&format!("log levels: {resp}")),
250250
Err(e) => {
@@ -294,34 +294,36 @@ fn print_graph_snapshot(json: bool) {
294294
let services = graph::list_services();
295295

296296
if json {
297-
let topics_json: Vec<String> = topics
298-
.iter()
299-
.map(|t| {
300-
format!(
301-
r#"{{"name":"{}","type":"{}","publishers":{},"subscribers":{}}}"#,
302-
t.name, t.type_name, t.publishers, t.subscribers
303-
)
304-
})
305-
.collect();
306-
let nodes_json: Vec<String> = nodes
307-
.iter()
308-
.map(|n| format!(r#"{{"namespace":"{}","name":"{}"}}"#, n.namespace, n.name))
309-
.collect();
310-
let services_json: Vec<String> = services
311-
.iter()
312-
.map(|s| {
313-
format!(
314-
r#"{{"name":"{}","type":"{}","servers":{}}}"#,
315-
s.name, s.type_name, s.servers
316-
)
317-
})
318-
.collect();
319-
render::println(&format!(
320-
r#"{{"topics":[{}],"nodes":[{}],"services":[{}]}}"#,
321-
topics_json.join(","),
322-
nodes_json.join(","),
323-
services_json.join(",")
324-
));
297+
// Serialize via serde_json so string fields (topic/service names,
298+
// namespaces, type names) are properly escaped — a name containing
299+
// `"` or `\` must not corrupt the output.
300+
let out = serde_json::json!({
301+
"topics": topics
302+
.iter()
303+
.map(|t| serde_json::json!({
304+
"name": t.name,
305+
"type": t.type_name,
306+
"publishers": t.publishers,
307+
"subscribers": t.subscribers,
308+
}))
309+
.collect::<Vec<_>>(),
310+
"nodes": nodes
311+
.iter()
312+
.map(|n| serde_json::json!({
313+
"namespace": n.namespace,
314+
"name": n.name,
315+
}))
316+
.collect::<Vec<_>>(),
317+
"services": services
318+
.iter()
319+
.map(|s| serde_json::json!({
320+
"name": s.name,
321+
"type": s.type_name,
322+
"servers": s.servers,
323+
}))
324+
.collect::<Vec<_>>(),
325+
});
326+
render::println(&out.to_string());
325327
return;
326328
}
327329

0 commit comments

Comments
 (0)