#!/usr/bin/env python3
# Copyright 2025 The Chromium Authors
# 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.
]

_PATTERN_PREFIX = "|".join(_VULN_PREFIXES)
PATTERN_VULN_ID = re.compile(
    rf"({_PATTERN_PREFIX})-[a-zA-Z0-9]{{4}}-[a-zA-Z0-9:-]+"
)
PATTERN_VULN_ID_WITH_ANCHORS = re.compile(f"^{PATTERN_VULN_ID.pattern}$")


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 PATTERN_VULN_ID_WITH_ANCHORS.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, **kwargs) -> 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
