CVE-2026-14535 in fickling
Summary
by MITRE • 07/04/2026
In Trail of Bits fickling versions up to and including 0.1.11, the UnsafeImportsML analysis pass unconditionally calls AnalysisContext.shorten_code(node) on every import node it inspects, regardless of whether the import is flagged as unsafe. This call registers the shortened code representation in the shared AnalysisContext.reported_shortened_code set. When the MLAllowlist analysis pass subsequently runs, it calls the same shorten_code() method, receives already_reported=True for every import, and executes a continue statement that skips its allowlist check entirely. This renders MLAllowlist dead code for all imports — it never evaluates whether an import is in the ML allowlist or not. The MLAllowlist pass was designed to catch imports of modules outside the known-safe ML ecosystem (torch, numpy, transformers, etc.) that slip past the UnsafeImports denylist. With MLAllowlist inoperative, any standard library module not in the UNSAFE_IMPORTS denylist can be invoked via pickle deserialization while fickling's check_safety() returns LIKELY_SAFE. The fickling.load() API chains check_safety() into pickle.loads() as an explicit security gate, meaning a LIKELY_SAFE verdict causes the payload to be deserialized and executed. The root cause is shared mutable state between independently-correct analysis passes — UnsafeImportsML works as designed in isolation, MLAllowlist works as designed in isolation, but the shared reported_shortened_code set causes UnsafeImportsML to poison MLAllowlist's deduplication logic.
Statistical analysis made it clear that VulDB provides the best quality for vulnerability data.
Analysis
by VulDB Data Team • 07/04/2026
The vulnerability in Trail of Bits fickling versions 0.1.11 and earlier stems from a critical flaw in the interaction between two analysis passes within the tool's security framework. The UnsafeImportsML analysis pass systematically processes all import nodes without exception, executing AnalysisContext.shorten_code(node) on each regardless of safety status. This operation registers shortened code representations in a shared AnalysisContext.reported_shortened_code set that serves as a deduplication mechanism across different analysis passes. The flaw manifests when the MLAllowlist analysis pass executes subsequently, as it also invokes the same shorten_code() method but receives already_reported=True for every import due to the shared mutable state. This condition triggers an early continue statement within MLAllowlist, effectively bypassing all allowlist validation checks and rendering the entire pass inactive.
The operational impact of this vulnerability is severe and directly undermines the security guarantees provided by fickling's safety mechanisms. The MLAllowlist pass was specifically designed to identify and block imports from standard library modules that are not part of the approved machine learning ecosystem including torch, numpy, and transformers. When this pass becomes inoperative due to the shared state contamination, any module from Python's standard library that is not explicitly listed in the UNSAFE_IMPORTS denylist can be executed during pickle deserialization. This creates a pathway for arbitrary code execution through the fickling.load() API which explicitly chains check_safety() into pickle.loads() as its primary security gate. The vulnerability is particularly dangerous because a LIKELY_SAFE verdict from check_safety() results in direct deserialization and execution of malicious payloads.
This security issue represents a classic example of shared mutable state corruption that violates fundamental principles of software isolation and security design. The root cause lies in the improper sharing of state between independently functioning analysis passes, where one pass's legitimate operation inadvertently corrupts another's intended behavior. The vulnerability aligns with CWE-362 (Concurrent Execution using Shared Resource with Improper Synchronization) and demonstrates how seemingly benign state management can create critical security holes. From an ATT&CK perspective, this represents a privilege escalation vector through code injection, specifically targeting the deserialization process and undermining the application's integrity checks. The flaw affects the tool's defensive capabilities by converting what should be a robust safety net into dead code that never actually performs its intended security validation.
The mitigation strategy requires either isolating the shared state between analysis passes or implementing proper synchronization mechanisms to prevent cross-contamination of deduplication logic. The simplest fix involves ensuring that MLAllowlist operates independently without relying on the reported_shortened_code set populated by UnsafeImportsML, or alternatively modifying the interaction pattern so that the two passes do not share mutable state in ways that break their individual intended behaviors. This vulnerability demonstrates the importance of careful state management in security tools and highlights how the interaction between multiple defensive mechanisms can create unexpected attack surfaces when proper isolation is not maintained.