diff --git a/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/audit/AuditLogService.java b/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/audit/AuditLogService.java
index 5cf5007a..6df20a55 100644
--- a/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/audit/AuditLogService.java
+++ b/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/audit/AuditLogService.java
@@ -16,7 +16,6 @@ package io.shinhanlife.dap.mcg.audit;
*
*/
import io.shinhanlife.dap.mcg.config.McpGatewayProperties;
-import io.shinhanlife.dap.mcg.guardrail.SensitiveDataMasker;
import io.shinhanlife.dap.mcg.security.McpRequestContext;
import com.fasterxml.jackson.databind.JsonNode;
import org.slf4j.Logger;
@@ -32,11 +31,8 @@ import org.springframework.stereotype.Service;
public class AuditLogService {
private static final Logger audit = LoggerFactory.getLogger("MCP_AUDIT");
private final McpGatewayProperties properties;
- private final SensitiveDataMasker masker;
-
- public AuditLogService(McpGatewayProperties properties, SensitiveDataMasker masker) {
+ public AuditLogService(McpGatewayProperties properties) {
this.properties = properties;
- this.masker = masker;
}
/**
@@ -47,7 +43,7 @@ public class AuditLogService {
return;
}
audit.info("event=tool_started requestId={} agentId={} userId={} clientAddress={} tool={} arguments={}",
- context.requestId(), context.agentId(), context.userId(), context.clientAddress(), toolName, masker.mask(arguments));
+ context.requestId(), context.agentId(), context.userId(), context.clientAddress(), toolName, arguments);
}
/**
diff --git a/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/guardrail/SensitiveDataMasker.java b/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/guardrail/SensitiveDataMasker.java
deleted file mode 100644
index c4f539d7..00000000
--- a/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/guardrail/SensitiveDataMasker.java
+++ /dev/null
@@ -1,92 +0,0 @@
-package io.shinhanlife.dap.mcg.guardrail;
-
-
-/**
- * @package io.shinhanlife.dap.mcg.guardrail
- * @className SensitiveDataMasker
- * @description AX HUB 시스템 처리 클래스
- * @author 0986406
- * @create 2026.09.01
- *
- * ---------- 개정이력 ----------
- * 수정일 수정자 수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01 0986406 최초생성
- *
- *
- */
-import org.springframework.stereotype.Component;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-import java.util.regex.Pattern;
-
-/**
- * Audit Log, Redis Trace, Agent 응답 preview에 남으면 안 되는 민감정보를 마스킹합니다.
- */
-@Component
-public class SensitiveDataMasker {
- private static final Set SENSITIVE_KEYS = Set.of(
- "password", "passwd", "pwd", "token", "accessToken", "refreshToken", "secret",
- "ssn", "rrn", "residentNumber", "cardNumber", "accountNumber", "accountNo",
- "phone", "mobile", "email", "idempotencyKey");
- private static final Pattern EMAIL = Pattern.compile("([a-zA-Z0-9._%+-]{2})[a-zA-Z0-9._%+-]*(@[a-zA-Z0-9.-]+)");
- private static final Pattern CARD_OR_ACCOUNT = Pattern.compile("\\b(\\d{4})\\d{4,12}(\\d{2,4})\\b");
- private final ObjectMapper json;
-
- public SensitiveDataMasker(ObjectMapper json) {
- this.json = json;
- }
-
- /**
- * JsonNode 전체를 재귀적으로 순회하며 민감 key와 민감 패턴을 마스킹합니다.
- */
- public JsonNode mask(JsonNode input) {
- if (input == null || input.isMissingNode() || input.isNull()) {
- return json.createObjectNode();
- }
- if (input.isArray()) {
- ArrayNode masked = json.createArrayNode();
- for (JsonNode item : input) {
- masked.add(mask(item));
- }
- return masked;
- }
- if (input.isObject()) {
- ObjectNode masked = json.createObjectNode();
- Iterator> fields = input.fields();
- while (fields.hasNext()) {
- Map.Entry entry = fields.next();
- String key = entry.getKey();
- JsonNode value = entry.getValue();
- if (isSensitiveKey(key)) {
- masked.put(key, "***");
- } else {
- masked.set(key, mask(value));
- }
- }
- return masked;
- }
- if (input.isTextual()) {
- return json.valueToTree(maskText(input.asText()));
- }
- return input;
- }
-
- private boolean isSensitiveKey(String key) {
- return key != null && SENSITIVE_KEYS.stream().anyMatch(sensitive -> sensitive.equalsIgnoreCase(key));
- }
-
- private String maskText(String value) {
- if (value == null || value.isBlank()) {
- return value;
- }
- String masked = EMAIL.matcher(value).replaceAll("$1***$2");
- return CARD_OR_ACCOUNT.matcher(masked).replaceAll("$1********$2");
- }
-}
diff --git a/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/redis/RedisToolTraceService.java b/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/redis/RedisToolTraceService.java
index a1d85332..7beab07e 100644
--- a/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/redis/RedisToolTraceService.java
+++ b/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/redis/RedisToolTraceService.java
@@ -16,7 +16,6 @@ package io.shinhanlife.dap.mcg.redis;
*
*/
import io.shinhanlife.dap.mcg.config.McpGatewayProperties;
-import io.shinhanlife.dap.mcg.guardrail.SensitiveDataMasker;
import io.shinhanlife.dap.mcg.security.McpRequestContext;
import io.shinhanlife.dap.mcg.dto.ToolMetadata;
import org.slf4j.Logger;
@@ -49,19 +48,16 @@ public class RedisToolTraceService {
private final ObjectProvider redisProvider;
private final ObjectMapper json;
private final McpMonitorEventService monitorEvents;
- private final SensitiveDataMasker masker;
private final Map attemptStates = new ConcurrentHashMap<>();
public RedisToolTraceService(McpGatewayProperties properties,
ObjectProvider redisProvider,
ObjectMapper json,
- McpMonitorEventService monitorEvents,
- SensitiveDataMasker masker) {
+ McpMonitorEventService monitorEvents) {
this.properties = properties;
this.redisProvider = redisProvider;
this.json = json;
this.monitorEvents = monitorEvents;
- this.masker = masker;
}
/**
@@ -206,7 +202,7 @@ public class RedisToolTraceService {
arguments.fieldNames().forEachRemaining(argNames::add);
trace.put("argumentNames", argNames);
- trace.put("arguments", masker.mask(arguments).toString());
+ trace.put("arguments", arguments.toString());
trace.put("responseSummary", responseSummary(responseText));
trace.put("timestamp", Instant.now().toString());
return json.writeValueAsString(trace);
diff --git a/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/service/ExecuteService.java b/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/service/ExecuteService.java
index e4c51559..8e71d2b1 100644
--- a/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/service/ExecuteService.java
+++ b/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/service/ExecuteService.java
@@ -23,7 +23,6 @@ import io.shinhanlife.dap.mcg.resilience.RetryPolicy;
import io.shinhanlife.dap.mcg.resilience.ToolExecutionException;
import io.shinhanlife.dap.mcg.dto.OperationType;
import io.shinhanlife.dap.mcg.config.McpGatewayProperties;
-import io.shinhanlife.dap.mcg.guardrail.SensitiveDataMasker;
import io.shinhanlife.dap.mcg.guardrail.GuardrailService;
import io.shinhanlife.dap.mcg.security.McpRequestContext;
import io.shinhanlife.dap.mcg.security.McpRequestContextResolver;
@@ -58,7 +57,6 @@ public class ExecuteService {
private final ToolPlanner planner;
private final KillSwitchService killSwitchService;
private final ObjectMapper objectMapper;
- private final SensitiveDataMasker dataMasker;
private final GuardrailService guardrailService;
private final McpRequestContextResolver contextResolver;
private final AuditLogService auditLogService;
@@ -76,7 +74,6 @@ public class ExecuteService {
public ExecuteService(ToolPlanner planner,
KillSwitchService killSwitchService,
ObjectMapper objectMapper,
- SensitiveDataMasker dataMasker,
GuardrailService guardrailService,
McpRequestContextResolver contextResolver,
AuditLogService auditLogService,
@@ -92,7 +89,6 @@ public class ExecuteService {
this.planner = planner;
this.killSwitchService = killSwitchService;
this.objectMapper = objectMapper;
- this.dataMasker = dataMasker;
this.guardrailService = guardrailService;
this.contextResolver = contextResolver;
this.auditLogService = auditLogService;
@@ -266,14 +262,14 @@ public class ExecuteService {
headers.put("trace-id", context.requestId());
headers.put("request-id", java.util.UUID.randomUUID().toString());
- ObjectNode pageArguments = paginationValidator.normalize(arguments);
+ ObjectNode pageArguments = paginationValidator.normalize(metadata, arguments);
LargeToolResponseService.Collector collector = largeResponses.newCollector(metadata.getName(), context.requestId());
while (true) {
Map pagePayload = objectMapper.convertValue(pageArguments, Map.class);
try {
- log.info(" [ExecuteService] 요청 페이로드(마스킹 적용): {}", objectMapper.writeValueAsString(dataMasker.mask(objectMapper.valueToTree(pagePayload))));
+ log.info(" [ExecuteService] 요청 페이로드: {}", objectMapper.writeValueAsString(pagePayload));
} catch (Exception ignore) {}
JsonNode data = null;
diff --git a/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/tool/large/AgentResponseBudgetService.java b/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/tool/large/AgentResponseBudgetService.java
index 357cfce9..5cee6a51 100644
--- a/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/tool/large/AgentResponseBudgetService.java
+++ b/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/tool/large/AgentResponseBudgetService.java
@@ -19,7 +19,6 @@ import java.util.Iterator;
import java.util.Map;
import io.shinhanlife.dap.mcg.config.AgentResponseBudgetProperties;
-import io.shinhanlife.dap.mcg.guardrail.SensitiveDataMasker;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -30,12 +29,9 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
public class AgentResponseBudgetService {
private final AgentResponseBudgetProperties properties;
private final ObjectMapper json;
- private final SensitiveDataMasker masker;
-
- public AgentResponseBudgetService(AgentResponseBudgetProperties properties, ObjectMapper json, SensitiveDataMasker masker) {
+ public AgentResponseBudgetService(AgentResponseBudgetProperties properties, ObjectMapper json) {
this.properties = properties;
this.json = json;
- this.masker = masker;
}
public ObjectNode apply(ObjectNode response) {
@@ -109,7 +105,7 @@ public class AgentResponseBudgetService {
}
private JsonNode budgetItem(JsonNode item, BudgetStats stats) {
- JsonNode masked = masker.mask(item);
+ JsonNode masked = item;
if (!masked.isObject()) {
return truncateByBytes(masked, stats);
}
diff --git a/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/tool/large/LargeToolResponseService.java b/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/tool/large/LargeToolResponseService.java
index df2be0af..3cba3c8f 100644
--- a/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/tool/large/LargeToolResponseService.java
+++ b/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/tool/large/LargeToolResponseService.java
@@ -18,7 +18,6 @@ package io.shinhanlife.dap.mcg.tool.large;
import java.util.Iterator;
import io.shinhanlife.dap.mcg.config.McpGatewayProperties;
-import io.shinhanlife.dap.mcg.guardrail.SensitiveDataMasker;
import io.shinhanlife.dap.mcg.resilience.FailureType;
import io.shinhanlife.dap.mcg.resilience.ToolExecutionException;
import org.springframework.stereotype.Service;
@@ -34,16 +33,13 @@ import java.time.Instant;
public class LargeToolResponseService {
private final McpGatewayProperties properties;
private final ObjectMapper json;
- private final SensitiveDataMasker masker;
private final AgentResponseBudgetService agentBudget;
public LargeToolResponseService(McpGatewayProperties properties,
ObjectMapper json,
- SensitiveDataMasker masker,
AgentResponseBudgetService agentBudget) {
this.properties = properties;
this.json = json;
- this.masker = masker;
this.agentBudget = agentBudget;
}
@@ -157,8 +153,7 @@ public class LargeToolResponseService {
}
Page page = pageFrom(data);
if (!page.paginated() && pageCount == 0 && count(page.items()) <= pageSize()) {
- JsonNode masked = masker.mask(data);
- normalData = masked;
+ normalData = data;
pageCount = 1;
returnedCount = count(page.items());
totalCount = returnedCount;
@@ -271,19 +266,18 @@ public class LargeToolResponseService {
}
private JsonNode previewItem(JsonNode item) {
- JsonNode masked = masker.mask(item);
- long itemBytes = jsonBytes(masked);
+ long itemBytes = jsonBytes(item);
if (itemBytes <= properties.largeResponseMaxItemBytes()) {
- return masked;
+ return item;
}
truncated = true;
ObjectNode preview = json.createObjectNode();
preview.put("truncated", true);
preview.put("originalBytes", itemBytes);
preview.put("maxItemBytes", properties.largeResponseMaxItemBytes());
- if (masked.isObject()) {
+ if (item.isObject()) {
ArrayNode fieldNames = json.createArrayNode();
- Iterator fieldNamesIter = masked.fieldNames();
+ Iterator fieldNamesIter = item.fieldNames();
while (fieldNamesIter.hasNext()) {
fieldNames.add(fieldNamesIter.next());
}
diff --git a/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/tool/large/PaginationRequestValidator.java b/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/tool/large/PaginationRequestValidator.java
index cf75fb9a..522a2f64 100644
--- a/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/tool/large/PaginationRequestValidator.java
+++ b/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/tool/large/PaginationRequestValidator.java
@@ -15,6 +15,7 @@ package io.shinhanlife.dap.mcg.tool.large;
*
*
*/
+import io.shinhanlife.dap.mcg.dto.ToolMetadata;
import io.shinhanlife.dap.mcg.config.McpGatewayProperties;
import io.shinhanlife.dap.mcg.resilience.FailureType;
import io.shinhanlife.dap.mcg.resilience.ToolExecutionException;
@@ -40,12 +41,18 @@ public class PaginationRequestValidator {
/**
* pageSize/cursor를 검증한 뒤 Tool 서버에 넘길 안전한 arguments 복사본을 만듭니다.
*/
- public ObjectNode normalize(ObjectNode arguments) {
+ public ObjectNode normalize(ToolMetadata metadata, ObjectNode arguments) {
try {
ObjectNode normalized = arguments == null
? json.createObjectNode()
: (ObjectNode) json.readTree(json.writeValueAsString(arguments));
- normalizePageSize(normalized);
+
+ if (metadata != null && metadata.allowedArguments().contains("pageSize")) {
+ normalizePageSize(normalized);
+ } else if (normalized.has("pageSize")) {
+ normalizePageSize(normalized);
+ }
+
validateCursor(normalized);
return normalized;
} catch (ToolExecutionException error) {
diff --git a/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/tool/result/ToolExecutionResultFormatter.java b/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/tool/result/ToolExecutionResultFormatter.java
index b1259d6d..1edf772c 100644
--- a/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/tool/result/ToolExecutionResultFormatter.java
+++ b/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/tool/result/ToolExecutionResultFormatter.java
@@ -15,7 +15,6 @@ package io.shinhanlife.dap.mcg.tool.result;
*
*
*/
-import io.shinhanlife.dap.mcg.guardrail.SensitiveDataMasker;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -34,11 +33,8 @@ public class ToolExecutionResultFormatter {
private static final int TEXT_PREVIEW_LIMIT = 2_000;
private final ObjectMapper json;
- private final SensitiveDataMasker masker;
-
- public ToolExecutionResultFormatter(ObjectMapper json, SensitiveDataMasker masker) {
+ public ToolExecutionResultFormatter(ObjectMapper json) {
this.json = json;
- this.masker = masker;
}
/**
@@ -71,7 +67,7 @@ public class ToolExecutionResultFormatter {
}
public ToolExecutionResult fromJson(String toolName, JsonNode parsed, long sizeBytes) {
- JsonNode masked = masker.mask(parsed);
+ JsonNode masked = parsed;
if (masked.isObject()) {
ObjectNode object = (ObjectNode) masked;
if (object.path("isError").asBoolean(false) || object.has("error") || object.has("failureType")) {