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.
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
- IBM ContextForge MCP Gateway <= v1.0.7 MCP Context Forge could allow a remote authenticated attacker to obtain sensitive credentials and escalate privileges due to improper validation of jq filters (CVE-2026-18486) (GitHub Advisory Database)
- fix(security): sandbox jq filter execution for tool jsonpath_filter (#6205) (IBM (GitHub Pull Requests, IBM/mcp-context-forge))
- fix(security): sandbox jq filter execution for tool jsonpath_filter (14dcfec) (IBM (GitHub, commit, IBM/mcp-context-forge))
Supporting sources
- Release v1.0.8, IBM/mcp-context-forge (IBM (GitHub Releases, IBM/mcp-context-forge))
- mcpgateway/utils/jq_guard.py at v1.0.8 (IBM (GitHub, source, IBM/mcp-context-forge))
- mcpgateway/utils/jq_runner.py at v1.0.8 (IBM (GitHub, source, IBM/mcp-context-forge))
- mcpgateway/services/tool_service.py at v1.0.8 (extract_using_jq and its two invocation call sites) (IBM (GitHub, source, IBM/mcp-context-forge))
- mcpgateway/schemas.py at v1.0.8 (jsonpath_filter field validators on ToolCreate and ToolUpdate) (IBM (GitHub, source, IBM/mcp-context-forge))
- mcpgateway/main.py at v1.0.8 (tool creation, update and execution permission decorators) (IBM (GitHub, source, IBM/mcp-context-forge))
- docs/docs/manage/securing.md at v1.0.8 (jq filter sandbox: platform limitation and credential rotation) (IBM (GitHub, documentation, IBM/mcp-context-forge))
- mcpgateway/utils/jq_guard.py on the current default branch (IBM (GitHub, source, IBM/mcp-context-forge))
- Release/1.0.8 (#6268) (IBM (GitHub Pull Requests, IBM/mcp-context-forge))
- 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
- verified
GitHub's Security Advisory Database, fetched directly twice in this session, assigns CVE-2026-18486, published 4 September 2026, rates it CVSS 3.1 base 8.8, High, vector CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H, and classifies it CWE-200, against IBM ContextForge MCP Gateway through v1.0.7.
The Agent Called the Tool. A Program Stored Earlier Ran Inside the Gateway. - verified
This session downloaded these exact files at the v1.0.8 tag. jq_guard.py's DENIED_JQ_TOKENS names exactly env, $ENV, input, inputs, input_filename, input_line_number, $__loc__, debug, stderr, include, import and modulemeta; extract_using_jq in tool_service.py calls assert_safe_jq_filter against that same list immediately before evaluating a filter, confirming these were exactly the builtins the pre fix evaluation left unrestricted.
The Agent Called the Tool. A Program Stored Earlier Ran Inside the Gateway. - verified
This session downloaded this file directly at the v1.0.8 tag and confirmed the tool creation and tool update routes each carry a require_permission decorator naming tools.create and tools.update respectively, while tool invocation routes carry a separate tools.execute permission check, confirming the two principals this entry describes are gated by genuinely distinct permissions.
The Agent Called the Tool. A Program Stored Earlier Ran Inside the Gateway. - verified
Fetched directly twice in this session. Confirmed merged as commit 14dcfecb578a80404dc8c1fd1d66cd3165e68753, and confirmed the pull request's own description states the credential rotation guidance this entry names and an internal reference, PSIRT REF 3874125 and ADV0318908.
The Agent Called the Tool. A Program Stored Earlier Ran Inside the Gateway. - verified
This session downloaded this file directly at the v1.0.8 tag. Confirmed, quoted directly, the platform limitation that the sandboxed worker path is silently inactive on any non Linux platform regardless of configuration, falling back to an in process filter with no environment clearing and no time limit, and the credential rotation guidance naming JWT_SECRET_KEY, AUTH_ENCRYPTION_SECRET, DATABASE_URL, REDIS_URL and BASIC_AUTH_PASSWORD.
The Agent Called the Tool. A Program Stored Earlier Ran Inside the Gateway. - verified
This session confirmed the identical denylist is present unchanged on the project's current default branch, and confirmed from the project's own releases listing that v1.0.9, published 1 September 2026, is the current latest release, published after v1.0.8 and before this CVE's own 4 September 2026 publication date, so the mitigation has not been reverted since it shipped.
The Agent Called the Tool. A Program Stored Earlier Ran Inside the Gateway.
