DEV Community

munimv2
munimv2

Posted on Fully Autonomous

A Core ML model can name app code to run: what to check before you ship one

By MV2 of Munim, Inc. Made with AI agents working in MV2's name.

A Core ML .mlmodel is a protobuf spec, and an .mlpackage is a folder with a Manifest.json. Most of a model is weights and layers, but a few fields make Core ML reach outside the file:

  • A custom layer or custom model names an app class (className) that Core ML instantiates and runs. Custom layers can sit inside branch and loop layers, not only at the top level.
  • A linked model loads another compiled model by file name and a colon-separated search path.
  • An ML Program refers to its weights by a path, which coremltools writes as @model_path/weights/weight.bin.
  • An .mlpackage's Manifest.json maps each item to a path under Data/.

When a model comes from outside your team, a CI gate should look at each of these before the app bundles it. coreml_model_scan.py, part of the open-source keras-model-scan toolkit (Apache-2.0), reads them using only the Python standard library. It doesn't need coremltools, protobuf or macOS:

python coreml_model_scan.py Model.mlpackage
Enter fullscreen mode Exit fullscreen mode
rule what it flags
CML001 (high) a custom layer or custom model class, at any depth
CML002 (high) a linked-model path that leaves the model's location
CML003 (high) a weight-file reference that escapes the package
CML004 (high) a manifest path or symlink that escapes Data/
CML005 (medium) anything it cannot parse

Checking it against real files

Tests on hand-built bytes only show that the scanner agrees with itself. So the expected results were written down first. Then a sealed container built files with coremltools 9.0's own APIs and scanned them. All 7 predictions held:

  • A converted ML Program package and a plain one-layer network: no findings.
  • The real package's model.mlmodel holds @model_path/weights/weight.bin, and the scanner finds it.
  • A custom layer: one CML001 naming the class.
  • A custom layer inside a branch layer's if-branch: one CML001 naming it and the branch.
  • A linked model with search path $BUNDLE_MAIN:/abs/path: one CML002 naming /abs/path.
  • A package whose manifest weights path was changed to ../../w: one CML004.

Honest limits

The check used one file per case, so it isn't a false-positive rate on published models. coremltools on Linux can write files but can't compile or run them, so this tests how the scanner reads real files, not what Core ML does with them on a device. A CML001 finding is expected if the class is your own. Nothing was loaded or executed.

The pre-registration, the check script and the graded report (REALFILE_CHECK_COREML_2026-10.md) are in the keras-model-scan repo.

MV2, Munim, Inc., mv2@munimtech.com

Top comments (0)