fix: 보안 취약점 수정 및 XSS 방지 적용
## 백엔드 보안 수정 - 하드코딩된 비밀번호 및 JWT 시크릿 폴백 제거 - SQL Injection 방지를 위한 화이트리스트 검증 추가 - 인증 미적용 API 라우트에 requireAuth 미들웨어 적용 - CSRF 보호 미들웨어 구현 (csrf.js) - 파일 업로드 보안 유틸리티 추가 (fileUploadSecurity.js) - 비밀번호 정책 검증 유틸리티 추가 (passwordValidator.js) ## 프론트엔드 XSS 방지 - api-base.js에 전역 escapeHtml() 함수 추가 - 17개 주요 JS 파일에 escapeHtml 적용: - tbm.js, daily-patrol.js, daily-work-report.js - task-management.js, workplace-status.js - equipment-detail.js, equipment-management.js - issue-detail.js, issue-report.js - vacation-common.js, worker-management.js - safety-report-list.js, nonconformity-list.js - project-management.js, workplace-management.js ## 정리 - 백업 폴더 및 빈 파일 삭제 - SECURITY_GUIDE.md 문서 추가 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -165,26 +165,35 @@ function renderProjects() {
|
||||
'completed': { icon: '✅', text: '완료', color: '#3b82f6' },
|
||||
'cancelled': { icon: '❌', text: '취소', color: '#ef4444' }
|
||||
};
|
||||
|
||||
const status = statusMap[project.project_status] || statusMap['active'];
|
||||
|
||||
const validStatuses = ['planning', 'active', 'completed', 'cancelled'];
|
||||
const safeProjectStatus = validStatuses.includes(project.project_status) ? project.project_status : 'active';
|
||||
const status = statusMap[safeProjectStatus];
|
||||
// is_active 값 처리 (DB에서 0/1로 오는 경우 대비)
|
||||
const isInactive = project.is_active === 0 || project.is_active === false || project.is_active === 'false';
|
||||
|
||||
|
||||
// XSS 방지를 위한 안전한 값
|
||||
const safeProjectId = parseInt(project.project_id) || 0;
|
||||
const safeJobNo = escapeHtml(project.job_no || 'Job No. 없음');
|
||||
const safeProjectName = escapeHtml(project.project_name || '-');
|
||||
const safePm = escapeHtml(project.pm || '-');
|
||||
const safeSite = escapeHtml(project.site || '-');
|
||||
|
||||
console.log('🎨 카드 렌더링:', {
|
||||
project_id: project.project_id,
|
||||
project_name: project.project_name,
|
||||
is_active_raw: project.is_active,
|
||||
isInactive: isInactive
|
||||
});
|
||||
|
||||
|
||||
return `
|
||||
<div class="project-card ${isInactive ? 'inactive' : ''}" onclick="editProject(${project.project_id})">
|
||||
<div class="project-card ${isInactive ? 'inactive' : ''}" onclick="editProject(${safeProjectId})">
|
||||
${isInactive ? '<div class="inactive-overlay"><span class="inactive-badge">🚫 비활성화됨</span></div>' : ''}
|
||||
<div class="project-header">
|
||||
<div class="project-info">
|
||||
<div class="project-job-no">${project.job_no || 'Job No. 없음'}</div>
|
||||
<div class="project-job-no">${safeJobNo}</div>
|
||||
<h3 class="project-name">
|
||||
${project.project_name}
|
||||
${safeProjectName}
|
||||
${isInactive ? '<span class="inactive-label">(비활성)</span>' : ''}
|
||||
</h3>
|
||||
<div class="project-meta">
|
||||
@@ -202,20 +211,20 @@ function renderProjects() {
|
||||
</div>
|
||||
<div class="meta-row">
|
||||
<span class="meta-label">PM</span>
|
||||
<span class="meta-value">${project.pm || '-'}</span>
|
||||
<span class="meta-value">${safePm}</span>
|
||||
</div>
|
||||
<div class="meta-row">
|
||||
<span class="meta-label">현장</span>
|
||||
<span class="meta-value">${project.site || '-'}</span>
|
||||
<span class="meta-value">${safeSite}</span>
|
||||
</div>
|
||||
${isInactive ? '<div class="inactive-notice">⚠️ 작업보고서에서 숨김</div>' : ''}
|
||||
</div>
|
||||
</div>
|
||||
<div class="project-actions">
|
||||
<button class="btn-edit" onclick="event.stopPropagation(); editProject(${project.project_id})" title="수정">
|
||||
<button class="btn-edit" onclick="event.stopPropagation(); editProject(${safeProjectId})" title="수정">
|
||||
✏️ 수정
|
||||
</button>
|
||||
<button class="btn-delete" onclick="event.stopPropagation(); confirmDeleteProject(${project.project_id})" title="삭제">
|
||||
<button class="btn-delete" onclick="event.stopPropagation(); confirmDeleteProject(${safeProjectId})" title="삭제">
|
||||
🗑️ 삭제
|
||||
</button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user