Skip to content

fix(contracts): require an absolute local path for byte-entropy input - #7759

Closed
z2Ace0107 wants to merge 6 commits into
morluto:mainfrom
z2Ace0107:fix/7537-byte-entropy-absolute-path
Closed

z2Ace0107 wants to merge 6 commits into
morluto:mainfrom
z2Ace0107:fix/7537-byte-entropy-absolute-path

Conversation

@z2Ace0107

@z2Ace0107 z2Ace0107 commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary

 export const inspectByteEntropyInputSchema = z.strictObject({
-  path: localPathStringSchema,
+  path: absoluteLocalPathStringSchema.describe(
+    "Absolute local filesystem path to the file to profile; relative paths are rejected",
+  ),

localPathStringSchema only requires a nonempty, NUL-free string, so the
advertised MCP request schema accepted {"path":"sample.bin"}. The runtime
refuses that value with invalid_request and requires an absolute local
filesystem path, so the advertised contract invited a request that cannot
succeed.

Problem and expected behavior

The stdio MCP server advertises an input schema that accepts
inspect_byte_entropy with {"path":"sample.bin"}, but that request returns
invalid_request requiring an absolute filesystem path. Selecting the same file
by its absolute path returns successful Evidence. Both the compact and full
input profiles exhibit the mismatch, and the canonical producer and MCP request
schemas both accept the relative value, so this is not solely an advertised JSON
Schema conversion discrepancy.

Expected behavior: the advertised schema states the same requirement the runtime
enforces, so an agent is not told a relative path is acceptable.

Change and scope

Constrain the advertised path to an absolute local path by reusing the existing
absoluteLocalPathStringSchema owner in src/domain/localPath.ts, which already
carries the message "must be an absolute local filesystem path" and is used by
sibling contracts (decompileDirectory, applicationWorkflowInputContracts,
electronToolContracts).

The property also carries an explicit description, for the same reason its
sibling native diagnostics contracts do (binaryLayout, nativeExports,
peCertificates, peImports, peLoadConfig, peResources, recordedCrash).
Without one this property falls back to the generic "Local filesystem path used
by this operation.", which says nothing about the requirement.

PR #7449 (still open) fixes the same class of mismatch for five sibling tools
using an MCP-only schema overlay, leaving the shared producer contract alone.
This tool has no second consumer to protect: inspect-binary-entropy declares
its own argument schema, so the contract is corrected directly instead of
overlaid.

The direction follows the repository's own documented intent: the CLI input
helper states that shared MCP contracts reject relative paths and that CLI
workflows resolve operator-relative values before validation. A relative path is
therefore expected to fail over MCP, and the schema is what was wrong.

Review follow-up: the service-level if (!isAbsolute(input.path)) guard in
ByteEntropyService is removed in this change as well. It ran after
parseAnalysisInput had already validated path against the contract this PR
tightens, and both entry points pass through that schema: the MCP tool
registration uses it directly, and the CLI command resolves its argument with
absoluteCallerPath before constructing the same schema-shaped input. No
admitted request can reach the guard, so keeping a node:path predicate beside
the regex contract left one invariant owned twice. The sibling services keep
their guards because their contracts still use localPathStringSchema; for them
the guard is the enforcing boundary, not a duplicate (the same drift PR #7449
addresses with an overlay).

Deliberately not included: the CLI path is unchanged. inspect-binary-entropy
declares its own localPathStringSchema argument and resolves it with
absoluteCallerPath, so an operator-relative path keeps working there. This PR
tightens the MCP contract only; it does not relax the runtime's absolute-path
requirement.

Known gap this PR does not close

The compact input-schema profile is the production default
(resolveMcpInputSchemaProfile(undefined) is compact). It reduces a required
property whose schema carries a combinator down to its declared type, so the
advertised path under that profile keeps no pattern, minLength, or allOf;
the requirement survives only as the property description and as canonical
server-side validation.

This is a pre-existing property of the profile, not something this change
introduces: 36 of the 43 tools that advertise an absoluteLocalPathStringSchema
property are already reduced, including open_binary and every sibling native
diagnostics tool, and docs/mcp-contracts.md records the reduction as the
deliberate projection for gateways that cannot translate allOf on a required
property. What this change does do is move inspect_byte_entropy from the inline
presentation into that reduced set, because it was the one tool in the family
that previously chained a single pattern. The honest cost is that the compact
advertisement for this tool loses the minLength and NUL-free pattern it used to
carry.

Fixing the profile itself would flip 36 tools back to inline and change the whole
compact catalog digest, so it is deliberately out of scope here. The description
above is what keeps the requirement readable on the default profile meanwhile,
and the boundary test asserts the canonical rejection that the reduced
advertisement no longer expresses.

Contract and boundary impact

  • Semantic owner and earliest changed stage: src/domain/native/byteEntropy.ts,
    the shared input contract for byte-entropy inspection. Consumers are the MCP
    tool registration and the advertised JSON Schema.
  • Contribution scope fit and linked maintainer decision: not applicable. This is
    a schema and description correction at an existing contract owner, with no
    target execution, injection, mutation, or protection recovery.
  • CLI and MCP/tool-catalog contract: the advertised inspect_byte_entropy input
    schema gains the absolute-path constraint, so a request that previously passed
    schema validation and then failed at runtime is now refused at the schema.
    Canonical server-side validation is unchanged for absolute paths.
  • Provider, bridge, target-format, or platform compatibility: none.
    absoluteLocalPathStringSchema selects its pattern by host platform, the same
    way the sibling contracts do.
  • Evidence, artifact, provenance, or reconstruction contract: none; no Evidence
    shape changes.
  • Generated metadata: docs/public/product-catalog.json is generated and
    validated by CI; it is not committed here. The MCP tool catalog is regenerated
    locally from dist/ and rechecked, and is likewise not committed.

Expected effects: documentation or usability only; no target interaction.

Evidence

  • Before: on unmodified main, the advertised schema accepted a relative
    path. The new contract assertion failed with expected true to be false for
    sample.bin, and the new advertised-schema assertion failed the same way,
    confirming both the canonical contract and the advertised JSON Schema admitted
    the value.
  • After: both assertions pass. The contract test rejects sample.bin,
    ./sample.bin and ../sample.bin while /file still parses. The
    advertised-schema test registers the tool on a real MCP server, reads it back
    with listTools, compiles the advertised schema with Ajv, and confirms
    {"path":"sample.bin"} is refused while {"path":"/file"} is accepted.
  • The tool joins its sibling native diagnostics cases in
    tests/boundary/mcp/absoluteLocalPathSchema.test.ts, which already drives both
    input-schema profiles through a real server, asserts the structured issue
    through parseMcpToolError, and records that the provider was never reached.
    That is the scope docs/mcp-contracts.md sets for compact workflow tests:
    canonical rejection before provider work, independent of how much detail the
    advertisement retains.
  • That file asserts each tool's issue path and zod issue code. This tool's
    reported issue shape is not stable across host platforms: Linux reports a
    path-scoped invalid_format like its siblings, while Windows reports a
    root-level invalid_value. Only the platform-stable facts are asserted for it
    — the error code, its category, and that the rejection lands at input
    validation — rather than a shape that would fail on the other host.
  • Mutation evidence (4 mutations, 2 killed per file, 3 distinct mutations
    killed overall): dropping the absolute requirement from the schema — killed
    by both files; widening the absolute pattern so it matches any prefix —
    killed by both files; changing the rejection message — survives;
    replacing the property description with the generic fallback text —
    survives. The two survivors are deliberate: docs/testing.md puts
    documentation wording in review rather than in substring tests, and the
    assertions carry the error code and structured facts instead.
  • Before the service guard was removed, dropping the schema requirement
    survived the wire-level test: ByteEntropyService kept its own
    absolute-path guard reporting the same invalid_request code and
    invalid_input category, so the wire test could not separate schema from
    handler rejection without matching prose. Removing the superseded guard in
    this change closes that gap, and the wire test now kills that mutation too.

Validation performed

  • vitest run src/domain/native/byteEntropy.test.ts src/contracts/native/binaryDiagnosticsToolContracts.test.ts tests/boundary/mcp/toolSchemaValidity.test.ts tests/boundary/mcp/absoluteLocalPathSchema.test.ts tests/boundary/mcp/compactInputSchemaMcp.test.ts tests/boundary/mcp/binaryDiagnosticsMcp.test.ts — 46 passed. src/application/binaryDiagnostics/ByteEntropyService.ts passes oxfmt --check, oxlint (the three reported warnings are pre-existing on the merge base, and the removal reduces one function by four lines), and npm run typecheck.
  • vitest run tests/boundary/mcp/compactInputSchemaMcp.test.ts tests/boundary/mcp/binaryDiagnosticsMcp.test.ts — 21 passed. These are the other consumers of the compact profile and of the shared absolute-path contract.
  • npm run compile then node scripts/generate-mcp-tool-catalog.mjs --check — pass. The description change moves the recorded catalog, so the generated cache is regenerated and rechecked; the catalog itself is not committed.
  • mutation check — 2/2 killed for the advertised-schema assertion; 2/4 killed for the wire-level rejection, with both survivors recorded above.
  • npm run verify:test-discovery — 2,705 files discovered exactly once.
  • npm run verify:module-boundaries — 3,289 source files checked.
  • oxfmt --check . — all matched files correctly formatted.
  • oxlint on the changed files — no new findings; the one reported complexity
    warning is pre-existing on unmodified main in the same function.
  • npm run typecheck — clean. compile uses tsconfig.build.json, which
    excludes tests, so typecheck is the only command covering the edited test
    files.

Compatibility, safety, and release

  • Breaking changes or migration steps: an MCP caller that sends a relative
    path to inspect_byte_entropy now fails schema validation instead of
    runtime validation. It already failed, so no working caller changes
    behaviour; only the error surface moves earlier.
  • Real Hopper/Ghidra, browser, or OS coverage: none required. Byte entropy is
    computed locally with no engine, and no target is executed.
  • Package or release metadata impact: none beyond the generated product catalog.
  • Security, privacy, process, or containment review: none. No credentials,
    paths, or target data are read or logged by this change.

Merge Danger

Door: two-way. Revert the one-line schema change and the tests together.

Blast Radius: the advertised MCP input schema for one tool.

Author readiness

Human owner: @z2Ace0107

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

Before merge

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

@greptile-apps

greptile-apps Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5 Tier: apex

[Low impact] The PR appears safe to merge; removing the duplicate check preserves rejection before file access.

Summary

The PR requires an absolute byte-entropy input path in the shared contract, adds contract and MCP tests, and removes the duplicate service check.

  • MCP byte-entropy requests now require an absolute file path.

Reviews (5) · Last reviewed commit: "refactor(native): remove the superseded ..." · Reviewed by Greptile

@z2Ace0107
z2Ace0107 marked this pull request as ready for review October 28, 2026 09:51
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ❌ Failed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ❌ Failed 2026-11-06T06:06:55.432970Z
We couldn’t complete this review. Comment @codex review to try again.
1f20e52 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Repo admins can enable using credits for code reviews in their settings.

@greptile-apps

greptile-apps Bot commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

Want your agent to iterate on Greptile's feedback? Try greploops.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9d2d81de60

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/domain/native/byteEntropy.ts Resolved
@morluto

morluto commented Nov 5, 2026

Copy link
Copy Markdown
Owner

Hey @z2Ace0107, thanks for putting this together, the fix looks useful. What's left: there is 1 unresolved review thread from Codex, about "Remove the superseded service absolute-path guard". Once that's done I'm happy to merge it.

@z2Ace0107

Copy link
Copy Markdown
Contributor Author

Done — the superseded guard is removed in 694c58c.

Verified before removing: parseAnalysisInput validates path against inspectByteEntropyInputSchema, which is exactly the contract this PR tightens, and both entry points pass through it — the MCP tool registration uses it directly, and the CLI command resolves its operator-relative argument with absoluteCallerPath before constructing the same schema-shaped input. No admitted request can reach the guard, so the node:path predicate and the regex contract no longer have two owners.

The sibling services (Devicetree, ElfNotes, KernelModule, AppleIps, ApkSignature, PythonPyc) keep their guards: their contracts still use localPathStringSchema, so for them the guard is the enforcing boundary rather than a duplicate. That is the same drift #7449 addresses for its five tools.

Validation: the six affected test files pass (46 tests), oxfmt --check and npm run typecheck clean, and the reported oxlint warnings on the service are pre-existing on the merge base. Mutation evidence in the PR body is updated — with the guard gone, the wire-level test now also kills the "drop the schema requirement" mutation that previously survived on the guard's identical error surface.

@z2Ace0107

Copy link
Copy Markdown
Contributor Author

待发评论(尚未发布,需用户确认)

对象:#7759
准备时间:2026-11-05(Asia/Singapore)


Rebased nothing yet — the red required check here is pre-existing on main, not introduced by this branch.

  • The one failing test in Source tests and Portable tests is
    src/domain/process/processCapture.comparison.test.ts > compares recoverable symlink bytes across old and current captures.
    It throws a ZodError from parseProcessCapture
    (src/domain/process/processCaptureParsing.ts:21), code custom on
    filesystem_checkpoints:
    top-level filesystem snapshots and effects must match the initial and final checkpoints.
  • The same test fails identically on three main runs that contain no commits from this branch:
    run 42004017236 (c7fce6603, 2026-11-05 09:38 UTC), run 42010916598
    (fbfb1b768, 10:27 UTC, schedule event), run 42012360835 (fe677f6cf,
    10:38 UTC). On those runs main also shows two further failures
    (tests/boundary/cli/fridaCli.test.ts, tests/boundary/mcp/analysisViewMcp.test.ts)
    that this branch does not reproduce.
  • This PR changes five files, none under src/domain/process/:
    src/application/binaryDiagnostics/ByteEntropyService.ts,
    src/domain/native/byteEntropy.ts,
    src/contracts/native/binaryDiagnosticsToolContracts.test.ts,
    tests/boundary/mcp/absoluteLocalPathSchema.test.ts,
    tests/boundary/mcp/toolSchemaValidity.test.ts.
  • Local verification on this branch: those five files plus
    processCapture.comparison.test.ts pass, 44 tests green, checked out at 694c58c3a,
    whose merge base with main is 2b8fb387b.

So CI required will stay red on this PR until main is green again. The branch is still reported MERGEABLE, so I have not rebased: rebasing onto current main would only pull in the same checkpoint failure. Say the word and I will rebase once that invariant is fixed upstream, or now if you would rather keep the branch current.

Separately: the Codex review thread on src/domain/native/byteEntropy.ts is addressed in 694c58c3a. I have not tried to resolve it, so I do not know whether the resolve action is available to me as the PR author — flagging it so it does not look like an ignored request.

…he description

The compact input-schema profile is the production default. It reduces a
required property whose schema carries a combinator down to its declared type,
so the advertised `path` under that profile keeps no `pattern`, `minLength` or
`allOf`; only the property description and canonical server-side validation
still carry the requirement.

Without an explicit description this property falls back to the generic "Local
filesystem path used by this operation.", which says nothing about the
requirement. Every sibling native diagnostics contract already describes its
absolute path, so this was the one gap in the family.
…ntropy path

docs/mcp-contracts.md scopes compact workflow tests to canonical rejection of
invalid inputs before provider work, independent of how much detail the selected
advertisement retains. The boundary test only read the advertisement back and
compared shapes, so it never exercised the rejection the reduced advertisement
can no longer express.

Connects a real EvidenceMcpServer under the production default profile and
calls the tool with a relative path, asserting the call is refused and the
message names the requirement. The full-profile test keeps its Ajv assertions
over the advertised schema and now names the profile it runs under, since a
plain McpServer is not the default.

The shared server setup is factored into a helper that hands the caller a
connected client, so both profiles share one connect path and the compact
branch keeps its EvidenceMcpServer type instead of being widened to McpServer.

Corrects a comment in the contract test that described the pre-change state,
where a relative path reached the schema and was refused only at runtime.
…jection

The compact-profile boundary test searched the whole reply for the shared
message text, so a harmless edit to the path message would fail it. That is the
same defect class this PR removed from two other assertions, and
docs/testing.md asks for error codes and structured facts instead of frozen
wording.

Moves the wire-level rejection into tests/boundary/mcp/absoluteLocalPathSchema.test.ts,
which already drives both input-schema profiles through a real server, asserts
the structured issue through parseMcpToolError, and records that the provider
was never reached. The tool joins its sibling native diagnostics cases there.

That file asserts each tool's issue path and zod issue code. This tool's
rejection surfaces as a root-level issue with no field path and
reason "invalid_value", where its siblings report a path-scoped
"invalid_format"; both are asserted as the structured facts they are rather than
matched against prose.

toolSchemaValidity.test.ts keeps the advertised-schema assertion it exists for
and drops the bespoke server scaffolding.
The previous commit pinned this tool's reported issue as a root-level
`invalid_value`, which is what the host it was written on produces. Linux reports
a path-scoped `invalid_format` instead, matching its siblings, so CI failed.

The issue shape for this tool is not stable across host platforms, so only the
platform-stable facts are asserted: the error code, its category, and that the
rejection lands at input validation. A companion probe confirmed the service
retains its own absolute-path guard, which reports the same code and category as
the schema rejection, so the wire-level test cannot separate the two without
matching prose. That mutation is carried by the advertised-schema assertion
instead, and the limitation is recorded in the PR body.
The service-level `if (!isAbsolute(input.path))` check ran after
`parseAnalysisInput` had already validated `path` against
`inspectByteEntropyInputSchema`, whose shared owner now requires an absolute
local path. Both entry points are covered by that schema: the MCP tool
registration uses it directly, and the CLI command resolves its operator-relative
argument with `absoluteCallerPath` before constructing the same schema-shaped
input. No admitted request can reach the guard, so keeping the `node:path`
predicate beside the regex contract left duplicate interpretations of one
invariant that could drift.

Removes the guard and the now-unused import.

The sibling services (Devicetree, ElfNotes, KernelModule, AppleIps, ApkSignature,
PythonPyc) keep their guards: their contracts still use `localPathStringSchema`,
so for them the guard is the enforcing boundary, not a duplicate. That is the
existing drift noted in PR morluto#7449.
@z2Ace0107
z2Ace0107 force-pushed the fix/7537-byte-entropy-absolute-path branch from 694c58c to 1f20e52 Compare November 6, 2026 06:06
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Repo admins can enable using credits for code reviews in their settings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants