Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
HELP.md
README.md
.git
.gitignore
.gradle
build
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
Expand All @@ -22,6 +26,7 @@ bin/
*.iws
*.iml
*.ipr
out
out/
!**/src/main/**/out/
!**/src/test/**/out/
Expand All @@ -35,4 +40,5 @@ out/
/.nb-gradle/

### VS Code ###
.vscode
.vscode/
24 changes: 10 additions & 14 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,27 +1,23 @@
FROM eclipse-temurin:17-jdk AS build
FROM gradle:8.12-jdk17 AS build

WORKDIR /app

ARG GPR_USER
ARG GPR_TOKEN

COPY gradlew .
COPY gradle gradle
COPY build.gradle .
COPY settings.gradle .

RUN chmod +x gradlew
COPY build.gradle settings.gradle ./
COPY gradle ./gradle

RUN ./gradlew dependencies --no-daemon \
-PGPR_USER=${GPR_USER} \
-PGPR_TOKEN=${GPR_TOKEN} || true
RUN GPR_USER=${GPR_USER} GPR_TOKEN=${GPR_TOKEN} gradle dependencies --no-daemon || true

COPY src src
COPY src ./src

RUN ./gradlew bootJar -x test --no-daemon \
-PGPR_USER=${GPR_USER} \
-PGPR_TOKEN=${GPR_TOKEN}
RUN GPR_USER=${GPR_USER} GPR_TOKEN=${GPR_TOKEN} gradle bootJar --no-daemon -x test

FROM eclipse-temurin:17-jre

WORKDIR /app

COPY --from=build /app/build/libs/*.jar app.jar

ENTRYPOINT ["java", "-jar", "app.jar"]
Comment on lines 17 to 23

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify Dockerfiles that do not set a non-root USER
fd -i 'Dockerfile' -t f | while read -r f; do
  echo "== $f =="
  rg -n '^\s*USER\b' "$f" || echo "No USER instruction found"
done

Repository: 3s-ticketing/queue-service

Length of output: 113


🏁 Script executed:

cat -n Dockerfile

Repository: 3s-ticketing/queue-service

Length of output: 684


Run the runtime container as a non-root user.

Lines 17–23 define the final image but lack a USER instruction, causing the app to run as root. This weakens container isolation and violates security best practices.

Proposed fix
 FROM eclipse-temurin:17-jre

 WORKDIR /app
-
-COPY --from=build /app/build/libs/*.jar app.jar
+RUN useradd --system --create-home --uid 10001 appuser
+COPY --from=build /app/build/libs/*.jar /app/app.jar
+RUN chown -R appuser:appuser /app
+USER appuser

 ENTRYPOINT ["java", "-jar", "app.jar"]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
FROM eclipse-temurin:17-jre
WORKDIR /app
COPY --from=build /app/build/libs/*.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]
FROM eclipse-temurin:17-jre
WORKDIR /app
RUN useradd --system --create-home --uid 10001 appuser
COPY --from=build /app/build/libs/*.jar /app/app.jar
RUN chown -R appuser:appuser /app
USER appuser
ENTRYPOINT ["java", "-jar", "app.jar"]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Dockerfile` around lines 17 - 23, The final image runs as root because there
is no USER instruction; create a non-root user and switch to it before the
ENTRYPOINT. In the Dockerfile (around WORKDIR /app, COPY of app.jar, and
ENTRYPOINT), add commands to create a dedicated user/group (e.g., uid/gid 1000),
chown the /app directory and app.jar to that user, set a sane HOME, and add a
USER instruction to switch to that user so the jar (app.jar) runs without root
privileges.

Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.ticketing.queue.infrastructure.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class QueueSecurityConfig {

// MVP 통합 테스트용 임시 SecurityConfig
@Bean
@Order(1)
public SecurityFilterChain queueFilterChain(HttpSecurity http) throws Exception {
return http
.securityMatcher("/**")
.csrf(AbstractHttpConfigurer::disable)
.formLogin(AbstractHttpConfigurer::disable)
.httpBasic(AbstractHttpConfigurer::disable)
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.authorizeHttpRequests(auth -> auth
.anyRequest().permitAll()
)
.build();
Comment on lines +11 to +29

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether permissive chain is profile-gated and whether other security chains/method-security exist.
rg -n --type=java '@Profile\(|@EnableMethodSecurity|@EnableWebSecurity|SecurityFilterChain|@Order\(' src/main/java
rg -n --type=java 'securityMatcher\("/\*\*"\)|anyRequest\(\)\.permitAll\(' src/main/java

Repository: 3s-ticketing/queue-service

Length of output: 740


🏁 Script executed:

# Check the full QueueSecurityConfig file
cat -n src/main/java/org/ticketing/queue/infrastructure/config/QueueSecurityConfig.java

Repository: 3s-ticketing/queue-service

Length of output: 1547


🏁 Script executed:

# Search for all SecurityFilterChain beans in the project to understand the overall security setup
rg -n --type=java 'public SecurityFilterChain' src/main/java

Repository: 3s-ticketing/queue-service

Length of output: 240


🏁 Script executed:

# Check if there are any Spring application properties or YAML configs that gate profiles
fd -e properties -e yml -e yaml src/main/resources | head -20

Repository: 3s-ticketing/queue-service

Length of output: 448


🏁 Script executed:

# Search for any conditional annotations or profile-based configurations
rg -n --type=java '@ConditionalOnProperty|@ConditionalOnProfile|@Profile|spring\.config\.activate' src/main/java

Repository: 3s-ticketing/queue-service

Length of output: 52


🏁 Script executed:

# Correct fd syntax to find properties files
fd . 'src/main/resources' -e properties -e yml -e yaml

Repository: 3s-ticketing/queue-service

Length of output: 147


🏁 Script executed:

# Search for any profile or conditional properties in config files
find src/main/resources -type f \( -name "*.properties" -o -name "*.yml" -o -name "*.yaml" \) 2>/dev/null | xargs grep -l "spring.config.activate\|spring.profiles" 2>/dev/null || echo "No profile config files found"

Repository: 3s-ticketing/queue-service

Length of output: 100


🏁 Script executed:

# Also check if there are any other Java config classes that might conditionally disable/replace this config
rg -n --type=java 'QueueSecurityConfig|@ConditionalOnMissingBean|@Primary' src/main/java

Repository: 3s-ticketing/queue-service

Length of output: 189


Gate this permissive filter chain to test environments only; it currently allows unauthenticated access in all environments.

The QueueSecurityConfig class lacks a @Profile annotation despite the comment indicating it is temporary for MVP integration tests. With @Order(1), securityMatcher("/**"), and anyRequest().permitAll(), this is the only SecurityFilterChain bean in the codebase and will be active in production, effectively disabling authentication globally.

Add @Profile("test") to the class to restrict it to test environments, or remove it entirely if no longer needed.

Suggested patch direction
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
 ...
 `@Configuration`
+@Profile("test")
 public class QueueSecurityConfig {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/org/ticketing/queue/infrastructure/config/QueueSecurityConfig.java`
around lines 11 - 29, The QueueSecurityConfig class currently exposes a
permissive SecurityFilterChain (securityMatcher("/**"),
anyRequest().permitAll()) in all environments; restrict it to tests by adding
the Spring profile annotation to the class (e.g., annotate QueueSecurityConfig
with `@Profile`("test")) or remove the entire class if it's no longer needed;
update imports to include org.springframework.context.annotation.Profile and
ensure the bean remains only active under the intended profile so production
authentication is not bypassed.

}
}
46 changes: 23 additions & 23 deletions src/main/resources/application-docker.yml
Original file line number Diff line number Diff line change
@@ -1,66 +1,66 @@
server:
tomcat:
threads:
max: 500
min-spare: 100
accept-count: 1000
max-connections: 8192
connection-timeout: 5000

spring:
config:
activate:
on-profile: docker

data:
redis:
host: ticketing-redis
port: 6379
host: ${REDIS_HOST:ticketing-redis}
port: ${REDIS_PORT:6379}
timeout: 5000ms
lettuce:
shutdown-timeout: 100ms

datasource:
url: jdbc:postgresql://${DB_HOST:ticketing-postgres}:${DB_PORT:15432}/${DB_NAME:ticketing}?currentSchema=${QUEUE_SCHEMA:queue}
url: jdbc:postgresql://${DB_HOST:ticketing-postgres}:${DB_PORT:15432}/${DB_NAME:ticketing}?currentSchema=${DB_SCHEMA:queue}
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
driver-class-name: org.postgresql.Driver
hikari:
maximum-pool-size: 30
minimum-idle: 10
connection-timeout: 3000
idle-timeout: 600000
max-lifetime: 1800000

jpa:
hibernate:
ddl-auto: ${JPA_DDL_AUTO:update} # 도커는 update (create면 매번 초기화 위험)
ddl-auto: ${JPA_DDL_AUTO:update}
properties:
hibernate:
default_schema: ${QUEUE_SCHEMA:queue}
default_schema: ${DB_SCHEMA:queue}
hbm2ddl:
create_schemas: true

kafka:
bootstrap-servers: kafka:9092
bootstrap-servers: ${SPRING_KAFKA_BOOTSTRAP_SERVERS}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add a safe default for Kafka bootstrap servers to avoid unresolved-placeholder startup failure.

Line 42 currently requires SPRING_KAFKA_BOOTSTRAP_SERVERS unconditionally. If it’s missing, the docker profile can fail at startup before the app is usable.

🔧 Proposed fix
-    bootstrap-servers: ${SPRING_KAFKA_BOOTSTRAP_SERVERS}
+    bootstrap-servers: ${SPRING_KAFKA_BOOTSTRAP_SERVERS:ticketing-kafka:9092}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/application-docker.yml` at line 42, The docker profile
currently requires the SPRING_KAFKA_BOOTSTRAP_SERVERS env var unconditionally
which can cause unresolved-placeholder startup failures; update the
bootstrap-servers property to provide a safe default using Spring’s property
placeholder syntax (e.g., change bootstrap-servers:
${SPRING_KAFKA_BOOTSTRAP_SERVERS} to use a fallback like
${SPRING_KAFKA_BOOTSTRAP_SERVERS:localhost:9092}) so the app can start when the
env var is missing; update the single property key bootstrap-servers
accordingly.


eureka:
client:
service-url:
defaultZone: ${EUREKA_DEFAULT_ZONE:http://eureka-server:10001/eureka/} # 도커 내부 서비스명
defaultZone: ${EUREKA_DEFAULT_ZONE:http://eureka-server:10001/eureka/}

management:
metrics:
tags:
application: ${spring.application.name}
endpoints:
web:
exposure:
include: health,info,prometheus
tracing:
sampling:
probability: 1.0
probability: 0.1
zipkin:
tracing:
endpoint: ${ZIPKIN_URL:http://localhost:9411/api/v2/spans}

# data:
# redis:
# password: ${REDIS_PASSWORD}
# sentinel:
# master: mymaster
# nodes:
# - redis-sentinel-1:26379
# - redis-sentinel-2:26379
# - redis-sentinel-3:26379
# timeout: 5000ms
# lettuce:
# shutdown-timeout: 100ms
queue:
token:
secret: ${QUEUE_TOKEN_SECRET}
29 changes: 23 additions & 6 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,27 @@ spring:
name: queue-service

config:
import: optional:configserver:${CONFIG_SERVER_URL:http://localhost:10002}
import: optional:configserver:http://localhost:10002

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid hardcoded Config Server endpoint in shared config.

Line 6 pins the Config Server to http://localhost:10002, which will fail in non-local environments and removes deployment-time override flexibility.

Suggested patch
-    import: optional:configserver:http://localhost:10002
+    import: optional:configserver:${CONFIG_SERVER_URL:http://localhost:10002}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import: optional:configserver:http://localhost:10002
import: optional:configserver:${CONFIG_SERVER_URL:http://localhost:10002}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/application.yml` at line 6, Replace the hardcoded Config
Server URI in the import declaration so deployments can override it at runtime:
update the line that currently reads "import:
optional:configserver:http://localhost:10002" to reference an
environment/property placeholder (e.g. use ${CONFIGSERVER_URI} or a Spring
property) and keep the same "import: optional:configserver:" prefix so the
effective URL is resolved from CONFIGSERVER_URI (with a sensible default if
desired) instead of being fixed to localhost.


cloud:
config:
fail-fast: false # config-server 없어도 로컬 기동 가능
import-check:
enabled: false
kafka:
bootstrap-servers: ${KAFKA_BOOTSTRAP_SERVERS:localhost:19092}
consumer:
group-id: queue-service
auto-offset-reset: latest
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.springframework.kafka.support.serializer.ErrorHandlingDeserializer
properties:
spring.deserializer.value.delegate.class: org.springframework.kafka.support.serializer.JsonDeserializer
spring.json.trusted.packages: "org.ticketing.queue.domain.event,java.util,java.lang"
spring.json.use.type.headers: false
spring.json.value.default.type: org.ticketing.queue.domain.event.MatchApprovedEvent

management:
tracing:
enabled: false
sampling:
probability: 0.0
zipkin:
tracing:
export:
enabled: false
36 changes: 28 additions & 8 deletions src/main/resources/logback.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,45 @@
</encoder>
</appender>

<!-- 파일 (컬러 없이 순수 로그) -->
<!-- Docker/Loki용: JSON 구조화 로그 -->
<appender name="CONSOLE_JSON" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<customFields>{"service":"queue-service"}</customFields>
<fieldNames>
<timestamp>timestamp</timestamp>
<message>message</message>
<logger>logger</logger>
<thread>thread</thread>
<level>level</level>
</fieldNames>
</encoder>
</appender>

<!-- 파일 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/queue-service.log</file>

<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/queue-service.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>14</maxHistory>
</rollingPolicy>

<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %thread %logger{36} [traceId=%X{traceId}] - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>

<!-- root -->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
<!-- 로컬: 텍스트, Docker: JSON -->
<springProfile name="default, local">
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
</springProfile>

<springProfile name="docker">
<root level="INFO">
<appender-ref ref="CONSOLE_JSON"/>
</root>
</springProfile>
Comment on lines +49 to +61

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify current logging config wiring and whether both logback variants exist
fd -i 'logback*.xml' src/main/resources
rg -n -C3 'logging\.config|springProfile|logback-spring\.xml|logback\.xml' src/main/resources

Repository: 3s-ticketing/queue-service

Length of output: 1084


🏁 Script executed:

#!/bin/bash
# Check if this is a Spring Boot project and verify dependencies
echo "=== Looking for Spring Boot dependencies ==="
find . -name "pom.xml" -o -name "build.gradle" | head -5

# Check pom.xml if it exists
if [ -f "pom.xml" ]; then
  echo -e "\n=== Spring Boot dependency in pom.xml ==="
  rg -i 'spring-boot' pom.xml | head -10
fi

# Check for application configuration files
echo -e "\n=== Application config files ==="
fd -i 'application.*\.(properties|yml|yaml)' src/

# Check if logging.config is specified anywhere
echo -e "\n=== Custom logging config references ==="
rg -i 'logging\.config' . --type properties --type yaml 2>/dev/null | head -10

Repository: 3s-ticketing/queue-service

Length of output: 318


🏁 Script executed:

#!/bin/bash
# Verify Spring Boot's behavior with logback.xml vs logback-spring.xml
# by checking Spring Boot documentation references in code or comments
echo "=== Checking for any Spring Boot version info ==="
if [ -f "pom.xml" ]; then
  rg -A2 'spring-boot-starter-parent|spring.boot.version' pom.xml
fi

# Check if there are any bootstrap or configuration files
echo -e "\n=== Bootstrap/Configuration setup ==="
find . -name "bootstrap*" -o -name "application-*" | grep -E '\.(properties|yml|yaml)$' | head -20

Repository: 3s-ticketing/queue-service

Length of output: 203


🏁 Script executed:

#!/bin/bash
# Check Spring Boot version in build.gradle
echo "=== Spring Boot version ==="
rg -i 'spring.boot|springboot' build.gradle -A2 -B2

# Check what profiles are defined
echo -e "\n=== Active profiles in application.yml ==="
rg -i 'profiles|spring:' src/main/resources/application.yml -A5 -B2

echo -e "\n=== Application docker profile ==="
cat src/main/resources/application-docker.yml

Repository: 3s-ticketing/queue-service

Length of output: 2457


🏁 Script executed:

#!/bin/bash
# Check if the current logback.xml is actually being processed with springProfile
echo "=== Full logback.xml content ==="
cat src/main/resources/logback.xml

# Check for any test that might verify logging behavior
echo -e "\n=== Looking for log configuration tests ==="
find . -path '*/test/*' -name '*Log*' -o -path '*/test/*' -name '*Appender*' | head -10

Repository: 3s-ticketing/queue-service

Length of output: 2498


Rename logback.xml to logback-spring.xml to enable <springProfile> processing.

The <springProfile> elements (lines 50 and 57) and Spring Boot–specific converters (ColorConverter, WhitespaceThrowableProxyConverter) are Spring Boot Logback extensions that require the file to be named logback-spring.xml. When named logback.xml, Spring Boot treats it as standard Logback and ignores these extensions. This silently breaks the profile-aware logging configuration: the docker profile will never activate, and both text and JSON appenders will be treated as standard elements.

Fix

Rename the file from src/main/resources/logback.xml to src/main/resources/logback-spring.xml. No content changes needed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/logback.xml` around lines 49 - 61, The logging
configuration file uses Spring Boot–specific features like <springProfile> and
converters (e.g., ColorConverter, WhitespaceThrowableProxyConverter) so rename
the resource file from logback.xml to logback-spring.xml so Spring Boot will
process these extensions and honor profiles (e.g., the "docker" <springProfile>
and the default/local profile) without changing the file contents; after
renaming, rebuild to verify the docker profile activates and the JSON
CONSOLE_JSON appender is used in that profile.


</configuration>
Loading