summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjoonhoekim <26rote@gmail.com>2025-10-21 18:37:46 +0900
committerjoonhoekim <26rote@gmail.com>2025-10-21 18:37:46 +0900
commit9d0f0304b744f17c2c346ba5a52ee6745a9f51af (patch)
treeeba24f5df071d77af5c88a8879f0f0ad9ff5c26d
parentcf77558c0ccc5c0c1bc3cdd6edd9a0475e1970c8 (diff)
(김준회) fix: EDP 보고서 관리: 템플릿 파일 못찾는 문제 처리
-rw-r--r--lib/pdftron/serverSDK/createReport.ts30
-rw-r--r--pages/api/pdftron/createVendorDataReports.ts15
2 files changed, 42 insertions, 3 deletions
diff --git a/lib/pdftron/serverSDK/createReport.ts b/lib/pdftron/serverSDK/createReport.ts
index 412ada87..afcec7a2 100644
--- a/lib/pdftron/serverSDK/createReport.ts
+++ b/lib/pdftron/serverSDK/createReport.ts
@@ -1,4 +1,5 @@
const { PDFNet } = require("@pdftron/pdfnet-node");
+const path = require("path");
type CreateReport = (
coverPage: Buffer,
@@ -12,6 +13,26 @@ type CreateReport = (
error?: any;
}>;
+// 웹 경로를 실제 파일 시스템 경로로 변환
+function convertWebPathToFilePath(webPath: string): string {
+ const isProduction = process.env.NODE_ENV === "production";
+ const nasPath = process.env.NAS_PATH || "/evcp_nas";
+
+ // /api/files/vendorFormData/xxx.docx → vendorFormData/xxx.docx
+ // /vendorFormData/xxx.docx → vendorFormData/xxx.docx
+ let relativePath = webPath
+ .replace(/^\/api\/files\//, '') // 운영 환경 경로 제거
+ .replace(/^\//, ''); // 개발 환경 슬래시 제거
+
+ if (isProduction) {
+ // 운영: /evcp_nas/vendorFormData/xxx.docx
+ return path.join(nasPath, relativePath);
+ } else {
+ // 개발: public/vendorFormData/xxx.docx
+ return path.join(process.cwd(), "public", relativePath);
+ }
+}
+
export const createReport: CreateReport = async (
coverPage,
reportTempPath,
@@ -37,8 +58,15 @@ export const createReport: CreateReport = async (
for (const reportData of reportDatas) {
const resportDataJson = JSON.stringify(reportData);
+ // 웹 경로를 실제 파일 시스템 경로로 변환
+ const actualFilePath = convertWebPathToFilePath(reportTempPath);
+
+ console.log(`📄 템플릿 파일 경로 변환:`);
+ console.log(` - 웹 경로: ${reportTempPath}`);
+ console.log(` - 실제 경로: ${actualFilePath}`);
+
const templateDoc = await PDFNet.Convert.createOfficeTemplateWithPath(
- "public" + reportTempPath,
+ actualFilePath,
options
);
diff --git a/pages/api/pdftron/createVendorDataReports.ts b/pages/api/pdftron/createVendorDataReports.ts
index e145b5d5..f873bd50 100644
--- a/pages/api/pdftron/createVendorDataReports.ts
+++ b/pages/api/pdftron/createVendorDataReports.ts
@@ -90,12 +90,23 @@ function validateFileSize(size: number): { valid: boolean; error?: string } {
function validateDirectory(directory: string): { valid: boolean; error?: string } {
const normalizedDir = path.normalize(directory).replace(/^\/+/, '');
+ // 경로 탐색 공격 방지
if (normalizedDir.includes('..') || normalizedDir.includes('//')) {
return { valid: false, error: "안전하지 않은 디렉터리 경로입니다" };
}
- if (path.isAbsolute(directory)) {
- return { valid: false, error: "절대 경로는 사용할 수 없습니다" };
+ // 절대 경로 검증 (단, 허용된 웹 경로 패턴은 제외)
+ const allowedPatterns = [
+ /^\/api\/files\//, // 운영 환경: /api/files/vendorFormData/...
+ /^\/vendorFormData\//, // 개발 환경: /vendorFormData/...
+ /^\/[\w-]+\//, // 기타 상대 웹 경로: /directory/...
+ ];
+
+ const isAllowedWebPath = allowedPatterns.some(pattern => pattern.test(directory));
+
+ // Windows 절대 경로(C:\...) 또는 허용되지 않은 Unix 절대 경로 차단
+ if (path.isAbsolute(directory) && !isAllowedWebPath) {
+ return { valid: false, error: "절대 파일 시스템 경로는 사용할 수 없습니다" };
}
return { valid: true };