T09 · Insecure Skill Coding Practices
- Location
scripts/decision_matrix.py:45- Finding
Non-Finite Numeric Values Bypass Decision Matrix Validation
- Content
View full analysis
float: try: return float(value) except (TypeError, ValueError) as exc: raise InputError(f"Error: {label} must be numeric. Received: {value!r}") from exc def validate(data: dict) -> None: if "weights" not in data or "options" not in data: raise InputError("Error: input JSON must contain 'weights' and 'options'.") weights = data["weights"] options = data["options"] direction = data.get("direction", {}) if not isinstance(weights, dict) or not weights: raise InputError("Error: 'weights' must be a non-empty object.") if not isinstance(options, list) or len(options) < 2: raise InputError("Error: 'options' must contain at least two options.") if not isinstance(direction, dict): raise InputError("Error: 'direction' must be an object if provided.") criteria = list(weights.keys()) names: set[str] = set() weight_total = 0.0 for criterion in criteria: weight_total += _coerce_number(weights[criterion], f"weight for '{criterion}'") if criterion in direction and direction[criterion] not in {"higher", "lower"}: raise InputError( f"Error: direction for '{criterion}' must be 'higher' or 'lower'. Received: {direction[criterion]!r}" ) if weight_total <= 0: raise InputError("Error: weights must sum to a positive number.") ``` ```python def normalise_weights(weights: Dict[str, float]) -> Dict[str, float]: total = sum(float(v) for v in weights.values()) return {k: float(v) / total for k, v in weights.items()} ``` ### Technical Analysis The `_coerce_number()` function treats every value accepted by Python's `float()` as valid. This inc ...[truncated 2182 chars]- Remediation
View remediation
float: try: number = float(value) except (TypeError, ValueError) as exc: raise InputError( f"Error: {label} must be numeric. Received: {value!r}" ) from exc if not math.isfinite(number): raise InputError(f"Error: {label} must be finite. Received: {value!r}") return number ``` 2. Make JSON parsing reject non-standard constants: ```python def _reject_constant(value: str) -> None: raise InputError(f"Error: non-finite JSON number is not allowed: {value}") data = json.loads( path.read_text(encoding="utf-8"), parse_constant=_reject_constant, ) ``` 3. Validate each weight individually. If negative weights are not a supported feature, reject weights below zero rather than checking only the aggregate total. 4. Use the validated numeric values during computation instead of repeatedly calling `float()` on the original untrusted data. 5. Add regression tests for `NaN`, `Infinity`, `-Infinity`, strings representing non-finite numbers, zero-sum weights, and extremely large finite values. 6. Before serialization, assert that every normalized weight, contribution, total, and ranking gap remains finite. ]]>
