fix: 보안 LOW 하드닝 (L2~L6)

- DTO 길이 바운드: 업무 content/assigneeIds/checklist ArrayMaxSize·요소 MaxLength, UpdateRepoDto.description MaxLength(300)
- 비밀번호 정책: 8~72자 + 영문·숫자 포함 강제, bcrypt rounds 10→12
- 그랜드페더링을 컷오프(2026-06-20) 이전 생성 계정으로 한정
- multer 업로드 오류를 400(VAL_001)로 매핑
- 운영 환경에서 CORS localhost:3000·Swagger /api-docs 노출 제외

(L1 에이전트 터미널 v-html 은 에이전트 작업 시 함께 처리 — 보류)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-19 21:46:08 +09:00
parent 8efcacb6bb
commit b9e2b0e67d
10 changed files with 98 additions and 20 deletions
@@ -22,6 +22,19 @@ export class HttpExceptionFilter implements ExceptionFilter {
let message =
'일시적인 시스템 오류가 발생했습니다. 잠시 후 다시 시도해 주세요.';
// multer 업로드 오류(크기 초과 등)는 HttpException 이 아니므로 별도 400(VAL_001) 매핑
if (this.isMulterError(exception)) {
const muMessage =
exception.code === 'LIMIT_FILE_SIZE'
? '파일 크기가 허용 한도(25MB)를 초과했습니다.'
: '파일 업로드에 실패했습니다. 파일을 확인해 주세요.';
response.status(HttpStatus.BAD_REQUEST).json({
success: false,
error: { code: 'VAL_001', message: muMessage },
});
return;
}
if (exception instanceof HttpException) {
status = exception.getStatus();
const exceptionResponse = exception.getResponse();
@@ -83,6 +96,15 @@ export class HttpExceptionFilter implements ExceptionFilter {
});
}
// multer 업로드 오류 판별 (name === 'MulterError', code 보유)
private isMulterError(e: unknown): e is { name: string; code?: string } {
return (
typeof e === 'object' &&
e !== null &&
(e as { name?: unknown }).name === 'MulterError'
);
}
// 예외 응답에서 명시적 { code, message } 추출 (둘 다 문자열일 때만)
private extractExplicitError(
res: string | object,