[GH-ISSUE #6694] [Bug] inspect-pe-certificates rejects WIN_CERTIFICATE padding counted in dwLength #4069

Closed
opened 2026-10-31 00:46:34 +00:00 by bsakaguchi · 0 comments
Owner

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

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

The production inspect-pe-certificates <path> --json command registration, executed directly from source, refuses a type-2 WIN_CERTIFICATE when its dwLength includes the required zero alignment padding. Both PE32 and PE32+ fixtures return invalid_request / invalid_input, with invalid_format and PKCS#7 signedData is malformed: ContentInfo has trailing data. The identical SignedData and padding bytes decode successfully when the entry length excludes those padding bytes. Microsoft documents that the finalized dwLength should include this padding.

Executed paths were the real production CLI registration and output wrapper, plus separate shared-service calls using the real stable-file provider. This was not an installed rea executable, compiled launcher, stdio MCP, Windows loader, or signature-verification run.

Reproduction

Use the source revision and locked dependencies stated below, with the supported Node runtime. Run from the checkout root. The following self-contained replay includes the full self-authored payload and PE headers. It combines the executed fixture generator, provider/service calls, and production CLI-registration harness, with scratch/import paths made portable and intermediate log-writing omitted. The original service and CLI scripts ran separately; this combined presentation was not separately executed.

node --import ./node_modules/tsx/dist/loader.mjs --input-type=module <<'JS'
import { mkdtempSync, readFileSync, writeFileSync } from 'node:fs';
import { createHash } from 'node:crypto';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { Writable } from 'node:stream';
import { performance } from 'node:perf_hooks';
import { Cli } from 'incur';
import pino from 'pino';
import { PeCertificatesService } from './src/application/binaryDiagnostics/PeCertificatesService.ts';
import { PeCertificatesProvider } from './src/native/pe/PeCertificatesProvider.ts';
import { registerBinaryDiagnosticsCommands } from './src/cli/binaryDiagnosticsCommands.ts';
import { createCliResultOutput, streamCliCommandResults } from './src/cli/cliResultOutput.ts';

const root = mkdtempSync(join(tmpdir(), 'rea-pe-padding-'));
// Complete definite-length ContentInfo/SignedData: one structural X.509
// certificate, one SignerInfo, serial 01 and commonName "Padding Fixture".
// Signature bytes are inert placeholders; cryptographic validity is unobserved.
const payload = Buffer.from(
  '3082012006092a864886f70d010702a08201113082010d020101310f300d06096086480165030402010500300b06092a864886f70d010701a081a030819d308187020101300d06092a864886f70d01010b0500301a3118301606035504030c0f50616464696e672046697874757265301e170d3235303130313030303030305a170d3236303130313030303030305a301a3118301606035504030c0f50616464696e672046697874757265301b300d06092a864886f70d0101010500030a00300702020ca1020111300d06092a864886f70d01010b05000302000131473045020101301f301a3118301606035504030c0f50616464696e672046697874757265020101300d06096086480165030402010500300d06092a864886f70d0101010500040101',
  'hex',
);
const rawLength = 8 + payload.length; // 300
const tableLength = Math.ceil(rawLength / 8) * 8; // 304
function image(plus, counted) {
  const b = Buffer.alloc(0x400 + tableLength);
  b.writeUInt16LE(0x5a4d, 0);
  b.writeUInt32LE(0x80, 0x3c);
  b.writeUInt32LE(0x4550, 0x80);
  b.writeUInt16LE(plus ? 0x8664 : 0x14c, 0x84);
  b.writeUInt16LE(1, 0x86);
  b.writeUInt16LE(plus ? 240 : 224, 0x94);
  b.writeUInt16LE(plus ? 0x2022 : 0x2102, 0x96); // Data-only DLL.
  b.writeUInt16LE(plus ? 0x20b : 0x10b, 0x98);
  b[0x98 + 2] = 14;
  b.writeUInt32LE(0x200, 0x98 + 8);
  if (plus) b.writeBigUInt64LE(0x180000000n, 0x98 + 24);
  else {
    b.writeUInt32LE(0x1000, 0x98 + 24);
    b.writeUInt32LE(0x10000000, 0x98 + 28);
  }
  b.writeUInt32LE(0x1000, 0x98 + 32);
  b.writeUInt32LE(0x200, 0x98 + 36);
  b.writeUInt16LE(6, 0x98 + 40);
  b.writeUInt16LE(6, 0x98 + 48);
  b.writeUInt32LE(0x2000, 0x98 + 56);
  b.writeUInt16LE(3, 0x98 + 68);
  b.writeUInt16LE(0x140, 0x98 + 70);
  if (plus) {
    b.writeBigUInt64LE(0x100000n, 0x98 + 72);
    b.writeBigUInt64LE(0x1000n, 0x98 + 80);
    b.writeBigUInt64LE(0x100000n, 0x98 + 88);
    b.writeBigUInt64LE(0x1000n, 0x98 + 96);
  } else {
    for (const at of [72, 80]) b.writeUInt32LE(0x100000, 0x98 + at);
    for (const at of [76, 84]) b.writeUInt32LE(0x1000, 0x98 + at);
  }
  const section = 0x98 + (plus ? 240 : 224);
  Buffer.from('.data').copy(b, section);
  b.writeUInt32LE(1, section + 8);
  b.writeUInt32LE(0x1000, section + 12);
  b.writeUInt32LE(0x200, section + 16);
  b.writeUInt32LE(0x200, section + 20);
  b.writeUInt32LE(0xc0000040, section + 36);
  b.writeUInt32LE(0x200, 0x98 + 60);
  b.writeUInt32LE(16, 0x98 + (plus ? 108 : 92));
  const directory = 0x98 + (plus ? 112 : 96) + 4 * 8;
  b.writeUInt32LE(0x400, directory);
  b.writeUInt32LE(tableLength, directory + 4);
  b.writeUInt32LE(counted ? tableLength : rawLength, 0x400);
  b.writeUInt16LE(0x200, 0x404);
  b.writeUInt16LE(2, 0x406);
  payload.copy(b, 0x408);
  return b;
}
const sha256 = b => createHash('sha256').update(b).digest('hex');
const service = new PeCertificatesService(new PeCertificatesProvider());
try {
  for (const plus of [false, true]) for (const counted of [false, true]) {
    const fixture = `${plus ? 'pe32plus' : 'pe32'}-padding-${counted ? 'counted' : 'excluded'}.exe`;
    const path = join(root, fixture);
    const bytes = image(plus, counted);
    writeFileSync(path, bytes);
    const before = sha256(bytes);
    const direct = await service.inspect({ path });
    const args = ['inspect-pe-certificates', path, '--json'];
    let stdout = '';
    const sink = new Writable({ write(chunk, encoding, done) { stdout += chunk.toString(); done(); } });
    const output = createCliResultOutput(args, sink);
    const cli = Cli.create('rea', { version: '6.7.0', sync: false, update: false });
    registerBinaryDiagnosticsCommands(cli, pino({ level: 'silent' }), {});
    streamCliCommandResults(cli, output);
    let explicitExit;
    const previousExitCode = process.exitCode;
    process.exitCode = undefined;
    const start = performance.now();
    await cli.serve(args, {
      stdout: text => { if (!output.handled) stdout += text; },
      exit: code => { explicitExit = code; },
    });
    const elapsedMs = performance.now() - start;
    const exitCode = explicitExit ?? process.exitCode ?? 0;
    process.exitCode = previousExitCode;
    const value = JSON.parse(stdout);
    console.log(JSON.stringify({ fixture, fileBytes: bytes.length, sha256: before,
      payloadBytes: payload.length, dwLength: bytes.readUInt32LE(0x400),
      tableLength, unchanged: before === sha256(readFileSync(path)),
      serviceOk: direct.ok, exitCode, elapsedMs,
      coverage: value.normalized_result?.coverage,
      certificateSerial: value.normalized_result?.entries[0].pkcs7.certificates[0].serial_hex,
      signerSerial: value.normalized_result?.entries[0].pkcs7.signers[0].serial_hex,
      error: value.details?.issues }));
  }
} finally { await service.close(); }
JS

The replay leaves four self-authored fixtures in a rea-pe-padding-* directory under the system temporary directory. The source CLI callback's exitCode is recorded per invocation; the outer multi-case harness does not itself exit 1 after the expected failures. Both final counted-padding fixtures failed in the executed source-CLI sample; both controls passed. Separate provider/service calls produced the same two failures and two successful controls.

Expected behavior

Return the structural table, certificate and signer facts for the unchanged padding-counted fixtures. Microsoft's PE attribute certificate table specification describes zero padding to the next eight-byte boundary and includes that padding in the finalized dwLength. The security-directory size is 304 in both cases, with no truncated table, missing payload bytes, extra entry or exceeded budget.

REA's documented observations promise type-2 structural certificate and signer facts; its failure profile does not exclude ordinary WIN_CERTIFICATE alignment padding. Keep raw artifact/entry ranges and hashes attributable to the original selected bytes. Signature verification is outside this expectation.

Impact and scope

P2 bounded format-compatibility failure: counting four documented zero padding bytes prevents the entire requested certificate inventory from being returned for both tested PE formats. The exact same definite-length SignedData bytes decode in the controls, isolating the PE length boundary from CMS encoding support. Each final-format pair differs only at file offset 0x400: the low byte of dwLength changes from 0x2c to 0x30 (300 to 304). The payload and padding remain identical.

No same-artifact REA workaround was demonstrated. Rewriting dwLength creates the passing control but changes the caller-selected bytes and digest. NOT_ENOUGH_INFO: real-artifact prevalence, other platforms and an introducing release were not measured. Windows loading and cryptographic validity are outside the executed structural inspection scope.

REA version

Source revision a7f967cf240eba8309cbf4526b2fc6338a52a617, manifest rea-agents 6.7.0. The parent investigation supplied the npm-latest check as 6.7.0. No dependency install, build, global installation, source change or remote issue filing occurred.

Runtime and platform

Node.js v24.22.1, Linux x64, kernel 7.2.8-9-omarchy; repository-pinned Incur 0.6.1 and tsx 4.23.15. No Hopper, Ghidra, Windows SDK or analysis engine was used. Measured final source-CLI handler durations in order PE32 control/failure, PE32+ control/failure were 19.105 / 3.507 / 5.682 / 1.074 ms. The separate service durations were 16.621 / 1.229 / 1.495 / 0.544 ms. These are individual same-process measurements, not performance or prevalence claims.

Target and artifact context

Self-authored 1,328-byte PE32/PE32+ data-only DLL fixtures with conventional headers, one initialized-data section and a 304-byte certificate table at file offset 1,024. The full 292-byte definite-length SignedData above contains one structural X.509 certificate with a DER RSAPublicKey field, one SignerInfo and inert signature placeholders. The failing finalized entry has eight header bytes, 292 payload bytes and four zero padding bytes counted in dwLength. Neither executing the fixture nor accepting its signature is required by this read-only structural contract.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Both source-CLI failures contain this observed output excerpt:

{
  "code": "invalid_request",
  "category": "invalid_input",
  "retryable": false,
  "details": {
    "operation": "inspect_pe_certificates",
    "issues": [{
      "path": ["path"],
      "reason": "invalid_format",
      "message": "PKCS#7 signedData is malformed: ContentInfo has trailing data."
    }]
  }
}

Both controls return coverage: {"status":"complete","examined_entries":1,"certificates":1} and certificate/signer serials 01. Final fixture SHA-256 values are: PE32 excluded 5c809299f4ca837e22ca790dfafd400e2799e31d94b3a7573cfdb1e4fcd6f83d; PE32 counted 76f33f51fa023ee472982df07273f21adc6df1fce63f25289121f1604a8a1a88; PE32+ excluded 2e1a1dcef692c9f0da505d87f4846b75d052ae5aa6c1f3d0833923fab9a77a23; PE32+ counted 31c148da6f04a79a36803ca07505d2536e5e26ec041c26b523ae633b913c6de0.

Typed outcome or recovery expected

Successful structural Evidence for each unchanged padding-counted fixture, including the one certificate and signer already observed in its control. Restoration should preserve artifact identity, table/entry range evidence and original digests; both controls should continue to return the same facts. This is a proposed restoration check; no fix was implemented. A proposal to tolerate arbitrary non-padding data after ContentInfo is outside this report.

Analysis or suspected cause

Source-derived explanation: the stable-file provider passes the unchanged file snapshot to the table parser. The parser uses all dwLength - 8 bytes as ASN.1 input, including finalized entry padding; the ASN.1 reader then requires ContentInfo to end exactly at that buffer's end. The catch converts this boundary mismatch to malformed PKCS#7. MCP registration reaches the same service, but that transport was not executed here.

Additional context

Open and closed GitHub issues were searched for certificate, Authenticode, WIN_CERTIFICATE, dwLength, certificate padding, and the exact trailing-data message. Explicit open and closed certificate padding searches returned no matches. The closest candidate, closed #6574, was read in full: it concerns an indefinite-length BER ContentInfo envelope. This fixture uses definite-length DER and changes only the PE entry's length field; the current documented BER support and its restoration check are distinct from the external alignment boundary. APK #6439 concerns issuer-string encoding during signer matching; APK #6603 concerns constructed BER. Neither covers PE entry padding. NOT_ENOUGH_INFO: no last-known-good release or real-world frequency was established.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: Both tested PE formats lose all requested structural certificate/signer output when dwLength counts documented zero padding; each same-payload control succeeds. The observed failure is bounded to this entry representation.
  • Workaround status: unverified
  • Workaround basis: No demonstrated REA route recovers the requested facts while preserving the original selected artifact. Changing dwLength succeeds only on a different artifact and digest; installed-launcher, alternate-tool and real-artifact recovery were not tested.
Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6694 ## 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 The production `inspect-pe-certificates <path> --json` command registration, executed directly from source, refuses a type-2 `WIN_CERTIFICATE` when its `dwLength` includes the required zero alignment padding. Both PE32 and PE32+ fixtures return `invalid_request` / `invalid_input`, with `invalid_format` and `PKCS#7 signedData is malformed: ContentInfo has trailing data.` The identical SignedData and padding bytes decode successfully when the entry length excludes those padding bytes. Microsoft documents that the finalized `dwLength` should include this padding. Executed paths were the real production CLI registration and output wrapper, plus separate shared-service calls using the real stable-file provider. This was not an installed `rea` executable, compiled launcher, stdio MCP, Windows loader, or signature-verification run. ## Reproduction Use the source revision and locked dependencies stated below, with the supported Node runtime. Run from the checkout root. The following self-contained replay includes the full self-authored payload and PE headers. It combines the executed fixture generator, provider/service calls, and production CLI-registration harness, with scratch/import paths made portable and intermediate log-writing omitted. The original service and CLI scripts ran separately; this combined presentation was not separately executed. ```sh node --import ./node_modules/tsx/dist/loader.mjs --input-type=module <<'JS' import { mkdtempSync, readFileSync, writeFileSync } from 'node:fs'; import { createHash } from 'node:crypto'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { Writable } from 'node:stream'; import { performance } from 'node:perf_hooks'; import { Cli } from 'incur'; import pino from 'pino'; import { PeCertificatesService } from './src/application/binaryDiagnostics/PeCertificatesService.ts'; import { PeCertificatesProvider } from './src/native/pe/PeCertificatesProvider.ts'; import { registerBinaryDiagnosticsCommands } from './src/cli/binaryDiagnosticsCommands.ts'; import { createCliResultOutput, streamCliCommandResults } from './src/cli/cliResultOutput.ts'; const root = mkdtempSync(join(tmpdir(), 'rea-pe-padding-')); // Complete definite-length ContentInfo/SignedData: one structural X.509 // certificate, one SignerInfo, serial 01 and commonName "Padding Fixture". // Signature bytes are inert placeholders; cryptographic validity is unobserved. const payload = Buffer.from( '3082012006092a864886f70d010702a08201113082010d020101310f300d06096086480165030402010500300b06092a864886f70d010701a081a030819d308187020101300d06092a864886f70d01010b0500301a3118301606035504030c0f50616464696e672046697874757265301e170d3235303130313030303030305a170d3236303130313030303030305a301a3118301606035504030c0f50616464696e672046697874757265301b300d06092a864886f70d0101010500030a00300702020ca1020111300d06092a864886f70d01010b05000302000131473045020101301f301a3118301606035504030c0f50616464696e672046697874757265020101300d06096086480165030402010500300d06092a864886f70d0101010500040101', 'hex', ); const rawLength = 8 + payload.length; // 300 const tableLength = Math.ceil(rawLength / 8) * 8; // 304 function image(plus, counted) { const b = Buffer.alloc(0x400 + tableLength); b.writeUInt16LE(0x5a4d, 0); b.writeUInt32LE(0x80, 0x3c); b.writeUInt32LE(0x4550, 0x80); b.writeUInt16LE(plus ? 0x8664 : 0x14c, 0x84); b.writeUInt16LE(1, 0x86); b.writeUInt16LE(plus ? 240 : 224, 0x94); b.writeUInt16LE(plus ? 0x2022 : 0x2102, 0x96); // Data-only DLL. b.writeUInt16LE(plus ? 0x20b : 0x10b, 0x98); b[0x98 + 2] = 14; b.writeUInt32LE(0x200, 0x98 + 8); if (plus) b.writeBigUInt64LE(0x180000000n, 0x98 + 24); else { b.writeUInt32LE(0x1000, 0x98 + 24); b.writeUInt32LE(0x10000000, 0x98 + 28); } b.writeUInt32LE(0x1000, 0x98 + 32); b.writeUInt32LE(0x200, 0x98 + 36); b.writeUInt16LE(6, 0x98 + 40); b.writeUInt16LE(6, 0x98 + 48); b.writeUInt32LE(0x2000, 0x98 + 56); b.writeUInt16LE(3, 0x98 + 68); b.writeUInt16LE(0x140, 0x98 + 70); if (plus) { b.writeBigUInt64LE(0x100000n, 0x98 + 72); b.writeBigUInt64LE(0x1000n, 0x98 + 80); b.writeBigUInt64LE(0x100000n, 0x98 + 88); b.writeBigUInt64LE(0x1000n, 0x98 + 96); } else { for (const at of [72, 80]) b.writeUInt32LE(0x100000, 0x98 + at); for (const at of [76, 84]) b.writeUInt32LE(0x1000, 0x98 + at); } const section = 0x98 + (plus ? 240 : 224); Buffer.from('.data').copy(b, section); b.writeUInt32LE(1, section + 8); b.writeUInt32LE(0x1000, section + 12); b.writeUInt32LE(0x200, section + 16); b.writeUInt32LE(0x200, section + 20); b.writeUInt32LE(0xc0000040, section + 36); b.writeUInt32LE(0x200, 0x98 + 60); b.writeUInt32LE(16, 0x98 + (plus ? 108 : 92)); const directory = 0x98 + (plus ? 112 : 96) + 4 * 8; b.writeUInt32LE(0x400, directory); b.writeUInt32LE(tableLength, directory + 4); b.writeUInt32LE(counted ? tableLength : rawLength, 0x400); b.writeUInt16LE(0x200, 0x404); b.writeUInt16LE(2, 0x406); payload.copy(b, 0x408); return b; } const sha256 = b => createHash('sha256').update(b).digest('hex'); const service = new PeCertificatesService(new PeCertificatesProvider()); try { for (const plus of [false, true]) for (const counted of [false, true]) { const fixture = `${plus ? 'pe32plus' : 'pe32'}-padding-${counted ? 'counted' : 'excluded'}.exe`; const path = join(root, fixture); const bytes = image(plus, counted); writeFileSync(path, bytes); const before = sha256(bytes); const direct = await service.inspect({ path }); const args = ['inspect-pe-certificates', path, '--json']; let stdout = ''; const sink = new Writable({ write(chunk, encoding, done) { stdout += chunk.toString(); done(); } }); const output = createCliResultOutput(args, sink); const cli = Cli.create('rea', { version: '6.7.0', sync: false, update: false }); registerBinaryDiagnosticsCommands(cli, pino({ level: 'silent' }), {}); streamCliCommandResults(cli, output); let explicitExit; const previousExitCode = process.exitCode; process.exitCode = undefined; const start = performance.now(); await cli.serve(args, { stdout: text => { if (!output.handled) stdout += text; }, exit: code => { explicitExit = code; }, }); const elapsedMs = performance.now() - start; const exitCode = explicitExit ?? process.exitCode ?? 0; process.exitCode = previousExitCode; const value = JSON.parse(stdout); console.log(JSON.stringify({ fixture, fileBytes: bytes.length, sha256: before, payloadBytes: payload.length, dwLength: bytes.readUInt32LE(0x400), tableLength, unchanged: before === sha256(readFileSync(path)), serviceOk: direct.ok, exitCode, elapsedMs, coverage: value.normalized_result?.coverage, certificateSerial: value.normalized_result?.entries[0].pkcs7.certificates[0].serial_hex, signerSerial: value.normalized_result?.entries[0].pkcs7.signers[0].serial_hex, error: value.details?.issues })); } } finally { await service.close(); } JS ``` The replay leaves four self-authored fixtures in a `rea-pe-padding-*` directory under the system temporary directory. The source CLI callback's `exitCode` is recorded per invocation; the outer multi-case harness does not itself exit 1 after the expected failures. Both final counted-padding fixtures failed in the executed source-CLI sample; both controls passed. Separate provider/service calls produced the same two failures and two successful controls. ## Expected behavior Return the structural table, certificate and signer facts for the unchanged padding-counted fixtures. [Microsoft's PE attribute certificate table specification](https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#the-attribute-certificate-table-image-only) describes zero padding to the next eight-byte boundary and includes that padding in the finalized `dwLength`. The security-directory size is 304 in both cases, with no truncated table, missing payload bytes, extra entry or exceeded budget. REA's [documented observations](https://github.com/morluto/rea/blob/a7f967cf240eba8309cbf4526b2fc6338a52a617/docs/pe-certificates.md#L24-L35) promise type-2 structural certificate and signer facts; its [failure profile](https://github.com/morluto/rea/blob/a7f967cf240eba8309cbf4526b2fc6338a52a617/docs/pe-certificates.md#L40-L64) does not exclude ordinary WIN_CERTIFICATE alignment padding. Keep raw artifact/entry ranges and hashes attributable to the original selected bytes. Signature verification is outside this expectation. ## Impact and scope P2 bounded format-compatibility failure: counting four documented zero padding bytes prevents the entire requested certificate inventory from being returned for both tested PE formats. The exact same definite-length SignedData bytes decode in the controls, isolating the PE length boundary from CMS encoding support. Each final-format pair differs only at file offset `0x400`: the low byte of `dwLength` changes from `0x2c` to `0x30` (300 to 304). The payload and padding remain identical. No same-artifact REA workaround was demonstrated. Rewriting `dwLength` creates the passing control but changes the caller-selected bytes and digest. NOT_ENOUGH_INFO: real-artifact prevalence, other platforms and an introducing release were not measured. Windows loading and cryptographic validity are outside the executed structural inspection scope. ## REA version Source revision `a7f967cf240eba8309cbf4526b2fc6338a52a617`, manifest `rea-agents` 6.7.0. The parent investigation supplied the npm-latest check as 6.7.0. No dependency install, build, global installation, source change or remote issue filing occurred. ## Runtime and platform Node.js v24.22.1, Linux x64, kernel `7.2.8-9-omarchy`; repository-pinned Incur 0.6.1 and tsx 4.23.15. No Hopper, Ghidra, Windows SDK or analysis engine was used. Measured final source-CLI handler durations in order PE32 control/failure, PE32+ control/failure were 19.105 / 3.507 / 5.682 / 1.074 ms. The separate service durations were 16.621 / 1.229 / 1.495 / 0.544 ms. These are individual same-process measurements, not performance or prevalence claims. ## Target and artifact context Self-authored 1,328-byte PE32/PE32+ data-only DLL fixtures with conventional headers, one initialized-data section and a 304-byte certificate table at file offset 1,024. The full 292-byte definite-length SignedData above contains one structural X.509 certificate with a DER RSAPublicKey field, one SignerInfo and inert signature placeholders. The failing finalized entry has eight header bytes, 292 payload bytes and four zero padding bytes counted in `dwLength`. Neither executing the fixture nor accepting its signature is required by this read-only structural contract. ## Interface CLI ## Earliest failure stage Native analysis or reconstruction ## Relevant output or evidence - Expected source: [REA structural inspection contract](https://github.com/morluto/rea/blob/a7f967cf240eba8309cbf4526b2fc6338a52a617/docs/pe-certificates.md#L24-L64), [public tool contract](https://github.com/morluto/rea/blob/a7f967cf240eba8309cbf4526b2fc6338a52a617/src/contracts/native/binaryDiagnosticsToolContracts.ts#L92-L98), and [Microsoft's WIN_CERTIFICATE padding rule](https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#the-attribute-certificate-table-image-only). - Failure source: executed production [CLI registration and dispatch](https://github.com/morluto/rea/blob/a7f967cf240eba8309cbf4526b2fc6338a52a617/src/cli/binaryDiagnosticsCommands.ts#L104-L135) and separate service/provider observations on the complete inline fixture; [entry payload slicing](https://github.com/morluto/rea/blob/a7f967cf240eba8309cbf4526b2fc6338a52a617/src/native/pe/PeCertificateParser.ts#L103-L114) supplies the padding bytes to the [ContentInfo trailing-data check](https://github.com/morluto/rea/blob/a7f967cf240eba8309cbf4526b2fc6338a52a617/src/native/pe/PeCertificateAsn1.ts#L560-L563). - Evidence provenance: observed; source inspection separately explains the boundary mismatch. - Local verification: reproduced through source CLI registration/output handling and the real shared service/provider. The installed launcher and MCP transport were not run. - Reproduction completeness: complete. Both source-CLI failures contain this observed output excerpt: ```json { "code": "invalid_request", "category": "invalid_input", "retryable": false, "details": { "operation": "inspect_pe_certificates", "issues": [{ "path": ["path"], "reason": "invalid_format", "message": "PKCS#7 signedData is malformed: ContentInfo has trailing data." }] } } ``` Both controls return `coverage: {"status":"complete","examined_entries":1,"certificates":1}` and certificate/signer serials `01`. Final fixture SHA-256 values are: PE32 excluded `5c809299f4ca837e22ca790dfafd400e2799e31d94b3a7573cfdb1e4fcd6f83d`; PE32 counted `76f33f51fa023ee472982df07273f21adc6df1fce63f25289121f1604a8a1a88`; PE32+ excluded `2e1a1dcef692c9f0da505d87f4846b75d052ae5aa6c1f3d0833923fab9a77a23`; PE32+ counted `31c148da6f04a79a36803ca07505d2536e5e26ec041c26b523ae633b913c6de0`. ## Typed outcome or recovery expected Successful structural Evidence for each unchanged padding-counted fixture, including the one certificate and signer already observed in its control. Restoration should preserve artifact identity, table/entry range evidence and original digests; both controls should continue to return the same facts. This is a proposed restoration check; no fix was implemented. A proposal to tolerate arbitrary non-padding data after ContentInfo is outside this report. ## Analysis or suspected cause Source-derived explanation: the [stable-file provider](https://github.com/morluto/rea/blob/a7f967cf240eba8309cbf4526b2fc6338a52a617/src/native/pe/PeCertificatesProvider.ts#L60-L80) passes the unchanged file snapshot to the table parser. The parser uses all `dwLength - 8` bytes as ASN.1 input, including finalized entry padding; the ASN.1 reader then requires ContentInfo to end exactly at that buffer's end. The [catch](https://github.com/morluto/rea/blob/a7f967cf240eba8309cbf4526b2fc6338a52a617/src/native/pe/PeCertificateParser.ts#L160-L164) converts this boundary mismatch to malformed PKCS#7. [MCP registration](https://github.com/morluto/rea/blob/a7f967cf240eba8309cbf4526b2fc6338a52a617/src/server/registerPeCertificatesTool.ts#L18-L29) reaches the same service, but that transport was not executed here. ## Additional context Open and closed GitHub issues were searched for `certificate`, `Authenticode`, `WIN_CERTIFICATE`, `dwLength`, `certificate padding`, and the exact trailing-data message. Explicit open and closed `certificate padding` searches returned no matches. The closest candidate, closed [#6574](https://github.com/morluto/rea/issues/6574), was read in full: it concerns an indefinite-length BER ContentInfo envelope. This fixture uses definite-length DER and changes only the PE entry's length field; [the current documented BER support](https://github.com/morluto/rea/blob/a7f967cf240eba8309cbf4526b2fc6338a52a617/docs/pe-certificates.md#L55-L61) and its restoration check are distinct from the external alignment boundary. APK [#6439](https://github.com/morluto/rea/issues/6439) concerns issuer-string encoding during signer matching; APK [#6603](https://github.com/morluto/rea/issues/6603) concerns constructed BER. Neither covers PE entry padding. NOT_ENOUGH_INFO: no last-known-good release or real-world frequency was established. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: Both tested PE formats lose all requested structural certificate/signer output when dwLength counts documented zero padding; each same-payload control succeeds. The observed failure is bounded to this entry representation. - Workaround status: unverified - Workaround basis: No demonstrated REA route recovers the requested facts while preserving the original selected artifact. Changing dwLength succeeds only on a different artifact and digest; installed-launcher, alternate-tool and real-artifact recovery were not tested.
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#4069
No description provided.