TencentCloud/TencentDB-Agent-Memory · error
Path traversal rejected in scoped storage key: ${key}
Error message
Path traversal rejected in scoped storage key: ${key} What it means
ScopedStorageAdapter.key() rejects any key that, after normalizing separators and slashes, contains a '..' path segment. This prevents path traversal out of the adapter's prefix scope (e.g. reading '../../etc/passwd'), a security hardening measure.
Source
Thrown at MemoryCore/src/core/storage/adapter.ts:32
import type { IStorageBackend, StorageObject, ListEntry, ListObjectsOptions, ListResult, PutObjectOptions } from "./types.js";
class ScopedStorageBackend implements IStorageBackend {
readonly type: "local" | "cos";
private readonly prefix: string;
constructor(private readonly base: IStorageBackend, prefix: string) {
this.type = base.type;
const normalized = prefix.replace(/^\/+/, "").replace(/\/+/g, "/");
this.prefix = normalized && !normalized.endsWith("/") ? `${normalized}/` : normalized;
}
private key(key: string): string {
if (typeof key !== "string" || key.includes("\0") || key.startsWith("/") || key.startsWith("\\")) {
throw new Error(`Invalid scoped storage key: ${JSON.stringify(key)}`);
}
const normalized = key.replace(/^\/+/, "").replace(/\\+/g, "/").replace(/\/+/g, "/");
if (normalized.split("/").some((part) => part === "..")) {
throw new Error(`Path traversal rejected in scoped storage key: ${key}`);
}
return `${this.prefix}${normalized}`;
}
private unkey(key: string): string {
return key.startsWith(this.prefix) ? key.slice(this.prefix.length) : key;
}
async putObject(key: string, content: string | Buffer, opts?: PutObjectOptions): Promise<void> {
return this.base.putObject(this.key(key), content, opts);
}
async appendObject(key: string, content: string | Buffer): Promise<void> {
return this.base.appendObject(this.key(key), content);
}
async getObject(key: string): Promise<StorageObject | null> {
const obj = await this.base.getObject(this.key(key));View on GitHub (pinned to 3efcd317b8)
Solutions
- Sanitize the key before use: resolve it and reject/strip any '..' segments
- Encode or hash user-supplied identifiers into safe key components (e.g. encodeURIComponent or an id)
- Use path.posix.normalize and verify the result stays inside the intended root
- Return a 400-class validation error to the caller instead of attempting the storage operation
Example fix
// before
await adapter.putObject(`files/${userPath}`, buf);
// after
const safe = path.posix.normalize(userPath).replace(/^(\.\.(\/|$))+/, '');
if (safe.split('/').includes('..')) throw new ValidationError('bad path');
await adapter.putObject(`files/${safe}`, buf); Defensive patterns
Strategy: validation
Validate before calling
function sanitizeKey(input) {
const normalized = String(input).replace(/\\+/g, '/').replace(/\/+/g, '/');
if (normalized.split('/').some((p) => p === '..')) throw new Error(`path traversal rejected: ${input}`);
return normalized.replace(/^\/+/, '');
}
await adapter.putObject(sanitizeKey(userPath), buf); Type guard
const hasNoTraversal = (k) => !String(k).split('/').includes('..'); Try / catch
try {
await adapter.obj(key);
} catch (e) {
if (String(e.message).startsWith('Path traversal rejected')) {
throw new ForbiddenError('key escapes storage scope');
}
throw e;
} Prevention
- Treat all user-derived key segments as untrusted; encode or hash identifiers
- Reject '..' segments at the API boundary, not just at the storage layer
- Prefer generated ids over filenames for user uploads
- Add security tests with traversal payloads ('../..', '..\\', encoded variants)
When it happens
Trigger: Calling any scoped storage operation (putObject, obj, exists, deleteObject, etc.) with a key whose normalized segments include '..', such as '../escape' or 'a/../../b'.
Common situations: Building keys from untrusted user input; joining relative paths computed with path.relative() which can yield '..'; archive extraction or upload handlers that don't sanitize filenames.
Related errors
- Storage key must be relative, got absolute: ${key}
- Path traversal rejected: key "${key}" escapes rootDir
- Invalid generation log key
- invalid team_id for template path: ${teamId}
- Generation log object key exceeds COS limit
AI-assisted analysis of TencentCloud/TencentDB-Agent-Memory@3efcd317b8 (2026-09-01).
Data as JSON: /api/errors/3873e2d667512c8f.
Report an issue: GitHub.