Skip to content

Chunked scans miss EXACT/NOT_EXACT matches that straddle a 256 MiB chunk boundary (overlap only applied to str) #85

Description

@krishna3554

Summary

In process/scanning.py, iter_search_results reads bufflength - 1 extra bytes past a chunk boundary only when pytype is str. But EXACT_VALUE and NOT_EXACT_VALUE scans are routed through scan_memory_for_exact_value, which locates the target bytes with bytes.find at any byte offset — not just the bufflength-aligned offsets a stepping numeric scan visits. For any region larger than DEFAULT_MAX_REGION_CHUNK (256 MiB), an exact match whose bytes cross an internal chunk boundary is found by neither chunk and is silently dropped from the results.

Location

  • File: PyMemoryEditor/process/scanning.py
  • Function: iter_search_results
  • Relevant code:
str_overlap=bufflength-1ifpytypeisstrelse0
...
is_last_chunk=chunk_offset+chunk_size>=sizeread_size=chunk_size+ (0ifis_last_chunkelsestr_overlap)

with the routing decision a few lines above:

ifscan_typein (ScanTypesEnum.EXACT_VALUE, ScanTypesEnum.NOT_EXACT_VALUE):
searching_method=scan_memory_for_exact_value

Problem

The overlap logic assumes non-string scans step in bufflength strides, so a typed numeric scan can never have a value straddle a boundary — the rationale documented on util.scan.iter_region_chunks. That holds for the comparison scans driven by scan_memory (BIGGER_THAN, VALUE_BETWEEN, ...), which iterate aligned windows inside each chunk. It does not hold for the two scan types that bypass scan_memory:

  1. EXACT_VALUEscan_memory_for_exact_value runs data.find(target) from every offset of the buffer it receives. Chunk N ends mid-value and chunk N+1 starts after the value began, so no chunk ever sees the complete byte sequence → missed result.
  2. NOT_EXACT_VALUE — the candidate range per chunk is range(0, read_size - bufflength + 1, step). Windows straddling a boundary fall outside both neighboring chunks' ranges, so offsets whose value differs from the target are silently omitted there as well.

Since EXACT_VALUE is the default scan_type of search_by_value, this affects the library's primary code path whenever a single region exceeds 256 MiB — large heaps (browsers, JVMs) being exactly the case the 256 MiB cap exists for.

Trigger / Reproduction

Static-analysis finding (not executed); the trace follows the code paths above.

  • A region larger than 256 MiB, split by iter_region_chunks into aligned chunks.
  • An int32 (or any-width) target whose bytes sit across a chunk boundary, e.g. starting at chunk_size - 2.
  • search_by_value(int, value=..., scan_type=ScanTypesEnum.EXACT_VALUE) over that region reports nothing for that address; the same scan on an unchunked region finds it.

The existing test tests/scan/test_chunking_integration.py::test_scan_memory_across_chunked_region_finds_all_matches plants targets at offsets 100 / 5000 / 60000 in each 64 KiB chunk — all safely ≥ 4 bytes from the end — so the straddling case is currently untested.

Expected Behavior

Every exact match present in the region should be reported regardless of where it falls relative to internal read chunks; NOT_EXACT should evaluate every candidate window exactly once.

Actual Behavior

Matches/windows that straddle an interior chunk boundary of a >256 MiB region are skipped without any error or warning.

Impact

Silent false negatives in memory scans — a particularly bad failure mode for a cheat-engine-style workflow, because the user concludes the value is not present rather than suspecting the tool. The larger the target process's regions, the more likely a boundary lands mid-value.

Suggested Direction

Widen the overlap condition to cover every scan mode that can match at a non-aligned offset, e.g.

needs_overlap=pytypeisstrorscan_typein (
ScanTypesEnum.EXACT_VALUE,
ScanTypesEnum.NOT_EXACT_VALUE,
)
overlap=bufflength-1ifneeds_overlapelse0

The existing clamp (if offset >= chunk_size: continue) already prevents double-emission of overlap-region matches, and the stepping comparisons driven by scan_memory keep their current no-overlap behavior unchanged. A regression test planting a target at chunk_size - bufflength // 2 would pin this.

Evidence

  • process/scanning.py gates the extra read solely on pytype is str, while the routing block sends EXACT/NOT_EXACT to the offset-arbitrary scan_memory_for_exact_value.
  • util/scan.py::scan_memory_for_exact_value yields every data.find hit with stride 1 — alignment is not considered.
  • util/scan.py::iter_region_chunks docstring documents the aligned-stride assumption that EXACT scanning does not follow.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
       blocks
      (function() {
      function addCopyButtons() {
      document.querySelectorAll('pre code').forEach(function(codeBlock) {
      if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
      codeBlock.parentElement.setAttribute('data-copy-added', 'true');
      var btn = document.createElement('button');
      btn.textContent = 'Copy';
      btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
      btn.onmouseover = function() { this.style.opacity = '1'; };
      btn.onmouseout = function() { this.style.opacity = '0.7'; };
      btn.onclick = function() {
      navigator.clipboard.writeText(codeBlock.textContent).then(function() {
      btn.textContent = 'Copied!';
      setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
      });
      };
      codeBlock.parentElement.style.position = 'relative';
      codeBlock.parentElement.appendChild(btn);
      });
      }
      addCopyButtons();
      // Re-run on dynamic content
      var observer = new MutationObserver(addCopyButtons);
      observer.observe(document.body, { childList: true, subtree: true });
      })();
      }
      } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
      })();
      (function(){
      try {
      var __m = "github.com";
      var __re = new RegExp('^' + "github\\.com" + '
      Chunked scans miss EXACT/NOT_EXACT matches that straddle a 256 MiB chunk boundary (overlap only applied to str) · Issue #85 · JeanExtreme002/PyMemoryEditor · GitHub
      Skip to content

      Chunked scans miss EXACT/NOT_EXACT matches that straddle a 256 MiB chunk boundary (overlap only applied to str) #85

      Description

      @krishna3554

      Summary

      In process/scanning.py, iter_search_results reads bufflength - 1 extra bytes past a chunk boundary only when pytype is str. But EXACT_VALUE and NOT_EXACT_VALUE scans are routed through scan_memory_for_exact_value, which locates the target bytes with bytes.find at any byte offset — not just the bufflength-aligned offsets a stepping numeric scan visits. For any region larger than DEFAULT_MAX_REGION_CHUNK (256 MiB), an exact match whose bytes cross an internal chunk boundary is found by neither chunk and is silently dropped from the results.

      Location

      • File: PyMemoryEditor/process/scanning.py
      • Function: iter_search_results
      • Relevant code:
      str_overlap=bufflength-1ifpytypeisstrelse0
      ...
      is_last_chunk=chunk_offset+chunk_size>=sizeread_size=chunk_size+ (0ifis_last_chunkelsestr_overlap)

      with the routing decision a few lines above:

      ifscan_typein (ScanTypesEnum.EXACT_VALUE, ScanTypesEnum.NOT_EXACT_VALUE):
      searching_method=scan_memory_for_exact_value

      Problem

      The overlap logic assumes non-string scans step in bufflength strides, so a typed numeric scan can never have a value straddle a boundary — the rationale documented on util.scan.iter_region_chunks. That holds for the comparison scans driven by scan_memory (BIGGER_THAN, VALUE_BETWEEN, ...), which iterate aligned windows inside each chunk. It does not hold for the two scan types that bypass scan_memory:

      1. EXACT_VALUEscan_memory_for_exact_value runs data.find(target) from every offset of the buffer it receives. Chunk N ends mid-value and chunk N+1 starts after the value began, so no chunk ever sees the complete byte sequence → missed result.
      2. NOT_EXACT_VALUE — the candidate range per chunk is range(0, read_size - bufflength + 1, step). Windows straddling a boundary fall outside both neighboring chunks' ranges, so offsets whose value differs from the target are silently omitted there as well.

      Since EXACT_VALUE is the default scan_type of search_by_value, this affects the library's primary code path whenever a single region exceeds 256 MiB — large heaps (browsers, JVMs) being exactly the case the 256 MiB cap exists for.

      Trigger / Reproduction

      Static-analysis finding (not executed); the trace follows the code paths above.

      • A region larger than 256 MiB, split by iter_region_chunks into aligned chunks.
      • An int32 (or any-width) target whose bytes sit across a chunk boundary, e.g. starting at chunk_size - 2.
      • search_by_value(int, value=..., scan_type=ScanTypesEnum.EXACT_VALUE) over that region reports nothing for that address; the same scan on an unchunked region finds it.

      The existing test tests/scan/test_chunking_integration.py::test_scan_memory_across_chunked_region_finds_all_matches plants targets at offsets 100 / 5000 / 60000 in each 64 KiB chunk — all safely ≥ 4 bytes from the end — so the straddling case is currently untested.

      Expected Behavior

      Every exact match present in the region should be reported regardless of where it falls relative to internal read chunks; NOT_EXACT should evaluate every candidate window exactly once.

      Actual Behavior

      Matches/windows that straddle an interior chunk boundary of a >256 MiB region are skipped without any error or warning.

      Impact

      Silent false negatives in memory scans — a particularly bad failure mode for a cheat-engine-style workflow, because the user concludes the value is not present rather than suspecting the tool. The larger the target process's regions, the more likely a boundary lands mid-value.

      Suggested Direction

      Widen the overlap condition to cover every scan mode that can match at a non-aligned offset, e.g.

      needs_overlap=pytypeisstrorscan_typein (
      ScanTypesEnum.EXACT_VALUE,
      ScanTypesEnum.NOT_EXACT_VALUE,
      )
      overlap=bufflength-1ifneeds_overlapelse0

      The existing clamp (if offset >= chunk_size: continue) already prevents double-emission of overlap-region matches, and the stepping comparisons driven by scan_memory keep their current no-overlap behavior unchanged. A regression test planting a target at chunk_size - bufflength // 2 would pin this.

      Evidence

      • process/scanning.py gates the extra read solely on pytype is str, while the routing block sends EXACT/NOT_EXACT to the offset-arbitrary scan_memory_for_exact_value.
      • util/scan.py::scan_memory_for_exact_value yields every data.find hit with stride 1 — alignment is not considered.
      • util/scan.py::iter_region_chunks docstring documents the aligned-stride assumption that EXACT scanning does not follow.

      Metadata

      Metadata

      Assignees

      No one assigned

        Labels

        No labels
        No labels

        Projects

        No projects

          Milestone

          No milestone

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Chunked scans miss EXACT/NOT_EXACT matches that straddle a 256 MiB chunk boundary (overlap only applied to str) · Issue #85 · JeanExtreme002/PyMemoryEditor · GitHub
          Skip to content

          Chunked scans miss EXACT/NOT_EXACT matches that straddle a 256 MiB chunk boundary (overlap only applied to str) #85

          Description

          @krishna3554

          Summary

          In process/scanning.py, iter_search_results reads bufflength - 1 extra bytes past a chunk boundary only when pytype is str. But EXACT_VALUE and NOT_EXACT_VALUE scans are routed through scan_memory_for_exact_value, which locates the target bytes with bytes.find at any byte offset — not just the bufflength-aligned offsets a stepping numeric scan visits. For any region larger than DEFAULT_MAX_REGION_CHUNK (256 MiB), an exact match whose bytes cross an internal chunk boundary is found by neither chunk and is silently dropped from the results.

          Location

          • File: PyMemoryEditor/process/scanning.py
          • Function: iter_search_results
          • Relevant code:
          str_overlap=bufflength-1ifpytypeisstrelse0
          ...
          is_last_chunk=chunk_offset+chunk_size>=sizeread_size=chunk_size+ (0ifis_last_chunkelsestr_overlap)

          with the routing decision a few lines above:

          ifscan_typein (ScanTypesEnum.EXACT_VALUE, ScanTypesEnum.NOT_EXACT_VALUE):
          searching_method=scan_memory_for_exact_value

          Problem

          The overlap logic assumes non-string scans step in bufflength strides, so a typed numeric scan can never have a value straddle a boundary — the rationale documented on util.scan.iter_region_chunks. That holds for the comparison scans driven by scan_memory (BIGGER_THAN, VALUE_BETWEEN, ...), which iterate aligned windows inside each chunk. It does not hold for the two scan types that bypass scan_memory:

          1. EXACT_VALUEscan_memory_for_exact_value runs data.find(target) from every offset of the buffer it receives. Chunk N ends mid-value and chunk N+1 starts after the value began, so no chunk ever sees the complete byte sequence → missed result.
          2. NOT_EXACT_VALUE — the candidate range per chunk is range(0, read_size - bufflength + 1, step). Windows straddling a boundary fall outside both neighboring chunks' ranges, so offsets whose value differs from the target are silently omitted there as well.

          Since EXACT_VALUE is the default scan_type of search_by_value, this affects the library's primary code path whenever a single region exceeds 256 MiB — large heaps (browsers, JVMs) being exactly the case the 256 MiB cap exists for.

          Trigger / Reproduction

          Static-analysis finding (not executed); the trace follows the code paths above.

          • A region larger than 256 MiB, split by iter_region_chunks into aligned chunks.
          • An int32 (or any-width) target whose bytes sit across a chunk boundary, e.g. starting at chunk_size - 2.
          • search_by_value(int, value=..., scan_type=ScanTypesEnum.EXACT_VALUE) over that region reports nothing for that address; the same scan on an unchunked region finds it.

          The existing test tests/scan/test_chunking_integration.py::test_scan_memory_across_chunked_region_finds_all_matches plants targets at offsets 100 / 5000 / 60000 in each 64 KiB chunk — all safely ≥ 4 bytes from the end — so the straddling case is currently untested.

          Expected Behavior

          Every exact match present in the region should be reported regardless of where it falls relative to internal read chunks; NOT_EXACT should evaluate every candidate window exactly once.

          Actual Behavior

          Matches/windows that straddle an interior chunk boundary of a >256 MiB region are skipped without any error or warning.

          Impact

          Silent false negatives in memory scans — a particularly bad failure mode for a cheat-engine-style workflow, because the user concludes the value is not present rather than suspecting the tool. The larger the target process's regions, the more likely a boundary lands mid-value.

          Suggested Direction

          Widen the overlap condition to cover every scan mode that can match at a non-aligned offset, e.g.

          needs_overlap=pytypeisstrorscan_typein (
          ScanTypesEnum.EXACT_VALUE,
          ScanTypesEnum.NOT_EXACT_VALUE,
          )
          overlap=bufflength-1ifneeds_overlapelse0

          The existing clamp (if offset >= chunk_size: continue) already prevents double-emission of overlap-region matches, and the stepping comparisons driven by scan_memory keep their current no-overlap behavior unchanged. A regression test planting a target at chunk_size - bufflength // 2 would pin this.

          Evidence

          • process/scanning.py gates the extra read solely on pytype is str, while the routing block sends EXACT/NOT_EXACT to the offset-arbitrary scan_memory_for_exact_value.
          • util/scan.py::scan_memory_for_exact_value yields every data.find hit with stride 1 — alignment is not considered.
          • util/scan.py::iter_region_chunks docstring documents the aligned-stride assumption that EXACT scanning does not follow.

          Metadata

          Metadata

          Assignees

          No one assigned

            Labels

            No labels
            No labels

            Projects

            No projects

              Milestone

              No milestone

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Chunked scans miss EXACT/NOT_EXACT matches that straddle a 256 MiB chunk boundary (overlap only applied to str) · Issue #85 · JeanExtreme002/PyMemoryEditor · GitHub
              Skip to content

              Chunked scans miss EXACT/NOT_EXACT matches that straddle a 256 MiB chunk boundary (overlap only applied to str) #85

              Description

              @krishna3554

              Summary

              In process/scanning.py, iter_search_results reads bufflength - 1 extra bytes past a chunk boundary only when pytype is str. But EXACT_VALUE and NOT_EXACT_VALUE scans are routed through scan_memory_for_exact_value, which locates the target bytes with bytes.find at any byte offset — not just the bufflength-aligned offsets a stepping numeric scan visits. For any region larger than DEFAULT_MAX_REGION_CHUNK (256 MiB), an exact match whose bytes cross an internal chunk boundary is found by neither chunk and is silently dropped from the results.

              Location

              • File: PyMemoryEditor/process/scanning.py
              • Function: iter_search_results
              • Relevant code:
              str_overlap=bufflength-1ifpytypeisstrelse0
              ...
              is_last_chunk=chunk_offset+chunk_size>=sizeread_size=chunk_size+ (0ifis_last_chunkelsestr_overlap)

              with the routing decision a few lines above:

              ifscan_typein (ScanTypesEnum.EXACT_VALUE, ScanTypesEnum.NOT_EXACT_VALUE):
              searching_method=scan_memory_for_exact_value

              Problem

              The overlap logic assumes non-string scans step in bufflength strides, so a typed numeric scan can never have a value straddle a boundary — the rationale documented on util.scan.iter_region_chunks. That holds for the comparison scans driven by scan_memory (BIGGER_THAN, VALUE_BETWEEN, ...), which iterate aligned windows inside each chunk. It does not hold for the two scan types that bypass scan_memory:

              1. EXACT_VALUEscan_memory_for_exact_value runs data.find(target) from every offset of the buffer it receives. Chunk N ends mid-value and chunk N+1 starts after the value began, so no chunk ever sees the complete byte sequence → missed result.
              2. NOT_EXACT_VALUE — the candidate range per chunk is range(0, read_size - bufflength + 1, step). Windows straddling a boundary fall outside both neighboring chunks' ranges, so offsets whose value differs from the target are silently omitted there as well.

              Since EXACT_VALUE is the default scan_type of search_by_value, this affects the library's primary code path whenever a single region exceeds 256 MiB — large heaps (browsers, JVMs) being exactly the case the 256 MiB cap exists for.

              Trigger / Reproduction

              Static-analysis finding (not executed); the trace follows the code paths above.

              • A region larger than 256 MiB, split by iter_region_chunks into aligned chunks.
              • An int32 (or any-width) target whose bytes sit across a chunk boundary, e.g. starting at chunk_size - 2.
              • search_by_value(int, value=..., scan_type=ScanTypesEnum.EXACT_VALUE) over that region reports nothing for that address; the same scan on an unchunked region finds it.

              The existing test tests/scan/test_chunking_integration.py::test_scan_memory_across_chunked_region_finds_all_matches plants targets at offsets 100 / 5000 / 60000 in each 64 KiB chunk — all safely ≥ 4 bytes from the end — so the straddling case is currently untested.

              Expected Behavior

              Every exact match present in the region should be reported regardless of where it falls relative to internal read chunks; NOT_EXACT should evaluate every candidate window exactly once.

              Actual Behavior

              Matches/windows that straddle an interior chunk boundary of a >256 MiB region are skipped without any error or warning.

              Impact

              Silent false negatives in memory scans — a particularly bad failure mode for a cheat-engine-style workflow, because the user concludes the value is not present rather than suspecting the tool. The larger the target process's regions, the more likely a boundary lands mid-value.

              Suggested Direction

              Widen the overlap condition to cover every scan mode that can match at a non-aligned offset, e.g.

              needs_overlap=pytypeisstrorscan_typein (
              ScanTypesEnum.EXACT_VALUE,
              ScanTypesEnum.NOT_EXACT_VALUE,
              )
              overlap=bufflength-1ifneeds_overlapelse0

              The existing clamp (if offset >= chunk_size: continue) already prevents double-emission of overlap-region matches, and the stepping comparisons driven by scan_memory keep their current no-overlap behavior unchanged. A regression test planting a target at chunk_size - bufflength // 2 would pin this.

              Evidence

              • process/scanning.py gates the extra read solely on pytype is str, while the routing block sends EXACT/NOT_EXACT to the offset-arbitrary scan_memory_for_exact_value.
              • util/scan.py::scan_memory_for_exact_value yields every data.find hit with stride 1 — alignment is not considered.
              • util/scan.py::iter_region_chunks docstring documents the aligned-stride assumption that EXACT scanning does not follow.

              Metadata

              Metadata

              Assignees

              No one assigned

                Labels

                No labels
                No labels

                Projects

                No projects

                  Milestone

                  No milestone

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions

                  , 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Chunked scans miss EXACT/NOT_EXACT matches that straddle a 256 MiB chunk boundary (overlap only applied to str) · Issue #85 · JeanExtreme002/PyMemoryEditor · GitHub
                  Skip to content

                  Chunked scans miss EXACT/NOT_EXACT matches that straddle a 256 MiB chunk boundary (overlap only applied to str) #85

                  Description

                  @krishna3554

                  Summary

                  In process/scanning.py, iter_search_results reads bufflength - 1 extra bytes past a chunk boundary only when pytype is str. But EXACT_VALUE and NOT_EXACT_VALUE scans are routed through scan_memory_for_exact_value, which locates the target bytes with bytes.find at any byte offset — not just the bufflength-aligned offsets a stepping numeric scan visits. For any region larger than DEFAULT_MAX_REGION_CHUNK (256 MiB), an exact match whose bytes cross an internal chunk boundary is found by neither chunk and is silently dropped from the results.

                  Location

                  • File: PyMemoryEditor/process/scanning.py
                  • Function: iter_search_results
                  • Relevant code:
                  str_overlap=bufflength-1ifpytypeisstrelse0
                  ...
                  is_last_chunk=chunk_offset+chunk_size>=sizeread_size=chunk_size+ (0ifis_last_chunkelsestr_overlap)

                  with the routing decision a few lines above:

                  ifscan_typein (ScanTypesEnum.EXACT_VALUE, ScanTypesEnum.NOT_EXACT_VALUE):
                  searching_method=scan_memory_for_exact_value

                  Problem

                  The overlap logic assumes non-string scans step in bufflength strides, so a typed numeric scan can never have a value straddle a boundary — the rationale documented on util.scan.iter_region_chunks. That holds for the comparison scans driven by scan_memory (BIGGER_THAN, VALUE_BETWEEN, ...), which iterate aligned windows inside each chunk. It does not hold for the two scan types that bypass scan_memory:

                  1. EXACT_VALUEscan_memory_for_exact_value runs data.find(target) from every offset of the buffer it receives. Chunk N ends mid-value and chunk N+1 starts after the value began, so no chunk ever sees the complete byte sequence → missed result.
                  2. NOT_EXACT_VALUE — the candidate range per chunk is range(0, read_size - bufflength + 1, step). Windows straddling a boundary fall outside both neighboring chunks' ranges, so offsets whose value differs from the target are silently omitted there as well.

                  Since EXACT_VALUE is the default scan_type of search_by_value, this affects the library's primary code path whenever a single region exceeds 256 MiB — large heaps (browsers, JVMs) being exactly the case the 256 MiB cap exists for.

                  Trigger / Reproduction

                  Static-analysis finding (not executed); the trace follows the code paths above.

                  • A region larger than 256 MiB, split by iter_region_chunks into aligned chunks.
                  • An int32 (or any-width) target whose bytes sit across a chunk boundary, e.g. starting at chunk_size - 2.
                  • search_by_value(int, value=..., scan_type=ScanTypesEnum.EXACT_VALUE) over that region reports nothing for that address; the same scan on an unchunked region finds it.

                  The existing test tests/scan/test_chunking_integration.py::test_scan_memory_across_chunked_region_finds_all_matches plants targets at offsets 100 / 5000 / 60000 in each 64 KiB chunk — all safely ≥ 4 bytes from the end — so the straddling case is currently untested.

                  Expected Behavior

                  Every exact match present in the region should be reported regardless of where it falls relative to internal read chunks; NOT_EXACT should evaluate every candidate window exactly once.

                  Actual Behavior

                  Matches/windows that straddle an interior chunk boundary of a >256 MiB region are skipped without any error or warning.

                  Impact

                  Silent false negatives in memory scans — a particularly bad failure mode for a cheat-engine-style workflow, because the user concludes the value is not present rather than suspecting the tool. The larger the target process's regions, the more likely a boundary lands mid-value.

                  Suggested Direction

                  Widen the overlap condition to cover every scan mode that can match at a non-aligned offset, e.g.

                  needs_overlap=pytypeisstrorscan_typein (
                  ScanTypesEnum.EXACT_VALUE,
                  ScanTypesEnum.NOT_EXACT_VALUE,
                  )
                  overlap=bufflength-1ifneeds_overlapelse0

                  The existing clamp (if offset >= chunk_size: continue) already prevents double-emission of overlap-region matches, and the stepping comparisons driven by scan_memory keep their current no-overlap behavior unchanged. A regression test planting a target at chunk_size - bufflength // 2 would pin this.

                  Evidence

                  • process/scanning.py gates the extra read solely on pytype is str, while the routing block sends EXACT/NOT_EXACT to the offset-arbitrary scan_memory_for_exact_value.
                  • util/scan.py::scan_memory_for_exact_value yields every data.find hit with stride 1 — alignment is not considered.
                  • util/scan.py::iter_region_chunks docstring documents the aligned-stride assumption that EXACT scanning does not follow.

                  Metadata

                  Metadata

                  Assignees

                  No one assigned

                    Labels

                    No labels
                    No labels

                    Projects

                    No projects

                      Milestone

                      No milestone

                      Relationships

                      None yet

                      Development

                      No branches or pull requests

                      Issue actions

                      , 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Chunked scans miss EXACT/NOT_EXACT matches that straddle a 256 MiB chunk boundary (overlap only applied to str) · Issue #85 · JeanExtreme002/PyMemoryEditor · GitHub
                      Skip to content

                      Chunked scans miss EXACT/NOT_EXACT matches that straddle a 256 MiB chunk boundary (overlap only applied to str) #85

                      Description

                      @krishna3554

                      Summary

                      In process/scanning.py, iter_search_results reads bufflength - 1 extra bytes past a chunk boundary only when pytype is str. But EXACT_VALUE and NOT_EXACT_VALUE scans are routed through scan_memory_for_exact_value, which locates the target bytes with bytes.find at any byte offset — not just the bufflength-aligned offsets a stepping numeric scan visits. For any region larger than DEFAULT_MAX_REGION_CHUNK (256 MiB), an exact match whose bytes cross an internal chunk boundary is found by neither chunk and is silently dropped from the results.

                      Location

                      • File: PyMemoryEditor/process/scanning.py
                      • Function: iter_search_results
                      • Relevant code:
                      str_overlap=bufflength-1ifpytypeisstrelse0
                      ...
                      is_last_chunk=chunk_offset+chunk_size>=sizeread_size=chunk_size+ (0ifis_last_chunkelsestr_overlap)

                      with the routing decision a few lines above:

                      ifscan_typein (ScanTypesEnum.EXACT_VALUE, ScanTypesEnum.NOT_EXACT_VALUE):
                      searching_method=scan_memory_for_exact_value

                      Problem

                      The overlap logic assumes non-string scans step in bufflength strides, so a typed numeric scan can never have a value straddle a boundary — the rationale documented on util.scan.iter_region_chunks. That holds for the comparison scans driven by scan_memory (BIGGER_THAN, VALUE_BETWEEN, ...), which iterate aligned windows inside each chunk. It does not hold for the two scan types that bypass scan_memory:

                      1. EXACT_VALUEscan_memory_for_exact_value runs data.find(target) from every offset of the buffer it receives. Chunk N ends mid-value and chunk N+1 starts after the value began, so no chunk ever sees the complete byte sequence → missed result.
                      2. NOT_EXACT_VALUE — the candidate range per chunk is range(0, read_size - bufflength + 1, step). Windows straddling a boundary fall outside both neighboring chunks' ranges, so offsets whose value differs from the target are silently omitted there as well.

                      Since EXACT_VALUE is the default scan_type of search_by_value, this affects the library's primary code path whenever a single region exceeds 256 MiB — large heaps (browsers, JVMs) being exactly the case the 256 MiB cap exists for.

                      Trigger / Reproduction

                      Static-analysis finding (not executed); the trace follows the code paths above.

                      • A region larger than 256 MiB, split by iter_region_chunks into aligned chunks.
                      • An int32 (or any-width) target whose bytes sit across a chunk boundary, e.g. starting at chunk_size - 2.
                      • search_by_value(int, value=..., scan_type=ScanTypesEnum.EXACT_VALUE) over that region reports nothing for that address; the same scan on an unchunked region finds it.

                      The existing test tests/scan/test_chunking_integration.py::test_scan_memory_across_chunked_region_finds_all_matches plants targets at offsets 100 / 5000 / 60000 in each 64 KiB chunk — all safely ≥ 4 bytes from the end — so the straddling case is currently untested.

                      Expected Behavior

                      Every exact match present in the region should be reported regardless of where it falls relative to internal read chunks; NOT_EXACT should evaluate every candidate window exactly once.

                      Actual Behavior

                      Matches/windows that straddle an interior chunk boundary of a >256 MiB region are skipped without any error or warning.

                      Impact

                      Silent false negatives in memory scans — a particularly bad failure mode for a cheat-engine-style workflow, because the user concludes the value is not present rather than suspecting the tool. The larger the target process's regions, the more likely a boundary lands mid-value.

                      Suggested Direction

                      Widen the overlap condition to cover every scan mode that can match at a non-aligned offset, e.g.

                      needs_overlap=pytypeisstrorscan_typein (
                      ScanTypesEnum.EXACT_VALUE,
                      ScanTypesEnum.NOT_EXACT_VALUE,
                      )
                      overlap=bufflength-1ifneeds_overlapelse0

                      The existing clamp (if offset >= chunk_size: continue) already prevents double-emission of overlap-region matches, and the stepping comparisons driven by scan_memory keep their current no-overlap behavior unchanged. A regression test planting a target at chunk_size - bufflength // 2 would pin this.

                      Evidence

                      • process/scanning.py gates the extra read solely on pytype is str, while the routing block sends EXACT/NOT_EXACT to the offset-arbitrary scan_memory_for_exact_value.
                      • util/scan.py::scan_memory_for_exact_value yields every data.find hit with stride 1 — alignment is not considered.
                      • util/scan.py::iter_region_chunks docstring documents the aligned-stride assumption that EXACT scanning does not follow.

                      Metadata

                      Metadata

                      Assignees

                      No one assigned

                        Labels

                        No labels
                        No labels

                        Projects

                        No projects

                          Milestone

                          No milestone

                          Relationships

                          None yet

                          Development

                          No branches or pull requests

                          Issue actions

                          , 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Chunked scans miss EXACT/NOT_EXACT matches that straddle a 256 MiB chunk boundary (overlap only applied to str) · Issue #85 · JeanExtreme002/PyMemoryEditor · GitHub
                          Skip to content

                          Chunked scans miss EXACT/NOT_EXACT matches that straddle a 256 MiB chunk boundary (overlap only applied to str) #85

                          Description

                          @krishna3554

                          Summary

                          In process/scanning.py, iter_search_results reads bufflength - 1 extra bytes past a chunk boundary only when pytype is str. But EXACT_VALUE and NOT_EXACT_VALUE scans are routed through scan_memory_for_exact_value, which locates the target bytes with bytes.find at any byte offset — not just the bufflength-aligned offsets a stepping numeric scan visits. For any region larger than DEFAULT_MAX_REGION_CHUNK (256 MiB), an exact match whose bytes cross an internal chunk boundary is found by neither chunk and is silently dropped from the results.

                          Location

                          • File: PyMemoryEditor/process/scanning.py
                          • Function: iter_search_results
                          • Relevant code:
                          str_overlap=bufflength-1ifpytypeisstrelse0
                          ...
                          is_last_chunk=chunk_offset+chunk_size>=sizeread_size=chunk_size+ (0ifis_last_chunkelsestr_overlap)

                          with the routing decision a few lines above:

                          ifscan_typein (ScanTypesEnum.EXACT_VALUE, ScanTypesEnum.NOT_EXACT_VALUE):
                          searching_method=scan_memory_for_exact_value

                          Problem

                          The overlap logic assumes non-string scans step in bufflength strides, so a typed numeric scan can never have a value straddle a boundary — the rationale documented on util.scan.iter_region_chunks. That holds for the comparison scans driven by scan_memory (BIGGER_THAN, VALUE_BETWEEN, ...), which iterate aligned windows inside each chunk. It does not hold for the two scan types that bypass scan_memory:

                          1. EXACT_VALUEscan_memory_for_exact_value runs data.find(target) from every offset of the buffer it receives. Chunk N ends mid-value and chunk N+1 starts after the value began, so no chunk ever sees the complete byte sequence → missed result.
                          2. NOT_EXACT_VALUE — the candidate range per chunk is range(0, read_size - bufflength + 1, step). Windows straddling a boundary fall outside both neighboring chunks' ranges, so offsets whose value differs from the target are silently omitted there as well.

                          Since EXACT_VALUE is the default scan_type of search_by_value, this affects the library's primary code path whenever a single region exceeds 256 MiB — large heaps (browsers, JVMs) being exactly the case the 256 MiB cap exists for.

                          Trigger / Reproduction

                          Static-analysis finding (not executed); the trace follows the code paths above.

                          • A region larger than 256 MiB, split by iter_region_chunks into aligned chunks.
                          • An int32 (or any-width) target whose bytes sit across a chunk boundary, e.g. starting at chunk_size - 2.
                          • search_by_value(int, value=..., scan_type=ScanTypesEnum.EXACT_VALUE) over that region reports nothing for that address; the same scan on an unchunked region finds it.

                          The existing test tests/scan/test_chunking_integration.py::test_scan_memory_across_chunked_region_finds_all_matches plants targets at offsets 100 / 5000 / 60000 in each 64 KiB chunk — all safely ≥ 4 bytes from the end — so the straddling case is currently untested.

                          Expected Behavior

                          Every exact match present in the region should be reported regardless of where it falls relative to internal read chunks; NOT_EXACT should evaluate every candidate window exactly once.

                          Actual Behavior

                          Matches/windows that straddle an interior chunk boundary of a >256 MiB region are skipped without any error or warning.

                          Impact

                          Silent false negatives in memory scans — a particularly bad failure mode for a cheat-engine-style workflow, because the user concludes the value is not present rather than suspecting the tool. The larger the target process's regions, the more likely a boundary lands mid-value.

                          Suggested Direction

                          Widen the overlap condition to cover every scan mode that can match at a non-aligned offset, e.g.

                          needs_overlap=pytypeisstrorscan_typein (
                          ScanTypesEnum.EXACT_VALUE,
                          ScanTypesEnum.NOT_EXACT_VALUE,
                          )
                          overlap=bufflength-1ifneeds_overlapelse0

                          The existing clamp (if offset >= chunk_size: continue) already prevents double-emission of overlap-region matches, and the stepping comparisons driven by scan_memory keep their current no-overlap behavior unchanged. A regression test planting a target at chunk_size - bufflength // 2 would pin this.

                          Evidence

                          • process/scanning.py gates the extra read solely on pytype is str, while the routing block sends EXACT/NOT_EXACT to the offset-arbitrary scan_memory_for_exact_value.
                          • util/scan.py::scan_memory_for_exact_value yields every data.find hit with stride 1 — alignment is not considered.
                          • util/scan.py::iter_region_chunks docstring documents the aligned-stride assumption that EXACT scanning does not follow.

                          Metadata

                          Metadata

                          Assignees

                          No one assigned

                            Labels

                            No labels
                            No labels

                            Projects

                            No projects

                              Milestone

                              No milestone

                              Relationships

                              None yet

                              Development

                              No branches or pull requests

                              Issue actions

                              , 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Chunked scans miss EXACT/NOT_EXACT matches that straddle a 256 MiB chunk boundary (overlap only applied to str) · Issue #85 · JeanExtreme002/PyMemoryEditor · GitHub
                              Skip to content

                              Chunked scans miss EXACT/NOT_EXACT matches that straddle a 256 MiB chunk boundary (overlap only applied to str) #85

                              Description

                              @krishna3554

                              Summary

                              In process/scanning.py, iter_search_results reads bufflength - 1 extra bytes past a chunk boundary only when pytype is str. But EXACT_VALUE and NOT_EXACT_VALUE scans are routed through scan_memory_for_exact_value, which locates the target bytes with bytes.find at any byte offset — not just the bufflength-aligned offsets a stepping numeric scan visits. For any region larger than DEFAULT_MAX_REGION_CHUNK (256 MiB), an exact match whose bytes cross an internal chunk boundary is found by neither chunk and is silently dropped from the results.

                              Location

                              • File: PyMemoryEditor/process/scanning.py
                              • Function: iter_search_results
                              • Relevant code:
                              str_overlap=bufflength-1ifpytypeisstrelse0
                              ...
                              is_last_chunk=chunk_offset+chunk_size>=sizeread_size=chunk_size+ (0ifis_last_chunkelsestr_overlap)

                              with the routing decision a few lines above:

                              ifscan_typein (ScanTypesEnum.EXACT_VALUE, ScanTypesEnum.NOT_EXACT_VALUE):
                              searching_method=scan_memory_for_exact_value

                              Problem

                              The overlap logic assumes non-string scans step in bufflength strides, so a typed numeric scan can never have a value straddle a boundary — the rationale documented on util.scan.iter_region_chunks. That holds for the comparison scans driven by scan_memory (BIGGER_THAN, VALUE_BETWEEN, ...), which iterate aligned windows inside each chunk. It does not hold for the two scan types that bypass scan_memory:

                              1. EXACT_VALUEscan_memory_for_exact_value runs data.find(target) from every offset of the buffer it receives. Chunk N ends mid-value and chunk N+1 starts after the value began, so no chunk ever sees the complete byte sequence → missed result.
                              2. NOT_EXACT_VALUE — the candidate range per chunk is range(0, read_size - bufflength + 1, step). Windows straddling a boundary fall outside both neighboring chunks' ranges, so offsets whose value differs from the target are silently omitted there as well.

                              Since EXACT_VALUE is the default scan_type of search_by_value, this affects the library's primary code path whenever a single region exceeds 256 MiB — large heaps (browsers, JVMs) being exactly the case the 256 MiB cap exists for.

                              Trigger / Reproduction

                              Static-analysis finding (not executed); the trace follows the code paths above.

                              • A region larger than 256 MiB, split by iter_region_chunks into aligned chunks.
                              • An int32 (or any-width) target whose bytes sit across a chunk boundary, e.g. starting at chunk_size - 2.
                              • search_by_value(int, value=..., scan_type=ScanTypesEnum.EXACT_VALUE) over that region reports nothing for that address; the same scan on an unchunked region finds it.

                              The existing test tests/scan/test_chunking_integration.py::test_scan_memory_across_chunked_region_finds_all_matches plants targets at offsets 100 / 5000 / 60000 in each 64 KiB chunk — all safely ≥ 4 bytes from the end — so the straddling case is currently untested.

                              Expected Behavior

                              Every exact match present in the region should be reported regardless of where it falls relative to internal read chunks; NOT_EXACT should evaluate every candidate window exactly once.

                              Actual Behavior

                              Matches/windows that straddle an interior chunk boundary of a >256 MiB region are skipped without any error or warning.

                              Impact

                              Silent false negatives in memory scans — a particularly bad failure mode for a cheat-engine-style workflow, because the user concludes the value is not present rather than suspecting the tool. The larger the target process's regions, the more likely a boundary lands mid-value.

                              Suggested Direction

                              Widen the overlap condition to cover every scan mode that can match at a non-aligned offset, e.g.

                              needs_overlap=pytypeisstrorscan_typein (
                              ScanTypesEnum.EXACT_VALUE,
                              ScanTypesEnum.NOT_EXACT_VALUE,
                              )
                              overlap=bufflength-1ifneeds_overlapelse0

                              The existing clamp (if offset >= chunk_size: continue) already prevents double-emission of overlap-region matches, and the stepping comparisons driven by scan_memory keep their current no-overlap behavior unchanged. A regression test planting a target at chunk_size - bufflength // 2 would pin this.

                              Evidence

                              • process/scanning.py gates the extra read solely on pytype is str, while the routing block sends EXACT/NOT_EXACT to the offset-arbitrary scan_memory_for_exact_value.
                              • util/scan.py::scan_memory_for_exact_value yields every data.find hit with stride 1 — alignment is not considered.
                              • util/scan.py::iter_region_chunks docstring documents the aligned-stride assumption that EXACT scanning does not follow.

                              Metadata

                              Metadata

                              Assignees

                              No one assigned

                                Labels

                                No labels
                                No labels

                                Projects

                                No projects

                                  Milestone

                                  No milestone

                                  Relationships

                                  None yet

                                  Development

                                  No branches or pull requests

                                  Issue actions