Repository: pinpoint-apm/pinpoint
Issue: Add an option to hide stack trace from web response
Labels: enhancement
module:web

#### Description
Stack trace could be considered too sensitive to be exposed to users.
Giving an option to hide stack trace when an internal server error occurs in pinpoint web would be simple to implement.

Issue comments:
merged in 1.8.1-SNAPSHOT

Pull request review comments:

Patch that resolved the issue:
diff --git a/quickstart/web/src/main/resources/pinpoint-web.properties b/quickstart/web/src/main/resources/pinpoint-web.properties
index 400f157531cd..62273edece7b 100644
--- a/quickstart/web/src/main/resources/pinpoint-web.properties
+++ b/quickstart/web/src/main/resources/pinpoint-web.properties
@@ -28,6 +28,7 @@ config.sendUsage=true
 config.editUserInfo=true
 config.show.activeThread=true
 config.show.activeThreadDump=true
+config.show.stackTraceOnError=true
 config.enable.activeThreadDump=true
 
 web.hbase.selectSpans.limit=500
diff --git a/web/src/main/java/com/navercorp/pinpoint/web/config/ConfigProperties.java b/web/src/main/java/com/navercorp/pinpoint/web/config/ConfigProperties.java
index 92af34f85214..10fadb418139 100644
--- a/web/src/main/java/com/navercorp/pinpoint/web/config/ConfigProperties.java
+++ b/web/src/main/java/com/navercorp/pinpoint/web/config/ConfigProperties.java
@@ -45,13 +45,16 @@ public class ConfigProperties {
 
     @Value("#{pinpointWebProps['config.openSource'] ?: true}")
     private boolean openSource;
-    
+
     @Value("#{pinpointWebProps['security.guide.url']}")
     private String securityGuideUrl;
 
     @Value("#{pinpointWebProps['config.show.applicationStat'] ?: false}")
     private boolean showApplicationStat;
 
+    @Value("#{pinpointWebProps['config.show.stackTraceOnError'] ?: true}")
+    private boolean showStackTraceOnError;
+
     @Value("#{pinpointWebProps['websocket.allowedOrigins']}")
     private String webSocketAllowedOrigins;
 
@@ -91,6 +94,10 @@ public boolean isShowApplicationStat() {
         return this.showApplicationStat;
     }
 
+    public boolean isShowStackTraceOnError() {
+        return showStackTraceOnError;
+    }
+
     public String getWebSocketAllowedOrigins() {
         return webSocketAllowedOrigins;
     }
@@ -107,6 +114,7 @@ public String toString() {
         sb.append(", openSource=").append(openSource);
         sb.append(", securityGuideUrl='").append(securityGuideUrl).append('\'');
         sb.append(", showApplicationStat=").append(showApplicationStat);
+        sb.append(", showStackTraceOnError=").append(showStackTraceOnError);
         sb.append(", webSocketAllowedOrigins=").append(webSocketAllowedOrigins);
         sb.append('}');
         return sb.toString();
diff --git a/web/src/main/java/com/navercorp/pinpoint/web/controller/ConfigController.java b/web/src/main/java/com/navercorp/pinpoint/web/controller/ConfigController.java
index fec44e0dbeb0..b25ba225f03d 100644
--- a/web/src/main/java/com/navercorp/pinpoint/web/controller/ConfigController.java
+++ b/web/src/main/java/com/navercorp/pinpoint/web/controller/ConfigController.java
@@ -59,6 +59,7 @@ public Map<String, Object> getProperties(@RequestHeader(value=SSO_USER, required
         result.put("showActiveThreadDump", webProperties.isShowActiveThreadDump());
         result.put("enableServerMapRealTime", webProperties.isEnableServerMapRealTime());
         result.put("showApplicationStat", webProperties.isShowApplicationStat());
+        result.put("showStackTraceOnError", webProperties.isShowStackTraceOnError());
         result.put("openSource", webProperties.isOpenSource());
         result.put("version", Version.VERSION);
 
diff --git a/web/src/main/java/com/navercorp/pinpoint/web/controller/ControllerExceptionHandler.java b/web/src/main/java/com/navercorp/pinpoint/web/controller/ControllerExceptionHandler.java
index 2ff14fcb7f1a..bef8e0f8bea0 100644
--- a/web/src/main/java/com/navercorp/pinpoint/web/controller/ControllerExceptionHandler.java
+++ b/web/src/main/java/com/navercorp/pinpoint/web/controller/ControllerExceptionHandler.java
@@ -26,8 +26,10 @@
 
 import javax.servlet.http.HttpServletRequest;
 
+import com.navercorp.pinpoint.web.config.ConfigProperties;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.AccessDeniedException;
 import org.springframework.web.bind.annotation.ControllerAdvice;
 import org.springframework.web.bind.annotation.ExceptionHandler;
@@ -45,6 +47,8 @@ public class ControllerExceptionHandler {
 
     private final Logger logger = LoggerFactory.getLogger(this.getClass());
 
+    @Autowired
+    private ConfigProperties webProperties;
 
     @ExceptionHandler(value = Exception.class)
     public ModelAndView defaultErrorHandler(HttpServletRequest request, Exception exception) throws Exception {
@@ -66,7 +70,9 @@ private Map<String, Object> createExceptionResource(HttpServletRequest request,
         Map<String, Object> exceptionMap = new HashMap<>();
 
         exceptionMap.put("message", getExceptionMessage(throwable));
-        exceptionMap.put("stacktrace", getExceptionStackTrace(throwable));
+        if (webProperties.isShowStackTraceOnError()) {
+            exceptionMap.put("stacktrace", getExceptionStackTrace(throwable));
+        }
         exceptionMap.put("request", createRequestResource(request));
 
         return exceptionMap;
diff --git a/web/src/main/resources/pinpoint-web.properties b/web/src/main/resources/pinpoint-web.properties
index ccf36a0fc854..818877d21591 100644
--- a/web/src/main/resources/pinpoint-web.properties
+++ b/web/src/main/resources/pinpoint-web.properties
@@ -36,6 +36,7 @@ config.show.activeThread=true
 config.show.activeThreadDump=true
 config.enable.activeThreadDump=true
 config.show.applicationStat=false
+config.show.stackTraceOnError=true
 
 # This option does not work because it is under development
 config.enable.serverMapRealTime=false


Test patch: