346 lines
18 KiB
Groovy
346 lines
18 KiB
Groovy
plugins {
|
|
id 'java'
|
|
id 'war'
|
|
id 'eclipse'
|
|
id 'idea'
|
|
id 'org.cyclonedx.bom' version '3.2.4'
|
|
id 'org.springframework.boot' version '2.7.18'
|
|
id 'io.spring.dependency-management' version '1.1.3'
|
|
}
|
|
|
|
group = 'com.eactive'
|
|
version = '1.0.0'
|
|
sourceCompatibility = "1.8"
|
|
targetCompatibility = "1.8"
|
|
|
|
def nexusUrl = "https://nexus.eactive.synology.me:8090"
|
|
|
|
allprojects {
|
|
repositories {
|
|
maven {
|
|
url "${nexusUrl}/repository/maven-public/"
|
|
allowInsecureProtocol = true
|
|
}
|
|
}
|
|
}
|
|
|
|
// 로컬 전용 라이브러리 — WebLogic 배포 산출물(war/bootWar)에서 제외한다.
|
|
// 정보보호 점검에서 Actuator/모니터링 라이브러리가 배포본에 실려 있으면 불필요하게 탐지되므로
|
|
// 로컬(bootRun/IDE)에서만 classpath 에 오르게 한다. 제외 로직은 아래 war/bootWar 블록.
|
|
//
|
|
// ※ Spring Boot 의 developmentOnly 를 쓰지 않는 이유
|
|
// developmentOnly 는 bootJar/bootWar 에서만 제외된다. 표준 war task 는 runtimeClasspath 를
|
|
// 그대로 쓰므로 산출물에 실린다 (실측: devtools 가 eapim-portal.war 에 포함되어 있었음).
|
|
// ※ 별도 configuration(localOnly)을 쓰지 않는 이유
|
|
// runtimeClasspath 에서 빠지면 IntelliJ 가 모듈 classpath 를 구성할 때도 빠져
|
|
// IDE 로 기동할 때 Actuator/SBA 가 동작하지 않는다.
|
|
def localOnlyLibPrefixes = [
|
|
'spring-boot-devtools',
|
|
'spring-boot-starter-actuator',
|
|
'spring-boot-actuator', // spring-boot-actuator, -autoconfigure 모두 매칭
|
|
'micrometer-', // actuator 전용(runtimeClasspath 상 다른 출처 없음 — 확인함)
|
|
'spring-boot-admin-',
|
|
]
|
|
|
|
dependencies {
|
|
annotationProcessor "org.projectlombok:lombok:1.18.28"
|
|
annotationProcessor "org.projectlombok:lombok-mapstruct-binding:0.2.0", "org.mapstruct:mapstruct-processor:1.5.5.Final"
|
|
|
|
implementation 'com.eactive.elink.common:elink-common-data:4.5.5'
|
|
|
|
// OpenAPI 3.0/3.1 spec 지원 (Nexus/Maven 해석). 기존 libs/swagger fileTree 대체.
|
|
// swagger 최신 릴리스(2.2.52/2.1.45)도 SpecVersion 은 V30/V31 까지만 — OpenAPI 3.2 는
|
|
// 아직 swagger-core/parser 어느 버전도 미지원. 지원 추가 시 버전만 상향하면 됨. JDK8 호환.
|
|
implementation 'io.swagger.core.v3:swagger-core:2.2.52' // io.swagger.v3.oas.models.*, io.swagger.v3.core.util.Json
|
|
implementation 'io.swagger.parser.v3:swagger-parser:2.1.45' // io.swagger.parser.OpenAPIParser, io.swagger.v3.parser.*
|
|
implementation 'io.swagger:swagger-annotations:1.6.14' // io.swagger.annotations.ApiModelProperty (CommissionSearch)
|
|
|
|
implementation project(':elink-online-core-jpa')
|
|
implementation project(':elink-portal-common')
|
|
// implementation project(':kjb-safedb')
|
|
|
|
implementation('org.springframework.boot:spring-boot-starter')
|
|
implementation('org.springframework.boot:spring-boot-starter-web')
|
|
implementation('org.springframework.boot:spring-boot-starter-validation')
|
|
|
|
// ↓ 로컬 전용. war/bootWar 산출물에서는 localOnlyLibPrefixes 로 제외된다.
|
|
// runtimeOnly 인 이유: compileClasspath 에서 빠지므로 자바 코드가 이 API 를 참조하면
|
|
// 컴파일 단계에서 막힌다. implementation 이면 참조가 컴파일에 통과해버리고,
|
|
// 배포본(actuator/SBA 제외됨)에서 NoClassDefFoundError 로 터진다.
|
|
// IntelliJ / bootRun 은 runtimeClasspath 기준이라 로컬 기동에는 정상 포함된다.
|
|
// 설정(application-local*.yml)으로만 사용한다.
|
|
runtimeOnly('org.springframework.boot:spring-boot-starter-actuator')
|
|
// Spring Boot Admin client. 2.7.16 = Spring Boot 2.7.x 대응 마지막 계열(Java 8 호환).
|
|
runtimeOnly('de.codecentric:spring-boot-admin-starter-client:2.7.16')
|
|
// 위 runtimeOnly 원칙의 유일한 예외. InterceptorsEndpointConfig 가 actuator API
|
|
// (@Endpoint / InfoContributor)를 참조해야 하므로 compileClasspath 에만 올린다.
|
|
// compileOnly 는 runtimeClasspath 에 포함되지 않으므로 war/bootWar 산출물에는 영향이 없다.
|
|
// 해당 클래스는 @ConditionalOnClass 로 actuator 부재 시 로드되지 않으니
|
|
// 배포본(actuator 제외)에서 NoClassDefFoundError 가 나지 않는다.
|
|
compileOnly('org.springframework.boot:spring-boot-actuator')
|
|
|
|
implementation group: 'javax.xml.bind', name: 'jaxb-api', version: '2.3.0'
|
|
implementation group: 'com.fasterxml.woodstox', name: 'woodstox-core', version: '6.5.1'
|
|
|
|
|
|
// Thymeleaf 3.1 코어가 #temporals 를 내장 제공 → java8time extras 를 함께 두면 표현식 객체가 중복 등록된다.
|
|
implementation('org.springframework.boot:spring-boot-starter-thymeleaf') {
|
|
exclude group: 'org.thymeleaf.extras', module: 'thymeleaf-extras-java8time'
|
|
}
|
|
// WW-5417 관련 public 필드 접근 권한 검사 누락 수정(OGNL #264/#265).
|
|
// 3.3.x EOL 계열의 단기 조치. 3.4.x는 Thymeleaf 3.1.5의 OgnlContext 생성자와 비호환.
|
|
// 일반 OGNL 경로도 ThymeleafExpressionCompatibilityTest로 검증한다(Spring EL만으로는 확인 불가).
|
|
implementation 'ognl:ognl:3.3.5'
|
|
implementation 'org.springframework.boot:spring-boot-starter-security'
|
|
implementation('org.springframework.boot:spring-boot-starter-cache')
|
|
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
|
implementation 'org.springframework.data:spring-data-envers'
|
|
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
|
|
|
|
developmentOnly 'org.springframework.boot:spring-boot-devtools'
|
|
// spring-expression 개별 pin 제거: 아래 ext 의 spring-framework.version 이 전 모듈을 일괄 관리한다.
|
|
// 개별 pin 이 남아 있으면 다른 spring-* 모듈보다 낮은 버전으로 고정되어 버전이 어긋난다.
|
|
|
|
implementation group: 'xalan', name: 'xalan', version: '2.7.3'
|
|
|
|
implementation 'org.hibernate:hibernate-envers:5.6.15.Final'
|
|
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:3.4.0' // 3.0.0 은 thymeleaf 3.0 전용
|
|
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
|
|
|
|
// uap-java 제거: 소스 전체에 ua_parser 참조 0건이고, 1.5.3 은 snakeyaml 2.x 에서 삭제된
|
|
// SafeConstructor() no-arg 를 호출해 NoSuchMethodError 를 낸다. 되살릴 경우 1.6.1 이상.
|
|
implementation 'org.apache.httpcomponents:httpclient:4.5.14'
|
|
implementation 'com.navercorp.lucy:lucy-xss-servlet:2.0.1'
|
|
implementation 'javax.servlet:javax.servlet-api:4.0.0'
|
|
implementation 'org.jasypt:jasypt:1.9.3'
|
|
implementation 'xerces:xercesImpl:2.12.2'
|
|
|
|
// weblogic.xml prefer-application-packages 가 com.fasterxml.jackson.* 를 앱 우선으로 강제한다
|
|
// (oracle_common jackson-databind 2.17.0 vs 앱 2.18.10 split-package 방지, WLog 실사용 확인).
|
|
// Thymeleaf StandardJavaScriptSerializer 가 ObjectMapper.findAndRegisterModules() 로
|
|
// jackson-module-jaxb-annotations 를 옵션 모듈로 자동탐색하는데, 이 모듈은 WebLogic 번들에만
|
|
// 있고 앱엔 없어 ServiceConfigurationError(Provider not found) 발생 → 앱에도 명시 추가해
|
|
// jackson 전체를 자체 완결시킨다(jackson-bom 2.18.10 로 버전 통일).
|
|
implementation 'com.fasterxml.jackson.module:jackson-module-jaxb-annotations'
|
|
|
|
// Uncontrolled recursion in ClassUtils.getClass(...) on very long inputs. 3.18.0+ 에서 수정.
|
|
implementation 'org.apache.commons:commons-lang3:3.20.0'
|
|
implementation 'org.apache.commons:commons-collections4:4.4'
|
|
|
|
implementation 'commons-net:commons-net:3.9.0'
|
|
// CVE-2025-48734 (PropertyUtilsBean 이 enum 의 declaredClass 프로퍼티 노출 → ClassLoader 접근/RCE).
|
|
// 1.11.0 부터 SuppressPropertiesBeanIntrospector 가 기본 활성이라 declaredClass 접근이 차단된다.
|
|
// 이 앱의 호출부(PasswordMatchValidator / PasswordRuleValidator / AuthNumberValidator)는
|
|
// 어노테이션에 박힌 고정 프로퍼티명만 넘기므로 외부 입력 경로는 없지만 버전은 올려 둔다.
|
|
// 1.11.0 = Java 8 바이트코드(major 52), PropertyUtils.getProperty/getNestedProperty API 동일.
|
|
implementation('commons-beanutils:commons-beanutils:1.11.0') {
|
|
// exclude group: 'commons-collections', module: 'commons-collections'
|
|
}
|
|
implementation 'org.mapstruct:mapstruct:1.5.5.Final'
|
|
// jackson 개별 pin 제거: 아래 ext 의 jackson-bom.version 이 전 모듈을 일괄 관리한다.
|
|
// 개별 pin 은 BOM 보다 우선하므로 남겨 두면 BOM 만 올렸을 때 core/annotations/databind 가
|
|
// 옛 버전에 고정돼 버전이 어긋난다(실제로 그런 상태였다).
|
|
|
|
implementation group: 'org.apache.velocity', name: 'velocity-engine-core', version: '2.3'
|
|
|
|
// runtimeOnly group: 'com.mysql', name: 'mysql-connector-j', version: '8.4.0'
|
|
runtimeOnly 'com.oracle.database.jdbc:ojdbc8:19.20.0.0'
|
|
|
|
implementation 'net.bytebuddy:byte-buddy:1.14.5'
|
|
|
|
// Commons FileUpload (WAS 독립적인 multipart 처리)
|
|
// CVE-2025-48976 (멀티파트 파트 헤더 크기 제한 부재 → DoS). 1.6.0 에서 partHeaderSizeMax 도입.
|
|
// 주의: 1.6 부터 파트 헤더 총량 기본 상한이 10240 → 512 바이트로 줄었다(DEFAULT_PART_HEADER_SIZE_MAX).
|
|
// 한글 파일명은 UTF-8 로 3바이트/자라 Content-Disposition 이 길어질 수 있어 실측으로 여유를 확인했다.
|
|
implementation 'commons-fileupload:commons-fileupload:1.6.0'
|
|
implementation 'commons-io:commons-io:2.15.1'
|
|
|
|
|
|
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
|
|
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
|
|
testImplementation 'org.mockito:mockito-inline:3.11.2'
|
|
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
|
testImplementation 'org.seleniumhq.selenium:selenium-java:4.8.3'
|
|
testImplementation group: 'io.github.bonigarcia', name: 'webdrivermanager', version: '5.9.1'
|
|
testImplementation 'org.apache.httpcomponents.client5:httpclient5:5.2.1'
|
|
|
|
}
|
|
|
|
ext {
|
|
springMavenArtifactVersion = '5.3.30'
|
|
encoding = 'UTF-8'
|
|
profile = 'local'
|
|
|
|
// 내장 Tomcat 버전 상향 (Spring Boot 2.7.18 기본값 9.0.83 → 9.0.120).
|
|
// Boot BOM 의 tomcat.version 프로퍼티를 덮어써서 tomcat-embed-core/-el/-websocket 이 함께 올라간다.
|
|
// 9.0.x 계열 유지 = Servlet 4.0 / javax.* 네임스페이스 그대로, JDK8 호환.
|
|
set('tomcat.version', '9.0.120')
|
|
|
|
// CVE-2022-1471 (snakeyaml Constructor 임의 타입 역직렬화 → RCE). Boot 2.7.18 BOM 기본값 1.30 → 2.6.
|
|
// 앱/프레임워크 실경로는 이미 SafeConstructor 계열(OriginTrackedYamlLoader, spring-beans
|
|
// FilteringConstructor, swagger-parser DeserializationUtils)이라 익스플로잇 경로는 없었으나
|
|
// SCA 는 버전으로 판정하므로 2.x 로 올린다. snakeyaml 2.6 = Java 8 바이트코드(major 52).
|
|
// 2.6 인 이유: swagger-core 2.2.52 / swagger-parser-v3 2.1.45 가 요구하는 버전이라 강등이 없다.
|
|
// 부수 효과: swagger-parser 가 호출하는 LoaderOptions.setCodePointLimit(1.32+ API) 도 해소.
|
|
set('snakeyaml.version', '2.6')
|
|
|
|
// jackson 전 모듈 버전 통일(Boot 2.7.18 BOM 기본 2.13.5). 2.18.x 는 JDK8 호환 라인이다.
|
|
// 이유 3가지
|
|
// 1) snakeyaml 2.x 는 ParserImpl(StreamReader) 를 제거했고 jackson-dataformat-yaml 은
|
|
// 2.15+ 부터 ParserImpl(StreamReader, LoaderOptions) 를 쓴다 — 위 snakeyaml 상향의 전제.
|
|
// 2) WS-2026-0003 (jackson-core async parser DoS, CVSS 7.5) — 2.18.6 에서 수정.
|
|
// 3) jackson-databind PolymorphicTypeValidator 우회(제네릭 타입 인자 미검증) — 2.18.8 에서 수정.
|
|
// 이 앱은 다형성 역직렬화(activateDefaultTyping/@JsonTypeInfo)를 쓰지 않아 노출 경로는 없다.
|
|
// 2.18.x 마지막 패치를 쓴다.
|
|
set('jackson-bom.version', '2.18.10')
|
|
|
|
// CVE-2026-41901: ≤3.1.4의 제한된 표현식 구문 검사 우회(SSTI)를 3.1.5에서 수정.
|
|
// Boot 의존성 관리로 core/spring5를 함께 맞추며 Java8 / Spring5 / javax.servlet을 유지한다.
|
|
// extras-springsecurity5 3.1.5 / layout-dialect 3.4.0은 유지.
|
|
// ThymeleafBootMvcCompatibilityTest가 Boot 2.7 자동 구성 엔진·ViewResolver의 초기화,
|
|
// MVC 폼·레이아웃·보안 표시를 검증한다. 실제 WAS 기동/재배포 검증은 별도 배포 조건이다.
|
|
// 주의: 3.1 은 #request/#session/#response/#servletContext 표현식 객체를 제거했다(IllegalArgumentException).
|
|
set('thymeleaf.version', '3.1.5.RELEASE')
|
|
set('thymeleaf-extras-springsecurity.version', '3.1.5.RELEASE')
|
|
|
|
// Spring Framework 5.3.x OSS 마지막 릴리스로 통일(Boot 2.7.18 BOM 기본 5.3.31, 일부 5.3.30 혼재였음).
|
|
// 5.3.32~5.3.39 구간 CVE 정리용. 5.3.x 는 OSS EOL 이라 이 위로는 상용(Enterprise) 빌드뿐이다.
|
|
// 남는 5.3.x 지적(CVE-2026-41855 JMS Jackson 역직렬화, CVE-2016-1000027 HttpInvoker)은
|
|
// 5.3 계열에 수정본이 없고 앱이 JMS/HttpInvoker 를 쓰지 않으므로 억제 항목으로 따로 관리한다.
|
|
set('spring-framework.version', '5.3.39')
|
|
|
|
// Spring Security 5.7.x OSS 마지막 릴리스(Boot 2.7.18 BOM 기본 5.7.11).
|
|
// CVE-2026-22732(응답 커밋 후 보안 헤더 미기록)는 수정본이 5.7.22/5.8.24 = Enterprise 전용이라
|
|
// OSS 로는 올릴 수 없다. 우회책(HeaderWriterFilter.shouldWriteHeadersEagerly=true)은 동작 변경이라 분리 검토.
|
|
set('spring-security.version', '5.7.14')
|
|
}
|
|
|
|
|
|
task printProfile {
|
|
doLast {
|
|
if (project.hasProperty('profile')) {
|
|
println "Current profile: ${project.property('profile')}"
|
|
} else {
|
|
println "No profile specified"
|
|
}
|
|
}
|
|
}
|
|
|
|
bootRun {
|
|
// 디버깅 사용시에는 아래 내용 주석 해제 하세요
|
|
// jvmArgs '-Xdebug', '-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005'
|
|
|
|
sourceResources sourceSets.main // processResources 필터 우회 (dev yml 직접 참조)
|
|
|
|
// 로컬 기동 기본 프로파일.
|
|
// dev/stage/prod 는 리버스프록시(OHS) 뒤 WAR 배포용이라 server.forward-headers-strategy=framework 가
|
|
// 걸려 있다. framework 는 신뢰 프록시 목록 없이 X-Forwarded-* 를 그대로 신뢰하므로 앞단이 없는
|
|
// 로컬 기동에는 쓰지 않는다(로컬은 공통 기본값 native = Tomcat RemoteIpValve, 사설대역만 신뢰).
|
|
// 다른 프로파일로 띄우려면: gradle bootRun -PbootProfile=dev
|
|
// ('profile' 이 아니라 'bootProfile' 인 이유: 위 ext 블록이 profile='local' 을 이미 점유하고 있어
|
|
// findProperty('profile') 은 -P 지정 여부와 무관하게 항상 'local' 을 돌려준다.)
|
|
def bootRunProfile = (project.findProperty('bootProfile') ?: 'local_rinjaemac').toString()
|
|
args = ["--spring.profiles.active=" + bootRunProfile]
|
|
}
|
|
|
|
|
|
// annotation processor 출력 디렉토리는 IntelliJ/Gradle 통합이 자동 등록함.
|
|
// 명시적으로 srcDir 추가하면 IntelliJ idea 모드에서 MapStruct가 같은 파일을
|
|
// 중복 생성하려다 javax.annotation.processing.FilerException 발생함.
|
|
sourceSets {
|
|
main {
|
|
java {
|
|
srcDir 'src/main/java'
|
|
}
|
|
}
|
|
}
|
|
|
|
configurations {
|
|
annotationProcessor
|
|
|
|
// WebLogic 배포 시 Tyrus WebSocket 필터(weblogic.websocket.tyrus.TyrusServletFilter)와
|
|
// 충돌 방지: WAR 에 번들된 Tomcat WsSci 가 javax.websocket.server.ServerContainer 속성을
|
|
// WsServerContainer 로 등록 → WebLogic Tyrus 필터가 TyrusServerContainer 로 캐스팅하다 실패.
|
|
// 앱은 WebSocket 미사용이므로 Tomcat WebSocket 모듈 제외.
|
|
all {
|
|
exclude group: 'org.apache.tomcat.embed', module: 'tomcat-embed-websocket'
|
|
}
|
|
}
|
|
|
|
compileJava {
|
|
options.annotationProcessorPath = configurations.annotationProcessor
|
|
options.compilerArgs += ["-parameters"]
|
|
options.encoding = 'UTF-8'
|
|
}
|
|
|
|
processResources {
|
|
// exclude { details ->
|
|
// details.file.name.startsWith('application-') &&
|
|
// details.file.name.endsWith('.yml') &&
|
|
// !(details.file.name in ['application-stage.yml', 'application-prod.yml'])
|
|
// }
|
|
}
|
|
|
|
test {
|
|
useJUnitPlatform {
|
|
if (project.hasProperty('includeE2E') && project.includeE2E.toBoolean()) {
|
|
includeTags 'e2e'
|
|
} else {
|
|
excludeTags 'e2e'
|
|
}
|
|
}
|
|
enabled = true
|
|
}
|
|
|
|
// 로컬 전용 설정 파일. 배포 산출물(WAR)에 실리면 Actuator/SBA 설정이 그대로 노출되어
|
|
// 정보보호 점검에 불필요하게 걸린다. processResources 는 건드리지 않는다
|
|
// (bootRun 이 build/resources/main 을 그대로 쓰므로 로컬 기동이 깨진다).
|
|
def localOnlyResources = ['**/application-local*.yml']
|
|
|
|
// 배포 산출물에서 로컬 전용 라이브러리를 걸러낸다. localOnlyLibPrefixes 는 파일 상단 정의.
|
|
def excludeLocalOnlyLibs = { org.gradle.api.file.FileCollection cp ->
|
|
cp.filter { f -> !localOnlyLibPrefixes.any { p -> f.name.startsWith(p) } }
|
|
}
|
|
|
|
bootWar {
|
|
archiveFileName = "eapim-portal-boot.war"
|
|
mainClass = 'com.eactive.apim.portal.PortalApplication'
|
|
rootSpec.exclude(localOnlyResources)
|
|
classpath = excludeLocalOnlyLibs(classpath)
|
|
}
|
|
|
|
war {
|
|
archiveFileName = "eapim-portal.war"
|
|
|
|
from('src/main/resources/jeus-web-dd.xml') { into 'WEB-INF' }
|
|
from('src/main/resources/weblogic.xml') { into 'WEB-INF' }
|
|
|
|
rootSpec.exclude(localOnlyResources)
|
|
classpath = excludeLocalOnlyLibs(classpath)
|
|
}
|
|
|
|
// 정적 리소스만 별도 zip (OHS 추가 배포용). war/bootWar 는 그대로 정적 리소스 포함 유지
|
|
// (WAS 단독 접속 인원 지원). sass/ 는 컴파일 소스이며 실제 서빙 경로에 없어 제외
|
|
// (PortalConfigWebDispatcherServlet#addResourceHandlers 기준).
|
|
task staticResourcesZip(type: Zip) {
|
|
archiveFileName = "eapim-portal-static.zip"
|
|
destinationDirectory = file("$buildDir/libs")
|
|
from('src/main/resources/static') {
|
|
exclude 'sass/**'
|
|
}
|
|
}
|
|
assemble.dependsOn staticResourcesZip
|
|
|
|
task printSourceSets {
|
|
doLast {
|
|
sourceSets.each { srcSet ->
|
|
println "SourceSet: ${srcSet.name}"
|
|
println " Java srcDirs : ${srcSet.allJava.srcDirs}"
|
|
println " Resources : ${srcSet.resources.srcDirs}"
|
|
println " Output dir : ${srcSet.output.classesDirs.asPath}"
|
|
}
|
|
}
|
|
}
|
|
// CycloneDX SBOM -> xlsx 변환 (gradle sbomXlsx)
|
|
apply from: "$projectDir/gradle/sbom-xlsx.gradle"
|