hcengineering/platform · warning

Key contains invalid path sequences

Error message

Key contains invalid path sequences

What it means

Error thrown by getFilePath when the key contains path-traversal-like sequences ('..', './', or '/.'). The cache derives a real file path from the key, so keys that could escape or probe directories are rejected to prevent path traversal.

Solutions

  1. Hash the key (sha256 hex digest) before storing so it only contains safe characters
  2. Sanitize/replace forbidden sequences before calling cache methods
  3. Validate keys at your API boundary: reject keys containing '.' adjacent to '/'
  4. Audit where user input flows into cache keys

Example fix

// before
await cache.put(url, previewFile) // url contains './'
// after
const key = createHash('sha256').update(url).digest('hex')
await cache.put(key, previewFile)
Defensive patterns

Strategy: validation

Validate before calling

function safeKey(raw: string): string {
  return createHash('sha256').update(raw).digest('hex')
}

Type guard

function isSafeCacheKey(key: string): boolean {
  return key.length > 0 && !key.includes('..') && !key.includes('./') && !key.includes('/.')
}

Try / catch

try {
  await cache.put(key, value)
} catch (e) {
  if (e.message === 'Key contains invalid path sequences') {
    return cache.put(createHash('sha256').update(key).digest('hex'), value)
  }
  throw e
}

Prevention

When it happens

Trigger: Passing a raw URL, file path, or user-controlled string as a cache key instead of a sanitized hash — anything containing dots with slashes, e.g. '../../etc/passwd' or './x'.

Common situations: Using the unhashed URL as the key, concatenating user input into keys, or a migration where keys were previously raw paths; also triggered by keys containing '..' inside normal text (e.g. 'foo../bar').

Related errors


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/bbc87401adbe7b55. Report an issue: GitHub.

Appendix: source

Thrown at pods/preview/src/cache.ts:172

    }

    const entry = { ...value, filePath, size }
    this.cache.set(key, entry)

    return entry
  }

  async delete (key: string): Promise<void> {
    this.cache.delete(key)
  }

  private getFilePath (key: string): string {
    if (key.length === 0) {
      throw new Error('Key cannot be empty')
    }

    if (key.includes('..') || key.includes('./') || key.includes('/.')) {
      throw new Error('Key contains invalid path sequences')
    }

    key = key.replace(/[^a-zA-Z0-9-_/]/g, '_')
    const path = join(this.cachePath, key)

    if (!this.isPathWithinCache(path)) {
      throw new Error('Cache path is outside of cache directory')
    }

    return path
  }

  private isPathWithinCache (filePath: string): boolean {
    const normalizedPath = resolve(normalize(filePath))
    const relativePath = relative(this.cachePath, normalizedPath)

    // If the relative path starts with '..', it's outside the cache directory
    return !relativePath.startsWith('..') && !isAbsolute(relativePath)

View on GitHub (pinned to 63e28dc964)