Back to Blog
critical SEVERITY7 min read

Path Traversal in node-tar: How Hardlink Bypass Exposed Your Files

A medium-severity vulnerability (CVE-2026-24842) in node-tar allowed attackers to bypass hardlink security checks and create arbitrary files through path traversal attacks. This vulnerability, combined with improper configuration management storing JWT secrets in plaintext .env files, created a dangerous attack vector for token forgery and unauthorized access.

O
By Orbis AppSec
Published March 6, 2026Reviewed June 3, 2026

Answer Summary

CVE-2026-24842 is a critical path traversal vulnerability in node-tar (CWE-22) that allowed attackers to bypass hardlink security checks and write arbitrary files outside the extraction directory. The vulnerability occurred because node-tar failed to properly validate hardlink targets, allowing malicious tar archives to create symlinks or hardlinks pointing to sensitive files like .env configuration files containing JWT secrets. The fix required implementing strict path validation for hardlink targets and migrating sensitive configuration data from plaintext .env files to secure environment variable management systems.

Vulnerability at a Glance

cweCWE-22 (Improper Limitation of a Pathname to a Restricted Directory)
fixImplement hardlink target validation and migrate to secure configuration management
riskArbitrary file creation and JWT secret exposure leading to authentication bypass
languageNode.js
root causeMissing path validation for hardlink targets combined with plaintext secret storage
vulnerabilityHardlink path traversal bypass in node-tar

Introduction

Archive extraction vulnerabilities are among the most insidious security issues in software development. They often fly under the radar until an attacker exploits them to gain unauthorized access to sensitive files or execute malicious code. The recently patched CVE-2026-24842 in node-tar demonstrates how a seemingly minor bypass in hardlink security checks can cascade into a major security incident—especially when combined with poor configuration management practices.

If your application uses node-tar for archive extraction and stores sensitive configuration data (like JWT secrets) in plaintext files, this vulnerability could have allowed attackers to compromise your entire authentication system.

The Vulnerability Explained

What is Path Traversal?

Path traversal (also known as directory traversal) is a web security vulnerability that allows attackers to access files and directories outside the intended directory structure. By manipulating file paths with special characters like ../ (dot-dot-slash), attackers can "traverse" up the directory tree to reach sensitive files.

The node-tar Hardlink Vulnerability

Node-tar is a popular npm package used to create and extract tar archives in Node.js applications. The library includes security checks to prevent malicious archives from exploiting path traversal vulnerabilities. However, CVE-2026-24842 revealed a critical flaw: the hardlink security check could be bypassed, allowing attackers to create arbitrary files anywhere on the filesystem.

Here's how the attack works:

  1. Crafting a Malicious Archive: An attacker creates a specially crafted tar archive containing hardlinks with path traversal sequences
  2. Bypassing Security Checks: The malicious archive exploits weaknesses in node-tar's hardlink validation logic
  3. Arbitrary File Creation: Upon extraction, the archive creates or overwrites files outside the intended extraction directory
  4. Accessing Sensitive Data: The attacker targets configuration files like .env that contain sensitive credentials

Real-World Attack Scenario

Consider a web application that:
- Allows users to upload and extract tar archives
- Uses dotenv (^16.4.5) to manage configuration
- Stores JWT secrets in a .env file at the application root

Attack Flow:

1. Attacker uploads malicious.tar containing:
   - A hardlink with path: ../../../../app/.env
   - Payload designed to read or exfiltrate the file

2. Application extracts the archive using vulnerable node-tar

3. Hardlink bypass allows file creation outside extraction directory

4. Attacker gains access to .env file contents:
   JWT_SECRET=super_secret_key_12345
   DATABASE_URL=postgresql://user:pass@localhost/db

5. Attacker forges valid JWT tokens for any user

6. Complete authentication bypass achieved

The Amplified Risk: Plaintext JWT Secrets

The vulnerability's impact is significantly amplified by a common misconfiguration: storing JWT secrets in plaintext .env files. While dotenv is a convenient configuration management tool, it creates several attack surfaces:

  • Directory Traversal: As demonstrated by this CVE
  • Exposed .git Folders: Accidentally deployed .git directories may contain .env files in history
  • Misconfigured Web Servers: Improper server configuration might serve .env files directly
  • Server Compromise: Any server-level breach exposes all secrets immediately

Once an attacker obtains the JWT secret, they can:
- Forge tokens for any user (including administrators)
- Bypass all authentication mechanisms
- Maintain persistent access even after password changes
- Impersonate users without detection

The Fix

What Changed?

The fix for CVE-2026-24842 strengthens the hardlink security validation in node-tar by:

  1. Enhanced Path Validation: Implementing stricter checks on hardlink target paths
  2. Canonical Path Resolution: Resolving all paths to their canonical form before validation
  3. Symlink and Hardlink Restrictions: Preventing links from pointing outside the extraction directory

Security Improvements

While the specific code changes weren't provided in the PR diff, a proper fix would implement logic similar to this:

Before (Vulnerable):

// Simplified vulnerable code example
function extractHardlink(entry, extractPath) {
  const targetPath = path.join(extractPath, entry.linkpath);
  const linkPath = path.join(extractPath, entry.path);

  // Insufficient validation
  fs.linkSync(targetPath, linkPath);
}

After (Secure):

// Simplified secure code example
function extractHardlink(entry, extractPath) {
  const targetPath = path.resolve(extractPath, entry.linkpath);
  const linkPath = path.resolve(extractPath, entry.path);
  const canonicalExtractPath = path.resolve(extractPath);

  // Strict validation: ensure both paths are within extraction directory
  if (!targetPath.startsWith(canonicalExtractPath + path.sep)) {
    throw new Error('Path traversal attempt detected in hardlink target');
  }

  if (!linkPath.startsWith(canonicalExtractPath + path.sep)) {
    throw new Error('Path traversal attempt detected in hardlink path');
  }

  // Additional check: verify target exists and is within bounds
  if (!fs.existsSync(targetPath)) {
    throw new Error('Hardlink target does not exist');
  }

  fs.linkSync(targetPath, linkPath);
}

Key Security Enhancements:

  1. Path Resolution: Using path.resolve() converts relative paths to absolute, canonical paths
  2. Boundary Checking: Verifying both target and link paths start with the extraction directory
  3. Existence Validation: Ensuring hardlink targets exist before creating links
  4. Fail-Safe Approach: Throwing errors rather than silently failing

Updating Your Dependencies

To apply this fix, update your package-lock.json:

# Update node-tar to the patched version
npm audit fix

# Or manually update
npm install tar@latest

# Verify the fix
npm audit

Prevention & Best Practices

1. Implement Defense in Depth for Configuration Management

Never rely solely on file system permissions to protect secrets:

// ❌ BAD: Plaintext secrets in .env
JWT_SECRET=my_secret_key

// ✅ GOOD: Use secret management services
// AWS Secrets Manager
const AWS = require('aws-sdk');
const secretsManager = new AWS.SecretsManager();

async function getJWTSecret() {
  const secret = await secretsManager.getSecretValue({
    SecretId: 'prod/jwt/secret'
  }).promise();

  return JSON.parse(secret.SecretString).JWT_SECRET;
}

// Or use encrypted environment variables
const { decrypt } = require('./encryption');
const JWT_SECRET = decrypt(process.env.ENCRYPTED_JWT_SECRET);

2. Validate Archive Contents Before Extraction

Always inspect tar archives before extraction:

const tar = require('tar');
const path = require('path');

async function safeExtract(archivePath, extractPath) {
  const entries = [];

  // First pass: validate all entries
  await tar.list({
    file: archivePath,
    onentry: (entry) => {
      const resolved = path.resolve(extractPath, entry.path);
      const canonical = path.resolve(extractPath);

      // Reject any entry trying to escape
      if (!resolved.startsWith(canonical + path.sep)) {
        throw new Error(`Dangerous path detected: ${entry.path}`);
      }

      // Reject suspicious patterns
      if (entry.path.includes('..')) {
        throw new Error(`Path traversal attempt: ${entry.path}`);
      }

      entries.push(entry);
    }
  });

  // Second pass: safe extraction
  await tar.extract({
    file: archivePath,
    cwd: extractPath,
    strict: true
  });
}

3. Apply the Principle of Least Privilege

Limit extraction permissions:

const fs = require('fs');
const tar = require('tar');

// Create isolated extraction directory with restricted permissions
const extractPath = '/tmp/safe-extraction-' + crypto.randomUUID();
fs.mkdirSync(extractPath, { mode: 0o700 }); // Owner read/write/execute only

try {
  await tar.extract({
    file: uploadedArchive,
    cwd: extractPath,
    strict: true
  });

  // Process files in isolation
  await processExtractedFiles(extractPath);
} finally {
  // Clean up
  fs.rmSync(extractPath, { recursive: true, force: true });
}

4. Implement Content Security Policies

Restrict what archives can contain:

const ALLOWED_EXTENSIONS = ['.txt', '.json', '.md', '.csv'];
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
const MAX_TOTAL_SIZE = 100 * 1024 * 1024; // 100MB

function validateArchiveEntry(entry) {
  const ext = path.extname(entry.path).toLowerCase();

  if (!ALLOWED_EXTENSIONS.includes(ext)) {
    throw new Error(`Forbidden file type: ${ext}`);
  }

  if (entry.size > MAX_FILE_SIZE) {
    throw new Error(`File too large: ${entry.path}`);
  }

  // Additional checks...
}

5. Use Security Scanning Tools

Integrate automated security scanning into your CI/CD pipeline:

# npm audit for dependency vulnerabilities
npm audit --audit-level=moderate

# Snyk for comprehensive scanning
snyk test

# OWASP Dependency-Check
dependency-check --project MyApp --scan .

# GitHub Dependabot (automatic PR creation)
# Enable in repository settings

6. Follow OWASP Guidelines

This vulnerability maps to several OWASP categories:

7. Secure Configuration Management Checklist

  • [ ] Never commit .env files to version control
  • [ ] Add .env to .gitignore immediately
  • [ ] Use secret management services (AWS Secrets Manager, HashiCorp Vault, Azure Key Vault)
  • [ ] Rotate secrets regularly (at least quarterly)
  • [ ] Implement secret scanning in CI/CD (git-secrets, truffleHog)
  • [ ] Use different secrets for each environment
  • [ ] Encrypt secrets at rest and in transit
  • [ ] Audit secret access logs
  • [ ] Implement secret expiration policies
  • [ ] Use short-lived tokens when possible

Conclusion

CVE-2026-24842 serves as a powerful reminder that security vulnerabilities rarely exist in isolation. A path traversal bypass in node-tar's hardlink handling, combined with plaintext configuration management, created a perfect storm for authentication bypass and unauthorized access.

The key takeaways:

  1. Update Immediately: Patch node-tar to the latest version to eliminate the path traversal vulnerability
  2. Defense in Depth: Never rely on a single security control; implement multiple layers of protection
  3. Secure Configuration: Move away from plaintext .env files to proper secret management solutions
  4. Validate Everything: Always validate and sanitize file paths, especially when dealing with user-supplied archives
  5. Stay Informed: Regularly audit dependencies and monitor security advisories

Security is not a one-time fix but an ongoing practice. By understanding vulnerabilities like this one and implementing robust security practices, you can significantly reduce your application's attack surface and protect your users' data.

Remember: the best time to fix a security vulnerability was before it was discovered. The second-best time is now.

Stay secure, and happy coding!


For more information about this vulnerability, consult the National Vulnerability Database entry for CVE-2026-24842 and review the node-tar security advisories.

Frequently Asked Questions

What is hardlink path traversal in node-tar?

Hardlink path traversal is a vulnerability where attackers craft malicious tar archives with hardlink entries that point outside the extraction directory, bypassing security checks to create or overwrite arbitrary files on the system.

How do you prevent hardlink path traversal in Node.js?

Prevent hardlink path traversal by validating all link targets against the extraction root using path normalization, checking for ".." sequences, and ensuring resolved paths remain within the intended directory. Always use updated versions of tar extraction libraries with proper hardlink validation.

What CWE is hardlink path traversal?

Hardlink path traversal is classified as CWE-22 (Improper Limitation of a Pathname to a Restricted Directory), also known as "Path Traversal." It's related to CWE-59 (Improper Link Resolution Before File Access) when specifically involving hardlinks or symlinks.

Is checking for "../" sequences enough to prevent path traversal?

No, checking for "../" alone is insufficient. Attackers can use absolute paths, URL encoding, double encoding, or OS-specific path separators to bypass simple string checks. Always use canonical path resolution and verify the final resolved path stays within bounds.

Can static analysis detect hardlink path traversal vulnerabilities?

Yes, static analysis tools can detect path traversal vulnerabilities by tracking data flow from untrusted sources (tar archive entries) to file system operations, identifying missing path validation, and flagging unsafe tar extraction patterns. Tools like Semgrep, CodeQL, and specialized security scanners can identify these issues.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #60

Related Articles

high

How missing Dependabot cooldown happens in GitHub Actions and how to fix it

A high-severity configuration vulnerability was discovered in a `.github/dependabot.yml` file that lacked a cooldown period for package updates. Without this safeguard, Dependabot could immediately propose updates to newly published package versions—including potentially malicious or unstable releases. The fix adds a simple `cooldown` block with a 7-day waiting period before any new package version is suggested.

high

How Server-Sent Events Injection via Unsanitized Newlines happens in Node.js h3 and how to fix it

A high-severity Server-Sent Events (SSE) injection vulnerability (CVE-2026-33128) was discovered in the h3 HTTP framework, where unsanitized newline characters in event stream fields could allow attackers to inject arbitrary SSE messages. The fix upgrades h3 from version 1.15.5 to 1.15.6 in the frontend's dependency tree, ensuring that newline characters are properly sanitized before being written to event streams.

high

How Memory Exhaustion via Large Comma-Separated Selector Lists happens in Python Soup Sieve and how to fix it

A high-severity memory exhaustion vulnerability (CVE-2026-49476) was discovered in Soup Sieve version 2.8.3, affecting Python applications that parse CSS selectors from user-controlled input. The vulnerability allows attackers to craft malicious selector lists that consume excessive memory, potentially causing denial of service. The fix involves upgrading to soupsieve 2.8.4, which implements proper resource limits on selector parsing.

high

How prototype pollution via `__proto__` key happens in Node.js defu and how to fix it

A high-severity prototype pollution vulnerability (CVE-2026-35209) was discovered in the `defu` package version 6.1.4, which allowed attackers to inject properties into JavaScript's `Object.prototype` via the `__proto__` key in defaults arguments. The fix upgrades `defu` to version 6.1.5 in the frontend's dependency tree, protecting downstream consumers like `c12` and `dotenv` configuration loaders from malicious property injection.

critical

How buffer overflow in memcpy() happens in Node.js N-API bindings and how to fix it

A critical buffer overflow vulnerability was discovered in the GetBufferAsVector() function in examples_nodejs/src/zupt_napi.cpp, where memcpy() copied data from JavaScript Uint8Array buffers without proper bounds validation. This vulnerability could allow attackers to trigger memory corruption by providing maliciously crafted input arrays to the native Node.js module, potentially leading to crashes or arbitrary code execution.

high

How memory exhaustion via large comma-separated selector lists happens in Python soupsieve and how to fix it

A high-severity memory exhaustion vulnerability (CVE-2026-49476) was discovered in soupsieve 2.8.3, a CSS selector library used by BeautifulSoup in Python. An attacker who could influence CSS selector input could craft large comma-separated selector lists to exhaust system memory, causing denial of service. The fix upgrades soupsieve from 2.8.3 to 2.8.4 in the backend's `uv.lock` dependency file.