CVE-2023-38508 (published 2023-08-24T23:15Z)

Tuleap is an open source suite to improve management of software developments and collaboration. In Tuleap Community Edition prior to version 14.11.99.28 and Tuleap Enterprise Edition prior to versions 14.10-6 and 14.11-3, the preview of an artifact link with a type does not respect the project, tracker and artifact level permissions. The issue occurs on the artifact view (not reproducible on the artifact modal). Users might get access to information they should not have access to. Only the title, status, assigned to and last update date fields as defined by the semantics are impacted. If those fields have strict permissions (e.g. the title is only visible to a specific user group) those permissions are still enforced. Tuleap Community Edition 14.11.99.28, Tuleap Enterprise Edition 14.10-6, and Tuleap Enterprise Edition 14.11-3 contain a fix for this issue.

CVSS v3 base score: 4.3, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N)

Weakness information:
cwe_id: CWE-862
cwe_name: Missing Authorization
description: The product does not perform an authorization check when an actor attempts to access a resource or perform an action.
extended_description: 
            Assuming a user with a given identity, authorization is the process of determining whether that user can access a given resource, based on the user's privileges and any permissions or other access-control specifications that apply to the resource.
            When access control checks are not applied, users are able to access data or perform actions that they should not be allowed to perform. This can lead to a wide range of problems, including information exposures, denial of service, and arbitrary code execution.
         
         

Fix information:
repo_url: https://github.com/Enalean/tuleap
commit_details: repo_url: https://github.com/Enalean/tuleap
author: Thomas Gerbet
author_date: 2023-07-25 15:14:11+02:00
msg: Fixes request #33608: Preview of a linked artifact with a type does not respect permissions

Change-Id: I94bab99f318a79e91f42b5fb67a6c2d45075ccba
repository: repo_url: https://github.com/Enalean/tuleap
repo_name: Enalean/tuleap
description: Tuleap is an Open Source Suite to improve management of software developments and collaboration. With a single web-based solution, project managers, developers & quality managers can easily build, deploy software projects.
repo_language: PHP
owner: Enalean
file_changes: filename: ArtifactInTypeTablePresenter.php
change_type: ModificationType.MODIFY
diff: @@ -37,6 +37,7 @@ class ArtifactInTypeTablePresenter
     public $artifactlink_field_id;
 
     public function __construct(
+        \PFUser $current_user,
         \Tuleap\Tracker\Artifact\Artifact $artifact,
         $html_classes,
         Tracker_FormElement_Field_ArtifactLink $field,
@@ -46,7 +47,6 @@ class ArtifactInTypeTablePresenter
         $tracker            = $artifact->getTracker();
         $project            = $tracker->getProject();
         $user_helper        = \UserHelper::instance();
-        $current_user       = \UserManager::instance()->getCurrentUser();
 
         $this->direct_link_to_artifact   = $artifact->fetchDirectLinkToArtifact();
         $this->project_public_name       = $project->getPublicName();

programming_language: PHP
filename: TypeTablePresenter.php
change_type: ModificationType.MODIFY
diff: @@ -43,6 +43,7 @@ class TypeTablePresenter
     public const TABLE_ID_PREFIX = "tracker_report_table_type_";
 
     public function __construct(
+        \PFUser $current_user,
         TypePresenter $type,
         array $artifact_links,
         bool $is_reverse_artifact_links,
@@ -67,8 +68,12 @@ class TypeTablePresenter
         $this->artifact_links = [];
         $html_classes         = '';
         foreach ($artifact_links as $artifact_link) {
-            $artifact               = $art_factory->getArtifactById($artifact_link->getArtifactId());
+            $artifact = $art_factory->getArtifactByIdUserCanView($current_user, $artifact_link->getArtifactId());
+            if ($artifact === null) {
+                continue;
+            }
             $this->artifact_links[] = new ArtifactInTypeTablePresenter(
+                $current_user,
                 $artifact,
                 $html_classes,
                 $field,
@@ -80,11 +85,13 @@ class TypeTablePresenter
     }
 
     public static function buildForHeader(
+        \PFUser $current_user,
         TypePresenter $type_presenter,
         Tracker_FormElement_Field_ArtifactLink $field,
         bool $are_links_deletable,
     ): TypeTablePresenter {
         return new TypeTablePresenter(
+            $current_user,
             $type_presenter,
             [],
             false,

programming_language: PHP
filename: Tracker_FormElement_Field_ArtifactLink.php
change_type: ModificationType.MODIFY
diff: @@ -565,7 +565,7 @@ class Tracker_FormElement_Field_ArtifactLink extends Tracker_FormElement_Field /
                         data-renderer-data="' . Codendi_HTMLPurifier::instance()->purify($json_encoded_data) . '"></div></div>';
             }
 
-            $html .= $this->fetchTypeTables($artifact_links_to_render, $reverse_artifact_links);
+            $html .= $this->fetchTypeTables($current_user, $artifact_links_to_render, $reverse_artifact_links);
         } else {
             $html .= $this->getNoValueLabelForLinks($artifact);
         }
@@ -609,7 +609,7 @@ class Tracker_FormElement_Field_ArtifactLink extends Tracker_FormElement_Field /
         return $renderer->fetchAsArtifactLink($matching_ids, $this->getId(), $read_only, $prefill_removed_values, $prefill_edited_types, $reverse_artifact_links, false, $from_aid);
     }
 
-    private function fetchTypeTables(ArtifactLinksToRender $artifact_links_to_render, $is_reverse_artifact_links)
+    private function fetchTypeTables(\PFUser $current_user, ArtifactLinksToRender $artifact_links_to_render, $is_reverse_artifact_links): string
     {
         static $type_tables_cache = [];
         if (isset($type_tables_cache[spl_object_hash($artifact_links_to_render)][$is_reverse_artifact_links])) {
@@ -621,6 +621,7 @@ class Tracker_FormElement_Field_ArtifactLink extends Tracker_FormElement_Field /
             $html .= $template_renderer->renderToString(
                 'artifactlink-type-table',
                 new TypeTablePresenter(
+                    $current_user,
                     $artifact_links_per_type->getTypePresenter(),
                     $artifact_links_per_type->getArtifactLinks(),
                     $is_reverse_artifact_links,
@@ -734,7 +735,7 @@ class Tracker_FormElement_Field_ArtifactLink extends Tracker_FormElement_Field /
                     }
                 }
 
-                $this->appendTypeTable($request, $result, $is_reverse);
+                $this->appendTypeTable($current_user, $request, $result, $is_reverse);
                 if ($result) {
                     $head = [];
                     $rows = [];
@@ -1735,7 +1736,7 @@ class Tracker_FormElement_Field_ArtifactLink extends Tracker_FormElement_Field /
         return TemplateRendererFactory::build()->getRenderer(TRACKER_TEMPLATE_DIR);
     }
 
-    private function appendTypeTable(Codendi_Request $request, array &$result, bool $is_reverse_artifact_links)
+    private function appendTypeTable(PFUser $current_user, Codendi_Request $request, array &$result, bool $is_reverse_artifact_links): void
     {
         if (! $this->getTracker()->isProjectAllowedToUseType()) {
             return;
@@ -1746,39 +1747,43 @@ class Tracker_FormElement_Field_ArtifactLink extends Tracker_FormElement_Field /
             return;
         }
 
-        $type_presenter        = $this->getTypePresenterFactory()->getFromShortname($type_shortname);
+        $type_presenter = $this->getTypePresenterFactory()->getFromShortname($type_shortname);
+        if ($type_presenter === null) {
+            return;
+        }
         $key                   = "type_$type_shortname";
         $art_factory           = $this->getArtifactFactory();
         $artifact_html_classes = 'additional';
         $type_html             = '';
-        $head_html             = '';
         $ids                   = $request->get('ids');
+        $are_links_deletable   = $this->areLinksDeletable(
+            $type_presenter,
+            $is_reverse_artifact_links,
+        );
 
         foreach (explode(',', $ids) as $id) {
-            $artifact = $art_factory->getArtifactById(trim($id));
+            $artifact = $art_factory->getArtifactByIdUserCanView($current_user, (int) trim($id));
 
-            $are_links_deletable = $this->areLinksDeletable(
-                $type_presenter,
-                $is_reverse_artifact_links,
-            );
-
-            if (! is_null($artifact) && $artifact->getTracker()->isActive()) {
-                $type_html .= $this->getTemplateRenderer()->renderToString(
-                    'artifactlink-type-table-row',
-                    new ArtifactInTypeTablePresenter(
-                        $artifact,
-                        $artifact_html_classes,
-                        $this,
-                        $are_links_deletable,
-                    )
-                );
+            if ($artifact === null) {
+                continue;
             }
+
+            $type_html .= $this->getTemplateRenderer()->renderToString(
+                'artifactlink-type-table-row',
+                new ArtifactInTypeTablePresenter(
+                    $current_user,
+                    $artifact,
+                    $artifact_html_classes,
+                    $this,
+                    $are_links_deletable,
+                )
+            );
         }
 
         if ($type_html !== '') {
             $head_html = $this->getTemplateRenderer()->renderToString(
                 'artifactlink-type-table-head',
-                TypeTablePresenter::buildForHeader($type_presenter, $this, $are_links_deletable)
+                TypeTablePresenter::buildForHeader($current_user, $type_presenter, $this, $are_links_deletable)
             );
 
             $result[$key] = ['head' => $head_html, 'rows' => $type_html];

programming_language: PHP