gardesk/garwarp / 4b69bf1

Browse files

harden response field parsing

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
4b69bf127b24d3f9c52e9bef36d4868f7d378f08
Parents
8060762
Tree
80907b4

1 changed file

StatusFile+-
M garwarp-ipc/src/lib.rs 108 39
garwarp-ipc/src/lib.rsmodified
@@ -299,38 +299,43 @@ impl ControlResponse {
299
                         .ok_or(ParseError::InvalidField(part.to_string()))?;
299
                         .ok_or(ParseError::InvalidField(part.to_string()))?;
300
                     match key {
300
                     match key {
301
                         "protocol" => {
301
                         "protocol" => {
302
-                            protocol_version = Some(
302
+                            let parsed = value
303
-                                value
304
                                 .parse::<u16>()
303
                                 .parse::<u16>()
305
-                                    .map_err(|_| ParseError::InvalidField(part.to_string()))?,
304
+                                .map_err(|_| ParseError::InvalidField(part.to_string()))?;
306
-                            );
305
+                            if protocol_version.replace(parsed).is_some() {
306
+                                return Err(ParseError::InvalidField(part.to_string()));
307
+                            }
307
                         }
308
                         }
308
                         "health" => {
309
                         "health" => {
309
-                            health = HealthStatus::parse(value);
310
+                            let parsed = HealthStatus::parse(value)
310
-                            if health.is_none() {
311
+                                .ok_or(ParseError::InvalidField(part.to_string()))?;
312
+                            if health.replace(parsed).is_some() {
311
                                 return Err(ParseError::InvalidField(part.to_string()));
313
                                 return Err(ParseError::InvalidField(part.to_string()));
312
                             }
314
                             }
313
                         }
315
                         }
314
                         "in_flight" => {
316
                         "in_flight" => {
315
-                            in_flight_requests = Some(
317
+                            let parsed = value
316
-                                value
317
                                 .parse::<usize>()
318
                                 .parse::<usize>()
318
-                                    .map_err(|_| ParseError::InvalidField(part.to_string()))?,
319
+                                .map_err(|_| ParseError::InvalidField(part.to_string()))?;
319
-                            );
320
+                            if in_flight_requests.replace(parsed).is_some() {
321
+                                return Err(ParseError::InvalidField(part.to_string()));
322
+                            }
320
                         }
323
                         }
321
                         "total" => {
324
                         "total" => {
322
-                            total_requests = Some(
325
+                            let parsed = value
323
-                                value
324
                                 .parse::<usize>()
326
                                 .parse::<usize>()
325
-                                    .map_err(|_| ParseError::InvalidField(part.to_string()))?,
327
+                                .map_err(|_| ParseError::InvalidField(part.to_string()))?;
326
-                            );
328
+                            if total_requests.replace(parsed).is_some() {
329
+                                return Err(ParseError::InvalidField(part.to_string()));
330
+                            }
327
                         }
331
                         }
328
                         "terminal" => {
332
                         "terminal" => {
329
-                            terminal_requests = Some(
333
+                            let parsed = value
330
-                                value
331
                                 .parse::<usize>()
334
                                 .parse::<usize>()
332
-                                    .map_err(|_| ParseError::InvalidField(part.to_string()))?,
335
+                                .map_err(|_| ParseError::InvalidField(part.to_string()))?;
333
-                            );
336
+                            if terminal_requests.replace(parsed).is_some() {
337
+                                return Err(ParseError::InvalidField(part.to_string()));
338
+                            }
334
                         }
339
                         }
335
                         _ => return Err(ParseError::InvalidField(part.to_string())),
340
                         _ => return Err(ParseError::InvalidField(part.to_string())),
336
                     }
341
                     }
@@ -358,8 +363,16 @@ impl ControlResponse {
358
                             .split_once('=')
363
                             .split_once('=')
359
                             .ok_or(ParseError::InvalidField(part.to_string()))?;
364
                             .ok_or(ParseError::InvalidField(part.to_string()))?;
360
                         match key {
365
                         match key {
361
-                            "id" => id = Some(value.to_string()),
366
+                            "id" => {
362
-                            "state" => state = Some(value.to_string()),
367
+                                if id.replace(value.to_string()).is_some() {
368
+                                    return Err(ParseError::InvalidField(part.to_string()));
369
+                                }
370
+                            }
371
+                            "state" => {
372
+                                if state.replace(value.to_string()).is_some() {
373
+                                    return Err(ParseError::InvalidField(part.to_string()));
374
+                                }
375
+                            }
363
                             _ => return Err(ParseError::InvalidField(part.to_string())),
376
                             _ => return Err(ParseError::InvalidField(part.to_string())),
364
                         }
377
                         }
365
                     }
378
                     }
@@ -379,15 +392,18 @@ impl ControlResponse {
379
                         .ok_or(ParseError::InvalidField(part.to_string()))?;
392
                         .ok_or(ParseError::InvalidField(part.to_string()))?;
380
                     match key {
393
                     match key {
381
                         "ids" => {
394
                         "ids" => {
382
-                            if value == "-" {
395
+                            let parsed = if value == "-" {
383
-                                ids = Some(Vec::new());
396
+                                Vec::new()
384
                             } else {
397
                             } else {
385
                                 let parsed =
398
                                 let parsed =
386
                                     value.split(',').map(str::to_string).collect::<Vec<_>>();
399
                                     value.split(',').map(str::to_string).collect::<Vec<_>>();
387
                                 if parsed.iter().any(|id| id.is_empty()) {
400
                                 if parsed.iter().any(|id| id.is_empty()) {
388
                                     return Err(ParseError::InvalidField(part.to_string()));
401
                                     return Err(ParseError::InvalidField(part.to_string()));
389
                                 }
402
                                 }
390
-                                ids = Some(parsed);
403
+                                parsed
404
+                            };
405
+                            if ids.replace(parsed).is_some() {
406
+                                return Err(ParseError::InvalidField(part.to_string()));
391
                             }
407
                             }
392
                         }
408
                         }
393
                         _ => return Err(ParseError::InvalidField(part.to_string())),
409
                         _ => return Err(ParseError::InvalidField(part.to_string())),
@@ -403,24 +419,50 @@ impl ControlResponse {
403
                 let mut sender = None;
419
                 let mut sender = None;
404
                 let mut app_id = None;
420
                 let mut app_id = None;
405
                 let mut parent_window = None;
421
                 let mut parent_window = None;
422
+                let mut saw_app_id = false;
423
+                let mut saw_parent_window = false;
406
 
424
 
407
                 for part in parts {
425
                 for part in parts {
408
                     let (key, value) = part
426
                     let (key, value) = part
409
                         .split_once('=')
427
                         .split_once('=')
410
                         .ok_or(ParseError::InvalidField(part.to_string()))?;
428
                         .ok_or(ParseError::InvalidField(part.to_string()))?;
411
                     match key {
429
                     match key {
412
-                        "id" => id = Some(value.to_string()),
430
+                        "id" => {
413
-                        "state" => state = Some(value.to_string()),
431
+                            if id.replace(value.to_string()).is_some() {
414
-                        "sender" => sender = Some(value.to_string()),
432
+                                return Err(ParseError::InvalidField(part.to_string()));
433
+                            }
434
+                        }
435
+                        "state" => {
436
+                            if state.replace(value.to_string()).is_some() {
437
+                                return Err(ParseError::InvalidField(part.to_string()));
438
+                            }
439
+                        }
440
+                        "sender" => {
441
+                            if sender.replace(value.to_string()).is_some() {
442
+                                return Err(ParseError::InvalidField(part.to_string()));
443
+                            }
444
+                        }
415
                         "app_id" => {
445
                         "app_id" => {
416
-                            if value != "-" {
446
+                            if saw_app_id {
417
-                                app_id = Some(value.to_string());
447
+                                return Err(ParseError::InvalidField(part.to_string()));
418
                             }
448
                             }
449
+                            saw_app_id = true;
450
+                            app_id = if value == "-" {
451
+                                None
452
+                            } else {
453
+                                Some(value.to_string())
454
+                            };
419
                         }
455
                         }
420
                         "parent" => {
456
                         "parent" => {
421
-                            if value != "-" {
457
+                            if saw_parent_window {
422
-                                parent_window = Some(value.to_string());
458
+                                return Err(ParseError::InvalidField(part.to_string()));
423
                             }
459
                             }
460
+                            saw_parent_window = true;
461
+                            parent_window = if value == "-" {
462
+                                None
463
+                            } else {
464
+                                Some(value.to_string())
465
+                            };
424
                         }
466
                         }
425
                         _ => return Err(ParseError::InvalidField(part.to_string())),
467
                         _ => return Err(ParseError::InvalidField(part.to_string())),
426
                     }
468
                     }
@@ -447,12 +489,18 @@ impl ControlResponse {
447
                             .ok_or(ParseError::InvalidField(field.to_string()))?;
489
                             .ok_or(ParseError::InvalidField(field.to_string()))?;
448
                         match key {
490
                         match key {
449
                             "code" => {
491
                             "code" => {
450
-                                code =
492
+                                let parsed = value
451
-                                    Some(value.parse::<u32>().map_err(|_| {
493
+                                    .parse::<u32>()
452
-                                        ParseError::InvalidField(field.to_string())
494
+                                    .map_err(|_| ParseError::InvalidField(field.to_string()))?;
453
-                                    })?);
495
+                                if code.replace(parsed).is_some() {
496
+                                    return Err(ParseError::InvalidField(field.to_string()));
497
+                                }
498
+                            }
499
+                            "reason" => {
500
+                                if reason.replace(value.to_string()).is_some() {
501
+                                    return Err(ParseError::InvalidField(field.to_string()));
502
+                                }
454
                             }
503
                             }
455
-                            "reason" => reason = Some(value.to_string()),
456
                             _ => return Err(ParseError::InvalidField(field.to_string())),
504
                             _ => return Err(ParseError::InvalidField(field.to_string())),
457
                         }
505
                         }
458
                     }
506
                     }
@@ -619,4 +667,25 @@ mod tests {
619
         let parsed = ControlRequest::parse_line("inspect id=req-1 bogus=1");
667
         let parsed = ControlRequest::parse_line("inspect id=req-1 bogus=1");
620
         assert_eq!(parsed, None);
668
         assert_eq!(parsed, None);
621
     }
669
     }
670
+
671
+    #[test]
672
+    fn response_parse_rejects_duplicate_fields() {
673
+        assert!(
674
+            ControlResponse::parse_line(
675
+                "status protocol=1 protocol=2 health=healthy in_flight=0 total=0 terminal=0",
676
+            )
677
+            .is_err()
678
+        );
679
+        assert!(
680
+            ControlResponse::parse_line("ack request id=req-1 id=req-2 state=pending").is_err()
681
+        );
682
+        assert!(ControlResponse::parse_line("list ids=req-1 ids=req-2").is_err());
683
+        assert!(
684
+            ControlResponse::parse_line(
685
+                "snapshot id=req-1 state=pending sender=:1.2 app_id=- app_id=org.test.App parent=-",
686
+            )
687
+            .is_err()
688
+        );
689
+        assert!(ControlResponse::parse_line("error code=2 reason=bad reason=worse").is_err());
690
+    }
622
 }
691
 }