From 2e78a7117da87ec84852fd049aa6f3ab8f85a430 Mon Sep 17 00:00:00 2001
From: Norbert Szulc <norbert@icetek.io>
Date: Thu, 6 Jun 2024 13:23:47 +0200
Subject: [PATCH] fix!: normalize Python depName in every manager (#27733)

---
 docs/usage/python.md                          |  7 +++
 lib/modules/manager/pep621/utils.ts           |  3 +-
 .../manager/pip-compile/extract.spec.ts       |  1 +
 lib/modules/manager/pip-compile/extract.ts    |  9 +---
 .../__snapshots__/extract.spec.ts.snap        | 41 ++++++++++++++++++
 .../manager/pip_requirements/extract.spec.ts  |  1 +
 .../manager/pip_requirements/extract.ts       |  2 +
 .../__snapshots__/extract.spec.ts.snap        | 16 +++++++
 lib/modules/manager/pip_setup/extract.ts      |  2 +
 lib/modules/manager/pipenv/extract.ts         |  2 +
 .../__snapshots__/extract.spec.ts.snap        | 43 +++++++++++++++++++
 lib/modules/manager/setup-cfg/extract.ts      |  2 +
 lib/modules/platform/github/index.spec.ts     | 32 ++++++++++++++
 lib/modules/platform/github/index.ts          |  5 ++-
 lib/modules/platform/utils/github-alerts.ts   | 14 ++++++
 .../repository/init/vulnerability.spec.ts     | 34 ---------------
 lib/workers/repository/init/vulnerability.ts  |  8 +---
 17 files changed, 172 insertions(+), 50 deletions(-)
 create mode 100644 lib/modules/platform/utils/github-alerts.ts

diff --git a/docs/usage/python.md b/docs/usage/python.md
index 031d49640d..6b3751c213 100644
--- a/docs/usage/python.md
+++ b/docs/usage/python.md
@@ -23,6 +23,13 @@ Legacy versions with the `===` prefix are ignored.
 1. Renovate searches for the latest version on [PyPI](https://pypi.org/) to decide if there are upgrades
 1. If the source package includes a GitHub URL as its source, and has a "changelog" file _or_ uses GitHub releases, a Release Note will be embedded in the generated PR
 
+## Package name matching
+
+Your `matchPackageName` or `matchPackagePattern` rules will be matching against normalized names.
+So if you have specified package `some.package` or `ANOTHER_DEP` in your package files (`requirements.txt`, `pyproject.toml`), they will be treated as `some-package` and `another-dep` respecitvely.
+Not only they will be case insensitive but will replace any amount `._-` to a single `-`.
+[Consult Python packaging documentation for the specification](https://packaging.python.org/en/latest/specifications/name-normalization/).
+
 ## Alternate registries
 
 By default Renovate checks for upgrades on the `pypi.org` registry.
diff --git a/lib/modules/manager/pep621/utils.ts b/lib/modules/manager/pep621/utils.ts
index 13293e38c2..dcc3bb8cc2 100644
--- a/lib/modules/manager/pep621/utils.ts
+++ b/lib/modules/manager/pep621/utils.ts
@@ -3,6 +3,7 @@ import { logger } from '../../../logger';
 import { regEx } from '../../../util/regex';
 import { parse as parseToml } from '../../../util/toml';
 import { PypiDatasource } from '../../datasource/pypi';
+import { normalizePythonDepName } from '../../datasource/pypi/common';
 import type { PackageDependency } from '../types';
 import { PyProject, PyProjectSchema } from './schema';
 import type { Pep508ParseResult } from './types';
@@ -60,7 +61,7 @@ export function pep508ToPackageDependency(
   }
 
   const dep: PackageDependency = {
-    packageName: parsed.packageName,
+    packageName: normalizePythonDepName(parsed.packageName),
     depName: parsed.packageName,
     datasource: PypiDatasource.id,
     depType,
diff --git a/lib/modules/manager/pip-compile/extract.spec.ts b/lib/modules/manager/pip-compile/extract.spec.ts
index cab96c8f09..fd70448a9a 100644
--- a/lib/modules/manager/pip-compile/extract.spec.ts
+++ b/lib/modules/manager/pip-compile/extract.spec.ts
@@ -384,6 +384,7 @@ describe('modules/manager/pip-compile/extract', () => {
       datasource: 'pypi',
       depType: 'indirect',
       depName: 'bards-friend',
+      packageName: 'bards-friend',
       lockedVersion: '1.0.0',
       enabled: false,
     });
diff --git a/lib/modules/manager/pip-compile/extract.ts b/lib/modules/manager/pip-compile/extract.ts
index 9960fb3b4c..2be0ad3fd3 100644
--- a/lib/modules/manager/pip-compile/extract.ts
+++ b/lib/modules/manager/pip-compile/extract.ts
@@ -2,7 +2,6 @@ import upath from 'upath';
 import { logger } from '../../../logger';
 import { readLocalFile } from '../../../util/fs';
 import { ensureLocalPath } from '../../../util/fs/util';
-import { normalizePythonDepName } from '../../datasource/pypi/common';
 import { extractPackageFile as extractRequirementsFile } from '../pip_requirements/extract';
 import { extractPackageFile as extractSetupPyFile } from '../pip_setup';
 import type {
@@ -165,9 +164,7 @@ export async function extractAllPackageFiles(
         }
         for (const dep of packageFileContent.deps) {
           const lockedVersion = lockedDeps?.find(
-            (lockedDep) =>
-              normalizePythonDepName(lockedDep.depName!) ===
-              normalizePythonDepName(dep.depName!),
+            (lockedDep) => lockedDep.packageName! === dep.packageName!,
           )?.currentVersion;
           if (lockedVersion) {
             dep.lockedVersion = lockedVersion;
@@ -246,9 +243,7 @@ function extendWithIndirectDeps(
   for (const lockedDep of lockedDeps) {
     if (
       !packageFileContent.deps.find(
-        (dep) =>
-          normalizePythonDepName(lockedDep.depName!) ===
-          normalizePythonDepName(dep.depName!),
+        (dep) => lockedDep.packageName! === dep.packageName!,
       )
     ) {
       packageFileContent.deps.push(indirectDep(lockedDep));
diff --git a/lib/modules/manager/pip_requirements/__snapshots__/extract.spec.ts.snap b/lib/modules/manager/pip_requirements/__snapshots__/extract.spec.ts.snap
index beb0d0c9f5..4bbeef11ea 100644
--- a/lib/modules/manager/pip_requirements/__snapshots__/extract.spec.ts.snap
+++ b/lib/modules/manager/pip_requirements/__snapshots__/extract.spec.ts.snap
@@ -8,23 +8,27 @@ exports[`modules/manager/pip_requirements/extract extractPackageFile() extracts
       "currentVersion": "0.3.1",
       "datasource": "pypi",
       "depName": "some-package",
+      "packageName": "some-package",
     },
     {
       "currentValue": "==1.0.0",
       "currentVersion": "1.0.0",
       "datasource": "pypi",
       "depName": "some-other-package",
+      "packageName": "some-other-package",
     },
     {
       "currentValue": undefined,
       "datasource": "pypi",
       "depName": "sphinx",
+      "packageName": "sphinx",
     },
     {
       "currentValue": "==1.9",
       "currentVersion": "1.9",
       "datasource": "pypi",
       "depName": "not_semver",
+      "packageName": "not-semver",
     },
   ],
   "registryUrls": [
@@ -40,30 +44,35 @@ exports[`modules/manager/pip_requirements/extract extractPackageFile() extracts
     "currentVersion": "1",
     "datasource": "pypi",
     "depName": "Django",
+    "packageName": "django",
   },
   {
     "currentValue": "==0.6.27",
     "currentVersion": "0.6.27",
     "datasource": "pypi",
     "depName": "distribute",
+    "packageName": "distribute",
   },
   {
     "currentValue": "==0.2",
     "currentVersion": "0.2",
     "datasource": "pypi",
     "depName": "dj-database-url",
+    "packageName": "dj-database-url",
   },
   {
     "currentValue": "==2.4.5",
     "currentVersion": "2.4.5",
     "datasource": "pypi",
     "depName": "psycopg2",
+    "packageName": "psycopg2",
   },
   {
     "currentValue": "==0.1.2",
     "currentVersion": "0.1.2",
     "datasource": "pypi",
     "depName": "wsgiref",
+    "packageName": "wsgiref",
   },
 ]
 `;
@@ -75,12 +84,14 @@ exports[`modules/manager/pip_requirements/extract extractPackageFile() handles c
     "currentVersion": "1.11.23",
     "datasource": "pypi",
     "depName": "Django",
+    "packageName": "django",
   },
   {
     "currentValue": "==0.6.27",
     "currentVersion": "0.6.27",
     "datasource": "pypi",
     "depName": "distribute",
+    "packageName": "distribute",
     "skipReason": "ignored",
   },
   {
@@ -88,18 +99,21 @@ exports[`modules/manager/pip_requirements/extract extractPackageFile() handles c
     "currentVersion": "0.2",
     "datasource": "pypi",
     "depName": "dj-database-url",
+    "packageName": "dj-database-url",
   },
   {
     "currentValue": "==2.4.5",
     "currentVersion": "2.4.5",
     "datasource": "pypi",
     "depName": "psycopg2",
+    "packageName": "psycopg2",
   },
   {
     "currentValue": "==0.1.2",
     "currentVersion": "0.1.2",
     "datasource": "pypi",
     "depName": "wsgiref",
+    "packageName": "wsgiref",
   },
 ]
 `;
@@ -115,36 +129,42 @@ exports[`modules/manager/pip_requirements/extract extractPackageFile() handles e
       "currentVersion": "2.0.12",
       "datasource": "pypi",
       "depName": "Django",
+      "packageName": "django",
     },
     {
       "currentValue": "==4.1.1",
       "currentVersion": "4.1.1",
       "datasource": "pypi",
       "depName": "celery",
+      "packageName": "celery",
     },
     {
       "currentValue": "== 3.2.1",
       "currentVersion": "3.2.1",
       "datasource": "pypi",
       "depName": "foo",
+      "packageName": "foo",
     },
     {
       "currentValue": "==0.3.1",
       "currentVersion": "0.3.1",
       "datasource": "pypi",
       "depName": "some-package",
+      "packageName": "some-package",
     },
     {
       "currentValue": "==1.0.0",
       "currentVersion": "1.0.0",
       "datasource": "pypi",
       "depName": "some-other-package",
+      "packageName": "some-other-package",
     },
     {
       "currentValue": "==1.9",
       "currentVersion": "1.9",
       "datasource": "pypi",
       "depName": "not_semver",
+      "packageName": "not-semver",
     },
   ],
   "registryUrls": [
@@ -164,36 +184,42 @@ exports[`modules/manager/pip_requirements/extract extractPackageFile() handles e
       "currentVersion": "2.0.12",
       "datasource": "pypi",
       "depName": "Django",
+      "packageName": "django",
     },
     {
       "currentValue": "==4.1.1",
       "currentVersion": "4.1.1",
       "datasource": "pypi",
       "depName": "celery",
+      "packageName": "celery",
     },
     {
       "currentValue": "== 3.2.1",
       "currentVersion": "3.2.1",
       "datasource": "pypi",
       "depName": "foo",
+      "packageName": "foo",
     },
     {
       "currentValue": "==0.3.1",
       "currentVersion": "0.3.1",
       "datasource": "pypi",
       "depName": "some-package",
+      "packageName": "some-package",
     },
     {
       "currentValue": "==1.0.0",
       "currentVersion": "1.0.0",
       "datasource": "pypi",
       "depName": "some-other-package",
+      "packageName": "some-other-package",
     },
     {
       "currentValue": "==1.9",
       "currentVersion": "1.9",
       "datasource": "pypi",
       "depName": "not_semver",
+      "packageName": "not-semver",
     },
   ],
 }
@@ -210,36 +236,42 @@ exports[`modules/manager/pip_requirements/extract extractPackageFile() handles e
       "currentVersion": "2.0.12",
       "datasource": "pypi",
       "depName": "Django",
+      "packageName": "django",
     },
     {
       "currentValue": "==4.1.1",
       "currentVersion": "4.1.1",
       "datasource": "pypi",
       "depName": "celery",
+      "packageName": "celery",
     },
     {
       "currentValue": "== 3.2.1",
       "currentVersion": "3.2.1",
       "datasource": "pypi",
       "depName": "foo",
+      "packageName": "foo",
     },
     {
       "currentValue": "==0.3.1",
       "currentVersion": "0.3.1",
       "datasource": "pypi",
       "depName": "some-package",
+      "packageName": "some-package",
     },
     {
       "currentValue": "==1.0.0",
       "currentVersion": "1.0.0",
       "datasource": "pypi",
       "depName": "some-other-package",
+      "packageName": "some-other-package",
     },
     {
       "currentValue": "==1.9",
       "currentVersion": "1.9",
       "datasource": "pypi",
       "depName": "not_semver",
+      "packageName": "not-semver",
     },
   ],
 }
@@ -253,18 +285,21 @@ exports[`modules/manager/pip_requirements/extract extractPackageFile() handles e
       "currentVersion": "2.0.12",
       "datasource": "pypi",
       "depName": "Django",
+      "packageName": "django",
     },
     {
       "currentValue": "==4.1.1",
       "currentVersion": "4.1.1",
       "datasource": "pypi",
       "depName": "celery",
+      "packageName": "celery",
     },
     {
       "currentValue": "== 3.2.1",
       "currentVersion": "3.2.1",
       "datasource": "pypi",
       "depName": "foo",
+      "packageName": "foo",
     },
   ],
   "registryUrls": [
@@ -281,18 +316,21 @@ exports[`modules/manager/pip_requirements/extract extractPackageFile() handles e
       "currentVersion": "2.0.12",
       "datasource": "pypi",
       "depName": "Django",
+      "packageName": "django",
     },
     {
       "currentValue": "==4.1.1",
       "currentVersion": "4.1.1",
       "datasource": "pypi",
       "depName": "celery",
+      "packageName": "celery",
     },
     {
       "currentValue": "== 3.2.1",
       "currentVersion": "3.2.1",
       "datasource": "pypi",
       "depName": "foo",
+      "packageName": "foo",
     },
   ],
   "registryUrls": [
@@ -309,18 +347,21 @@ exports[`modules/manager/pip_requirements/extract extractPackageFile() should ha
       "currentVersion": "1.9.1",
       "datasource": "pypi",
       "depName": "Django",
+      "packageName": "django",
     },
     {
       "currentValue": "==0.22.1",
       "currentVersion": "0.22.1",
       "datasource": "pypi",
       "depName": "bgg",
+      "packageName": "bgg",
     },
     {
       "currentValue": "==2016.1.8",
       "currentVersion": "2016.1.8",
       "datasource": "pypi",
       "depName": "html2text",
+      "packageName": "html2text",
     },
   ],
 }
diff --git a/lib/modules/manager/pip_requirements/extract.spec.ts b/lib/modules/manager/pip_requirements/extract.spec.ts
index 76932dce19..f088de89f6 100644
--- a/lib/modules/manager/pip_requirements/extract.spec.ts
+++ b/lib/modules/manager/pip_requirements/extract.spec.ts
@@ -190,6 +190,7 @@ some-package==0.3.1`;
             currentVersion: '20.3.0',
             datasource: 'pypi',
             depName: 'attrs',
+            packageName: 'attrs',
           },
         ],
       });
diff --git a/lib/modules/manager/pip_requirements/extract.ts b/lib/modules/manager/pip_requirements/extract.ts
index 3d1a3a7bfc..1921ae49a9 100644
--- a/lib/modules/manager/pip_requirements/extract.ts
+++ b/lib/modules/manager/pip_requirements/extract.ts
@@ -6,6 +6,7 @@ import { isSkipComment } from '../../../util/ignore';
 import { newlineRegex, regEx } from '../../../util/regex';
 import { GitTagsDatasource } from '../../datasource/git-tags';
 import { PypiDatasource } from '../../datasource/pypi';
+import { normalizePythonDepName } from '../../datasource/pypi/common';
 import type { PackageDependency, PackageFileContent } from '../types';
 import { extractPackageFileFlags } from './common';
 import type { PipRequirementsManagerData } from './types';
@@ -81,6 +82,7 @@ export function extractPackageFile(
       dep = {
         ...dep,
         depName,
+        packageName: normalizePythonDepName(depName),
         currentValue,
         datasource: PypiDatasource.id,
       };
diff --git a/lib/modules/manager/pip_setup/__snapshots__/extract.spec.ts.snap b/lib/modules/manager/pip_setup/__snapshots__/extract.spec.ts.snap
index 7246f4f88a..9ebc8247dc 100644
--- a/lib/modules/manager/pip_setup/__snapshots__/extract.spec.ts.snap
+++ b/lib/modules/manager/pip_setup/__snapshots__/extract.spec.ts.snap
@@ -10,6 +10,7 @@ exports[`modules/manager/pip_setup/extract extractPackageFile() returns found de
       "managerData": {
         "lineNumber": 49,
       },
+      "packageName": "celery",
     },
     {
       "currentValue": ">=1.7",
@@ -18,6 +19,7 @@ exports[`modules/manager/pip_setup/extract extractPackageFile() returns found de
       "managerData": {
         "lineNumber": 52,
       },
+      "packageName": "logging-tree",
     },
     {
       "currentValue": ">=2.2",
@@ -26,6 +28,7 @@ exports[`modules/manager/pip_setup/extract extractPackageFile() returns found de
       "managerData": {
         "lineNumber": 53,
       },
+      "packageName": "pygments",
     },
     {
       "currentValue": ">=5.0",
@@ -34,6 +37,7 @@ exports[`modules/manager/pip_setup/extract extractPackageFile() returns found de
       "managerData": {
         "lineNumber": 54,
       },
+      "packageName": "psutil",
     },
     {
       "currentValue": ">=3.0",
@@ -42,6 +46,7 @@ exports[`modules/manager/pip_setup/extract extractPackageFile() returns found de
       "managerData": {
         "lineNumber": 55,
       },
+      "packageName": "objgraph",
     },
     {
       "currentValue": ">=1.11.23,<2.0",
@@ -50,6 +55,7 @@ exports[`modules/manager/pip_setup/extract extractPackageFile() returns found de
       "managerData": {
         "lineNumber": 58,
       },
+      "packageName": "django",
     },
     {
       "currentValue": ">=0.11,<2.0",
@@ -58,6 +64,7 @@ exports[`modules/manager/pip_setup/extract extractPackageFile() returns found de
       "managerData": {
         "lineNumber": 61,
       },
+      "packageName": "flask",
     },
     {
       "currentValue": ">=1.4,<2.0",
@@ -66,6 +73,7 @@ exports[`modules/manager/pip_setup/extract extractPackageFile() returns found de
       "managerData": {
         "lineNumber": 62,
       },
+      "packageName": "blinker",
     },
     {
       "currentValue": ">=19.7.0,<20.0",
@@ -74,6 +82,7 @@ exports[`modules/manager/pip_setup/extract extractPackageFile() returns found de
       "managerData": {
         "lineNumber": 74,
       },
+      "packageName": "gunicorn",
     },
     {
       "currentValue": ">=0.15.3,<0.16",
@@ -82,6 +91,7 @@ exports[`modules/manager/pip_setup/extract extractPackageFile() returns found de
       "managerData": {
         "lineNumber": 75,
       },
+      "packageName": "werkzeug",
     },
     {
       "currentValue": ">=3.2.1,<4.0",
@@ -90,6 +100,7 @@ exports[`modules/manager/pip_setup/extract extractPackageFile() returns found de
       "managerData": {
         "lineNumber": 75,
       },
+      "packageName": "statsd",
     },
     {
       "currentValue": ">=2.10.0,<3.0",
@@ -98,6 +109,7 @@ exports[`modules/manager/pip_setup/extract extractPackageFile() returns found de
       "managerData": {
         "lineNumber": 76,
       },
+      "packageName": "requests",
       "skipReason": "ignored",
     },
     {
@@ -107,6 +119,7 @@ exports[`modules/manager/pip_setup/extract extractPackageFile() returns found de
       "managerData": {
         "lineNumber": 77,
       },
+      "packageName": "raven",
     },
     {
       "currentValue": ">=0.15.2,<0.17",
@@ -115,6 +128,7 @@ exports[`modules/manager/pip_setup/extract extractPackageFile() returns found de
       "managerData": {
         "lineNumber": 78,
       },
+      "packageName": "future",
     },
     {
       "currentValue": ">=1.0.16,<2.0",
@@ -123,6 +137,7 @@ exports[`modules/manager/pip_setup/extract extractPackageFile() returns found de
       "managerData": {
         "lineNumber": 79,
       },
+      "packageName": "ipaddress",
     },
     {
       "currentValue": ">=5.5.2,<6.0.0",
@@ -131,6 +146,7 @@ exports[`modules/manager/pip_setup/extract extractPackageFile() returns found de
       "managerData": {
         "lineNumber": 80,
       },
+      "packageName": "zope-interface",
     },
   ],
 }
diff --git a/lib/modules/manager/pip_setup/extract.ts b/lib/modules/manager/pip_setup/extract.ts
index cef10e2322..d1bb84626b 100644
--- a/lib/modules/manager/pip_setup/extract.ts
+++ b/lib/modules/manager/pip_setup/extract.ts
@@ -2,6 +2,7 @@ import { RANGE_PATTERN } from '@renovatebot/pep440';
 import { lang, lexer, parser, query as q } from 'good-enough-parser';
 import { regEx } from '../../../util/regex';
 import { PypiDatasource } from '../../datasource/pypi';
+import { normalizePythonDepName } from '../../datasource/pypi/common';
 import type {
   ExtractConfig,
   PackageDependency,
@@ -45,6 +46,7 @@ function depStringHandler(
 
   const dep: PackageDependency<ManagerData> = {
     depName,
+    packageName: normalizePythonDepName(depName),
     currentValue,
     managerData: {
       lineNumber: token.line - 1,
diff --git a/lib/modules/manager/pipenv/extract.ts b/lib/modules/manager/pipenv/extract.ts
index 10cc367e37..cfdae749cb 100644
--- a/lib/modules/manager/pipenv/extract.ts
+++ b/lib/modules/manager/pipenv/extract.ts
@@ -6,6 +6,7 @@ import { localPathExists } from '../../../util/fs';
 import { regEx } from '../../../util/regex';
 import { parse as parseToml } from '../../../util/toml';
 import { PypiDatasource } from '../../datasource/pypi';
+import { normalizePythonDepName } from '../../datasource/pypi/common';
 import type { PackageDependency, PackageFileContent } from '../types';
 import type { PipFile, PipRequirement, PipSource } from './types';
 
@@ -76,6 +77,7 @@ function extractFromSection(
       const dep: PackageDependency = {
         depType: sectionName,
         depName,
+        packageName: normalizePythonDepName(depName),
         managerData: {},
       };
       if (currentValue) {
diff --git a/lib/modules/manager/setup-cfg/__snapshots__/extract.spec.ts.snap b/lib/modules/manager/setup-cfg/__snapshots__/extract.spec.ts.snap
index bef3179985..f0be467fd0 100644
--- a/lib/modules/manager/setup-cfg/__snapshots__/extract.spec.ts.snap
+++ b/lib/modules/manager/setup-cfg/__snapshots__/extract.spec.ts.snap
@@ -8,12 +8,14 @@ exports[`modules/manager/setup-cfg/extract extractPackageFile() extracts depende
       "datasource": "pypi",
       "depName": "coloredlogs",
       "depType": "install",
+      "packageName": "coloredlogs",
     },
     {
       "currentValue": "~=1.0",
       "datasource": "pypi",
       "depName": "first",
       "depType": "install",
+      "packageName": "first",
     },
     {
       "currentValue": "==2.2",
@@ -21,48 +23,56 @@ exports[`modules/manager/setup-cfg/extract extractPackageFile() extracts depende
       "datasource": "pypi",
       "depName": "second",
       "depType": "install",
+      "packageName": "second",
     },
     {
       "currentValue": ">=3.0",
       "datasource": "pypi",
       "depName": "third",
       "depType": "install",
+      "packageName": "third",
     },
     {
       "currentValue": ">=5.5.5",
       "datasource": "pypi",
       "depName": "quux",
       "depType": "install",
+      "packageName": "quux",
     },
     {
       "currentValue": "~=2.1",
       "datasource": "pypi",
       "depName": "python-dateutil",
       "depType": "install",
+      "packageName": "python-dateutil",
     },
     {
       "currentValue": ">=1.1.1",
       "datasource": "pypi",
       "depName": "foo",
       "depType": "install",
+      "packageName": "foo",
     },
     {
       "currentValue": ">=3.3.3.",
       "datasource": "pypi",
       "depName": "baz",
       "depType": "install",
+      "packageName": "baz",
     },
     {
       "currentValue": "~=0.4",
       "datasource": "pypi",
       "depName": "docopt",
       "depType": "install",
+      "packageName": "docopt",
     },
     {
       "currentValue": "~=2.1",
       "datasource": "pypi",
       "depName": "fs",
       "depType": "install",
+      "packageName": "fs",
     },
     {
       "currentValue": "==1.0",
@@ -70,198 +80,231 @@ exports[`modules/manager/setup-cfg/extract extractPackageFile() extracts depende
       "datasource": "pypi",
       "depName": "nmspc.pkg",
       "depType": "install",
+      "packageName": "nmspc-pkg",
     },
     {
       "currentValue": "~=2.18",
       "datasource": "pypi",
       "depName": "requests",
       "depType": "install",
+      "packageName": "requests",
     },
     {
       "currentValue": "~=1.2.3",
       "datasource": "pypi",
       "depName": "compact",
       "depType": "install",
+      "packageName": "compact",
     },
     {
       "currentValue": ">=2.27.0",
       "datasource": "pypi",
       "depName": "responses",
       "depType": "install",
+      "packageName": "responses",
     },
     {
       "currentValue": "~=1.4",
       "datasource": "pypi",
       "depName": "six",
       "depType": "setup",
+      "packageName": "six",
     },
     {
       "currentValue": "~=4.19",
       "datasource": "pypi",
       "depName": "tqdm",
       "depType": "setup",
+      "packageName": "tqdm",
     },
     {
       "currentValue": "~=6.0",
       "datasource": "pypi",
       "depName": "tenacity",
       "depType": "setup",
+      "packageName": "tenacity",
     },
     {
       "currentValue": "~=3.6",
       "datasource": "pypi",
       "depName": "typing",
       "depType": "test",
+      "packageName": "typing",
     },
     {
       "currentValue": "~=1.7",
       "datasource": "pypi",
       "depName": "verboselogs",
       "depType": "test",
+      "packageName": "verboselogs",
     },
     {
       "currentValue": undefined,
       "datasource": "pypi",
       "depName": "piexif",
       "depType": "extra",
+      "packageName": "piexif",
     },
     {
       "currentValue": undefined,
       "datasource": "pypi",
       "depName": "Pillow",
       "depType": "extra",
+      "packageName": "pillow",
     },
     {
       "currentValue": ">=2.2.2",
       "datasource": "pypi",
       "depName": "bar",
       "depType": "extra",
+      "packageName": "bar",
     },
     {
       "currentValue": ">=4.4.4",
       "datasource": "pypi",
       "depName": "qux",
       "depType": "extra",
+      "packageName": "qux",
     },
     {
       "currentValue": "~=0.1",
       "datasource": "pypi",
       "depName": "contexter",
       "depType": "extra",
+      "packageName": "contexter",
     },
     {
       "currentValue": "~=2.0",
       "datasource": "pypi",
       "depName": "mock",
       "depType": "extra",
+      "packageName": "mock",
     },
     {
       "currentValue": "~=0.6",
       "datasource": "pypi",
       "depName": "parameterized",
       "depType": "extra",
+      "packageName": "parameterized",
     },
     {
       "currentValue": "~=2.12",
       "datasource": "pypi",
       "depName": "green",
       "depType": "extra",
+      "packageName": "green",
     },
     {
       "currentValue": undefined,
       "datasource": "pypi",
       "depName": "coverage",
       "depType": "extra",
+      "packageName": "coverage",
     },
     {
       "currentValue": undefined,
       "datasource": "pypi",
       "depName": "codecov",
       "depType": "extra",
+      "packageName": "codecov",
     },
     {
       "currentValue": undefined,
       "datasource": "pypi",
       "depName": "codacy-coverage",
       "depType": "extra",
+      "packageName": "codacy-coverage",
     },
     {
       "currentValue": "~=1.7",
       "datasource": "pypi",
       "depName": "sphinx",
       "depType": "extra",
+      "packageName": "sphinx",
     },
     {
       "currentValue": "~=0.6",
       "datasource": "pypi",
       "depName": "sphinx-bootstrap-theme",
       "depType": "extra",
+      "packageName": "sphinx-bootstrap-theme",
     },
     {
       "currentValue": "~=2.6",
       "datasource": "pypi",
       "depName": "semantic-version",
       "depType": "extra",
+      "packageName": "semantic-version",
     },
     {
       "currentValue": undefined,
       "datasource": "pypi",
       "depName": "docutils",
       "depType": "extra",
+      "packageName": "docutils",
     },
     {
       "currentValue": undefined,
       "datasource": "pypi",
       "depName": "Pygments",
       "depType": "extra",
+      "packageName": "pygments",
     },
     {
       "currentValue": ">=0.9",
       "datasource": "pypi",
       "depName": "aiortc",
       "depType": "install",
+      "packageName": "aiortc",
     },
     {
       "currentValue": ">=8.1",
       "datasource": "pypi",
       "depName": "websockets",
       "depType": "install",
+      "packageName": "websockets",
     },
     {
       "currentValue": ">=3.6",
       "datasource": "pypi",
       "depName": "aiohttp",
       "depType": "install",
+      "packageName": "aiohttp",
     },
     {
       "currentValue": ">=6.0",
       "datasource": "pypi",
       "depName": "pyee",
       "depType": "install",
+      "packageName": "pyee",
     },
     {
       "currentValue": ">=8.1",
       "datasource": "pypi",
       "depName": "websockets",
       "depType": "install",
+      "packageName": "websockets",
     },
     {
       "currentValue": ">=0.3",
       "datasource": "pypi",
       "depName": "dataclasses_json",
       "depType": "install",
+      "packageName": "dataclasses-json",
     },
     {
       "currentValue": ">=10.0",
       "datasource": "pypi",
       "depName": "coloredlogs",
       "depType": "install",
+      "packageName": "coloredlogs",
     },
     {
       "currentValue": "~=8.0.0",
       "datasource": "pypi",
       "depName": "av",
       "depType": "install",
+      "packageName": "av",
     },
   ],
 }
diff --git a/lib/modules/manager/setup-cfg/extract.ts b/lib/modules/manager/setup-cfg/extract.ts
index 3aaca63523..017f9903ad 100644
--- a/lib/modules/manager/setup-cfg/extract.ts
+++ b/lib/modules/manager/setup-cfg/extract.ts
@@ -3,6 +3,7 @@ import { RANGE_PATTERN } from '@renovatebot/pep440';
 import { logger } from '../../../logger';
 import { newlineRegex, regEx } from '../../../util/regex';
 import { PypiDatasource } from '../../datasource/pypi';
+import { normalizePythonDepName } from '../../datasource/pypi/common';
 import type { PackageDependency, PackageFileContent, Result } from '../types';
 
 function getSectionName(str: string): string {
@@ -73,6 +74,7 @@ function parseDep(
 
   const dep: PackageDependency = {
     depName,
+    packageName: normalizePythonDepName(depName),
     currentValue,
     datasource: PypiDatasource.id,
     depType,
diff --git a/lib/modules/platform/github/index.spec.ts b/lib/modules/platform/github/index.spec.ts
index 4577891621..47127a5504 100644
--- a/lib/modules/platform/github/index.spec.ts
+++ b/lib/modules/platform/github/index.spec.ts
@@ -3889,6 +3889,38 @@ describe('modules/platform/github/index', () => {
       );
       expect(logger.logger.error).not.toHaveBeenCalled();
     });
+
+    it('returns normalized names for PIP ecosystem', async () => {
+      const scope = httpMock.scope(githubApiHost);
+      initRepoMock(scope, 'some/repo');
+      scope
+        .get(
+          '/repos/some/repo/dependabot/alerts?state=open&direction=asc&per_page=100',
+        )
+        .reply(200, [
+          {
+            security_advisory: {
+              description: 'description',
+              identifiers: [{ type: 'type', value: 'value' }],
+              references: [],
+            },
+            security_vulnerability: {
+              package: {
+                ecosystem: 'pip',
+                name: 'FrIeNdLy.-.BARD',
+              },
+              vulnerable_version_range: '0.0.2',
+              first_patched_version: { identifier: '0.0.3' },
+            },
+            dependency: {
+              manifest_path: 'bar/foo',
+            },
+          },
+        ]);
+      await github.initRepo({ repository: 'some/repo' });
+      const res = await github.getVulnerabilityAlerts();
+      expect(res[0].security_vulnerability!.package.name).toBe('friendly-bard');
+    });
   });
 
   describe('getJsonFile()', () => {
diff --git a/lib/modules/platform/github/index.ts b/lib/modules/platform/github/index.ts
index a805ed7d7a..01f508b5ff 100644
--- a/lib/modules/platform/github/index.ts
+++ b/lib/modules/platform/github/index.ts
@@ -64,6 +64,7 @@ import type {
   UpdatePrConfig,
 } from '../types';
 import { repoFingerprint } from '../util';
+import { normalizeNamePerEcosystem } from '../utils/github-alerts';
 import { smartTruncate } from '../utils/pr-body';
 import { remoteBranchExists } from './branch';
 import { coerceRestPr, githubApi } from './common';
@@ -2008,7 +2009,9 @@ export async function getVulnerabilityAlerts(): Promise<VulnerabilityAlert[]> {
         } = alert.security_vulnerability;
         const patch = firstPatchedVersion?.identifier;
 
-        const key = `${ecosystem.toLowerCase()}/${name}`;
+        const normalizedName = normalizeNamePerEcosystem({ name, ecosystem });
+        alert.security_vulnerability.package.name = normalizedName;
+        const key = `${ecosystem.toLowerCase()}/${normalizedName}`;
         const range = vulnerableVersionRange;
         const elem = shortAlerts[key] || {};
         elem[range] = coerceToNull(patch);
diff --git a/lib/modules/platform/utils/github-alerts.ts b/lib/modules/platform/utils/github-alerts.ts
new file mode 100644
index 0000000000..b34a75e094
--- /dev/null
+++ b/lib/modules/platform/utils/github-alerts.ts
@@ -0,0 +1,14 @@
+import type { VulnerabilityPackage } from '../../../types';
+import { normalizePythonDepName } from '../../datasource/pypi/common';
+
+export function normalizeNamePerEcosystem({
+  name,
+  ecosystem,
+}: VulnerabilityPackage): string {
+  switch (ecosystem) {
+    case 'pip':
+      return normalizePythonDepName(name);
+    default:
+      return name;
+  }
+}
diff --git a/lib/workers/repository/init/vulnerability.spec.ts b/lib/workers/repository/init/vulnerability.spec.ts
index 96d1a68ccc..1be284c8e9 100644
--- a/lib/workers/repository/init/vulnerability.spec.ts
+++ b/lib/workers/repository/init/vulnerability.spec.ts
@@ -351,39 +351,5 @@ describe('workers/repository/init/vulnerability', () => {
       expect(res.packageRules).toMatchSnapshot();
       expect(res.packageRules).toHaveLength(1);
     });
-
-    it('returns pip alerts with normalized name', async () => {
-      // TODO #22198
-      delete config.vulnerabilityAlerts!.enabled;
-      platform.getVulnerabilityAlerts.mockResolvedValue([
-        {
-          dismissed_reason: null,
-          dependency: {
-            manifest_path: 'requirements.txt',
-          },
-          security_advisory: {
-            description: 'Description',
-            identifiers: [
-              { type: 'GHSA', value: 'GHSA-m956-frf4-m2wr' },
-              { type: 'CVE', value: 'CVE-2016-2137' },
-            ],
-            references: [
-              { url: 'https://nvd.nist.gov/vuln/detail/CVE-2016-9587' },
-            ],
-          },
-          security_vulnerability: {
-            package: { name: 'Pillow', ecosystem: 'pip' },
-            first_patched_version: { identifier: '2.1.4' },
-            vulnerable_version_range: '< 2.1.4',
-          },
-        },
-      ]);
-      const res = await detectVulnerabilityAlerts(config);
-      expect(res.packageRules).toHaveLength(1);
-      expect(res.packageRules![0].matchPackageNames).toEqual([
-        'Pillow',
-        'pillow',
-      ]);
-    });
   });
 });
diff --git a/lib/workers/repository/init/vulnerability.ts b/lib/workers/repository/init/vulnerability.ts
index fb64fca278..264ba11d69 100644
--- a/lib/workers/repository/init/vulnerability.ts
+++ b/lib/workers/repository/init/vulnerability.ts
@@ -8,7 +8,6 @@ import { NpmDatasource } from '../../../modules/datasource/npm';
 import { NugetDatasource } from '../../../modules/datasource/nuget';
 import { PackagistDatasource } from '../../../modules/datasource/packagist';
 import { PypiDatasource } from '../../../modules/datasource/pypi';
-import { normalizePythonDepName } from '../../../modules/datasource/pypi/common';
 import { RubyGemsDatasource } from '../../../modules/datasource/rubygems';
 import { platform } from '../../../modules/platform';
 import * as allVersioning from '../../../modules/versioning';
@@ -191,12 +190,7 @@ export async function detectVulnerabilityAlerts(
           matchPackageNames: [depName],
           matchFileNames,
         };
-        if (
-          datasource === PypiDatasource.id &&
-          normalizePythonDepName(depName) !== depName
-        ) {
-          matchRule.matchPackageNames?.push(normalizePythonDepName(depName));
-        }
+
         // Remediate only direct dependencies
         matchRule = {
           ...matchRule,
-- 
GitLab