Skip to content

Workspace files can make DataLab import modules and call functions through stored parameter sets #407

Closed

Description

@PierreRaybaut

Summary

When DataLab reads the parameter sets stored in a workspace (HDF5 file), it decodes them with guidata's json_to_dataset in its permissive mode. This concerns processing parameters, analysis-result parameters, creation parameters and History arguments.

The permissive mode imports the module named by the class_module field of the stored JSON and calls the object named by class_name without arguments, before checking anything. A crafted workspace can therefore make DataLab import any installed module, which runs its import-time code, and call any zero-argument callable of that module. Examples are os.abort, which crashes the application, and tempfile.mkdtemp, which creates a folder.

When it happens

It is enough to open a received workspace and select an object:

  • Properties panel: display_analysis_parameters decodes analysis-result parameters (BaseResultAdapter.get_param).
  • Processing tab, History tools and recomputation: extract_processing_parameters decodes stored processing parameters (ProcessingParameters.set_param_from_json).
  • Creation tab: extract_creation_parameters and convert_legacy_creation_parameters.
  • History sessions read from a workspace or a .dlhist file: decode_kwargs.

The pattern exists since DataLab 1.0, with 5 call sites, and has 9 call sites in 1.3.0 and develop.

Harmless reproduction

import json, os, tempfile
import guidata.dataset as gds
from sigima.params import NormalizeParam
from datalab.gui.processor.base import ProcessingParameters

payload = json.loads(gds.dataset_to_json(NormalizeParam()))
payload.update(class_module="tempfile", class_name="mkdtemp")
before = set(os.listdir(tempfile.gettempdir()))
ProcessingParameters("normalize", "1-to-1").set_param_from_json(json.dumps(payload))
print(set(os.listdir(tempfile.gettempdir())) - before)  # a folder was created

DataLab only shows the warning "Failed to deserialize processing parameters from JSON." after the call has happened. With "class_module": "this", the module is imported and prints its poem.

Impact

A file cannot supply code or arguments: only code already installed can be reached. Effects range from a crash or an exit (denial of service) to side effects of installed modules. The issue would only become arbitrary code execution if a malicious module were already importable.

Workspace capsules make exchanging workspaces between users routine, so a file should not be able to choose the code DataLab loads.

Not affected:

  • capsule inspection, verification and replay, which decode parameters through the Sigima operation contracts;
  • DataLab-Web;
  • ROI payloads of the History, already restricted to sigima. ROI classes.

Proposed fix

Decode every stored parameter set through one helper that resolves its class safely:

  • the class must be a guidata DataSet subclass;
  • its module must either be already loaded by DataLab (Sigima, DataLab, guidata, installed plugins) or belong to the sigima, sigimax, datalab or guidata packages, which may be imported;
  • nothing else is imported or called, and a refused payload is reported like any other unreadable parameter set.

Field reading stays tolerant, as today: unknown fields are ignored and missing ones keep their defaults. Workspaces saved by earlier versions therefore keep their parameters.

Out of scope

  • Remote control (XML-RPC): its clients can already import and run macros by design.
  • NativeH5Reader callable references: they are restricted to packages whose name starts with datalab, and are returned, not called.

Activity

  1. PierreRaybaut commented on Oct 20, 2026

    @PierreRaybaut
    ContributorAuthor

    Fixed by #408 (merged into \develop\ as b6d78b3), to be released in DataLab 1.4.0. Stored parameter sets are now decoded by \datalab.utils.datasetjson.decode_dataset_json, which only builds guidata \DataSet\ subclasses from modules already loaded by DataLab or from public modules of DataLab's own packages; nothing else is imported or called.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions