Übermäßige Halstead-Komplexität

Beschreibung

Übermäßige Halstead-Komplexität tritt auf, wenn Code so strukturiert ist, dass die Halstead-Komplexitätsmaße wünschenswerte Höchstwerte überschreiten. Halstead-Komplexitätsmetriken quantifizieren die Softwarekomplexität basierend auf Operatoren und Operanden im Code. Wichtige Halstead-Metriken umfassen das Programmvokabular (n = n1 + n2, Gesamtzahl der unterschiedlichen Operatoren und Operanden), die Programmlange (N = N1 + N2, Gesamtzahl der Operatoren und Operanden), das Volumen (V = N × log2(n)), die Schwierigkeit (D = (n1/2) × (N2/n2)) und den Aufwand (E = D × V). Hohe Halstead-Metriken weisen auf Code hin, der schwer zu verstehen, zu warten und auf Sicherheit zu prüfen ist.

Risiko

Übermäßige Halstead-Komplexität hat indirekte Sicherheitsauswirkungen. Code mit hohem Volumen ist schwerer vollständig zu verstehen. Hohe Schwierigkeitswerte weisen auf fehleranfälligen Code hin. Hohe Aufwandsmetriken korrelieren mit der Fehlerdichte. Sicherheitsprüfer können Schwachstellen in dichtem Code übersehen. Wartungsänderungen führen eher zu Defekten. Testabdeckung ist schwer zu erreichen. Statische Analyse kann unzuverlässige Ergebnisse liefern. Komplexe Ausdrücke sind schwer auf Korrektheit zu überprüfen.

Lösung

Legen Sie maximale Halstead-Metrikschwellenwerte für die Codequalität fest. Zerlegen Sie komplexe Ausdrücke in einfachere Komponenten. Reduzieren Sie die Anzahl der eindeutigen Operatoren, wo möglich. Verwenden Sie aussagekräftige Variablennamen, um die kognitive Belastung zu reduzieren. Extrahieren Sie komplexe Berechnungen in gut benannte Methoden. Vermeiden Sie tief verschachtelte Ausdrücke. Verwenden Sie Zwischenvariablen zur Verdeutlichung. Wenden Sie automatisierte Tools an, um Halstead-Metriken zu messen und zu verfolgen. Refaktorisieren Sie Code mit hoher Komplexität, insbesondere sicherheitskritische Abschnitte. Berücksichtigen Sie Halstead-Metriken bei der Code-Review.

Häufige Auswirkungen

AuswirkungDetails
SonstigeBereich: Sonstige

Reduzierte Wartbarkeit - Der Code wird schwerer zu verstehen und zu warten, was die Sicherheit indirekt beeinträchtigt, indem die Erkennung und Behebung von Schwachstellen erschwert wird.
SonstigeBereich: Sonstige

Erhöhte analytische Komplexität - Hohe Halstead-Komplexität erschwert die Codeanalyse und macht sie fehleranfälliger, was möglicherweise das Einführen neuer Schwachstellen erleichtert.

Beispielcode und Lösung

Verwundbarer Code

// VERWUNDBAR: Hohe Halstead-Komplexität
// Viele unterschiedliche Operatoren und Operanden in dichten Ausdrücken

public class ComplexCalculator {

    // Hohes Volumen, hohe Schwierigkeit - schwer auf Korrektheit zu überprüfen
    public double calculateRisk(Portfolio p, Market m, double t, double r, double v) {
        // Dieser einzelne Ausdruck hat:
        // - Viele Operatoren: +, -, *, /, %, Math.pow, Math.sqrt, Math.exp, Math.log
        // - Viele Operanden: p.value, m.volatility, t, r, v, Konstanten
        // Ergebnis: Sehr hohes Halstead-Volumen und Schwierigkeit

        return ((p.getValue() * Math.exp(-r * t) *
                (1 + Math.pow(Math.log(p.getValue() / m.getStrike()) +
                (r + v * v / 2) * t, 2) / (v * Math.sqrt(t)))) -
                (m.getStrike() * Math.exp(-r * t) *
                (1 - Math.pow(Math.log(p.getValue() / m.getStrike()) +
                (r - v * v / 2) * t, 2) / (v * Math.sqrt(t))))) *
                (p.getQuantity() * (1 - p.getHedgeRatio()) +
                m.getCorrelation() * p.getBeta() * Math.sqrt(p.getDuration() / 365.0));
    }

    // Eine weitere Methode mit hoher Komplexität
    public boolean validateTransaction(Transaction tx, User u, Context ctx) {
        // Dichter boolescher Ausdruck - sehr hoher Schwierigkeitsgrad
        return tx != null && u != null && ctx != null &&
               tx.getAmount() > 0 && tx.getAmount() <= u.getLimit() &&
               (u.getRole().equals("admin") || u.getRole().equals("trader")) &&
               (!ctx.isHighRisk() || (u.hasApproval() && tx.getAmount() < 10000)) &&
               (tx.getType().equals("buy") || tx.getType().equals("sell")) &&
               (ctx.getTime().isAfter(ctx.getMarketOpen()) &&
                ctx.getTime().isBefore(ctx.getMarketClose())) &&
               (!u.isRestricted() || ctx.hasOverride()) &&
               (tx.getCurrency().equals("USD") ||
                (tx.getCurrency().equals("EUR") && ctx.allowsForeignCurrency()));
    }
}
# VERWUNDBAR: Python mit hoher Halstead-Komplexität

def process_data(data, config, state, params):
    """
    Komplexe Funktion mit hohen Halstead-Metriken.
    Viele Operatoren, viele Operanden, dichte Ausdrücke.
    """
    # Ausdruck mit hoher Komplexität - schwer auf Korrektheit zu überprüfen
    result = (
        ((data['value'] * params['factor'] + config['offset']) /
         (state['divisor'] if state['divisor'] != 0 else 1)) *
        (1 + (data['adjustment'] - config['baseline']) /
         (params['range'] if params['range'] > 0 else 1)) *
        ((state['multiplier'] ** params['exponent']) /
         (config['scale'] * (1 + data['variance']))) +
        (params['constant'] * (1 - state['decay_rate'] ** data['age'])) -
        (config['penalty'] * max(0, data['deviation'] - params['threshold']))
    )

    # Komplexe Validierung mit vielen Operatoren
    is_valid = (
        data is not None and
        config is not None and
        state is not None and
        params is not None and
        'value' in data and
        'factor' in params and
        data['value'] >= config.get('min_value', 0) and
        data['value'] <= config.get('max_value', float('inf')) and
        (params['factor'] > 0 or params.get('allow_negative', False)) and
        (state['divisor'] != 0 or config.get('allow_zero_divisor', False)) and
        (data.get('age', 0) < params.get('max_age', 365) or
         config.get('ignore_age', False))
    )

    return result if is_valid else None


# Ein weiteres Beispiel mit dichten bitweisen Operationen
def encode_flags(permissions, status, options, metadata):
    """Hohe Halstead-Komplexität durch dichte Operatorverwendung."""
    return (
        ((permissions & 0xFF) << 24) |
        ((status & 0x0F) << 20) |
        ((options & 0x0FFF) << 8) |
        (metadata & 0xFF) |
        ((permissions >> 8) & 0x0F) << 4 |
        ((1 if (permissions & 0x100) else 0) |
         (2 if (status & 0x10) else 0) |
         (4 if (options & 0x1000) else 0))
    )
// VERWUNDBAR: C-Code mit extrem hoher Halstead-Komplexität

// Dichtes Makro mit hoher Operator-/Operandenanzahl
#define COMPLEX_CALC(a, b, c, d, e) \
    (((a) * (b) + (c)) / ((d) != 0 ? (d) : 1) * \
     ((e) > 0 ? pow((a), (e)) : 1.0) + \
     ((b) - (c)) * ((d) / ((e) + 1.0)) - \
     sqrt(abs((a) * (b) - (c) * (d))) / \
     (1.0 + exp(-((a) + (b)) / ((c) + (d) + 1.0))))

// Funktion mit sehr hohem Halstead-Volumen
double process_sensor_data(SensorData *s, Config *c, State *st) {
    // Einzelner Ausdruck mit massiver Komplexität
    return COMPLEX_CALC(s->temp, s->pressure, c->offset, st->scale, c->exp) *
           (s->humidity > c->threshold ?
            (s->humidity - c->threshold) * c->factor :
            (c->threshold - s->humidity) * c->inverse_factor) +
           ((s->temp > c->max_temp || s->temp < c->min_temp) ?
            c->temp_penalty * abs(s->temp - c->normal_temp) : 0) -
           ((s->pressure < c->min_pressure) ?
            c->pressure_adjustment * (c->min_pressure - s->pressure) : 0) *
           (1.0 / (1.0 + exp(-st->sensitivity * (s->reading - st->baseline))));
}

Sichere Lösung

// SICHER: Reduzierte Halstead-Komplexität durch Zerlegung

public class ClearCalculator {

    /**
     * Berechnet das Portfoliorisiko mit einem Black-Scholes-inspirierten Modell.
     * Halstead-Metriken durch Zerlegung reduziert.
     */
    public double calculateRisk(Portfolio portfolio, Market market,
                                double time, double rate, double volatility) {
        // Komplexe Berechnung in verständliche Teile zerlegen
        double discountFactor = calculateDiscountFactor(rate, time);
        double portfolioTerm = calculatePortfolioTerm(portfolio, market, rate,
                                                       volatility, time, discountFactor);
        double marketTerm = calculateMarketTerm(portfolio, market, rate,
                                                volatility, time, discountFactor);
        double positionAdjustment = calculatePositionAdjustment(portfolio, market);

        return (portfolioTerm - marketTerm) * positionAdjustment;
    }

    private double calculateDiscountFactor(double rate, double time) {
        return Math.exp(-rate * time);
    }

    private double calculatePortfolioTerm(Portfolio portfolio, Market market,
                                          double rate, double volatility,
                                          double time, double discountFactor) {
        double moneyness = calculateMoneyness(portfolio, market);
        double adjustedRate = rate + volatility * volatility / 2;
        double d1Component = calculateD1Component(moneyness, adjustedRate,
                                                   volatility, time);

        return portfolio.getValue() * discountFactor * (1 + d1Component);
    }

    private double calculateMoneyness(Portfolio portfolio, Market market) {
        return Math.log(portfolio.getValue() / market.getStrike());
    }

    private double calculateD1Component(double moneyness, double adjustedRate,
                                        double volatility, double time) {
        double numerator = Math.pow(moneyness + adjustedRate * time, 2);
        double denominator = volatility * Math.sqrt(time);
        return numerator / denominator;
    }

    private double calculatePositionAdjustment(Portfolio portfolio, Market market) {
        double unhedgedPosition = portfolio.getQuantity() *
                                  (1 - portfolio.getHedgeRatio());
        double correlationEffect = market.getCorrelation() * portfolio.getBeta();
        double durationFactor = Math.sqrt(portfolio.getDuration() / 365.0);

        return unhedgedPosition + correlationEffect * durationFactor;
    }

    // ... ähnliche Zerlegung für Marktterm

    /**
     * Validiert Transaktion mit klaren, lesbaren Bedingungen.
     */
    public boolean validateTransaction(Transaction tx, User user, Context ctx) {
        // Jede Kategorie separat für Klarheit validieren
        if (!hasValidInputs(tx, user, ctx)) {
            return false;
        }
        if (!hasValidAmount(tx, user)) {
            return false;
        }
        if (!hasRequiredRole(user)) {
            return false;
        }
        if (!meetsRiskRequirements(tx, user, ctx)) {
            return false;
        }
        if (!hasValidTransactionType(tx)) {
            return false;
        }
        if (!isWithinTradingHours(ctx)) {
            return false;
        }
        if (!meetsRestrictionRequirements(user, ctx)) {
            return false;
        }
        return meetsCurrencyRequirements(tx, ctx);
    }

    private boolean hasValidInputs(Transaction tx, User user, Context ctx) {
        return tx != null && user != null && ctx != null;
    }

    private boolean hasValidAmount(Transaction tx, User user) {
        return tx.getAmount() > 0 && tx.getAmount() <= user.getLimit();
    }

    private boolean hasRequiredRole(User user) {
        String role = user.getRole();
        return "admin".equals(role) || "trader".equals(role);
    }

    private boolean meetsRiskRequirements(Transaction tx, User user, Context ctx) {
        if (!ctx.isHighRisk()) {
            return true;
        }
        return user.hasApproval() && tx.getAmount() < 10000;
    }

    private boolean hasValidTransactionType(Transaction tx) {
        String type = tx.getType();
        return "buy".equals(type) || "sell".equals(type);
    }

    private boolean isWithinTradingHours(Context ctx) {
        return ctx.getTime().isAfter(ctx.getMarketOpen())
            && ctx.getTime().isBefore(ctx.getMarketClose());
    }

    private boolean meetsRestrictionRequirements(User user, Context ctx) {
        return !user.isRestricted() || ctx.hasOverride();
    }

    private boolean meetsCurrencyRequirements(Transaction tx, Context ctx) {
        if ("USD".equals(tx.getCurrency())) {
            return true;
        }
        return "EUR".equals(tx.getCurrency()) && ctx.allowsForeignCurrency();
    }
}
# SICHER: Python mit reduzierter Halstead-Komplexität

def process_data(data, config, state, params):
    """
    Verarbeitet Daten mit reduzierter Halstead-Komplexität.
    Komplexe Berechnungen in klare Schritte zerlegt.
    """
    # Eingaben zuerst validieren
    if not validate_inputs(data, config, state, params):
        return None

    # Komponenten separat berechnen
    base_value = calculate_base_value(data, params, config, state)
    adjustment_factor = calculate_adjustment_factor(data, config, params)
    scaling_factor = calculate_scaling_factor(state, params, config, data)
    decay_component = calculate_decay_component(params, state, data)
    penalty_component = calculate_penalty(config, data, params)

    # Mit klarer Formel kombinieren
    result = base_value * adjustment_factor * scaling_factor
    result += decay_component
    result -= penalty_component

    return result


def validate_inputs(data, config, state, params):
    """Validiert, dass alle Eingaben vorhanden sind und erforderliche Felder haben."""
    if any(x is None for x in [data, config, state, params]):
        return False

    if 'value' not in data or 'factor' not in params:
        return False

    return validate_value_range(data, config, params)


def validate_value_range(data, config, params):
    """Prüft, ob der Wert im akzeptablen Bereich liegt."""
    value = data['value']
    min_val = config.get('min_value', 0)
    max_val = config.get('max_value', float('inf'))

    if value < min_val or value > max_val:
        return False

    if params['factor'] <= 0 and not params.get('allow_negative', False):
        return False

    return True


def calculate_base_value(data, params, config, state):
    """Berechnet die Basiswertkomponente."""
    raw_value = data['value'] * params['factor'] + config['offset']
    divisor = state['divisor'] if state['divisor'] != 0 else 1
    return raw_value / divisor


def calculate_adjustment_factor(data, config, params):
    """Berechnet den Anpassungsfaktor basierend auf der Abweichung vom Basiswert."""
    deviation = data['adjustment'] - config['baseline']
    range_val = params['range'] if params['range'] > 0 else 1
    return 1 + (deviation / range_val)


def calculate_scaling_factor(state, params, config, data):
    """Berechnet den Skalierungsfaktor."""
    power_component = state['multiplier'] ** params['exponent']
    denominator = config['scale'] * (1 + data['variance'])
    return power_component / denominator


def calculate_decay_component(params, state, data):
    """Berechnet die Verfallskomponente basierend auf dem Alter."""
    decay_factor = state['decay_rate'] ** data['age']
    return params['constant'] * (1 - decay_factor)


def calculate_penalty(config, data, params):
    """Berechnet die Strafe für Abweichungen über dem Schwellenwert."""
    excess = data['deviation'] - params['threshold']
    if excess <= 0:
        return 0
    return config['penalty'] * excess

CVE-Beispiele

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


Verwandte CWEs

  • CWE-1120: Übermäßige Code-Komplexität (Eltern)
  • CWE-1121: Übermäßige McCabe-Zyklomatische Komplexität (verwandt)
  • CWE-1226: Komplexitätsprobleme (Kategoriemitglied)

Referenzen

  1. MITRE Corporation. "CWE-1122: Excessive Halstead Complexity." https://cwe.mitre.org/data/definitions/1122.html
  2. Halstead, M.H. (1977). "Elements of Software Science." Elsevier.
  3. Software Metrics and Software Metrology