This is an automated email from the ASF dual-hosted git repository.

jongyoul pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/zeppelin.git


The following commit(s) were added to refs/heads/master by this push:
     new ddb8bc6f35 [MINOR] Prevent WebSocket tickets from being logged
ddb8bc6f35 is described below

commit ddb8bc6f35cdc6babd69150c447223a4068dd53a
Author: Jongyoul Lee <[email protected]>
AuthorDate: Sat Aug 8 19:54:29 2026 +0900

    [MINOR] Prevent WebSocket tickets from being logged
    
    ### What is this PR for?
    
    This fixes CVE-2026-44614, where the WebSocket bearer ticket could be 
written to server or browser diagnostic logs in plaintext.
    
    The login and security ticket endpoints no longer log their complete 
ticket-bearing responses. Server-side WebSocket handling now logs only 
non-secret operation, principal, success, and error type metadata instead of 
the complete message, payload, or exception details. The active Angular SDK and 
classic UI likewise log only operation/principal metadata rather than complete 
WebSocket messages.
    
    Ticket response bodies, authentication behavior, and the WebSocket wire 
protocol remain unchanged. Both clients still attach the bearer ticket to 
outbound WebSocket frames, but no longer copy it or the complete payload to the 
browser console.
    
    ### What type of PR is it?
    
    Hot Fix
    
    ### Todos
    
    * [x] Remove ticket-bearing REST response logging
    * [x] Remove raw WebSocket message logging at DEBUG, TRACE, and ERROR
    * [x] Remove ticket-bearing WebSocket message logging from both browser 
clients
    * [x] Add regression coverage for login, `/api/security/ticket`, WebSocket 
failures, and classic-client console logging
    
    ### What is the Jira issue?
    
    Not applicable. This addresses 
[CVE-2026-44614](https://lists.apache.org/thread/zlphtbzb8p2sr604597kldpjhhqktsbz).
    
    ### How should this be tested?
    
    * `./mvnw -pl zeppelin-server --am 
-Dtest=SecurityRestApiTest,NotebookServerLoggingTest,NotebookServerTest 
-Dsurefire.failIfNoSpecifiedTests=false -Dmaven.gitcommitid.skip=true test`
      * 27 tests passed with no failures, errors, or skips.
    * `cd zeppelin-web && npm run karma-test`
      * 184 tests passed, including the two new WebSocket console regression 
tests.
    * `cd zeppelin-web-angular && npm run build-project:sdk`
      * SDK build passed.
    * Focused ESLint and Prettier checks passed for both changed clients.
    * A built-SDK sentinel smoke test confirmed that ticket and payload remain 
in the transmitted message but are absent from browser console arguments.
    * Apache RAT passed for `zeppelin-server`, `zeppelin-web-angular`, and 
`zeppelin-web` with no unapproved licenses.
    * `git diff --check apache/master...HEAD`
    
    The standalone `checkstyle:check` command reports the same existing 
baseline on clean `apache/master` and this branch: 1,795 repository-wide 
violations and 103 violations when restricted to the original server files. No 
violation is reported on a line added or modified by this PR.
    
    ### Screenshots (if appropriate)
    
    Not applicable.
    
    ### Questions:
    
    * Does the license files need to update? No.
    * Is there breaking changes for older versions? No.
    * Does this needs documentation? No.
    
    
    Closes #5388 from jongyoul/codex/cve-2026-44614-ticket-log-redaction.
    
    Signed-off-by: Jongyoul Lee <[email protected]>
---
 .../org/apache/zeppelin/rest/LoginRestApi.java     |   3 +-
 .../org/apache/zeppelin/rest/SecurityRestApi.java  |   3 +-
 .../org/apache/zeppelin/socket/NotebookServer.java |  22 ++--
 .../apache/zeppelin/rest/SecurityRestApiTest.java  | 123 +++++++++++++++++++
 .../zeppelin/socket/NotebookServerLoggingTest.java | 133 +++++++++++++++++++++
 .../projects/zeppelin-sdk/src/message.ts           |   4 +-
 .../websocket/websocket-event.factory.js           |   4 +-
 .../websocket/websocket-event.factory.test.js      |  94 +++++++++++++++
 8 files changed, 371 insertions(+), 15 deletions(-)

diff --git 
a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/LoginRestApi.java 
b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/LoginRestApi.java
index d8b8c93b93..b0e3f14c3e 100644
--- a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/LoginRestApi.java
+++ b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/LoginRestApi.java
@@ -242,7 +242,8 @@ public class LoginRestApi extends AbstractRestApi {
       response = new JsonResponse<>(Response.Status.FORBIDDEN, "", null);
     }
 
-    LOGGER.info(response.toString());
+    LOGGER.info("Login request completed: principal={}, success={}",
+        userName, response.getCode() == Response.Status.OK);
     return response.build();
   }
 
diff --git 
a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/SecurityRestApi.java 
b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/SecurityRestApi.java
index 5eec6e7713..f145f8b1f6 100644
--- 
a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/SecurityRestApi.java
+++ 
b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/SecurityRestApi.java
@@ -80,7 +80,8 @@ public class SecurityRestApi extends AbstractRestApi {
     data.put("ticket", ticketEntry.getTicket());
 
     JsonResponse<Map<String, String>> response = new 
JsonResponse<>(Response.Status.OK, "", data);
-    LOGGER.warn("{}", response);
+    LOGGER.info("WebSocket ticket request completed: principal={}, 
success=true",
+        ticketEntry.getPrincipal());
     return response.build();
   }
 
diff --git 
a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java 
b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
index 090272ce5d..2d78ae7fb5 100644
--- 
a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
+++ 
b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
@@ -274,17 +274,15 @@ public class NotebookServer implements 
AngularObjectRegistryListener,
   }
 
   public void onMessage(NotebookSocket conn, String msg) {
+    Message receivedMessage = null;
     try {
-      Message receivedMessage = deserializeMessage(msg);
+      receivedMessage = deserializeMessage(msg);
       if (receivedMessage.op != OP.PING) {
-        LOGGER.debug("RECEIVE: " + receivedMessage.op +
-            ", RECEIVE PRINCIPAL: " + receivedMessage.principal +
-            ", RECEIVE ROLES: " + receivedMessage.roles +
-            ", RECEIVE DATA: " + receivedMessage.data);
-      }
-      if (LOGGER.isTraceEnabled()) {
-        LOGGER.trace("RECEIVE MSG = " + receivedMessage);
+        LOGGER.debug("WebSocket message received: operation={}, principal={}",
+            receivedMessage.op, receivedMessage.principal);
       }
+      LOGGER.trace("WebSocket message processing started: operation={}, 
principal={}",
+          receivedMessage.op, receivedMessage.principal);
 
       TicketContainer.Entry ticketEntry = 
TicketContainer.instance.getTicketEntry(receivedMessage.principal);
       if (ticketEntry == null || StringUtils.isEmpty(ticketEntry.getTicket())) 
{
@@ -485,7 +483,13 @@ public class NotebookServer implements 
AngularObjectRegistryListener,
           break;
       }
     } catch (Exception e) {
-      LOGGER.error("Can't handle message: {}", msg, e);
+      String operation = receivedMessage == null || receivedMessage.op == null
+          ? "unknown" : receivedMessage.op.name();
+      String principal = receivedMessage == null || 
StringUtils.isEmpty(receivedMessage.principal)
+          ? "unknown" : receivedMessage.principal;
+      LOGGER.error("WebSocket message handling completed: operation={}, 
principal={}, "
+              + "success=false, errorType={}",
+          operation, principal, e.getClass().getSimpleName());
       try {
         conn.send(serializeMessage(new Message(OP.ERROR_INFO).put("info", 
e.getMessage())));
       } catch (IOException iox) {
diff --git 
a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/SecurityRestApiTest.java
 
b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/SecurityRestApiTest.java
index 25912b063f..960bc0cdbd 100644
--- 
a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/SecurityRestApiTest.java
+++ 
b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/SecurityRestApiTest.java
@@ -19,9 +19,18 @@ package org.apache.zeppelin.rest;
 import com.google.gson.Gson;
 import com.google.gson.reflect.TypeToken;
 
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
 import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.message.BasicNameValuePair;
 import org.apache.http.util.EntityUtils;
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.spi.LoggingEvent;
 import org.apache.zeppelin.MiniZeppelinServer;
+import org.apache.zeppelin.ticket.TicketContainer;
 import org.hamcrest.CoreMatchers;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
@@ -30,10 +39,14 @@ import org.junit.jupiter.api.Test;
 
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 class SecurityRestApiTest extends AbstractTestRestApi {
   Gson gson = new Gson();
@@ -69,6 +82,80 @@ class SecurityRestApiTest extends AbstractTestRestApi {
     get.close();
   }
 
+  @Test
+  void testLoginTicketIsNotLogged() throws IOException {
+    String principal = "user1";
+    TicketContainer.instance.removeTicket(principal);
+    TestAppender appender = new TestAppender();
+    Logger logger = Logger.getLogger(LoginRestApi.class);
+    Level previousLevel = logger.getLevel();
+    boolean previousAdditivity = logger.getAdditivity();
+    logger.setLevel(Level.TRACE);
+    logger.setAdditivity(false);
+    logger.addAppender(appender);
+
+    try {
+      HttpPost login = new HttpPost(getUrlToTest(zConf) + "/login");
+      login.addHeader("Origin", getUrlToTest(zConf));
+      List<NameValuePair> parameters = new ArrayList<>();
+      parameters.add(new BasicNameValuePair("password", "password2"));
+      parameters.add(new BasicNameValuePair("userName", principal));
+      login.setEntity(new UrlEncodedFormEntity(parameters, 
StandardCharsets.UTF_8));
+
+      try (CloseableHttpResponse post = getHttpClient().execute(login)) {
+        Map<String, Object> resp = gson.fromJson(
+            EntityUtils.toString(post.getEntity(), StandardCharsets.UTF_8),
+            new TypeToken<Map<String, Object>>(){}.getType());
+        Map<String, String> body = (Map<String, String>) resp.get("body");
+        String ticket = body.get("ticket");
+        assertThat("Login response ticket", ticket, 
CoreMatchers.notNullValue());
+        assertThat("Login response ticket", ticket, 
CoreMatchers.not("anonymous"));
+        assertTrue(appender.contains("principal=" + principal));
+        assertTrue(appender.contains("success=true"));
+        assertFalse(appender.contains(ticket), "Login logs must not contain 
the ticket");
+      }
+    } finally {
+      logger.removeAppender(appender);
+      logger.setLevel(previousLevel);
+      logger.setAdditivity(previousAdditivity);
+      appender.close();
+      TicketContainer.instance.removeTicket(principal);
+    }
+  }
+
+  @Test
+  void testSecurityTicketIsNotLogged() throws IOException {
+    String principal = "user2";
+    TicketContainer.instance.removeTicket(principal);
+    TestAppender appender = new TestAppender();
+    Logger logger = Logger.getLogger(SecurityRestApi.class);
+    Level previousLevel = logger.getLevel();
+    boolean previousAdditivity = logger.getAdditivity();
+    logger.setLevel(Level.TRACE);
+    logger.setAdditivity(false);
+    logger.addAppender(appender);
+
+    try (CloseableHttpResponse get =
+             httpGet("/security/ticket", principal, "password3")) {
+      Map<String, Object> resp = gson.fromJson(
+          EntityUtils.toString(get.getEntity(), StandardCharsets.UTF_8),
+          new TypeToken<Map<String, Object>>(){}.getType());
+      Map<String, String> body = (Map<String, String>) resp.get("body");
+      String ticket = body.get("ticket");
+      assertThat("Security response ticket", ticket, 
CoreMatchers.notNullValue());
+      assertThat("Security response ticket", ticket, 
CoreMatchers.not("anonymous"));
+      assertTrue(appender.contains("principal=" + principal));
+      assertTrue(appender.contains("success=true"));
+      assertFalse(appender.contains(ticket), "Security ticket logs must not 
contain the ticket");
+    } finally {
+      logger.removeAppender(appender);
+      logger.setLevel(previousLevel);
+      logger.setAdditivity(previousAdditivity);
+      appender.close();
+      TicketContainer.instance.removeTicket(principal);
+    }
+  }
+
   @Test
   void testGetUserList() throws IOException {
     CloseableHttpResponse get = httpGet("/security/userlist/admi", "admin", 
"password1");
@@ -119,4 +206,40 @@ class SecurityRestApiTest extends AbstractTestRestApi {
     get.close();
   }
 
+  private static class TestAppender extends AppenderSkeleton {
+    private final List<LoggingEvent> events = new CopyOnWriteArrayList<>();
+
+    @Override
+    protected void append(LoggingEvent event) {
+      events.add(event);
+    }
+
+    boolean contains(String value) {
+      for (LoggingEvent event : events) {
+        String message = event.getRenderedMessage();
+        if (message != null && message.contains(value)) {
+          return true;
+        }
+        String[] throwable = event.getThrowableStrRep();
+        if (throwable != null) {
+          for (String line : throwable) {
+            if (line.contains(value)) {
+              return true;
+            }
+          }
+        }
+      }
+      return false;
+    }
+
+    @Override
+    public void close() {
+    }
+
+    @Override
+    public boolean requiresLayout() {
+      return false;
+    }
+  }
+
 }
diff --git 
a/zeppelin-server/src/test/java/org/apache/zeppelin/socket/NotebookServerLoggingTest.java
 
b/zeppelin-server/src/test/java/org/apache/zeppelin/socket/NotebookServerLoggingTest.java
new file mode 100644
index 0000000000..cd19dd8c6d
--- /dev/null
+++ 
b/zeppelin-server/src/test/java/org/apache/zeppelin/socket/NotebookServerLoggingTest.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zeppelin.socket;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.Level;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.zeppelin.common.Message;
+import org.apache.zeppelin.common.Message.OP;
+import org.apache.zeppelin.conf.ZeppelinConfiguration;
+import org.apache.zeppelin.ticket.TicketContainer;
+import org.junit.jupiter.api.Test;
+
+class NotebookServerLoggingTest {
+
+  @Test
+  void testWebSocketTicketIsNotLoggedOnMessageFailure() {
+    String principal = "ticket-log-test-" + UUID.randomUUID();
+    TicketContainer.Entry ticketEntry =
+        TicketContainer.instance.getTicketEntry(principal, 
Collections.emptySet());
+    String ticket = ticketEntry.getTicket();
+    String sensitivePayload = "sensitive-payload-" + UUID.randomUUID();
+
+    ZeppelinConfiguration zConf = mock(ZeppelinConfiguration.class);
+    when(zConf.isAnonymousAllowed()).thenReturn(true);
+    NotebookServer notebookServer = new NotebookServer();
+    notebookServer.setZeppelinConfiguration(zConf);
+    NotebookSocket conn = mock(NotebookSocket.class);
+    when(conn.getUser()).thenReturn(principal);
+
+    Message message = new Message(OP.CONVERT_NOTE_NBFORMAT)
+        .put("ticketCopy", ticket)
+        .put("sensitivePayload", sensitivePayload);
+    message.principal = principal;
+    message.roles = "[]";
+    message.ticket = ticket;
+
+    TestAppender appender = new TestAppender();
+    org.apache.log4j.Logger logger = 
org.apache.log4j.Logger.getLogger(NotebookServer.class);
+    Level previousLevel = logger.getLevel();
+    boolean previousAdditivity = logger.getAdditivity();
+    logger.setLevel(Level.TRACE);
+    logger.setAdditivity(false);
+    logger.addAppender(appender);
+
+    try {
+      notebookServer.onMessage(conn, message.toJson());
+
+      assertTrue(appender.hasLevel(Level.ERROR), "The WebSocket error path 
must be exercised");
+      assertTrue(appender.containsMessage("operation=" + 
OP.CONVERT_NOTE_NBFORMAT));
+      assertTrue(appender.containsMessage("principal=" + principal));
+      assertFalse(appender.contains(ticket), "WebSocket logs must not contain 
the ticket");
+      assertFalse(appender.contains(sensitivePayload),
+          "WebSocket logs must not contain message payload data");
+    } finally {
+      logger.removeAppender(appender);
+      logger.setLevel(previousLevel);
+      logger.setAdditivity(previousAdditivity);
+      appender.close();
+      TicketContainer.instance.removeTicket(principal);
+    }
+  }
+
+  private static class TestAppender extends AppenderSkeleton {
+    private final List<LoggingEvent> events = new CopyOnWriteArrayList<>();
+
+    @Override
+    protected void append(LoggingEvent event) {
+      events.add(event);
+    }
+
+    boolean hasLevel(Level level) {
+      return events.stream().anyMatch(event -> level.equals(event.getLevel()));
+    }
+
+    boolean containsMessage(String value) {
+      return events.stream()
+          .map(LoggingEvent::getRenderedMessage)
+          .anyMatch(message -> message != null && message.contains(value));
+    }
+
+    boolean contains(String value) {
+      for (LoggingEvent event : events) {
+        String message = event.getRenderedMessage();
+        if (message != null && message.contains(value)) {
+          return true;
+        }
+        String[] throwable = event.getThrowableStrRep();
+        if (throwable != null) {
+          for (String line : throwable) {
+            if (line.contains(value)) {
+              return true;
+            }
+          }
+        }
+      }
+      return false;
+    }
+
+    @Override
+    public void close() {
+    }
+
+    @Override
+    public boolean requiresLayout() {
+      return false;
+    }
+  }
+}
diff --git a/zeppelin-web-angular/projects/zeppelin-sdk/src/message.ts 
b/zeppelin-web-angular/projects/zeppelin-sdk/src/message.ts
index 42821062eb..6262bff26a 100644
--- a/zeppelin-web-angular/projects/zeppelin-sdk/src/message.ts
+++ b/zeppelin-web-angular/projects/zeppelin-sdk/src/message.ts
@@ -126,7 +126,7 @@ export class Message {
         retryWhen(errors => errors.pipe(mergeMap(() => 
this.close$.pipe(take(1), delay(4000)))))
       )
       .subscribe(e => {
-        console.log('Receive:', e);
+        console.log('Receive:', e.op);
         this.received$.next(this.interceptReceived(e as 
WebSocketMessage<MessageReceiveDataTypeMap>));
       });
   }
@@ -166,7 +166,7 @@ export class Message {
       data,
       ...this.ticket
     };
-    console.log('Send:', message);
+    console.log('Send:', message.op, message.principal);
 
     this.ws.next(message);
     this.sent$.next(message);
diff --git a/zeppelin-web/src/components/websocket/websocket-event.factory.js 
b/zeppelin-web/src/components/websocket/websocket-event.factory.js
index 36e94231e5..18b2affb26 100644
--- a/zeppelin-web/src/components/websocket/websocket-event.factory.js
+++ b/zeppelin-web/src/components/websocket/websocket-event.factory.js
@@ -45,7 +45,7 @@ function WebsocketEventFactory($rootScope, $websocket, 
$location, baseUrlSrv, sa
     }
 
     data.msgId = uniqueClientId + '-' + ++lastMsgIdSeqSent;
-    console.log('Send >> %o, %o, %o, %o, %o', data.op, data.principal, 
data.ticket, data.roles, data);
+    console.log('Send >> %o, %o', data.op, data.principal);
     return websocketCalls.ws.send(JSON.stringify(data));
   };
 
@@ -59,7 +59,7 @@ function WebsocketEventFactory($rootScope, $websocket, 
$location, baseUrlSrv, sa
       payload = angular.fromJson(event.data);
     }
 
-    console.log('Receive << %o, %o', payload.op, payload);
+    console.log('Receive << %o', payload.op);
 
     let op = payload.op;
     let data = payload.data;
diff --git 
a/zeppelin-web/src/components/websocket/websocket-event.factory.test.js 
b/zeppelin-web/src/components/websocket/websocket-event.factory.test.js
new file mode 100644
index 0000000000..1d95f63980
--- /dev/null
+++ b/zeppelin-web/src/components/websocket/websocket-event.factory.test.js
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+describe('Factory: websocketEvents', function() {
+  let fakeWebsocket;
+  let messageCallback;
+  let ngToast;
+  let rootScope;
+  let websocketEvents;
+
+  beforeEach(function() {
+    fakeWebsocket = {
+      onOpen: jasmine.createSpy('onOpen'),
+      onMessage: function(callback) {
+        messageCallback = callback;
+      },
+      onError: jasmine.createSpy('onError'),
+      onClose: jasmine.createSpy('onClose'),
+      send: jasmine.createSpy('send'),
+      socket: {readyState: 1},
+    };
+    ngToast = {info: jasmine.createSpy('info')};
+
+    angular.mock.module('zeppelinWebApp', function($provide) {
+      $provide.value('$websocket', function() {
+        return fakeWebsocket;
+      });
+      $provide.value('baseUrlSrv', {getWebsocketUrl: function() {
+        return 'ws://localhost/ws';
+      }});
+      $provide.value('saveAsService', {saveAs: angular.noop});
+      $provide.value('ngToast', ngToast);
+    });
+  });
+
+  beforeEach(inject(function($rootScope, _websocketEvents_) {
+    rootScope = $rootScope;
+    websocketEvents = _websocketEvents_;
+  }));
+
+  it('does not log the ticket or payload when sending a message', function() {
+    const ticket = 'websocket-ticket-secret';
+    const payloadSecret = 'paragraph-payload-secret';
+    rootScope.ticket = {
+      principal: 'test-user',
+      ticket: ticket,
+      roles: '["users"]',
+    };
+    spyOn(console, 'log');
+
+    websocketEvents.sendNewEvent({
+      op: 'RUN_PARAGRAPH',
+      data: {paragraph: payloadSecret},
+    });
+
+    const sentMessage = 
JSON.parse(fakeWebsocket.send.calls.mostRecent().args[0]);
+    expect(sentMessage.ticket).toBe(ticket);
+    expect(sentMessage.data.paragraph).toBe(payloadSecret);
+    expect(console.log).toHaveBeenCalledWith('Send >> %o, %o', 
'RUN_PARAGRAPH', 'test-user');
+    const consoleOutput = JSON.stringify(console.log.calls.allArgs());
+    expect(consoleOutput).not.toContain(ticket);
+    expect(consoleOutput).not.toContain(payloadSecret);
+  });
+
+  it('does not log the payload when receiving a message', function() {
+    const payloadSecret = 'notice-payload-secret';
+    spyOn(console, 'log');
+
+    messageCallback({
+      data: JSON.stringify({
+        op: 'NOTICE',
+        data: {notice: payloadSecret},
+      }),
+    });
+
+    expect(ngToast.info).toHaveBeenCalledWith(payloadSecret);
+    expect(console.log).toHaveBeenCalledWith('Receive << %o', 'NOTICE');
+    
expect(JSON.stringify(console.log.calls.allArgs())).not.toContain(payloadSecret);
+  });
+});

Reply via email to