refactor: deny empty names

This commit is contained in:
Nicolas Meienberger 2026-01-29 20:50:06 +01:00
parent 779542e8bd
commit 138a63780a
4 changed files with 19 additions and 15 deletions

View file

@ -87,7 +87,7 @@ export const createApp = () => {
const { status, message } = handleServiceError(err); const { status, message } = handleServiceError(err);
return c.json({ message }, status); return c.json({ message }, status as 500);
}); });
return app; return app;

View file

@ -1,5 +1,5 @@
import { eq, and, inArray } from "drizzle-orm"; import { eq, and, inArray } from "drizzle-orm";
import { ConflictError, InternalServerError, NotFoundError } from "http-errors-enhanced"; import { BadRequestError, ConflictError, InternalServerError, NotFoundError } from "http-errors-enhanced";
import { db } from "../../db/db"; import { db } from "../../db/db";
import { import {
notificationDestinationsTable, notificationDestinationsTable,
@ -144,7 +144,7 @@ const createDestination = async (name: string, config: NotificationConfig) => {
const trimmedName = name.trim(); const trimmedName = name.trim();
if (trimmedName.length === 0) { if (trimmedName.length === 0) {
throw new InternalServerError("Name cannot be empty or whitespace-only"); throw new BadRequestError("Name cannot be empty");
} }
const encryptedConfig = await encryptSensitiveFields(config); const encryptedConfig = await encryptSensitiveFields(config);
@ -184,7 +184,7 @@ const updateDestination = async (
if (updates.name !== undefined) { if (updates.name !== undefined) {
const trimmedName = updates.name.trim(); const trimmedName = updates.name.trim();
if (trimmedName.length === 0) { if (trimmedName.length === 0) {
throw new InternalServerError("Name cannot be empty or whitespace-only"); throw new BadRequestError("Name cannot be empty");
} }
updateData.name = trimmedName; updateData.name = trimmedName;
} }
@ -195,7 +195,7 @@ const updateDestination = async (
const newConfig = notificationConfigSchema(updates.config || existing.config); const newConfig = notificationConfigSchema(updates.config || existing.config);
if (newConfig instanceof type.errors) { if (newConfig instanceof type.errors) {
throw new InternalServerError("Invalid notification configuration"); throw new BadRequestError("Invalid notification configuration");
} }
const encryptedConfig = await encryptSensitiveFields(newConfig); const encryptedConfig = await encryptSensitiveFields(newConfig);

View file

@ -2,7 +2,7 @@ import * as fs from "node:fs/promises";
import * as os from "node:os"; import * as os from "node:os";
import * as path from "node:path"; import * as path from "node:path";
import { and, eq } from "drizzle-orm"; import { and, eq } from "drizzle-orm";
import { InternalServerError, NotFoundError } from "http-errors-enhanced"; import { BadRequestError, InternalServerError, NotFoundError } from "http-errors-enhanced";
import { db } from "../../db/db"; import { db } from "../../db/db";
import { volumesTable } from "../../db/schema"; import { volumesTable } from "../../db/schema";
import { cryptoUtils } from "../../utils/crypto"; import { cryptoUtils } from "../../utils/crypto";
@ -66,6 +66,10 @@ const createVolume = async (name: string, backendConfig: BackendConfig) => {
const organizationId = getOrganizationId(); const organizationId = getOrganizationId();
const trimmedName = name.trim(); const trimmedName = name.trim();
if (trimmedName.length === 0) {
throw new BadRequestError("Volume name cannot be empty");
}
const shortId = generateShortId(); const shortId = generateShortId();
const encryptedConfig = await encryptSensitiveFields(backendConfig); const encryptedConfig = await encryptSensitiveFields(backendConfig);
@ -184,6 +188,10 @@ const updateVolume = async (idOrShortId: string | number, volumeData: UpdateVolu
const newName = volumeData.name !== undefined ? volumeData.name.trim() : existing.name; const newName = volumeData.name !== undefined ? volumeData.name.trim() : existing.name;
if (newName.length === 0) {
throw new BadRequestError("Volume name cannot be empty");
}
const configChanged = const configChanged =
JSON.stringify(existing.config) !== JSON.stringify(volumeData.config) && volumeData.config !== undefined; JSON.stringify(existing.config) !== JSON.stringify(volumeData.config) && volumeData.config !== undefined;
@ -195,7 +203,7 @@ const updateVolume = async (idOrShortId: string | number, volumeData: UpdateVolu
const newConfig = volumeConfigSchema(volumeData.config || existing.config); const newConfig = volumeConfigSchema(volumeData.config || existing.config);
if (newConfig instanceof type.errors) { if (newConfig instanceof type.errors) {
throw new InternalServerError("Invalid volume configuration"); throw new BadRequestError("Invalid volume configuration");
} }
const encryptedConfig = await encryptSensitiveFields(newConfig); const encryptedConfig = await encryptSensitiveFields(newConfig);
@ -306,7 +314,7 @@ const listFiles = async (idOrShortId: string | number, subPath?: string) => {
const relative = path.relative(volumePath, normalizedPath); const relative = path.relative(volumePath, normalizedPath);
if (relative.startsWith("..") || path.isAbsolute(relative)) { if (relative.startsWith("..") || path.isAbsolute(relative)) {
throw new InternalServerError("Invalid path"); throw new BadRequestError("Invalid path");
} }
try { try {

View file

@ -1,13 +1,9 @@
import { ConflictError, NotFoundError } from "http-errors-enhanced"; import { HttpError } from "http-errors-enhanced";
import { sanitizeSensitiveData } from "./sanitize"; import { sanitizeSensitiveData } from "./sanitize";
export const handleServiceError = (error: unknown) => { export const handleServiceError = (error: unknown) => {
if (error instanceof ConflictError) { if (error instanceof HttpError) {
return { message: sanitizeSensitiveData(error.message), status: 409 as const }; return { message: sanitizeSensitiveData(error.message), status: error.statusCode };
}
if (error instanceof NotFoundError) {
return { message: sanitizeSensitiveData(error.message), status: 404 as const };
} }
return { message: sanitizeSensitiveData(toMessage(error)), status: 500 as const }; return { message: sanitizeSensitiveData(toMessage(error)), status: 500 as const };