Fold the dependency malware field into dependency_malware_detection

What does this MR do and why?

Malware on the dependency list was gated by two flags at once:

  • dependency_malware_detection — the badge and filter token in the UI.
  • dependency_malware_field_project — whether the API returned the malware field at all.

Enabling either one alone produces nothing useful: the field with no badge, or a badge with no data. The second flag is also about to gate group surfaces via !251901 (merged) and !251903, at which point its _project suffix stops being true.

This folds them into one. The backend field now goes through the same dependency_malware_detection_feature_flag_enabled? helpers on Group and Project that the frontend gating already uses, and dependency_malware_field_project is deleted. This is the arrangement agreed for the vulnerability report in !249360 (merged) and !249418 (merged), applied to the dependency list.

Callers changed: Types::Sbom::DependencyInterface, DependencyEntity, API::Entities::Dependency.

Filtering keeps its own flag, malicious_packages_dependency_list_filtering, until the Elasticsearch APIs behind it are finished.

Feature flag

Flag Type Default Gates
dependency_malware_detection beta off Backend malware field, plus the UI badge and filter token

Removed: dependency_malware_field_project. It was never enabled anywhere, so no rollout state is lost. Its rollout issue #600274 (closed) can be closed in favour of #611434.

One behaviour change: callers previously passed the project straight to Feature.enabled?. The helper walks up the namespace hierarchy, so enabling the flag on a group now also reaches its projects and subgroups. Both flags are off everywhere, so nothing changes for users today.

Database review

No database changes. No migrations, no new or modified queries, no new scopes.

How to set up and validate locally

Local testing steps
  1. Confirm the old flag is gone and only the consolidated one remains:

    Feature::Definition.definitions.key?(:dependency_malware_field_project)  # => false
    Feature::Definition.definitions.key?(:dependency_malware_detection)      # => true
  2. Use a project inside a subgroup so the walk up the hierarchy is exercised:

    root     = Group.find_by_full_path('your-group')
    subgroup = root.children.first
    project  = subgroup.projects.first
    
    project.dependency_malware_detection_feature_flag_enabled?  # => false
  3. Enable on the root group only, then re-check:

    Feature.enable(:dependency_malware_detection, root)
    
    root.dependency_malware_detection_feature_flag_enabled?      # => true
    subgroup.dependency_malware_detection_feature_flag_enabled?  # => true
    project.dependency_malware_detection_feature_flag_enabled?   # => true
  4. Confirm the project dependency API still gates correctly. With the flag on, malware appears on each entry:

    curl --header "PRIVATE-TOKEN: <token>" \
      "http://gdk.test:3000/api/v4/projects/<id>/dependencies" \
      | jq '[.[] | {name, malware}]'

    With Feature.disable(:dependency_malware_detection, root) and the flag off for the project, the malware key is absent.

Before, on master: two switches. Turning on dependency_malware_field_project returns the field but renders no badge; turning on dependency_malware_detection renders a badge with no data. Enabling either for a group does not reach its projects.

After: one switch. Feature.enable(:dependency_malware_detection, root) turns on the field and the badge together, for the group and everything under it.

Flag enabled on Root group Subgroup Project
root group true true true
subgroup false true true
project false false true
nothing false false false
Edited by Bala Kumar

Merge request reports

Loading
Loading