Skip to content

Tool Coverage data not updated when archiving last project

Description: In the "Security Inventory" section, when archiving the final project in a subgroup, the "Tool Coverage" statistics are not properly updated, unlike when archiving non-final projects.

Related issue: #571765 (closed)

Reproduction Steps

  1. Create a subgroup
  2. Import first project
  3. Import second project
  4. Run pipeline for first project → "Tool Coverage" stats update correctly
  5. Run pipeline for second project → "Tool Coverage" stats update correctly
  6. Archive the first project → "Tool Coverage" stats update correctly
  7. Archive the second (last) project → "Tool Coverage" stats remain unchanged

Supporting Evidence

Screenshots:

1. Importing the first project

2. Importing the second project

2-two-imported

3. Running the pipeline for the first project

3-one-pipeline

4. Running the pipeline for the second project

4-two-pipelines

5. Archive the first project

5-one-archived

6. Archive the second project

6-two-archived

GraphQL Response Data:

GraphQL Response - Step 5: After archiving first project (Tool Coverage updates correctly)
{
  "data": {
    "group": {
      "id": "gid://gitlab/Group/22",
      "descendantGroups": {
        "pageInfo": {
          "hasNextPage": false,
          "endCursor": "eyJuYW1lIjoidGVzdCBncm91cCIsImlkIjoiMTMzIn0"
        },
        "nodes": [
          {
            "__typename": "Group",
            "id": "gid://gitlab/Group/133",
            "name": "Test Group",
            "descendantGroupsCount": 0,
            "projectsCount": 1,
            "path": "test-group",
            "fullPath": "toolbox/test-group",
            "avatarUrl": null,
            "webUrl": "http://gdk.test:3000/groups/toolbox/test-group",
            "updatedAt": "2025-09-26T07:28:37Z",
            "analyzerStatuses": [
              {
                "success": 1,
                "failure": 0,
                "notConfigured": 0,
                "analyzerType": "API_FUZZING",
                "updatedAt": "2025-09-26T08:34:14Z"
              },
              {
                "success": 1,
                "failure": 0,
                "notConfigured": 0,
                "analyzerType": "CLUSTER_IMAGE_SCANNING",
                "updatedAt": "2025-09-26T08:34:14Z"
              },
              {
                "success": 1,
                "failure": 0,
                "notConfigured": 0,
                "analyzerType": "SECRET_DETECTION_PIPELINE_BASED",
                "updatedAt": "2025-09-26T08:34:14Z"
              },
              {
                "success": 1,
                "failure": 0,
                "notConfigured": 0,
                "analyzerType": "CONTAINER_SCANNING_PIPELINE_BASED",
                "updatedAt": "2025-09-26T08:34:14Z"
              },
              {
                "success": 1,
                "failure": 0,
                "notConfigured": 0,
                "analyzerType": "SAST",
                "updatedAt": "2025-09-26T08:34:14Z"
              },
              {
                "success": 1,
                "failure": 0,
                "notConfigured": 0,
                "analyzerType": "DAST",
                "updatedAt": "2025-09-26T08:34:14Z"
              },
              {
                "success": 0,
                "failure": 1,
                "notConfigured": 0,
                "analyzerType": "DEPENDENCY_SCANNING",
                "updatedAt": "2025-09-26T08:34:14Z"
              },
              {
                "success": 1,
                "failure": 0,
                "notConfigured": 0,
                "analyzerType": "CONTAINER_SCANNING",
                "updatedAt": "2025-09-26T08:34:14Z"
              },
              {
                "success": 1,
                "failure": 0,
                "notConfigured": 0,
                "analyzerType": "SECRET_DETECTION",
                "updatedAt": "2025-09-26T08:34:14Z"
              },
              {
                "success": 1,
                "failure": 0,
                "notConfigured": 0,
                "analyzerType": "COVERAGE_FUZZING",
                "updatedAt": "2025-09-26T08:34:14Z"
              },
              {
                "success": 0,
                "failure": 0,
                "notConfigured": 1,
                "analyzerType": "SECRET_DETECTION_SECRET_PUSH_PROTECTION",
                "updatedAt": "2025-09-26T08:34:14Z"
              },
              {
                "success": 0,
                "failure": 0,
                "notConfigured": 1,
                "analyzerType": "CONTAINER_SCANNING_FOR_REGISTRY",
                "updatedAt": "2025-09-26T08:34:14Z"
              },
              {
                "success": 0,
                "failure": 0,
                "notConfigured": 1,
                "analyzerType": "SAST_ADVANCED",
                "updatedAt": "2025-09-26T08:35:28Z"
              },
              {
                "success": 0,
                "failure": 0,
                "notConfigured": 1,
                "analyzerType": "SAST_IAC",
                "updatedAt": "2025-09-26T08:35:28Z"
              }
            ],
            "vulnerabilityNamespaceStatistic": {
              "critical": 11,
              "high": 3,
              "low": 6,
              "info": 0,
              "medium": 48,
              "unknown": 6,
              "updatedAt": "2025-09-26T08:34:14Z"
            }
          }
        ]
      },
      "projects": {
        "pageInfo": {
          "hasNextPage": false,
          "endCursor": "eyJpZCI6IjEifQ"
        },
        "nodes": [
          {
            "__typename": "Project",
            "id": "gid://gitlab/Project/1",
            "name": "Gitlab Smoke Tests",
            "path": "gitlab-smoke-tests",
            "fullPath": "toolbox/gitlab-smoke-tests",
            "avatarUrl": null,
            "webUrl": "http://gdk.test:3000/toolbox/gitlab-smoke-tests",
            "updatedAt": "2025-09-08T14:20:42Z",
            "secretPushProtectionEnabled": false,
            "containerScanningForRegistryEnabled": false,
            "vulnerabilityStatistic": null,
            "analyzerStatuses": []
          }
        ]
      }
    }
  },
  "correlationId": "01K62JKEWVWZS74Y1M8ZXC5HHF"
}
GraphQL Response - Step 6: After archiving last project (Tool Coverage remains unchanged / partially updated)
{
  "data": {
    "group": {
      "id": "gid://gitlab/Group/22",
      "descendantGroups": {
        "pageInfo": {
          "hasNextPage": false,
          "endCursor": "eyJuYW1lIjoidGVzdCBncm91cCIsImlkIjoiMTMzIn0"
        },
        "nodes": [
          {
            "__typename": "Group",
            "id": "gid://gitlab/Group/133",
            "name": "Test Group",
            "descendantGroupsCount": 0,
            "projectsCount": 0,
            "path": "test-group",
            "fullPath": "toolbox/test-group",
            "avatarUrl": null,
            "webUrl": "http://gdk.test:3000/groups/toolbox/test-group",
            "updatedAt": "2025-09-26T07:28:37Z",
            "analyzerStatuses": [
              {
                "success": 1,
                "failure": 0,
                "notConfigured": 0,
                "analyzerType": "API_FUZZING",
                "updatedAt": "2025-09-26T08:34:14Z"
              },
              {
                "success": 1,
                "failure": 0,
                "notConfigured": 0,
                "analyzerType": "CLUSTER_IMAGE_SCANNING",
                "updatedAt": "2025-09-26T08:34:14Z"
              },
              {
                "success": 1,
                "failure": 0,
                "notConfigured": 0,
                "analyzerType": "SECRET_DETECTION_PIPELINE_BASED",
                "updatedAt": "2025-09-26T08:34:14Z"
              },
              {
                "success": 1,
                "failure": 0,
                "notConfigured": 0,
                "analyzerType": "CONTAINER_SCANNING_PIPELINE_BASED",
                "updatedAt": "2025-09-26T08:34:14Z"
              },
              {
                "success": 1,
                "failure": 0,
                "notConfigured": 0,
                "analyzerType": "SAST",
                "updatedAt": "2025-09-26T08:34:14Z"
              },
              {
                "success": 1,
                "failure": 0,
                "notConfigured": 0,
                "analyzerType": "DAST",
                "updatedAt": "2025-09-26T08:34:14Z"
              },
              {
                "success": 0,
                "failure": 1,
                "notConfigured": 0,
                "analyzerType": "DEPENDENCY_SCANNING",
                "updatedAt": "2025-09-26T08:34:14Z"
              },
              {
                "success": 1,
                "failure": 0,
                "notConfigured": 0,
                "analyzerType": "CONTAINER_SCANNING",
                "updatedAt": "2025-09-26T08:34:14Z"
              },
              {
                "success": 1,
                "failure": 0,
                "notConfigured": 0,
                "analyzerType": "SECRET_DETECTION",
                "updatedAt": "2025-09-26T08:34:14Z"
              },
              {
                "success": 1,
                "failure": 0,
                "notConfigured": 0,
                "analyzerType": "COVERAGE_FUZZING",
                "updatedAt": "2025-09-26T08:34:14Z"
              },
              {
                "success": 0,
                "failure": 0,
                "notConfigured": 0,
                "analyzerType": "SECRET_DETECTION_SECRET_PUSH_PROTECTION",
                "updatedAt": "2025-09-26T08:34:14Z"
              },
              {
                "success": 0,
                "failure": 0,
                "notConfigured": 0,
                "analyzerType": "CONTAINER_SCANNING_FOR_REGISTRY",
                "updatedAt": "2025-09-26T08:34:14Z"
              },
              {
                "success": 0,
                "failure": 0,
                "notConfigured": 0,
                "analyzerType": "SAST_ADVANCED",
                "updatedAt": "2025-09-26T08:43:00Z"
              },
              {
                "success": 0,
                "failure": 0,
                "notConfigured": 0,
                "analyzerType": "SAST_IAC",
                "updatedAt": "2025-09-26T08:43:00Z"
              }
            ],
            "vulnerabilityNamespaceStatistic": {
              "critical": 0,
              "high": 0,
              "low": 0,
              "info": 0,
              "medium": 0,
              "unknown": 0,
              "updatedAt": "2025-09-26T08:42:44Z"
            }
          }
        ]
      },
      "projects": {
        "pageInfo": {
          "hasNextPage": false,
          "endCursor": "eyJpZCI6IjEifQ"
        },
        "nodes": [
          {
            "__typename": "Project",
            "id": "gid://gitlab/Project/1",
            "name": "Gitlab Smoke Tests",
            "path": "gitlab-smoke-tests",
            "fullPath": "toolbox/gitlab-smoke-tests",
            "avatarUrl": null,
            "webUrl": "http://gdk.test:3000/toolbox/gitlab-smoke-tests",
            "updatedAt": "2025-09-08T14:20:42Z",
            "secretPushProtectionEnabled": false,
            "containerScanningForRegistryEnabled": false,
            "vulnerabilityStatistic": null,
            "analyzerStatuses": []
          }
        ]
      }
    }
  },
  "correlationId": "01K62K18913E526BYQ2R2QTQ7A"
}

Analysis

The inconsistent behavior suggests there may be a logic issue in how the system handles Tool Coverage recalculation when the non-archived project count reaches zero. The first bug fix should be straightforward, but the second bug requires deeper investigation into the Tool Coverage update mechanism. Related entity: Security::AnalyzerNamespaceStatus


Note: for debugging purposes, I ran the following workers, but they did not adjust the statistics:

Edited by Nicolae Rotaru