# Unhandled TypeError (`float(None)`) importing a PMML `SupportVectorMachineModel` whose kernel element omits gamma/coef0/degree (spec-default) — load-time DoS ## Target - **Project:** `sklearn-pmml-model` (PyPI: `sklearn-pmml-model`) - **Version tested:** 1.0.8 (installed from PyPI) - **Runtime:** Python 3.13, scikit-learn 1.9.0 - **Affected file:** `sklearn_pmml_model/svm/_base.py`, `PMMLBaseSVM.__init__` (lines 72–86) - **Affected estimators / entrypoints:** `PMMLSVC`, `PMMLSVR` (both subclass `PMMLBaseSVM`), and the `sklearn_pmml_model.auto_detect` dispatcher that routes any `SupportVectorMachineModel` PMML to these classes. ## Summary `PMMLBaseSVM.__init__` parses the SVM kernel parameters from the PMML kernel element with **no defaults and no type guards**. When a spec-valid PMML file omits an *optional* kernel attribute (e.g. `gamma`), `Element.get('gamma')` returns `None`, and the subsequent `float(None)` / `int(None)` raises an unhandled `TypeError` during **model construction** — before any prediction is attempted. Because there is no `try/except` around construction, loading an untrusted PMML file crashes the caller: a load-time denial of service. Per the DMG PMML v4.x specification (), these kernel attributes are **OPTIONAL** with documented defaults: - `RadialBasisKernelType`: `gamma` default `1` - `PolynomialKernelType`: `gamma` default `1`, `coef0` default `1`, `degree` default `1` - `SigmoidKernelType`: `gamma` default `1`, `coef0` default `1` A PMML producer that relies on any of these documented defaults emits a perfectly spec-valid file that this loader cannot parse. ## Root cause `sklearn_pmml_model/svm/_base.py`, `PMMLBaseSVM.__init__`: ```python linear = model.find('LinearKernelType') poly = model.find('PolynomialKernelType') rbf = model.find('RadialBasisKernelType') sigmoid = model.find('SigmoidKernelType') if linear is not None: self.kernel = 'linear' self._gamma = self.gamma = 0.0 elif poly is not None: self.kernel = 'poly' self._gamma = self.gamma = float(poly.get('gamma')) # line 77 -> float(None) if absent self.coef0 = float(poly.get('coef0')) # line 78 -> float(None) if absent self.degree = int(poly.get('degree')) # line 79 -> int(None) if absent elif rbf is not None: self.kernel = 'rbf' self._gamma = self.gamma = float(rbf.get('gamma')) # line 82 -> float(None) if absent [PoC trigger] elif sigmoid is not None: self.kernel = 'sigmoid' self._gamma = self.gamma = float(sigmoid.get('gamma')) # line 85 -> float(None) if absent self.coef0 = float(sigmoid.get('coef0')) # line 86 -> float(None) if absent ``` `Element.get(name)` returns `None` when the attribute is absent. `float(None)` and `int(None)` raise `TypeError`. The spec says these attributes are optional with defaults, so the correct behavior would be to fall back to the documented default (e.g. `poly.get('gamma', 1)` / `... or 1`) rather than crash. ## Proof of concept Two PMML files differing by exactly one attribute isolate the cause: - **Negative control** — `svc-baseline.pmml`: a real SVC PMML whose kernel is ``. Loads successfully. - **PoC** — `svc-nogamma.pmml`: byte-for-byte identical except the single `gamma` attribute is removed, leaving `` (spec-valid; relies on the `gamma` default of `1`). Loading raises `TypeError` at `svm/_base.py:82`. ```python from sklearn_pmml_model.svm import PMMLSVC # Negative control — succeeds m = PMMLSVC(pmml='svc-baseline.pmml') print('BASELINE OK kernel=', m.kernel, 'gamma=', m.gamma) # PoC — raises TypeError during construction (no prediction call) m = PMMLSVC(pmml='svc-nogamma.pmml') ``` ### Sibling attack surfaces (same no-default pattern) The identical unguarded-`get` pattern also crashes for: - `PolynomialKernelType` missing `gamma` / `coef0` / `degree` (lines 77–79) - `SigmoidKernelType` missing `gamma` / `coef0` (lines 85–86) ## Captured evidence (verbatim) Run under the installed `sklearn-pmml-model==1.0.8`, Python 3.13: ``` === BASELINE === BASELINE OK kernel= rbf gamma= 0.09090909090909091 === POC === Traceback (most recent call last): File "", line 3, in m = PMMLSVC(pmml='/home/kali/hunt-workspace/pmml-13thbug/svc-nogamma.pmml') File "/home/kali/hunt-workspace/pmml-12thbug-venv/lib/python3.13/site-packages/sklearn_pmml_model/svm/_classes.py", line 249, in __init__ PMMLBaseSVM.__init__(self) ~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/home/kali/hunt-workspace/pmml-12thbug-venv/lib/python3.13/site-packages/sklearn_pmml_model/svm/_base.py", line 82, in __init__ self._gamma = self.gamma = float(rbf.get('gamma')) ~~~~~^^^^^^^^^^^^^^^^^^ TypeError: float() argument must be a string or a real number, not 'NoneType' ``` ## Impact - **Type:** Load-time denial of service (unhandled exception during model construction) when importing an untrusted / third-party PMML file. - **Trigger:** A spec-valid `SupportVectorMachineModel` PMML that relies on any documented default for a kernel attribute. No malformed input required. - **Reachability:** Reached by `PMMLSVC`, `PMMLSVR`, and the `auto_detect` dispatcher — the standard public entrypoints for loading SVM PMML models. No prediction call is needed; the crash occurs in `__init__`. ## Suggested fix Apply the spec defaults instead of passing `None` into `float`/`int`, e.g.: ```python self._gamma = self.gamma = float(rbf.get('gamma', 1)) # poly: self._gamma = self.gamma = float(poly.get('gamma', 1)) self.coef0 = float(poly.get('coef0', 1)) self.degree = int(poly.get('degree', 1)) # sigmoid: self._gamma = self.gamma = float(sigmoid.get('gamma', 1)) self.coef0 = float(sigmoid.get('coef0', 1)) ``` ## Dedup note This is distinct from other `sklearn-pmml-model` findings in this program: - The **SVM coefficient-count mismatch** finding concerns `` / support-vector pairing, not kernel-attribute parsing. - The GLM/linreg/logreg/kNN `TypeError` findings concern different modules (`_classes.py` for those estimators), different elements, and different parameters. This finding is specific to `svm/_base.py:72–86` kernel-attribute parsing (`float(None)`/`int(None)` on absent optional attributes). No known CVE corresponds to this specific crash site.