feat: rebrand Hemmelig to paste.es for cloudhost.es
- Set Spanish as default language with ephemeral/encrypted privacy focus - Translate all user-facing strings and legal pages to Spanish - Replace Norwegian flag with Spanish flag in footer - Remove Hemmelig/terces.cloud links, add cloudhost.es sponsorship - Rewrite PrivacyPage: zero data collection, ephemeral design emphasis - Rewrite TermsPage: Spanish law, RGPD, paste.es/CloudHost.es references - Update PWA manifest, HTML meta tags, package.json branding - Rename webhook headers to X-Paste-Event / X-Paste-Signature - Update API docs title and contact to paste.es / cloudhost.es Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
77
api/lib/files.ts
Normal file
77
api/lib/files.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { mkdir } from 'fs/promises';
|
||||
import { basename, join, resolve } from 'path';
|
||||
import { FILE } from './constants';
|
||||
import settingsCache from './settings';
|
||||
|
||||
/** Upload directory path */
|
||||
export const UPLOAD_DIR = resolve(process.cwd(), 'uploads');
|
||||
|
||||
/**
|
||||
* Sanitizes a filename by removing path traversal sequences and directory separators.
|
||||
* Returns only the base filename to prevent directory escape attacks.
|
||||
*/
|
||||
export function sanitizeFilename(filename: string): string {
|
||||
// Get only the base filename, stripping any directory components
|
||||
const base = basename(filename);
|
||||
// Remove any remaining null bytes or other dangerous characters
|
||||
return base.replace(/[\x00-\x1f]/g, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that a file path is safely within the upload directory.
|
||||
* Prevents path traversal attacks by checking the resolved absolute path.
|
||||
*/
|
||||
export function isPathSafe(filePath: string): boolean {
|
||||
const resolvedPath = resolve(filePath);
|
||||
return resolvedPath.startsWith(UPLOAD_DIR + '/') || resolvedPath === UPLOAD_DIR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets max file size from instance settings (in KB), converted to bytes.
|
||||
* Defaults to 10MB if not configured.
|
||||
*/
|
||||
export function getMaxFileSize(): number {
|
||||
const settings = settingsCache.get('instanceSettings');
|
||||
const maxSecretSizeKB = settings?.maxSecretSize ?? FILE.DEFAULT_MAX_SIZE_KB;
|
||||
return maxSecretSizeKB * 1024; // Convert KB to bytes
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the upload directory exists, creating it if necessary.
|
||||
*/
|
||||
export async function ensureUploadDir(): Promise<void> {
|
||||
try {
|
||||
await mkdir(UPLOAD_DIR, { recursive: true });
|
||||
} catch (error) {
|
||||
console.error('Failed to create upload directory:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a safe file path within the upload directory.
|
||||
* @param id - Unique identifier for the file
|
||||
* @param originalFilename - Original filename to sanitize
|
||||
* @returns Object with sanitized filename and full path, or null if invalid
|
||||
*/
|
||||
export function generateSafeFilePath(
|
||||
id: string,
|
||||
originalFilename: string
|
||||
): { filename: string; path: string } | null {
|
||||
const safeFilename = sanitizeFilename(originalFilename);
|
||||
if (!safeFilename) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const filename = `${id}-${safeFilename}`;
|
||||
const path = join(UPLOAD_DIR, filename);
|
||||
|
||||
// Verify path is safe
|
||||
if (!isPathSafe(path)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return { filename, path };
|
||||
}
|
||||
|
||||
// Initialize upload directory on module load
|
||||
ensureUploadDir();
|
||||
Reference in New Issue
Block a user