Übermäßige McCabe-zyklomatische Komplexität

Beschreibung

Übermäßige McCabe-zyklomatische Komplexität tritt auf, wenn Code eine McCabe-zyklomatische Komplexität aufweist, die einen wünschenswerten Maximalwert überschreitet. Die zyklomatische Komplexität ist ein quantitatives Maß für die Anzahl linear unabhängiger Pfade durch den Quellcode eines Programms, berechnet basierend auf dem Kontrollfluss-Graphen. Jeder Entscheidungspunkt (if, else, switch case, Schleife, logischer Operator) erhöht die Komplexität. Hohe zyklomatische Komplexität zeigt an, dass Code schwer zu verstehen, zu testen und zu warten ist, was Sicherheitslücken schwerer identifizier- und behebbar macht.

Risiko

Hohe zyklomatische Komplexität hat signifikante indirekte Sicherheitsauswirkungen. Mehr Ausführungspfade bedeuten mehr Potenzial für Bugs, einschließlich Sicherheitsbugs. Das Testen aller Pfade wird unpraktisch und lässt ungetestete Grenzfälle. Code-Reviews sind weniger effektiv, wenn Reviewer nicht allen Pfaden folgen können. Sicherheitsauditoren übersehen möglicherweise Schwachstellen in komplexen Funktionen. Wartungsänderungen haben ein höheres Risiko, bestehende Funktionalität zu brechen. Bedingte Sicherheitsprüfungen können über Pfade hinweg inkonsistent angewendet werden. Statische Analysetools können Timeout-Probleme haben oder unzuverlässige Ergebnisse liefern. Notfall-Sicherheitspatches sind in komplexem Code riskanter.

Lösung

Setzen Sie maximale Schwellenwerte für zyklomatische Komplexität (üblicherweise 10 oder 15). Refaktorisieren Sie Funktionen, die Schwellenwerte überschreiten, in kleinere Einheiten. Extrahieren Sie komplexe bedingte Logik in separate Methoden. Verwenden Sie Polymorphismus statt komplexer switch-Anweisungen. Wenden Sie Guard-Klauseln an, um Verschachtelung und Komplexität zu reduzieren. Verwenden Sie Lookup-Tabellen statt länger if-else-Ketten. Automatisieren Sie Komplexitätsprüfungen in CI/CD-Pipelines. Priorisieren Sie das Refactoring sicherheitskritischen hochkomplexen Codes. Verwenden Sie Entwurfsmuster wie Strategy oder State für komplexes Verhalten. Verfolgen Sie Komplexitätsmetriken über die Zeit.

Häufige Auswirkungen

AuswirkungDetails
SonstigesBereich: Sonstiges

Reduzierte Wartbarkeit -- Macht es schwieriger, das Produkt zu verstehen und zu warten, was die Sicherheit indirekt beeinträchtigt, indem Schwachstellen schwerer zu finden und zu beheben sind. Kann die Einführung neuer Schwachstellen erleichtern.
SonstigesBereich: Sonstiges

Erhöhte analytische Komplexität -- Hohe zyklomatische Komplexität macht eine gründliche Sicherheitsanalyse unpraktisch.

Beispielcode und Lösung

Verwundbarer Code

// Verwundbar: Zyklomatische Komplexität von ungefähr 25+
// Jedes if/else/case/&&/|| erhöht die Komplexität

public class PaymentValidator {

    public ValidationResult validatePayment(Payment payment, User user, Context ctx) {
        // Komplexität beginnt bei 1, steigt mit jeder Verzweigung

        if (payment == null) {
            return ValidationResult.error("null_payment");
        }

        if (user == null) {
            return ValidationResult.error("null_user");
        }

        if (!user.isActive() && !user.isGracePeriod()) {
            return ValidationResult.error("inactive_user");
        }

        // ... viele weitere Verzweigungen ...

        switch (payment.getMethod()) {
            case CREDIT_CARD:
                if (payment.getCardNumber() == null) {
                    return ValidationResult.error("missing_card");
                }
                if (!validateLuhn(payment.getCardNumber())) {
                    return ValidationResult.error("invalid_card");
                }
                // ... weitere verschachtelte Prüfungen ...
                break;
            case DEBIT_CARD:
                // ... ähnliche Prüfungen ...
                break;
            case BANK_TRANSFER:
                // ... ähnliche Prüfungen ...
                break;
        }

        // Gesamte zyklomatische Komplexität: ~27
        return ValidationResult.success();
    }
}

Sichere Lösung

// SICHER: Refaktorisiert um zyklomatische Komplexität zu reduzieren
// Jede Methode hat Komplexität < 10

public class PaymentValidator {

    private final Map<PaymentMethod, PaymentMethodValidator> validators;

    public PaymentValidator() {
        // Strategy-Pattern ersetzt komplexen Switch
        validators = Map.of(
            PaymentMethod.CREDIT_CARD, new CreditCardValidator(),
            PaymentMethod.DEBIT_CARD, new DebitCardValidator(),
            PaymentMethod.BANK_TRANSFER, new BankTransferValidator(),
            PaymentMethod.CRYPTO, new CryptoValidator()
        );
    }

    /**
     * Haupt-Validierungseinstiegspunkt.
     * Zyklomatische Komplexität: 4
     */
    public ValidationResult validatePayment(Payment payment, User user, Context ctx) {
        ValidationResult inputValidation = validateInputs(payment, user);
        if (!inputValidation.isSuccess()) {
            return inputValidation;
        }

        ValidationResult contextValidation = validateContext(user, ctx);
        if (!contextValidation.isSuccess()) {
            return contextValidation;
        }

        ValidationResult amountValidation = validateAmount(payment, ctx);
        if (!amountValidation.isSuccess()) {
            return amountValidation;
        }

        return validatePaymentMethod(payment);
    }

    /** Eingabevalidierung. Zyklomatische Komplexität: 3 */
    private ValidationResult validateInputs(Payment payment, User user) {
        if (payment == null) return ValidationResult.error("null_payment");
        if (user == null) return ValidationResult.error("null_user");
        if (!user.isActive() && !user.isGracePeriod())
            return ValidationResult.error("inactive_user");
        return ValidationResult.success();
    }

    /** Zahlungsmethoden-Validierung mit Strategy-Pattern. Komplexität: 2 */
    private ValidationResult validatePaymentMethod(Payment payment) {
        PaymentMethodValidator validator = validators.get(payment.getMethod());
        if (validator == null) return ValidationResult.error("unsupported_method");
        return validator.validate(payment);
    }
}

// Separate Validator-Klassen mit niedriger Komplexität
class CreditCardValidator implements PaymentMethodValidator {
    /** Kreditkarten-Validierung. Zyklomatische Komplexität: 5 */
    @Override
    public ValidationResult validate(Payment payment) {
        if (payment.getCardNumber() == null) return ValidationResult.error("missing_card");
        if (!LuhnValidator.validate(payment.getCardNumber())) return ValidationResult.error("invalid_card");
        if (ExpiryChecker.isExpired(payment.getExpiry())) return ValidationResult.error("card_expired");
        if (!isValidCvv(payment.getCvv())) return ValidationResult.error("invalid_cvv");
        return ValidationResult.success();
    }
}
# SICHER: Refaktorisiert mit niedrigerer zyklomatischer Komplexität

class TransactionProcessor:
    """Verarbeitet Transaktionen mit handhabbarer Komplexität pro Methode."""

    def __init__(self, config):
        self.config = config
        # Strategy-Pattern für Transaktionstypen
        self.handlers = {
            "withdrawal": self._handle_withdrawal,
            "deposit": self._handle_deposit,
            "transfer": self._handle_transfer,
        }

    def process_transaction(self, transaction, account):
        """Haupt-Einstiegspunkt. Zyklomatische Komplexität: 4"""
        error = self._validate_inputs(transaction, account)
        if error:
            return TransactionResult.error(error)

        error = self._validate_amount(transaction, account)
        if error:
            return TransactionResult.error(error)

        handler = self.handlers.get(transaction.type)
        if not handler:
            return TransactionResult.error("unknown_transaction_type")

        return handler(transaction, account)

    def _validate_inputs(self, transaction, account):
        """Eingabeparameter validieren. Zyklomatische Komplexität: 4"""
        if transaction is None: return "null_transaction"
        if account is None: return "null_account"
        if not account.is_active and not account.in_grace_period:
            return "inactive_account"
        return None

    def _handle_withdrawal(self, transaction, account):
        """Abhebungen behandeln. Zyklomatische Komplexität: 3"""
        if account.balance < transaction.amount:
            return TransactionResult.error("insufficient_funds")
        if transaction.amount > self.config.max_withdrawal:
            return TransactionResult.error("exceeds_max_withdrawal")
        return self._execute(transaction, account)

CVE-Beispiele

Diese CWE ist als VERBOTEN für direkte CVE-Zuordnung markiert, da sie ein Codequalitätsproblem darstellt und keine direkte Sicherheitslücke.


Verwandte CWEs

  • CWE-1120: Übermäßige Code-Komplexität (Eltern)
  • CWE-1122: Übermäßige Halstead-Komplexität (verwandt)
  • CWE-1226: Komplexitätsprobleme (Kategoriemitglied)
  • CWE-1130: CISQ-Qualitätsmaße - Wartbarkeit (Kategoriemitglied)

Referenzen

  1. MITRE Corporation. "CWE-1121: Excessive McCabe Cyclomatic Complexity." https://cwe.mitre.org/data/definitions/1121.html
  2. McCabe, T.J. (1976). "A Complexity Measure." IEEE Transactions on Software Engineering.
  3. NIST Complexity Guidelines
  4. CISQ Automated Quality Characteristic Measures