Heuristic:Trailofbits Fickling Severity Threshold Selection
| Knowledge Sources | |
|---|---|
| Domains | Security, CI_CD, ML_Safety |
| Last Updated | 2026-02-14 13:00 GMT |
Overview
Guidelines for selecting the appropriate severity threshold when integrating fickling's safety analysis into CI/CD pipelines and automated model validation workflows.
Description
Fickling classifies pickle file safety using a six-level severity scale from `LIKELY_SAFE` (0) to `OVERTLY_MALICIOUS` (5). The `max_acceptable_severity` parameter in `fickling.load()` and the verbosity threshold in `check_safety()` determine what level of risk triggers rejection. Choosing the wrong threshold leads to either false positives blocking legitimate models or false negatives allowing malicious files through.
Usage
Use this heuristic when configuring fickling in CI/CD pipelines, model registries, or automated scanning systems where you need to decide which severity level should trigger a block or alert.
The Insight (Rule of Thumb)
- Strict (recommended for production): Use `max_acceptable_severity=Severity.LIKELY_SAFE` (the default). Only files with zero safety findings are loaded. Any non-standard import triggers rejection.
- Moderate (for development): Use `Severity.POSSIBLY_UNSAFE`. Allows files flagged as possibly unsafe but blocks anything suspicious or worse. Useful when working with models from known-safe sources that use non-standard imports.
- Permissive (for research only): Use `Severity.SUSPICIOUS` or higher. Only blocks overtly malicious files. Not recommended for production as it allows many attack patterns through.
- CLI exit codes: When using `fickling --check-safety`, the tool returns exit code 1 for any severity > `LIKELY_SAFE` and exit code 0 otherwise. There is no CLI flag to adjust the threshold; use the Python API for custom thresholds.
Reasoning
The six severity levels map to specific detection patterns:
| Level | Value | Triggers | Recommendation |
|---|---|---|---|
| LIKELY_SAFE | 0 | No findings | Always accept |
| POSSIBLY_UNSAFE | 1 | Non-standard but potentially benign patterns | Review case-by-case |
| SUSPICIOUS | 2 | Unused variables assigned from function calls | Investigate before accepting |
| LIKELY_UNSAFE | 3 | Non-standard imports, duplicate/misplaced PROTO opcodes, invalid opcodes | Block in production |
| LIKELY_OVERTLY_MALICIOUS | 4 | Imports from os, subprocess, builtins, etc. | Always block |
| OVERTLY_MALICIOUS | 5 | Direct calls to eval(), exec(), compile(), open() | Always block |
The default `LIKELY_SAFE` threshold is deliberately strict because pickle deserialization is inherently dangerous — even a single non-standard import can lead to arbitrary code execution. The analysis result includes a warning: "Fickling failed to detect any overtly unsafe code, but the pickle file may still be unsafe."
Code evidence from `fickling/loader.py:9-11`:
def load(
file,
max_acceptable_severity=Severity.LIKELY_SAFE,
Severity enum from `fickling/analysis.py:76-82`:
class Severity(Enum):
LIKELY_SAFE = (0, "No Unsafe Operations Discovered")
POSSIBLY_UNSAFE = (1, "Possibly Unsafe")
SUSPICIOUS = (2, "Suspicious")
LIKELY_UNSAFE = (3, "Likely Unsafe")
LIKELY_OVERTLY_MALICIOUS = (4, "Likely Overtly Malicious")
OVERTLY_MALICIOUS = (5, "Overtly Malicious")