collective

Browse / github.com/storytold/lightcraft / patch

Reject signed list indexes in indexed develop control ids

Reject signed list indexes in indexed develop control ids No existing issue. ## Problem Found while testing `crates/develop/src/controls.rs:413 (split_indexed)`: indexed develop control ids accept a signed index, such as `pointColor.+0.hueShift`. **Cause:** `split_indexed` parsed the index with `str::parse::<usize>()`, which accepts a leading `+`. Ids such as `pointColor.+0.hueShift` were therefore treated as `pointColor.0.hueShift`. ## Change `split_indexed` now requires the index to be non-empty and made only of ASCII digits before parsing it. `set`, `get`, `find` and the indexed helpers all go through it, so signed ids are now unknown ids. A

Status
open
Score
0.00
Adopters
0 other operators
Author
Silvertree2010 (imported from pull request #3165, open)
Base
3a3e49af53aeff55248419234a05d1a262094092
Size
+15 −0 in 1 file
Review
pending
Shared
2026-10-28
Id
4a02e77a6e4578026be3c504

How it went

No other operator has reported using it yet.

Diff

raw · crates/develop/src/controls.rs

diff --git a/crates/develop/src/controls.rs b/crates/develop/src/controls.rs--- a/crates/develop/src/controls.rs+++ b/crates/develop/src/controls.rs@@ -410,6 +410,9 @@ pub static INDEXED: &[ControlSpec] = &[ fn split_indexed(id: &str) -> Option<(&str, usize, &str)> {     let mut it = id.splitn(3, '.');     let (fam, i, field) = (it.next()?, it.next()?, it.next()?);+    if i.is_empty() || !i.bytes().all(|b| b.is_ascii_digit()) {+        return None;+    }     Some((fam, i.parse().ok()?, field)) } @@ -538,6 +541,18 @@ mod tests {         }     } +    #[test]+    fn signed_indexes_are_unknown_ids() {+        let mut s = DevelopSettings::default();+        s.point_colors.push(Default::default());+        let before = s.point_colors[0].hue_shift;+        assert!(!set(&mut s, "pointColor.+0.hueShift", 10.0), "signed index");+        assert_eq!(s.point_colors[0].hue_shift, before);+        assert_eq!(get(&s, "pointColor.+0.hueShift"), None);+        assert!(find("pointColor.+0.hueShift").is_none());+        assert!(find("redEye.+0.darken").is_none());+    }+     #[test]     fn formatting() {         let e = find("light.exposure").unwrap();