{"record":{"id":"b795d1995cb09a72","repo":"postalserver/postal","slug":"invalid-endpoint-class-name-class-name","errorCode":null,"errorMessage":"Invalid endpoint class name '#{class_name}'","messagePattern":"Invalid endpoint class name '#(.+?)'","errorType":"exception","errorClass":"Postal::Error","httpStatus":500,"severity":"error","filePath":"app/models/route.rb","lineNumber":80,"sourceCode":"    end\n  end\n\n  def _endpoint\n    if mode == \"Endpoint\"\n      @endpoint ||= endpoint ? \"#{endpoint.class}##{endpoint.uuid}\" : nil\n    else\n      @endpoint ||= mode\n    end\n  end\n\n  def _endpoint=(value)\n    if value.blank?\n      self.endpoint = nil\n      self.mode = nil\n    elsif value =~ /\\#/\n      class_name, id = value.split(\"#\", 2)\n      unless ENDPOINT_TYPES.include?(class_name)\n        raise Postal::Error, \"Invalid endpoint class name '#{class_name}'\"\n      end\n\n      self.endpoint = class_name.constantize.find_by_uuid(id)\n      self.mode = \"Endpoint\"\n    else\n      self.endpoint = nil\n      self.mode = value\n    end\n  end\n\n  def forward_address\n    @forward_address ||= \"#{token}@#{Postal::Config.dns.route_domain}\"\n  end\n\n  def wildcard?\n    name == \"*\"\n  end\n","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/postalserver/postal/blob/d038eaa8c763d3cafa797ccd6f773d53470bd336/app/models/route.rb#L62-L98","documentation":"Route's virtual attribute _endpoint= accepts strings of the form 'ClassName#uuid' (what the admin UI and API submit). It splits on '#' and requires the class part to be one of Route::ENDPOINT_TYPES (SMTPEndpoint, HTTPEndpoint, AddressEndpoint); anything else raises Postal::Error before constantize is attempted, preventing arbitrary class instantiation from user input.","triggerScenarios":"Assigning route.endpoint_attributes/_endpoint a value like 'HttpEndpoint#...' (JS-style casing), 'CredentialEndpoint#...', 'SMTPServer#...', or a plain string containing a '#' with a made-up prefix; typically via the routes form/API when creating or updating a route for a server.","commonSituations":"API clients guessing or hard-coding type names with wrong casing; client code written against older/renamed endpoint class names; hand-built payload strings where the uuid separator or class name is malformed; admin UI tampering.","solutions":["Use exactly one of SMTPEndpoint, HTTPEndpoint, AddressEndpoint as the class prefix","Build the reference from the model rather than by hand: \"#{endpoint.class.name}##{endpoint.uuid}\"","Read the allowed set programmatically instead of hard-coding: Route::ENDPOINT_TYPES","Validate the string client-side/admin-side before assignment (see typeGuard)"],"exampleFix":"# before\nroute._endpoint = \"HttpEndpoint##{http_endpoint.uuid}\"  # wrong casing -> Postal::Error\n\n# after\nroute._endpoint = \"HTTPEndpoint##{http_endpoint.uuid}\"\n# or derive it: http_endpoint._endpoint / \"#{endpoint.class.name}##{endpoint.uuid}\"","handlingStrategy":"type-guard","validationCode":"# before assigning route._endpoint\nclass_name, id = value.split(\"#\", 2)\nraise ArgumentError, \"Unknown endpoint type #{class_name}\" unless Route::ENDPOINT_TYPES.include?(class_name) && id.present?","typeGuard":"def valid_endpoint_reference?(value)\n  return false unless value.is_a?(String) && value.include?(\"#\")\n  class_name, id = value.split(\"#\", 2)\n  Route::ENDPOINT_TYPES.include?(class_name) && id.present?\nend","tryCatchPattern":"begin\n  route._endpoint = value\nrescue Postal::Error => e\n  # client-supplied type name: reject with 422 echoing the allowed list\n  render json: { error: e.message, allowed: Route::ENDPOINT_TYPES }, status: :unprocessable_entity\nend","preventionTips":["Never hand-write 'Class#uuid' strings; derive them: \"#{endpoint.class.name}##{endpoint.uuid}\"","Reference Route::ENDPOINT_TYPES instead of duplicating the list in client code","Add a form/API whitelist so unknown type names fail validation, not with a 500","After upgrades, re-check that stored type names still appear in ENDPOINT_TYPES"],"tags":["postal","routes","polymorphic","input-validation","api"],"backgroundTag":"invalid-polymorphic-type","analyzedSha":"d038eaa8c763d3cafa797ccd6f773d53470bd336","analyzedAt":"2026-08-21T13:52:57.446Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}