63dcf7676c
parse_cidr_v4 split on '/' and read the first two components without checking for a third, so "10.8.1.0/24/garbage" parsed as a valid /24 with the tail silently dropped. The value reaches nftables rules and tc filters, so accepting malformed input meant applying rules derived from a config the operator did not write. Now a second separator is an error, and the negative cases are pinned by a test: missing prefix, empty prefix, double separator, negative and out-of-range prefixes, a malformed octet, an empty string, and a missing address. Interface names were only checked for being non-empty, and only for ifb_name. They are passed straight to ip and tc, so a name that cannot exist produces a confusing runtime failure instead of a config error. Validation now covers amnezia.interface_candidates and traffic.ifb_name: non-empty, within the Linux IFNAMSIZ-1 limit of 15 characters, not '.' or '..', and free of whitespace, control characters, '/' and ':'. interface_patterns get the same rules plus one specific to the matcher: only a single trailing '*' is supported, so a pattern like "*wg" or "a*g*" would have silently never matched anything. A bare "*" is rejected outright - matching every interface is not a fallback, it is a way to attach shaping to the wrong device. container_names are matched exactly against docker ps output, so a name outside Docker's own [a-zA-Z0-9][a-zA-Z0-9_.-]* grammar can never be found. That is now a config error rather than a container that mysteriously never appears. All four ordered lists - container_names, interface_candidates, interface_patterns, wg_binaries - are checked for blank and duplicate entries through one shared helper. A blank entry becomes an unmatchable candidate; a duplicate makes an ordered list lie about its priority. Neither is normalized away silently. Seven tests added, covering accepted and rejected values for each rule. cargo test --workspace: 155 + 4 passed. clippy -D warnings clean.