{"record":{"id":"20ad34bf70521071","repo":"opf/openproject","slug":"unsupported-model-for-inplace-edit","errorCode":null,"errorMessage":"Unsupported model for inplace edit","messagePattern":"Unsupported model for inplace edit","errorType":"exception","errorClass":"ArgumentError","httpStatus":404,"severity":"error","filePath":"app/controllers/inplace_edit_fields_controller.rb","lineNumber":119,"sourceCode":"  end\n\n  def find_model\n    model_class = resolve_model_class(params[:model])\n    @model = model_class.visible.find(params[:id])\n  rescue ActiveRecord::RecordNotFound, ArgumentError\n    head :not_found\n  end\n\n  def resolve_model_class(model_param)\n    return nil if model_param.blank?\n\n    model_class =\n      update_registry.resolve_model_class(model_param)\n\n    unless model_class &&\n           model_class < ApplicationRecord &&\n           model_class.respond_to?(:visible)\n      raise ArgumentError, \"Unsupported model for inplace edit\"\n    end\n\n    model_class\n  end\n\n  def set_attribute\n    @attribute = params[:attribute].to_sym\n  end\n\n  def authorize_project_custom_field_visibility!\n    return unless @model.is_a?(Project)\n\n    custom_field_id = @attribute.to_s.delete_prefix(\"custom_field_\").to_i\n    unless ProjectCustomField.visible(current_user, project: @model).exists?(custom_field_id)\n      head :not_found\n    end\n  end\n","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/opf/openproject/blob/d9742c43f3424c34b63550f8c03f201fe5c3040c/app/controllers/inplace_edit_fields_controller.rb#L101-L137","documentation":"InplaceEditFieldsController#resolve_model_class takes the 'model' request parameter, camelizes it, and resolves it only against classes present in OpenProject::InplaceEdit::UpdateRegistry. The resolved class must additionally be an ApplicationRecord descendant that responds to .visible; otherwise ArgumentError('Unsupported model for inplace edit'). This whitelist exists because the endpoint would otherwise allow arbitrary attribute edits on any model.","triggerScenarios":"GET/POST /inplace_edit_fields with model=<class not in the registry> — e.g. 'user', 'milestone' or a misspelled value — or a registered class that fails the ApplicationRecord/.visible checks.","commonSituations":"Frontend code updated to offer inline edit on a new model before the backend registered it; stale JS requesting a model removed from the registry; a fork adding a non-AR model (plain class) to the registry.","solutions":["Only request inplace edits for models registered in OpenProject::InplaceEdit::UpdateRegistry (list keys in console).","If a legitimate model is missing, register it with handler and contract so it passes both resolve_model_class and invoke_update_handler.","Ensure the registered class is an ActiveRecord model exposing a .visible scope."],"exampleFix":"# before (client requests an unregistered model)\nfetch('/inplace_edit_fields?model=meeting&attribute=name')\n\n# after (guard client-side against the whitelist)\nconst SUPPORTED = ['project', 'work_package'];\nif (!SUPPORTED.includes(model)) return;\nfetch(`/inplace_edit_fields?model=${model}&attribute=name`)","handlingStrategy":"validation","validationCode":"klass = OpenProject::InplaceEdit::UpdateRegistry.resolve_model_class(params[:model])\nreturn head :not_found unless klass && klass < ApplicationRecord && klass.respond_to?(:visible)","typeGuard":"def inplace_model?(param)\n  klass = OpenProject::InplaceEdit::UpdateRegistry.resolve_model_class(param)\n  !klass.nil? && klass < ApplicationRecord && klass.respond_to?(:visible)\nend","tryCatchPattern":"begin\n  resolve_model_class(params[:model])\nrescue ArgumentError\n  head :not_found\nend","preventionTips":["Drive the frontend model list from the registry keys instead of a duplicated hardcoded list.","Never broaden resolve_model_class to arbitrary constantize — the whitelist is the security boundary.","When adding a model, register it first, then ship the UI that requests it."],"tags":["inplace-edit","whitelist","controller","parameter-validation"],"backgroundTag":"unsupported-model-type","analyzedSha":"d9742c43f3424c34b63550f8c03f201fe5c3040c","analyzedAt":"2026-08-21T14:40:06.829Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}