[GH-ISSUE #7403] [Bug] PE resources and certificates classify a DOS MZ target as an invalid request #4410

Open
opened 2026-10-31 00:49:34 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @coygeek on GitHub (Oct 27, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/7403

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Native analysis or decompilation

Observed behavior

On the same readable synthetic DOS MZ file, inspect-pe-resources and
inspect-pe-certificates return invalid_request, category invalid_input,
and recovery kind inspect_input_contract. Their diagnostic says the MZ header
does not declare a PE image, but their remediation asks the caller to inspect
the command schema and correct fields.

inspect-pe-imports and inspect-pe-load-config correctly identify this file
as an unsupported DOS MZ target. They return unsupported_target for both
code and category, and recommend choosing a supported target or operation.
All four commands exit 1 and report retryable: false.

These results were observed through the public CLI. A separate call to REA's
canonical DOS parser accepted the fixture's DOS declarations and image bounds
and reported windowsHeader.state: "absent". DOS program execution was not
performed or implied.

Reproduction

Run this shell script from the baseline checkout with its dependencies and
compiled runtime available, using Node.js v24.22.1. It creates the synthetic
input outside the checkout and isolates client configuration and temporary files.
The assertions capture the observed classification mismatch.

set -eu
WORK="$(mktemp -d)"
export WORK
export HOME="$WORK/home"
export XDG_CONFIG_HOME="$WORK/config"
export XDG_CACHE_HOME="$WORK/cache"
export XDG_DATA_HOME="$WORK/data"
export XDG_STATE_HOME="$WORK/state"
export XDG_RUNTIME_DIR="$WORK/runtime"
export TMPDIR="$WORK/temp"
mkdir -p "$HOME" "$XDG_CONFIG_HOME" "$XDG_CACHE_HOME" \
  "$XDG_DATA_HOME" "$XDG_STATE_HOME" "$XDG_RUNTIME_DIR" "$TMPDIR"
node --input-type=module <<'JS'
import assert from 'node:assert/strict';
import { writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { spawnSync } from 'node:child_process';

const bytes = Buffer.alloc(1024);
bytes.write('MZ', 0, 'ascii');
for (const [offset, value] of [
  [2, 0], [4, 2], [8, 2], [0x0a, 0], [0x0c, 0xffff],
  [0x0e, 0], [0x10, 0xb8], [0x12, 0], [0x14, 0], [0x16, 0],
  [0x18, 0x1c], [0x1a, 0],
]) bytes.writeUInt16LE(value, offset);
bytes.writeUInt32LE(0x40, 0x3c);
const target = join(process.env.WORK, 'dos-mz.exe');
writeFileSync(target, bytes);

for (const [command, expectedCode, expectedCategory] of [
  ['inspect-pe-imports', 'unsupported_target', 'unsupported_target'],
  ['inspect-pe-load-config', 'unsupported_target', 'unsupported_target'],
  ['inspect-pe-resources', 'invalid_request', 'invalid_input'],
  ['inspect-pe-certificates', 'invalid_request', 'invalid_input'],
]) {
  const run = spawnSync(process.execPath,
    ['scripts/rea.mjs', command, target, '--json'],
    { encoding: 'utf8', env: process.env });
  assert.equal(run.status, 1, run.stderr);
  const result = JSON.parse(run.stdout);
  assert.equal(result.code, expectedCode);
  assert.equal(result.category, expectedCategory);
  if (expectedCode === 'invalid_request') {
    assert.equal(result.recovery.kind, 'inspect_input_contract');
    assert.equal(result.details.issues[0].reason, 'invalid_format');
    assert.equal(result.details.issues[0].message,
      'MZ header does not declare a PE image in the inspected bytes.');
  }
  console.log(JSON.stringify({
    command, exit: run.status, code: result.code,
    category: result.category, recovery: result.recovery ?? null,
    remediation: result.remediation,
  }));
}
JS

Expected behavior

A readable DOS MZ image is outside these operations' PE32/PE32+ coverage.
Resource and certificate inspection should refuse it with
unsupported_target, category unsupported_target, and target/operation
selection advice, consistent with imports and load-configuration inspection.
They should preserve the distinction between unsupported coverage and malformed
input; this does not request PE analysis of a DOS image.

Expected source: src/domain/analysisErrorCore.ts:8 explicitly defines
AnalysisUnsupportedTargetError for readable artifacts outside an operation's
supported target formats.

Expected source: docs/binary-diagnostics.md:219 and
docs/pe-certificates.md:3 establish PE32/PE32+ scope.

Expected source: src/native/metadata/PeImage.ts:26 validates DOS evidence
before classifying a DOS-only image as unsupported.

Impact and scope

P2: two public operations return the wrong machine-readable error category and
recovery discriminator for a bounded input class. A caller routing on error code
or recovery kind receives input-contract correction instead of a format refusal.
This is not merely a different display message. An actual caller retry loop was
not observed; both responses prohibit an unchanged retry.

A practical workaround is to use the imports or load-configuration diagnostic
to identify the unsupported DOS target and avoid applying PE-only operations to
it. No crash, data loss, or incorrect successful analysis was observed. The
verification covers one synthetic DOS MZ fixture, with one run per command in
each reproduction pass. Real DOS applications, NE/LE/LX variants, and MCP were
not exercised.

REA version

Exact node scripts/rea.mjs --version output:

6.8.0

Source commit: f8f7ac96de8a66a4ab9a23d87e04779bfcd11045.

Runtime and platform

Node.js v24.22.1; macOS 27.0.1; arm64. No disassembler or Windows runtime is
required or used.

Target and artifact context

Synthetic 1024-byte DOS MZ image: two 512-byte pages, 32-byte header, no
relocations, relocation table offset 28, and e_lfanew = 0x40 without a PE
signature there. REA's DOS parser reports a 992-byte module and no overlay.
The module contains synthetic zero bytes and was never executed.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Observed summary from the runnable script:

Command Exit Code Category Recovery kind
inspect-pe-imports 1 unsupported_target unsupported_target absent
inspect-pe-load-config 1 unsupported_target unsupported_target absent
inspect-pe-resources 1 invalid_request invalid_input inspect_input_contract
inspect-pe-certificates 1 invalid_request invalid_input inspect_input_contract

Both failing classifications contain this issue:

{"path":["path"],"reason":"invalid_format","message":"MZ header does not declare a PE image in the inspected bytes."}

The resources remediation says:

Read the declared input schema or CLI usage for inspect_pe_resources; if using MCP and the catalog may be stale, call tools/list. Correct the listed fields, then retry. Do not repeat the unchanged call.

The certificate response substitutes inspect_pe_certificates. The two sibling
commands instead say to select a supported target or another operation, and
identify the selected format as DOS MZ.

Failure source: src/native/pe/PeResourceLayout.ts:97 maps every non-PE Windows
header observation to the format-failure helper at
src/native/pe/PeResourceLayout.ts:28.

Failure source: src/native/pe/PeResourceLayout.ts:154 and
src/native/pe/PeCertificateParser.ts:58 reach that shared admission from the two
parsers.

Failure source: src/native/pe/PeResourcesProvider.ts:167 and
src/native/pe/PeCertificatesProvider.ts:144 map the format failure to
AnalysisInputError.

Typed outcome or recovery expected

code: "unsupported_target", category: "unsupported_target", and supported
target/operation selection remediation, without inspect_input_contract recovery
for this DOS-only input.

Analysis or suspected cause

Source-derived: the shared resource/certificate admission helper retains only a
format failure when the canonical Windows-header observation is not PE. Its
providers then classify that failure as invalid input. The other two PE readers
retain a separate unsupported-format failure after validating DOS structure,
which their providers preserve in the public error. The runtime comparison
confirms the resulting classification difference.

Originally created by @coygeek on GitHub (Oct 27, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/7403 ## Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ## Area Native analysis or decompilation ## Observed behavior On the same readable synthetic DOS MZ file, `inspect-pe-resources` and `inspect-pe-certificates` return `invalid_request`, category `invalid_input`, and recovery kind `inspect_input_contract`. Their diagnostic says the MZ header does not declare a PE image, but their remediation asks the caller to inspect the command schema and correct fields. `inspect-pe-imports` and `inspect-pe-load-config` correctly identify this file as an unsupported DOS MZ target. They return `unsupported_target` for both code and category, and recommend choosing a supported target or operation. All four commands exit 1 and report `retryable: false`. These results were observed through the public CLI. A separate call to REA's canonical DOS parser accepted the fixture's DOS declarations and image bounds and reported `windowsHeader.state: "absent"`. DOS program execution was not performed or implied. ## Reproduction Run this shell script from the baseline checkout with its dependencies and compiled runtime available, using Node.js v24.22.1. It creates the synthetic input outside the checkout and isolates client configuration and temporary files. The assertions capture the observed classification mismatch. ```sh set -eu WORK="$(mktemp -d)" export WORK export HOME="$WORK/home" export XDG_CONFIG_HOME="$WORK/config" export XDG_CACHE_HOME="$WORK/cache" export XDG_DATA_HOME="$WORK/data" export XDG_STATE_HOME="$WORK/state" export XDG_RUNTIME_DIR="$WORK/runtime" export TMPDIR="$WORK/temp" mkdir -p "$HOME" "$XDG_CONFIG_HOME" "$XDG_CACHE_HOME" \ "$XDG_DATA_HOME" "$XDG_STATE_HOME" "$XDG_RUNTIME_DIR" "$TMPDIR" node --input-type=module <<'JS' import assert from 'node:assert/strict'; import { writeFileSync } from 'node:fs'; import { join } from 'node:path'; import { spawnSync } from 'node:child_process'; const bytes = Buffer.alloc(1024); bytes.write('MZ', 0, 'ascii'); for (const [offset, value] of [ [2, 0], [4, 2], [8, 2], [0x0a, 0], [0x0c, 0xffff], [0x0e, 0], [0x10, 0xb8], [0x12, 0], [0x14, 0], [0x16, 0], [0x18, 0x1c], [0x1a, 0], ]) bytes.writeUInt16LE(value, offset); bytes.writeUInt32LE(0x40, 0x3c); const target = join(process.env.WORK, 'dos-mz.exe'); writeFileSync(target, bytes); for (const [command, expectedCode, expectedCategory] of [ ['inspect-pe-imports', 'unsupported_target', 'unsupported_target'], ['inspect-pe-load-config', 'unsupported_target', 'unsupported_target'], ['inspect-pe-resources', 'invalid_request', 'invalid_input'], ['inspect-pe-certificates', 'invalid_request', 'invalid_input'], ]) { const run = spawnSync(process.execPath, ['scripts/rea.mjs', command, target, '--json'], { encoding: 'utf8', env: process.env }); assert.equal(run.status, 1, run.stderr); const result = JSON.parse(run.stdout); assert.equal(result.code, expectedCode); assert.equal(result.category, expectedCategory); if (expectedCode === 'invalid_request') { assert.equal(result.recovery.kind, 'inspect_input_contract'); assert.equal(result.details.issues[0].reason, 'invalid_format'); assert.equal(result.details.issues[0].message, 'MZ header does not declare a PE image in the inspected bytes.'); } console.log(JSON.stringify({ command, exit: run.status, code: result.code, category: result.category, recovery: result.recovery ?? null, remediation: result.remediation, })); } JS ``` ## Expected behavior A readable DOS MZ image is outside these operations' PE32/PE32+ coverage. Resource and certificate inspection should refuse it with `unsupported_target`, category `unsupported_target`, and target/operation selection advice, consistent with imports and load-configuration inspection. They should preserve the distinction between unsupported coverage and malformed input; this does not request PE analysis of a DOS image. Expected source: `src/domain/analysisErrorCore.ts:8` explicitly defines `AnalysisUnsupportedTargetError` for readable artifacts outside an operation's supported target formats. Expected source: `docs/binary-diagnostics.md:219` and `docs/pe-certificates.md:3` establish PE32/PE32+ scope. Expected source: `src/native/metadata/PeImage.ts:26` validates DOS evidence before classifying a DOS-only image as unsupported. ## Impact and scope P2: two public operations return the wrong machine-readable error category and recovery discriminator for a bounded input class. A caller routing on error code or recovery kind receives input-contract correction instead of a format refusal. This is not merely a different display message. An actual caller retry loop was not observed; both responses prohibit an unchanged retry. A practical workaround is to use the imports or load-configuration diagnostic to identify the unsupported DOS target and avoid applying PE-only operations to it. No crash, data loss, or incorrect successful analysis was observed. The verification covers one synthetic DOS MZ fixture, with one run per command in each reproduction pass. Real DOS applications, NE/LE/LX variants, and MCP were not exercised. ## REA version Exact `node scripts/rea.mjs --version` output: ```text 6.8.0 ``` Source commit: `f8f7ac96de8a66a4ab9a23d87e04779bfcd11045`. ## Runtime and platform Node.js v24.22.1; macOS 27.0.1; arm64. No disassembler or Windows runtime is required or used. ## Target and artifact context Synthetic 1024-byte DOS MZ image: two 512-byte pages, 32-byte header, no relocations, relocation table offset 28, and `e_lfanew = 0x40` without a PE signature there. REA's DOS parser reports a 992-byte module and no overlay. The module contains synthetic zero bytes and was never executed. ## Interface CLI ## Earliest failure stage Native analysis or reconstruction ## Relevant output or evidence Observed summary from the runnable script: | Command | Exit | Code | Category | Recovery kind | | --- | --- | --- | --- | --- | | inspect-pe-imports | 1 | unsupported_target | unsupported_target | absent | | inspect-pe-load-config | 1 | unsupported_target | unsupported_target | absent | | inspect-pe-resources | 1 | invalid_request | invalid_input | inspect_input_contract | | inspect-pe-certificates | 1 | invalid_request | invalid_input | inspect_input_contract | Both failing classifications contain this issue: ```json {"path":["path"],"reason":"invalid_format","message":"MZ header does not declare a PE image in the inspected bytes."} ``` The resources remediation says: ```text Read the declared input schema or CLI usage for inspect_pe_resources; if using MCP and the catalog may be stale, call tools/list. Correct the listed fields, then retry. Do not repeat the unchanged call. ``` The certificate response substitutes `inspect_pe_certificates`. The two sibling commands instead say to select a supported target or another operation, and identify the selected format as DOS MZ. Failure source: `src/native/pe/PeResourceLayout.ts:97` maps every non-PE Windows header observation to the format-failure helper at `src/native/pe/PeResourceLayout.ts:28`. Failure source: `src/native/pe/PeResourceLayout.ts:154` and `src/native/pe/PeCertificateParser.ts:58` reach that shared admission from the two parsers. Failure source: `src/native/pe/PeResourcesProvider.ts:167` and `src/native/pe/PeCertificatesProvider.ts:144` map the format failure to `AnalysisInputError`. ## Typed outcome or recovery expected `code: "unsupported_target"`, `category: "unsupported_target"`, and supported target/operation selection remediation, without `inspect_input_contract` recovery for this DOS-only input. ## Analysis or suspected cause Source-derived: the shared resource/certificate admission helper retains only a format failure when the canonical Windows-header observation is not PE. Its providers then classify that failure as invalid input. The other two PE readers retain a separate unsupported-format failure after validating DOS structure, which their providers preserve in the public error. The runtime comparison confirms the resulting classification difference.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#4410
No description provided.