Skip to content

Fix DoS vulnerability and resource leak in from_json() and save2file() - #247

Open
lilu5458 wants to merge 1 commit into
caesar0301:masterfrom
lilu5458:fix/from-json-dos-and-fd-leak
Open

Fix DoS vulnerability and resource leak in from_json() and save2file()#247
lilu5458 wants to merge 1 commit into
caesar0301:masterfrom
lilu5458:fix/from-json-dos-and-fd-leak

Conversation

@lilu5458

Copy link
Copy Markdown

Summary

This PR fixes two security issues identified during a white-box audit of treelib/tree.py:

  1. Denial-of-Service via deeply nested JSON (CWE-400)Tree.from_json() relied on unbounded recursion in _append_node(). A malicious JSON payload with deep nesting caused RecursionError (crash) and could be used to exhaust the interpreter stack.

  2. File descriptor leak in save2file() (CWE-404) — the inner handler() callback opened the target file on every emitted line and never closed it, leaking one file descriptor per line. Saving a large tree would exhaust the process file-descriptor limit.

Changes

treelib/tree.py only (1 file, +29/-9):

  • from_json():
    • Catch RecursionError from json.loads() and raise a clear ValueError.
    • Validate that the parsed root is a dict; raise ValueError otherwise.
    • Add a max_depth (500) guard to _append_node() so traversal depth is bounded.
    • Handle non-dict node_info gracefully (treat as leaf data).
  • save2file():
    • Open the file once, reuse the handle in the print callback, and close it when done.

Validation

  • Existing test suite: python -m pytest tests/ — all 210 tests pass.
  • PoC poc_recursion_dos.py: a 600-level nested payload now raises ValueError instead of RecursionError.
  • PoC poc_fd_leak.py: large-tree save no longer leaks file descriptors.

Backward Compatibility

Behavior is unchanged for well-formed inputs. Only malformed/malicious inputs that previously raised RecursionError or AttributeError, or leaked file descriptors, now raise ValueError / close the handle. The max_depth of 500 is well above any realistic tree depth.

- from_json(): Add recursion depth limit (max_depth=500) to prevent
  RecursionError DoS when parsing deeply nested JSON (CWE-400)
- from_json(): Catch RecursionError from json.loads and raise ValueError
  with a meaningful message
- from_json(): Validate root JSON is a dict before processing
- from_json(): Handle non-dict node_info gracefully instead of
  raising AttributeError (input validation)
- save2file(): Fix file descriptor leak by opening file once instead
  of per-line (CWE-404)
@lilu5458
lilu5458 requested a review from liamlundy as a code owner July 28, 2026 02:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant