Security: harden HDF5 deserialization and executable lookup - #44
Open
jacquelinegarrahan wants to merge 1 commit into
Open
Security: harden HDF5 deserialization and executable lookup#44jacquelinegarrahan wants to merge 1 commit into
jacquelinegarrahan wants to merge 1 commit into
Conversation
- Reject non-serializable classes before instantiation in HDF5Serializer.deserialize, and validate the 'object' attribute, to limit the deserialization gadget where an untrusted archive names an arbitrary importable class (CWE-502). Add a security warning to the docstring noting import-time side effects are inherent to loading untrusted archives. - Search PATH before the current working directory in find_executable so a binary planted in the cwd cannot shadow a trusted executable (CWE-426). Also guards against PATH being unset. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Security hardening: HDF5 deserialization and executable lookup
Branch:
security/hardening→mainThis PR addresses two security findings from a scan of the codebase. Both are
in code paths that handle attacker-influenceable input (a serialized archive
file; the process working directory). All 121 existing tests pass unchanged.
Scan scope & tooling
bandit -r lume(only low-severityassert/subprocessinformational hits, addressed contextually below).
pip-audit— no known vulnerable dependencies.and
pull_request(notpull_request_target); no secrets exposure found.import paths.
Finding 1 — Deserialization gadget in
HDF5Serializer.deserialize(CWE-502)lume/serializers/hdf5.pydeserialize()reads theobjectattribute from the HDF5 file (a dottedmodule.ClassNamestring), imports that module, fetches the named attribute,instantiates it, and calls
load_archive()on it — all driven entirely byvalues inside the file.
Confirmed exploit. A crafted archive naming
antigravity.foocausedimport antigravityto run its module-level side effect (opening a web page)purely from opening the file:
Fix (defense in depth):
objectattribute is present and well-formed before use.load_archivebefore instantiating it. This blocks the"instantiate an arbitrary class and call
load_archiveon it" gadget(e.g.
os.system,pathlib.Path,builtins.dictare now rejected withClassInitError). The check is intentionally interface-based rather thanissubclass(Base, ...)because the serializer's documented contract isduck-typed (see
test_hdf5.py, which round-trips a non-Baseobject).Residual risk (documented, not fully closable here): the module import
still executes before any class check, so import-time side effects of an
arbitrary installed module remain possible. This is inherent to a
self-describing archive format that names its own class. The robust mitigation
is operational — only deserialize archives from trusted sources — now stated
explicitly in the API docs. A future hardening could add an opt-in module
allowlist.
Finding 2 — Untrusted search path in
find_executable(CWE-426)lume/tools.pyThe executable search built its path as
[os.getcwd(), PATH], soshutil.whichreturned a match in the current working directory in preferenceto PATH. A binary planted in a working directory (e.g. a shared scratch or
download dir) named after the expected simulation code would run instead of the
trusted one on PATH.
Fix: search
PATHbefore the cwd. An executable that legitimately livesonly in the working directory is still found; the only behavior change is that a
same-named binary on PATH now wins over one in the cwd. Also guards against
PATHbeing unset (previouslyos.pathsep.join([...None...])would raiseTypeError).Not changed (reviewed, acceptable)
subprocessusage intools.py(execute,execute2) uses list-formargs with
shell=False— no shell injection. Bandit's B603/B404 areinformational for this pattern.
assertstatements inbase.py/tools.py(bandit B101) are argumentvalidation, not security controls; left as-is to avoid behavior changes.
yaml.safe_loadis already used (notyaml.load) — safe.Verification
Plus the confirmed-exploit archives above are now rejected with
ClassInitErrorbefore instantiation.
🤖 Generated with Claude Code