Summary
GET /api/v3/time_entries and GET /api/v3/cost_entries are instance/workspace-scope collection endpoints. Each entry links a Work Package through the API representer's associated_resource :work_package, which renders _links.workPackage.title (the Work Package subject) and _links.workPackage.href (the sequential Work Package id) without checking that the linked Work Package is visible to the requesting user.
The collection is authorized only by the time-entry / cost-entry project permission (view_time_entries / view_cost_entries), which is an independent project permission with no view_work_packages dependency. A user holding view_time_entries (or view_cost_entries) in a project, but not view_work_packages, therefore reads the subjects and ids of Work Packages they cannot access through the direct Work Package API (which returns 404 for those WPs).
This is the same disclosure class already fixed in GHSA-g387-6rm2-xw88 ("Private work package data disclosure through single meeting agenda item API", Medium, CWE-200/CWE-639), whose fix migrated the meeting-agenda-item representer's linked Work Package from the ungated associated_resource to the visibility-gated associated_visible_resource. The Time Entries and Cost Entries representers were not updated.
Details (all at tag v17.5.1)
The ungated leak
modules/costs/lib/api/v3/time_entries/time_entry_representer.rb:103:
associated_resource :work_package,
skip_render: ->(*) { represented.entity_type != "WorkPackage" },
link_property_name: :entity,
link_getter: :entity_id,
getter: ->(*) { represented.entity if represented.entity_type == "WorkPackage" }
modules/costs/lib/api/v3/cost_entries/cost_entry_representer.rb:48 is identical.
associated_resource (the ungated variant, lib/api/decorators/linked_resource.rb:169-197) renders the link with link_title_attribute: :name unconditionally. WorkPackage#name aliases subject (app/models/work_package.rb:358-360), so _links.workPackage.title is the Work Package subject. No visibility predicate is applied.
The fixed sibling that proves intent (GHSA-g387)
modules/meeting/lib/api/v3/meeting_agenda_items/meeting_agenda_item_representer.rb:80:
associated_visible_resource :work_package
associated_visible_resource (linked_resource.rb:199-217) adds skip_render on !visible?(current_user) and an undisclosed-link lambda. This is the canonical fix for this exact class.
The collection authorization omits the child visibility
The Index scope is TimeEntry.not_ongoing.visible(User.current) (modules/costs/app/models/queries/time_entries/time_entry_query.rb:40-46). TimeEntry.visible -> CostScopes#with_visible_entries_on (modules/costs/app/models/cost_scopes.rb:38-76) scopes only by the time-entry's own project_id via Project.allowed_to(user, :view_time_entries). It never references the linked Work Package's project or view_work_packages. view_time_entries/view_cost_entries are declared with no dependencies: :view_work_packages (modules/costs/lib/costs/engine.rb:39-41, :108-110), so a custom role can grant them while withholding view_work_packages.
The direct Work Package API enforces what the leak skips
lib/api/v3/work_packages/work_packages_api.rb:74-77:
@work_package = WorkPackage.visible.find(declared_params[:id])
authorize_in_work_package(:view_work_packages, work_package: @work_package) do
raise API::Errors::NotFound.new model: :work_package
WorkPackage.visible = allowed_to(user, :view_work_packages) (app/models/work_package.rb:85). A user without view_work_packages on the WP gets 404 on the direct path.
Impact
A project member with view_time_entries (or view_cost_entries) but not view_work_packages reads the subject and id of every Work Package referenced by a time/cost entry in that project, despite being unable to open those Work Packages directly. Work Package subjects routinely encode sensitive information. Secondary cross-project reach exists when a Work Package is moved to an inaccessible project while time entries briefly retain their original project_id (app/services/work_packages/update_service.rb:136-145).
Affected versions
<= 17.5.1 (latest release). Community Edition (modules/costs is bundled in the AGPL CE, not EE-gated).
Remediation
Replace associated_resource :work_package with associated_visible_resource :work_package in time_entry_representer.rb:103 and cost_entry_representer.rb:48 (and audit lib/api/v3/custom_actions/custom_action_execute_representer.rb:36 for the same one-line change). This mirrors the GHSA-g387 fix.
Severity
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N (Medium), parity-anchored to GHSA-g387-6rm2-xw88 (same disclosure class, same vector).
Summary
GET /api/v3/time_entriesandGET /api/v3/cost_entriesare instance/workspace-scope collection endpoints. Each entry links a Work Package through the API representer'sassociated_resource :work_package, which renders_links.workPackage.title(the Work Package subject) and_links.workPackage.href(the sequential Work Package id) without checking that the linked Work Package is visible to the requesting user.The collection is authorized only by the time-entry / cost-entry project permission (
view_time_entries/view_cost_entries), which is an independent project permission with noview_work_packagesdependency. A user holdingview_time_entries(orview_cost_entries) in a project, but notview_work_packages, therefore reads the subjects and ids of Work Packages they cannot access through the direct Work Package API (which returns 404 for those WPs).This is the same disclosure class already fixed in GHSA-g387-6rm2-xw88 ("Private work package data disclosure through single meeting agenda item API", Medium, CWE-200/CWE-639), whose fix migrated the meeting-agenda-item representer's linked Work Package from the ungated
associated_resourceto the visibility-gatedassociated_visible_resource. The Time Entries and Cost Entries representers were not updated.Details (all at tag v17.5.1)
The ungated leak
modules/costs/lib/api/v3/time_entries/time_entry_representer.rb:103:modules/costs/lib/api/v3/cost_entries/cost_entry_representer.rb:48is identical.associated_resource(the ungated variant,lib/api/decorators/linked_resource.rb:169-197) renders the link withlink_title_attribute: :nameunconditionally.WorkPackage#namealiasessubject(app/models/work_package.rb:358-360), so_links.workPackage.titleis the Work Package subject. No visibility predicate is applied.The fixed sibling that proves intent (GHSA-g387)
modules/meeting/lib/api/v3/meeting_agenda_items/meeting_agenda_item_representer.rb:80:associated_visible_resource(linked_resource.rb:199-217) addsskip_renderon!visible?(current_user)and an undisclosed-link lambda. This is the canonical fix for this exact class.The collection authorization omits the child visibility
The Index scope is
TimeEntry.not_ongoing.visible(User.current)(modules/costs/app/models/queries/time_entries/time_entry_query.rb:40-46).TimeEntry.visible->CostScopes#with_visible_entries_on(modules/costs/app/models/cost_scopes.rb:38-76) scopes only by the time-entry's ownproject_idviaProject.allowed_to(user, :view_time_entries). It never references the linked Work Package's project orview_work_packages.view_time_entries/view_cost_entriesare declared with nodependencies: :view_work_packages(modules/costs/lib/costs/engine.rb:39-41,:108-110), so a custom role can grant them while withholdingview_work_packages.The direct Work Package API enforces what the leak skips
lib/api/v3/work_packages/work_packages_api.rb:74-77:WorkPackage.visible=allowed_to(user, :view_work_packages)(app/models/work_package.rb:85). A user withoutview_work_packageson the WP gets 404 on the direct path.Impact
A project member with
view_time_entries(orview_cost_entries) but notview_work_packagesreads the subject and id of every Work Package referenced by a time/cost entry in that project, despite being unable to open those Work Packages directly. Work Package subjects routinely encode sensitive information. Secondary cross-project reach exists when a Work Package is moved to an inaccessible project while time entries briefly retain their originalproject_id(app/services/work_packages/update_service.rb:136-145).Affected versions
<= 17.5.1(latest release). Community Edition (modules/costsis bundled in the AGPL CE, not EE-gated).Remediation
Replace
associated_resource :work_packagewithassociated_visible_resource :work_packageintime_entry_representer.rb:103andcost_entry_representer.rb:48(and auditlib/api/v3/custom_actions/custom_action_execute_representer.rb:36for the same one-line change). This mirrors the GHSA-g387 fix.Severity
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N(Medium), parity-anchored to GHSA-g387-6rm2-xw88 (same disclosure class, same vector).