Skip to content

v3.3.0

Choose a tag to compare

@JeanExtreme002 JeanExtreme002 released this 12 Oct 02:25
8f852d6

What's changed

A full technical review of the project — library, platform backends, MCP server, app and CI — re-verified finding by finding. The headline fixes: generators that could keep using a released handle after close() (on Windows, possibly reading another process), a Linux crash that one oddly named process could trigger for every caller, and an MCP server whose self-protection missed the client that launched it under uvx. No public default changed; every fix comes with a test.

Fixed

  • Scan and listing generators are bound to their process object. They captured the raw handle or task port and checked it only when created; after p.close(), Windows can hand the same handle value to the next OpenProcess, so a kept generator could read a different process. Resuming one after close() now raises ClosedProcess (#111)
  • Linux: non-UTF-8 names no longer break listings. /proc/*/comm and /proc/<pid>/maps were read in strict text mode, so a single process with a non-UTF-8 name (any process can set one) broke OpenProcess(name=...) for everyone, and a non-UTF-8 mapped path broke scans of that target. They are decoded with replacement characters (#111)
  • Scan arguments are validated when the method is called — search_by_value(int, value=2**40) or a malformed pattern raises immediately instead of on the first next() (#111)
  • Typed writes: write_uint* truncated floats silently (write_uint(a, 3.7) wrote 3) and now raises TypeError like write_int; out-of-range NumPy integers are range-checked instead of wrapped; OpenProcess(pid=True) is rejected (#111)
  • Pattern and pointer scans: variable-width regexes no longer report a match twice across a chunk boundary; chunk overlaps no longer read past the end of a region; the unaligned pointer map finds pointers that straddle a chunk; intersecting pointer scans keeps 32- and 64-bit paths apart (#111)
  • resolve_pointer_chain raises ValueError — also an OSError, as before — for a hop outside the 64-bit address space instead of reading a silently wrapped address (#111)
  • Windows: a short read no longer leaves a zero-filled tail that could match a scan for 0; get_modules retries on ERROR_BAD_LENGTH and raises ProcessIDNotExistsError for an exited target; long mapped-file names are no longer truncated (#111)
  • macOS: writing to a protected page no longer leaves WRITE in the page's maximum protection; close() reports a failed port release; get_modules raises ProcessIDNotExistsError for an exited target; the region walk no longer wraps or stops at a zero-size region (#111)
  • App: finished scan workers and closed tool dialogs are freed instead of accumulating; cancelling or failing a first scan no longer starts an automatic refresh; promoting results and refining large result sets are linear instead of quadratic (a 200k-row refine went from 18.7 s to 0.7 s); an inline edit on a frozen row is no longer overwritten by the old value, and one against an exited target is reverted with a message; pointer-scan Stop says it stopped, its results have back-pressure, and Compare no longer resolves a path through the previous run's base when its module is not loaded (#111)
  • MCP: bufflength="0" means "use the default" as documented; read_value / write_value echo the normalized value type; a single huge region no longer overruns the scan deadline (#111)

Changed

  • MCP: the whole launch chain is protected. Self-protection covered only the server and its parent — under uvx that is uv, leaving the MCP client attachable. Every ancestor is now refused, which also includes the shell or terminal the server was started from (#111)
  • MCP: approvals are scoped more tightly. Remembering an approval for a shared runtime (python3, node, shells, …) covers only that running process (reported as approval_scope), and a pid reused while the prompt is open is refused; a process whose name cannot be read always asks, even with --allow-any-process; more password managers are denied, and browsers and AI clients are flagged sensitive (#111)
  • MCP: responses are size-capped. Text values of str / bytes scans share a budget per response, reported through additive *_truncated fields, and tool descriptions mark memory contents as untrusted (#111)
  • Faster with NumPy: pointer-map building (about 5x) and integer NOT_EXACT scans use the vectorized path when NumPy is installed (#111)
  • Packaging: psutil 7.x is accepted (<8); PySide6 is capped below 7 (#111)

Added

  • --confirm-writes for the MCP server: it asks the user before every write_value through an elicitation prompt — for clients that do not honour the per-tool confirmation hint. Off by default (#111)
  • prepare_search_target in PyMemoryEditor.util to validate scan arguments up front (#111)
  • CI: CodeQL analysis; releases require a green test run on the release commit and build separately from the PyPI upload; macOS is tested on Python 3.10, 3.12 and 3.14; mypy checks each platform; the dependency audit covers only the extras and blocks on main (#111)

Install

pip install --upgrade PyMemoryEditor

📖 Full documentation: pymemoryeditor.readthedocs.io · MCP guide: pymemoryeditor.readthedocs.io/en/latest/mcp.html

Full Changelog: v3.2.0...v3.3.0