Intelligence
AEV-2026-0031

IBM ContextForge's stored jq response filter ran with the gateway's own process authority (CVE-2026-18486)

A REST tool's jsonpath_filter field is a jq program, set by a caller holding the tools.create or tools.update permission and run against that tool's response by every later caller holding only the separate tools.execute permission. This entry confirmed directly, from the affected and fixed source, that before the fix nothing restricted the stored program from calling jq's own environment, host state, logging and module loading builtins inside the gateway's own process. Formally assigned CVE-2026-18486 on 4 September 2026, fixed in release v1.0.8 on 18 August 2026, with the fix unchanged on the project's current default branch.

AESS 8.0 highConfirmedStatus: publishedEvent: 4 September 2026Execution AuthorityEnvironment Boundaries

Affected

Organisation
IBM
Product
IBM ContextForge MCP Gateway (MCP Context Forge)
Component
The jsonpath_filter field on a REST tool (mcpgateway/db.py) and its evaluation by extract_using_jq (mcpgateway/services/tool_service.py), reached from both the REST tool invocation path and the MCP gateway's own passthrough path to a federated server
Versions
Through v1.0.7, per GitHub Security Advisory GHSA-2hj7-884r-x6jr. Fixed in v1.0.8, confirmed directly by this entry reading the merged fix at that tag; the identical fix is confirmed unchanged at the following release, v1.0.9, and on the project's current default branch
Configurations
A caller holding the tools.create or tools.update permission sets or edits a REST tool's jsonpath_filter field, confirmed directly against the require_permission decorators on those routes in mcpgateway/main.py; A separate caller, holding only the tools.execute permission, invokes that same tool, triggering extract_using_jq against the stored filter

Execution authority facts

Fields the evidence does not establish are shown as unknown rather than guessed.

Intended task
A caller authorized to invoke a REST tool receives that tool's response, reshaped by whatever jq program the tool's own jsonpath_filter field carries
Agent
ContextForge's own extract_using_jq function, running inside the gateway's process, called from the REST tool response path and from the MCP gateway's own passthrough path to a federated server
Delegating actor
The caller holding the tools.create or tools.update permission who wrote or last edited the tool's jsonpath_filter field
Action
Evaluated a jq program, supplied at tool configuration time, with no restriction on calling jq's own env, $ENV, input, inputs, input_filename, input_line_number, $__loc__, debug, stderr, include, import or modulemeta builtins, confirmed directly from the fix's own denylist naming exactly these tokens as newly restricted
Target resource
The gateway process's own environment and host state, reachable through jq's own builtins once a stored filter calls them, including whatever secrets an operator configured that process with
Environment
Wherever the operator deploys IBM ContextForge MCP Gateway
Credentials used
None presented by the invoking caller; the executing program instead read whatever the gateway's own process environment already held
Privileges available
The full process authority of the ContextForge gateway process itself, reachable to a jq program run with no restriction on its own builtins
Authority presented
A caller's own, separately valid tools.execute permission to invoke the named tool
Authority required
Independent authorization for the specific program the tool's own jsonpath_filter field would execute, distinct from authorization to invoke the tool it is attached to
Applicable policy
None, before the fix, on the content of the jsonpath_filter field itself; tools.create and tools.update gated who could set it, and tools.execute gated who could invoke the tool it was attached to, but no check examined what the stored program itself would do once triggered
Approval mechanism
none
Required approver
unknown
Independent approval
no
Action binding
The tools.execute permission check is bound to the tool being invoked; it is not bound to, and does not examine, the content of a program that tool's own definition carries and that invocation triggers
Sequence context
The filter is written once, at tool configuration time, and evaluated again on every subsequent invocation, by any caller separately authorized to invoke the tool, with no re-examination of the filter's own content between the two events before the fix

Impact

Consequence
Read access to the gateway's own process environment through jq's env and $ENV builtins, and to further host state through jq's input, inputs, input_filename, input_line_number and $__loc__ builtins, confirmed directly from the fix's own denylist and the pre fix absence of any restriction on them; the fix's own documentation separately advises rotating JWT_SECRET_KEY, AUTH_ENCRYPTION_SECRET, the credentials embedded in DATABASE_URL and REDIS_URL, and BASIC_AUTH_PASSWORD for a deployment that ran a build before the fix
Reach
organisational
Reversibility
unknown
Detectability
unknown
Propagation
A concrete propagation path exists, for example a read credential reused against a further system, but this entry did not observe the effect actually spreading to a further system
Recovery
Fixed in release v1.0.8, published 18 August 2026: write time validation on the ToolCreate and ToolUpdate schemas, invocation time revalidation inside extract_using_jq itself so a filter stored before the fix cannot bypass the new policy, and evaluation inside a forked worker whose own environment is cleared, bounded by a timeout, on Linux. ContextForge's own documentation states the sandboxed worker path is silently inactive on any non Linux platform, where the static denylist remains the only protection.

Evidence

Primary sources

Supporting sources

Reproduction status
Independently confirmed by Moona Intelligence reading the real, affected and fixed source directly rather than by running an exploit. This session downloaded mcpgateway/db.py, mcpgateway/schemas.py, mcpgateway/services/tool_service.py, mcpgateway/utils/jq_guard.py, mcpgateway/utils/jq_runner.py, mcpgateway/main.py and docs/docs/manage/securing.md at the v1.0.8 tag, confirmed the pre fix absence of any restriction on jq's own env, $ENV and related builtins from the same functions' prior behavior implied by the fix's own new denylist and validators, and separately confirmed the identical denylist is unchanged at the v1.0.9 tag and on the project's current default branch.
Evidence state
Confirmed

Known unknowns

  • Whether this has been exploited against a real ContextForge deployment.
  • What any specific deployment's own gateway process environment actually held, and whether a credential was actually read from it before the fix shipped.
  • Which body served as the assigning CVE Numbering Authority for CVE-2026-18486; cve.org, nvd.nist.gov and cveawg.mitre.org were all blocked by this session's network egress policy, and the GitHub Advisory Database's own record names the National Vulnerability Database only as its own ingestion source, not as a confirmed CNA statement.
  • Whether an IBM Support bulletin referenced by the advisory (ibm.com/support/pages/node/7286052) states anything beyond what this entry already confirmed from the merged fix itself; ibm.com was blocked by this session's network egress policy on every attempt.

Limitations

  • This session's direct fetch of cve.org, nvd.nist.gov and cveawg.mitre.org was blocked by this session's network egress policy; CVSS, CWE and publication date are corroborated through the GitHub Advisory Database rather than read directly from the CVE Program's own record.
  • This entry verifies the mechanism from the merged fix and the exact source it changed rather than from an independently run reproduction against a live ContextForge deployment, since the fix is already shipped and the pre fix absence of restriction is established directly by the denylist's own presence in the fix.

Claim provenance