{"record":{"id":"841473677d535cf8","repo":"signalapp/Signal-Server","slug":"must-not-use-authenticated-connection-for-call-qua","errorCode":null,"errorMessage":"must not use authenticated connection for call quality survey submissions","messagePattern":"must not use authenticated connection for call quality survey submissions","errorType":"http","errorClass":"ForbiddenException","httpStatus":403,"severity":"error","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/controllers/CallQualitySurveyController.java","lineNumber":62,"sourceCode":"\n  @PUT\n  @Consumes(MediaType.APPLICATION_OCTET_STREAM)\n  @Produces(MediaType.APPLICATION_JSON)\n  @Operation(summary = \"Submit survey response\", description = \"Submits a call quality survey response\")\n  @ApiResponse(responseCode = \"204\", description = \"The survey response was submitted successfully\")\n  @ApiResponse(responseCode = \"422\", description = \"The survey response could not be parsed\")\n  @ApiResponse(responseCode = \"429\", description = \"Too many attempts\", headers = @Header(\n      name = \"Retry-After\",\n      description = \"If present, an positive integer indicating the number of seconds before a subsequent attempt could succeed\"))\n  @RateLimitedByIp(RateLimiters.For.SUBMIT_CALL_QUALITY_SURVEY)\n  public void submitCallQualitySurvey(@Auth final Optional<AuthenticatedDevice> authenticatedDevice,\n      @RequestBody(description = \"A serialized survey response protobuf entity\")\n      @NotNull final byte[] surveyResponse,\n      @HeaderParam(HttpHeaders.USER_AGENT) final String userAgentString,\n      @Context final ContainerRequestContext requestContext) {\n\n    if (authenticatedDevice.isPresent()) {\n      throw new ForbiddenException(\"must not use authenticated connection for call quality survey submissions\");\n    }\n\n    final SubmitCallQualitySurveyRequest submitCallQualitySurveyRequest;\n\n    try {\n      submitCallQualitySurveyRequest = SubmitCallQualitySurveyRequest.parseFrom(surveyResponse);\n    } catch (final InvalidProtocolBufferException e) {\n      throw new WebApplicationException(\"Invalid protobuf entity\", 422);\n    }\n\n    final String remoteAddress = (String) requestContext.getProperty(RemoteAddressFilter.REMOTE_ADDRESS_ATTRIBUTE_NAME);\n\n    try {\n      callQualitySurveyManager.submitCallQualitySurvey(submitCallQualitySurveyRequest, remoteAddress, userAgentString);\n    } catch (final CallQualityInvalidArgumentsException e) {\n      throw new WebApplicationException(e.getMessage(), 422);\n    }\n  }","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/controllers/CallQualitySurveyController.java#L44-L80","documentation":"CallQualitySurveyController.submitCallQualitySurvey is designed for anonymous submissions only. If the request contains an authenticated device (session/basic auth), the controller throws ForbiddenException (HTTP 403) to guarantee survey submissions cannot be linked to an account.","triggerScenarios":"POST to the call quality survey endpoint while logged in over the same connection — authenticatedDevice is present.","commonSituations":"In-app clients reusing the main account's authenticated HTTP client for the survey upload; proxies injecting credentials.","solutions":["Submit the survey from a connection without any account credentials","Use a separate anonymous HTTP client for survey endpoints","Verify no auth cookies/headers are auto-attached by the transport layer"],"exampleFix":"// before\naccountClient.post(\"/v1/call_quality/...\", surveyBytes);\n// after\nnewAnonymousClient().post(\"/v1/call_quality/...\", surveyBytes);","handlingStrategy":"validation","validationCode":"if (client.hasStoredCredentials()) throw new IllegalStateException(\"survey submission must be anonymous\");","typeGuard":null,"tryCatchPattern":"try { submitSurvey(bytes); } catch (ForbiddenException e) { switchToAnonymousClientAndResubmit(); }","preventionTips":["Dedicate an anonymous transport for survey uploads","Log out / clear auth headers before survey submission","Privacy: never send surveys over authenticated sessions"],"tags":["http-403","authentication","survey","privacy"],"backgroundTag":"permission-denied","analyzedSha":"100ab61c82627582c867d19e1c0561ba2781e927","analyzedAt":"2026-09-09T13:29:47.883Z","contentChangedAt":"2026-09-09T13:29:47.883Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}