YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
sklearn-pmml-model β Unhandled TypeError (float(None)) in GeneralRegressionModel coefficient/intercept parsing on load of crafted PMML (DoS)
Target
- Package:
sklearn-pmml-model - Version: 1.0.8 (pip, from PyPI β
Location: .../site-packages) - Affected file:
sklearn_pmml_model/linear_model/base.py - Affected estimators:
PMMLGeneralizedLinearRegressor/PMMLGeneralizedLinearClassifierand their public subclasses βPMMLRidge,PMMLLasso,PMMLElasticNet,PMMLRidgeClassifier(all GeneralRegressionModel-backed). - Environment: Python 3.13, numpy, scikit-learn; verified by real execution.
Summary
The GeneralRegressionModel parser converts <PCell> coefficient values with a raw float()
call over an unchecked attribute lookup. When a crafted PMML file contains a <PCell> that
omits its beta attribute, Element.get('beta') returns None, and float(None) raises an
unhandled TypeError at model-construction time, before any prediction is made. A sibling
AttributeError arises from the same missing guards when the <ParamMatrix> (or <PPMatrix>)
element is absent. Any application that loads an untrusted PMML file of this model type is subject
to a denial-of-service crash.
Root cause
In sklearn_pmml_model/linear_model/base.py, _get_coefficients -> coefficient_for_parameter:
pp = model.find('PPMatrix')
params = model.find('ParamMatrix')
def coefficient_for_parameter(p):
if not p:
return 0
pcells = params.findall(f"PCell[@parameterName='{p}']") # line 107 β params may be None
if len(pcells) > 1:
raise Exception('This model does not support multiple outputs.')
if not pcells:
return 0
return float(pcells[0].get('beta')) # line 114 β get('beta') may be None
And in _get_intercept:
pp = model.find('PPMatrix')
params = model.find('ParamMatrix')
specified = [p.get('parameterName') for p in pp.findall('PPCell')] # lines 170/173 β pp/params may be None
used = [p.get('parameterName') for p in params.findall('PCell')]
...
return sum([float(i.get('beta')) for i in intercepts]) # line 182 β get('beta') may be None
There is no None/attribute validation anywhere in this parser:
Element.get('beta')returnsNonewhen the crafted<PCell>lacks abetaattribute βfloat(None)raisesTypeError.model.find('ParamMatrix')/model.find('PPMatrix')returnNonewhen the element is absent βNone.findall(...)raisesAttributeError.
Both fire during __init__ (self.coef_ = np.array(_get_coefficients(...)) / self.intercept_ = _get_intercept(model)), i.e. at load time.
Reachability / untrusted-input entry point
The crash is reachable both directly (PMMLRidge('file.pmml')) and through the library's
generic untrusted-input loader auto_detect_estimator('file.pmml'), which auto-selects
PMMLRidge for a GeneralRegressionModel document and crashes identically.
Proof of Concept
Files in this repo:
glm-baseline.pmmlβ NEGATIVE CONTROL. Pristine upstream GeneralRegressionModel test file (linear-model-glm.pmml). Loads cleanly.glm-missing-beta-coef.pmmlβ TRIGGER A. Byte-identical to baseline except one<PCell parameterName="p1" df="1" beta="0.0220430321140947"/>becomes<PCell parameterName="p1" df="1"/>(thebetaattribute is removed). Crashes atbase.py:114.glm-missing-beta-intercept.pmmlβ TRIGGER B. InterceptPCell p0loses itsbetaattribute. Crashes atbase.py:182.glm-no-parammatrix.pmmlβ TRIGGER C. The entire<ParamMatrix>element is removed. Crashes atbase.py:107(AttributeError).
Reproduce:
# Trigger A (direct)
python -c "from sklearn_pmml_model.linear_model import PMMLRidge; PMMLRidge('glm-missing-beta-coef.pmml')"
# Trigger A (via untrusted-input entry point)
python -c "from sklearn_pmml_model.auto_detect import auto_detect_estimator; auto_detect_estimator('glm-missing-beta-coef.pmml')"
# Trigger B
python -c "from sklearn_pmml_model.linear_model import PMMLRidge; PMMLRidge('glm-missing-beta-intercept.pmml')"
# Trigger C
python -c "from sklearn_pmml_model.linear_model import PMMLRidge; PMMLRidge('glm-no-parammatrix.pmml')"
Captured evidence (verbatim, real execution β sklearn-pmml-model 1.0.8, Python 3.13)
NEGATIVE CONTROL (glm-baseline.pmml):
LOADED OK coef_.shape= (11,) intercept_= -0.839478621884241
TRIGGER A (glm-missing-beta-coef.pmml) β identical crash via PMMLRidge and via auto_detect_estimator:
File ".../sklearn_pmml_model/linear_model/base.py", line 134, in coefficients_for_field
return [coefficient_for_parameter(pp_cells[0].get('parameterName'))]
File ".../sklearn_pmml_model/linear_model/base.py", line 114, in coefficient_for_parameter
return float(pcells[0].get('beta'))
TypeError: float() argument must be a string or a real number, not 'NoneType'
TRIGGER B (glm-missing-beta-intercept.pmml):
self.intercept_ = _get_intercept(model)
File ".../sklearn_pmml_model/linear_model/base.py", line 182, in _get_intercept
return sum([float(i.get('beta')) for i in intercepts])
TypeError: float() argument must be a string or a real number, not 'NoneType'
TRIGGER C (glm-no-parammatrix.pmml):
File ".../sklearn_pmml_model/linear_model/base.py", line 107, in coefficient_for_parameter
pcells = params.findall(f"PCell[@parameterName='{p}']")
AttributeError: 'NoneType' object has no attribute 'findall'
The negative control confirms the exact same file loads fine when beta is present, isolating
the single-attribute cause.
Impact
Denial of service: an application that loads an attacker-supplied PMML model of the
GeneralRegressionModel family (e.g. via PMMLRidge/PMMLLasso/PMMLElasticNet/
PMMLRidgeClassifier or the generic auto_detect_estimator) crashes with an unhandled
exception during model construction. PMML files are commonly treated as model-exchange
artifacts and passed between parties, so the untrusted-input surface is realistic.
Suggested fix
Validate presence before conversion: raise a clear, documented parse error (or default sensibly)
when ParamMatrix/PPMatrix is missing and when a PCell's beta attribute is absent β e.g.
guard params/pp against None and use float(pcells[0].get('beta', 0)) / explicit
None checks with a descriptive ValueError.
Dedup note
This is a distinct code path and crash class from prior sklearn-pmml-model findings:
- The affected parser is
linear_model/base.py(GeneralRegressionModel), not touched by the previously reported tree/forest/SVM/naive-bayes/logistic-regression/neural-network/SparseArray findings. - The crash class here is a
TypeErrorfromfloat(None)on a missingbetaattribute (plus a siblingAttributeErrorfrom a missingParamMatrix/PPMatrix), which is distinct from the covered AttributeError/IndexError/allocation findings in other model families. - No known CVE covers the GeneralRegressionModel
beta/ParamMatrixparsing path in sklearn-pmml-model 1.0.8.