Files
parsedmarc/tests
Sean WhalenandClaude Opus 5 d185bd0526 Fix file descriptor leak in parse_report_file (CodeQL py/file-not-closed) (#899)
parse_report_file's path branch opened the file with open(file_path, "rb")
and then, on a later line, called file_object.read() followed by
file_object.close() with no exception handling between them. An exception
from read() (e.g. an OSError from the underlying storage) skipped close()
and left the descriptor to be released only when Python's garbage
collector eventually finalized the object, rather than closed
deterministically (CPython's io.IOBase.__del__ closes an unclosed file on
finalization: https://docs.python.org/3/library/io.html). This is the
pattern CodeQL's py/file-not-closed query flags, found in a local
code-quality scan.

The path branch now opens the file with a `with` block, so the handle is
closed on both the success and exception paths. The fix is scoped to only
that branch: a BytesIO created internally from bytes/bytearray/memoryview
input, and a file-like object supplied by the caller, are both closed
only after a successful read and left open if read() raises, exactly as
before this commit. Widening that to close a caller-supplied handle on
the exception path too would be a behavior change outside this fix's
scope.

Also closes tests/test_init.py's TestGetDmarcReportsFromMailboxMaildir
._deliver, which did open(source, "rb").read() with no close() at all;
it now uses a `with` block.

Two regression tests cover the narrowed contract:
- testParseReportFileClosesHandleOnReadError patches builtins.open (the
  SDK boundary) with a fake handle implementing the context-manager
  protocol, and asserts close() is called when read() raises. Verified
  against origin/master's version of parsedmarc/__init__.py: the test
  fails with "AssertionError: False is not true" (close was never
  called), confirming it catches the leak.
- testParseReportFileLeavesCallerHandleOpenOnReadError passes a
  caller-supplied fake handle directly as input_ and asserts close() is
  NOT called when read() raises. Verified against a naive whole-block
  try/finally (wrapping close() around all three branches instead of
  only the path branch): the test fails with "AssertionError: True is
  not false" (close was called), confirming it catches the scope
  widening this PR must avoid.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 18:44:38 -04:00
..