Add custom Mitigated field

This field stores a list of comma-separated CVE IDs that the dependency mitigates.

The field is validated to contain only valid CVE IDs.


Bug: b/392026683
Change-Id: I9578fc709086131695cfa7eee51e717c24440853
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/6197756
Reviewed-by: Jiewei Qian <qjw@chromium.org>
Commit-Queue: Jordan Brown <rop@google.com>
changes/56/6197756/29
Jordan Brown 4 months ago committed by LUCI CQ
parent f4ac21ad79
commit 98b7273c8d

@ -305,6 +305,10 @@ class DependencyMetadata:
def name(self) -> Optional[str]:
return self._return_as_property(known_fields.NAME)
@property
def mitigated(self) -> Optional[List[str]]:
return self._return_as_property(known_fields.MITIGATED)
@property
def short_name(self) -> Optional[str]:
return self._return_as_property(known_fields.SHORT_NAME)

@ -0,0 +1,85 @@
#!/usr/bin/env python3
# Copyright 2025 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import re
from typing import List, Optional, Tuple
import metadata.fields.field_types as field_types
import metadata.fields.util as util
import metadata.validation_result as vr
# List of supported vulnerability ID prefixes.
_VULN_PREFIXES = [
"CVE", # Common Vulnerabilities and Exposures.
"GHSA", # GitHub Security Advisory.
"PYSEC", # Python Security Advisory.
"OSV", # Open Source Vulnerability.
"DSA", # Debian Security Advisory.
]
_PREFIX_PATTERN = "|".join(_VULN_PREFIXES)
_VULN_ID_PATTERN = re.compile(
rf"^({_PREFIX_PATTERN})-[a-zA-Z0-9]{{4}}-[a-zA-Z0-9:-]+$")
def validate_vuln_ids(vuln_ids: str) -> Tuple[List[str], List[str]]:
"""
Validates a list of vulnerability identifiers and returns valid and invalid IDs.
Supports multiple formats:
- CVE IDs (e.g., CVE-2024-12345)
- GitHub Security Advisories (e.g., GHSA-1234-5678-90ab)
- Python Security Advisories (e.g., PYSEC-2024-1234)
- Open Source Vulnerabilities (e.g., OSV-2024-1234)
- Debian Security Advisories (e.g., DSA-1234-1)
Args:
vuln_ids: List of vulnerability identifiers to validate
Returns:
Tuple of (valid_ids, invalid_ids)
"""
valid_vuln_ids = []
invalid_vuln_ids = []
for cve in vuln_ids.split(","):
cve_stripped = cve.strip()
if _VULN_ID_PATTERN.match(cve_stripped):
valid_vuln_ids.append(cve_stripped)
else:
invalid_vuln_ids.append(cve)
return valid_vuln_ids, invalid_vuln_ids
class MitigatedField(field_types.SingleLineTextField):
"""Field for comma-separated vulnerability IDs."""
def __init__(self):
super().__init__(name="Mitigated")
def validate(self, value: str) -> Optional[vr.ValidationResult]:
"""Checks if the value contains valid CVE IDs."""
if util.is_empty(value):
return None
_, invalid_vuln_ids = validate_vuln_ids(value)
if invalid_vuln_ids:
return vr.ValidationWarning(
reason=f"{self._name} contains invalid vulnerability IDs.",
additional=[
f"Invalid Vulnerability IDs: {util.quoted(invalid_vuln_ids)}",
"The following identifiers are supported: " +
", ".join(_VULN_PREFIXES),
],
)
return None
def narrow_type(self, value: str) -> Optional[List[str]]:
if not value:
return None
vuln_ids, _ = validate_vuln_ids(value)
return vuln_ids

@ -19,6 +19,7 @@ import metadata.fields.custom.date
import metadata.fields.custom.license
import metadata.fields.custom.license_file
import metadata.fields.custom.local_modifications
import metadata.fields.custom.mitigated
import metadata.fields.custom.url
import metadata.fields.custom.version
import metadata.fields.custom.revision
@ -46,6 +47,7 @@ VERSION = metadata.fields.custom.version.VersionField()
REVISION = metadata.fields.custom.revision.RevisionField()
LOCAL_MODIFICATIONS = metadata.fields.custom.local_modifications.LocalModificationsField(
)
MITIGATED = metadata.fields.custom.mitigated.MitigatedField()
ALL_FIELDS = (
NAME,
@ -61,6 +63,7 @@ ALL_FIELDS = (
SHIPPED_IN_CHROMIUM,
LICENSE_ANDROID_COMPATIBLE,
CPE_PREFIX,
MITIGATED,
DESCRIPTION,
LOCAL_MODIFICATIONS,
)

@ -18,6 +18,7 @@ sys.path.insert(0, _ROOT_DIR)
import metadata.fields.known as known_fields
import metadata.fields.field_types as field_types
import metadata.validation_result as vr
import metadata.fields.custom.mitigated
class FieldValidationTest(unittest.TestCase):
@ -232,5 +233,32 @@ class FieldValidationTest(unittest.TestCase):
self.assertFalse(
known_fields.LOCAL_MODIFICATIONS.should_terminate_field(value))
def test_vulnerability_ids(self):
valid_ids = [
"CVE-2024-12345",
"CVE-2024-1234567",
"PYSEC-2024-1234",
"OSV-2024-1234",
"DSA-1234-1",
"GHSA-1234-5678-90ab",
]
invalid_ids = [
"CVE-123-456",
"GHSA-123-456",
"PYSEC-2024", # Missing ID part.
"NOT-A-VALID-ID", # Bad prefix.
"CVE_2024_12345", # Wrong separator.
"", # Empty.
" ", # Just space.
]
test_ids = valid_ids + invalid_ids
valid_result, invalid_result = metadata.fields.custom.mitigated.validate_vuln_ids(
",".join(test_ids))
self.assertListEqual(sorted(valid_result), sorted(valid_ids))
self.assertListEqual(sorted(invalid_result), sorted(invalid_ids))
if __name__ == "__main__":
unittest.main()

Loading…
Cancel
Save