HIGH zip slipadonisjsjwt tokens

Zip Slip in Adonisjs with Jwt Tokens

Zip Slip in Adonisjs with Jwt Tokens — how this specific combination creates or exposes the vulnerability

Zip Slip is a path traversal vulnerability where an attacker-supplied archive or file path traverses outside the intended extraction directory, often via malicious zip files. In AdonisJS applications that handle file uploads and also use JWT Tokens for authentication and authorization, the combination can expose a critical security gap if file operations are tied to identity claims without strict path validation.

Consider an AdonisJS route that accepts an uploaded archive and extracts it to a user-specific directory derived from a JWT Token payload. If the JWT Token identifies the user but the server uses unsanitized input from the archive entries to construct filesystem paths, an attacker can include paths like ../../../etc/passwd or ../../../../var/www/html/.env inside the archive. When the archive is extracted, these paths can overwrite arbitrary files or escape the intended directory, regardless of the JWT Token’s correctness.

This risk is amplified when JWT Tokens carry user identifiers used to derive storage paths without canonicalization. For example, an endpoint that trusts the sub claim to build a destination folder (uploads/${userId}/files) may still process malicious filenames if extraction logic does not validate each entry. The JWT Token ensures the request comes from an authenticated user, but it does not guarantee that the user’s actions are confined to their own directory if path traversal is possible.

In AdonisJS, this can occur in controller logic that uses third-party unzip libraries without sanitizing entry names. Even with JWT-based authentication middleware in place, if the server does not normalize and restrict extraction paths, the attack surface remains open. Real-world patterns include using adm-zip or similar packages where entries are iterated and extracted naively.

Because middleBrick scans unauthenticated attack surfaces, it can detect such path traversal risks even when JWT Tokens are required for access, by testing endpoint behavior with traversal patterns. Findings will highlight missing path canonicalization and provide remediation guidance mapped to frameworks like OWASP API Top 10 and relevant compliance controls.

Jwt Tokens-Specific Remediation in Adonisjs — concrete code fixes

Remediation focuses on strict path validation, canonicalization, and avoiding any direct use of user-controlled filenames from archives. Below are concrete code examples for secure handling in AdonisJS when JWT Tokens are used for authentication.

1. Validate and sanitize archive entries before extraction

import { join, resolve, normalize } from 'path'
import { existsSync, mkdirSync } from 'fs'
import { ApiResponse } from '@ioc:Adonis/Core/Helpers'

export default class ArchivesController {
  public async extractUserArchive({ request, auth }) {
    const user = auth.user! // JWT Token validated user
    const userId = user.id
    const baseDir = resolve(`uploads/${userId}`)

    // Ensure base directory exists
    if (!existsSync(baseDir)) {
      mkdirSync(baseDir, { recursive: true })
    }

    const archiveBuffer = request.file('archive')?.buffer
    if (!archiveBuffer) {
      return ApiResponse.badRequest({ message: 'Archive file is required' })
    }

    const zip = new (require('adm-zip'))(archiveBuffer)
    const zipEntries = zip.getEntries()

    for (const entry of zipEntries) {
      // Normalize and resolve the target path
      const targetPath = resolve(baseDir, entry.entryName)

      // Ensure the normalized path starts with the base directory
      if (!targetPath.startsWith(baseDir)) {
        throw new Error('Invalid archive entry: path traversal detected')
      }

      // Reject entries containing null bytes or suspicious patterns
      if (entry.entryName.includes('\0') || entry.entryName.includes('..')) {
        throw new Error('Invalid archive entry: unsafe filename')
      }

      if (entry.isDirectory) {
        mkdirSync(targetPath, { recursive: true })
      } else {
        // Ensure parent directories exist
        const parentDir = resolve(targetPath, '..')
        if (!existsSync(parentDir)) {
          mkdirSync(parentDir, { recursive: true })
        }
        entry.extract({ path: parentDir, maintainEntryDirectory: false })
      }
    }

    return ApiResponse.ok({ message: 'Archive extracted successfully' })
  }
}

2. Use a dedicated extraction utility with strict options

Instead of manual loops, prefer libraries that support safe extraction modes. For example, using unzipper with strict path checks:

import { response } from 'celebrate'
import { createReadStream, createWriteStream, mkdirSync } from 'fs'
import { resolve } from 'path'
import { pipeline } from 'stream'
import { promisify } from 'util'
import { ApiResponse } from '@ioc:Adonis/Core/Helpers'

const pump = promisify(pipeline)

export default class SafeArchivesController {
  public async extractSafe({ request, auth }) {
    const user = auth.user!
    const userId = user.id
    const baseDir = resolve(`uploads/${userId}`)
    mkdirSync(baseDir, { recursive: true })

    const archiveStream = request.file('archive')?.createReadStream()
    if (!archiveStream) {
      return ApiResponse.badRequest({ message: 'Archive file is required' })
    }

    // Use unzipper with strict options
    const unzipper = require('unzipper')
    const parser = unzipper.Parse()
    archiveStream.pipe(parser)

    for await (const entry of parser) {
      const fileName = entry.path
      const type = entry.type

      // Validate entry path
      if (fileName.includes('..') || fileName.includes('\0')) {
        entry.autodrain()
        throw new Error('Invalid archive entry: path traversal detected')
      }

      const destination = resolve(baseDir, fileName)
      if (!destination.startsWith(baseDir)) {
        entry.autodrain()
        throw new Error('Invalid archive entry: extraction outside base directory')
      }

      if (type === 'Directory') {
        mkdirSync(destination, { recursive: true })
        entry.autodrain()
      } else {
        await pump(entry, createWriteStream(destination))
      }
    }

    return ApiResponse.ok({ message: 'Archive extracted safely' })
  }
}

3. JWT Token usage remains unchanged but must not influence path resolution

Ensure JWT authentication middleware validates tokens, but do not derive filesystem paths directly from claims that could be manipulated server-side. Use the user ID for namespacing only after strict validation.

These fixes ensure that even when JWT Tokens authenticate requests, file extraction cannot escape intended boundaries. middleBrick can verify such protections by scanning endpoints that handle archives and JWT-based auth, highlighting missing canonicalization and unsafe extraction patterns.

Frequently Asked Questions

Can JWT Tokens prevent Zip Slip if the server uses them for user identification?
No. JWT Tokens provide authentication and identity, but they do not prevent path traversal if file extraction logic does not sanitize and validate archive entry paths. Attackers can still include traversing paths inside archives.
Does middleBrick test for Zip Slip in authenticated contexts involving JWT Tokens?
middleBrick scans the unauthenticated attack surface and can detect path traversal patterns, including Zip Slip, even when endpoints require JWT Tokens. Findings include specific remediation guidance for AdonisJS and other frameworks.