HIGH cache poisoningadonisjstypescript

Cache Poisoning in Adonisjs (Typescript)

Cache Poisoning in Adonisjs with Typescript — how this specific combination creates or exposes the vulnerability

Cache poisoning in AdonisJS with TypeScript occurs when an attacker influences cache keys or cached response content, causing malicious or incorrect data to be served to users. Because AdonisJS often caches rendered views, HTTP responses, or computed values in server or CDN caches, TypeScript type discipline does not prevent logic that builds cache keys from untrusted inputs.

Consider a controller that caches user-specific data using a concatenated string without validation:

import Cache from '@ioc:Adonis/Core/Cache'

export default class ProfilesController {
  public async show({ request, view, response }) {
    const userId = request.qs().userId
    const cacheKey = `profile:${userId}`
    const cached = await Cache.get(cacheKey)
    if (cached) {
      return response.send(cached)
    }
    const data = await Profile.query().where('id', userId).preload('posts')
    const html = await view.render('profiles/show', { data })
    await Cache.put(cacheKey, html, 300)
    return response.send(html)
  }
}

If userId is user-supplied (e.g., query parameter) and not validated, an attacker can supply values like ../../admin or other path traversals, effectively polluting the cache under keys that other users might inadvertently hit. In a shared cache layer (e.g., Redis), this can lead to one user’s data being served to another, a classic cache poisoning impact. Even with TypeScript’s static checks, the framework does not enforce that cache keys are canonical or safe; it is the developer’s responsibility to normalize and validate inputs before using them in cache operations.

AdonisJS’s reliance on view compilation and route-based caching amplifies the risk when TypeScript code directly incorporates request-derived values into cache identifiers without normalization. An attacker may also exploit cache poisoning to inject malicious JavaScript if the cached HTML is later rendered in a browser context, leading to stored XSS. Because AdonisJS can generate varied cache entries based on headers or query parameters, unchecked inputs allow an attacker to create many poisoned entries, increasing noise and potentially evading detection.

TypeScript aids in maintaining consistent shapes for cached data structures, but it does not sanitize keys or values. For example, a cached object typed as ProfileData may still contain injected fields if the rendering step does not enforce a strict schema. Developers must treat cache as an untrusted boundary, even when using strongly typed languages, and apply strict input validation and key canonicalization before caching.

Typescript-Specific Remediation in Adonisjs — concrete code fixes

To mitigate cache poisoning in AdonisJS with TypeScript, enforce strict input validation and canonicalize cache keys. Use Joi or validator libraries to ensure userId is a positive integer, and derive cache keys from normalized values rather than raw request data.

import Cache from '@ioc:Adonis/Core/Cache'
import { schema, rules } from '@ioc:Adonis/Core/Validator'

const profileSchema = schema.create({
  userId: schema.number([rules.range(1, Number.MAX_SAFE_INTEGER)])
})

export default class ProfilesController {
  public async show({ request, view, response }) {
    const payload = await request.validate({ schema: profileSchema })
    const userId = payload.userId
    // Canonical key derived only from validated, normalized data
    const cacheKey = `profile:${userId}`
    const cached = await Cache.get(cacheKey)
    if (cached) {
      return response.send(cached)
    }
    const data = await Profile.query().where('id', userId).preload('posts')
    const html = await view.render('profiles/show', { data })
    await Cache.put(cacheKey, html, 300)
    return response.send(html)
  }
}

Additionally, avoid including user-controlled values in cached HTML when possible; instead cache data and render on the server with a strict serialization layer. If caching rendered output, ensure the cache key includes a version or namespace that you control:

const safeKey = `v1:profile:${userId}`

For shared caches, consider namespacing keys by tenant or account ID that you derive from authenticated session data, not from the request. This prevents cross-user pollution even if an attacker manipulates query parameters. In a micro-frontend or CDN caching setup, use request normalization (strip unnecessary headers/query params) before generating the cache key to reduce variability introduced by attacker-controlled inputs.

TypeScript interfaces can enforce shape consistency for cached objects, but they do not replace validation at the boundary. Combine runtime validation with unit tests that verify cache keys are deterministic and safe across a range of inputs, including edge cases like zero, large integers, and special characters.

Frequently Asked Questions

Can TypeScript types alone prevent cache poisoning in AdonisJS?
No. TypeScript provides compile-time type safety but does not validate or sanitize runtime inputs. Cache poisoning depends on how keys and values are derived from requests; you must validate and canonicalize inputs explicitly.
How can I test my AdonisJS endpoints for cache poisoning using middleBrick?
Submit your API URL to middleBrick; it runs unauthenticated checks including input validation and data exposure. Review findings for cache-related risks and follow the remediation guidance to harden key generation and caching logic.