Aufrufbares Kontrollelement mit übermäßigen Datei- oder Datenzugriffsoperationen

Beschreibung

Aufrufbares Kontrollelement mit übermäßigen Datei- oder Datenzugriffsoperationen tritt auf, wenn eine Funktion oder Methode zu viele Operationen mit Datei- oder Datenmanagerressourcen durchführt. CISQ empfiehlt einen maximalen Schwellenwert von 7 Operationen auf demselben Datenmanager oder derselben Datei innerhalb eines einzelnen aufrufbaren Kontrollelements. Wenn eine einzelne Funktion viele Datei- oder Datenbankoperationen durchführt, wird sie schwer zu verstehen, zu warten und zu testen. Diese Komplexität erhöht die Wahrscheinlichkeit von Fehlern, einschließlich Sicherheitsschwachstellen.

Risiko

Übermäßige Datei- oder Datenoperationen in einer einzelnen Funktion haben Sicherheitsimplikationen. Komplexe Funktionen mit vielen I/O-Operationen sind schwerer auf Sicherheitsprobleme zu prüfen. Ressourcenverwaltung wird fehleranfällig, was das Risiko von Lecks erhöht. Fehlerbehandlung für mehrere Operationen ist komplex und oft unvollständig. Die Funktion verletzt wahrscheinlich die einzelne Verantwortlichkeit und mischt Belange, die getrennt werden sollten. Leistungsprobleme durch viele Operationen können zu Denial-of-Service führen. Transaktionsgrenzen werden bei vielen Datenbankoperationen unklar.

Lösung

Wenden Sie das Single-Responsibility-Prinzip an - jede Funktion sollte eine Sache tun. Extrahieren Sie verwandte Operationen in separate, fokussierte Methoden. Verwenden Sie das Repository-Pattern, um Datenzugriff zu kapseln. Fassen Sie mehrere Datenbankoperationen wenn möglich zusammen. Verwenden Sie Transaktionen angemessen für verwandte Operationen. Erwägen Sie das Unit-of-Work-Pattern für komplexe Datenoperationen. Implementieren Sie ordnungsgemäße Fehlerbehandlung und Ressourcenverwaltung. Verwenden Sie statische Analysetools, um Funktionen mit übermäßigen Operationen zu erkennen. Refaktorisieren Sie große Funktionen in kleinere, testbare Einheiten.

Häufige Auswirkungen

AuswirkungDetails
AndereBereich: Ändere

Reduzierte Wartbarkeit - Funktionen mit vielen Operationen sind schwer zu verstehen und zu modifizieren.
VerfügbarkeitBereich: Verfügbarkeit

DoS: Ressourcenverbrauch - Mehrere Operationen erhöhen Ressourcennutzung und Potenzial für Lecks.
AndereBereich: Ändere

Qualitätsverschlechterung - Komplexität erhöht Fehlerwahrscheinlichkeit einschließlich Sicherheitsproblemen.

Beispielcode

Anfälliger Code

// Anfällig: Einzelne Methode mit übermäßigen Datenoperationen (15+ Operationen)
public class VulnerableReportGenerator {

    public Report generateComprehensiveReport(Long userId) throws Exception {
        Connection conn = dataSource.getConnection();

        try {
            // Operation 1: Benutzer abrufen
            PreparedStatement ps1 = conn.prepareStatement(
                "SELECT * FROM users WHERE id = ?");
            ps1.setLong(1, userId);
            ResultSet rs1 = ps1.executeQuery();
            User user = mapUser(rs1);

            // Operation 2: Benutzerprofil abrufen
            PreparedStatement ps2 = conn.prepareStatement(
                "SELECT * FROM profiles WHERE user_id = ?");
            ps2.setLong(1, userId);
            ResultSet rs2 = ps2.executeQuery();
            Profile profile = mapProfile(rs2);

            // Operation 3: Benutzereinstellungen abrufen
            PreparedStatement ps3 = conn.prepareStatement(
                "SELECT * FROM settings WHERE user_id = ?");
            ps3.setLong(1, userId);
            ResultSet rs3 = ps3.executeQuery();
            Settings settings = mapSettings(rs3);

            // Operation 4: Benutzerbestellungen abrufen
            PreparedStatement ps4 = conn.prepareStatement(
                "SELECT * FROM orders WHERE user_id = ?");
            ps4.setLong(1, userId);
            ResultSet rs4 = ps4.executeQuery();
            List<Order> orders = mapOrders(rs4);

            // Operation 5: Benutzerzahlungen abrufen
            PreparedStatement ps5 = conn.prepareStatement(
                "SELECT * FROM payments WHERE user_id = ?");
            ps5.setLong(1, userId);
            ResultSet rs5 = ps5.executeQuery();
            List<Payment> payments = mapPayments(rs5);

            // Operation 6: Benutzeraktivitäten abrufen
            PreparedStatement ps6 = conn.prepareStatement(
                "SELECT * FROM activities WHERE user_id = ?");
            ps6.setLong(1, userId);
            ResultSet rs6 = ps6.executeQuery();
            List<Activity> activities = mapActivities(rs6);

            // Operationen 7-10: Verwandte Daten für Bestellungen abrufen
            for (Order order : orders) {
                PreparedStatement psItems = conn.prepareStatement(
                    "SELECT * FROM order_items WHERE order_id = ?");
                psItems.setLong(1, order.getId());
                ResultSet rsItems = psItems.executeQuery();
                order.setItems(mapOrderItems(rsItems));
            }

            // Operationen 11-15: Zusätzliche Abfragen
            // ... weitere Datenbankoperationen ...

            // Anfällig: 15+ Datenbankoperationen in einzelner Methode
            // - Schwer zu verstehen und zu warten
            // - Ressourcenverwaltung ist komplex
            // - Fehlerbehandlung ist unvollständig
            // - Keine Klarheit über Transaktionsgrenzen

            return new Report(user, profile, settings, orders, payments, activities);

        } finally {
            conn.close();  // Schließt möglicherweise nicht alle Statements/ResultSets
        }
    }
}
# Anfällig: Funktion mit übermäßigen Dateioperationen
class VulnerableFileProcessor:

    def process_all_files(self, input_dir: str, output_dir: str) -> dict:
        """Verarbeitet alle Dateien - zu viele Dateioperationen in einer Funktion."""
        results = {}

        # Operation 1: Konfigurationsdatei lesen
        with open(f"{input_dir}/config.json") as f:
            config = json.load(f)

        # Operation 2: Eingabedatendatei lesen
        with open(f"{input_dir}/data.csv") as f:
            data = csv.reader(f)
            rows = list(data)

        # Operation 3: Referenzdaten lesen
        with open(f"{input_dir}/reference.json") as f:
            reference = json.load(f)

        # Operation 4: Mapping-Datei lesen
        with open(f"{input_dir}/mapping.yaml") as f:
            mapping = yaml.safe_load(f)

        # Operation 5: Template lesen
        with open(f"{input_dir}/template.html") as f:
            template = f.read()

        # Daten mit allen geladenen Dateien verarbeiten...
        processed = self._process(rows, reference, mapping, config)

        # Operation 6: Hauptausgabe schreiben
        with open(f"{output_dir}/output.json", 'w') as f:
            json.dump(processed, f)

        # Operation 7: Log-Datei schreiben
        with open(f"{output_dir}/process.log", 'w') as f:
            f.write(self._generate_log())

        # Operation 8: Bericht schreiben
        with open(f"{output_dir}/report.html", 'w') as f:
            f.write(self._render_template(template, processed))

        # Operationen 9-12: Individuelle Ausgabedateien schreiben
        for item in processed['items']:
            filename = f"{output_dir}/item_{item['id']}.json"
            with open(filename, 'w') as f:
                json.dump(item, f)

        # Operation 13: An Zusammenfassungsdatei anhängen
        with open(f"{output_dir}/summary.csv", 'a') as f:
            writer = csv.writer(f)
            writer.writerow([datetime.now(), len(processed['items'])])

        # Anfällig: 13+ Dateioperationen in einzelner Funktion
        # - Komplexe Fehlerbehandlung erforderlich
        # - Teilfehler hinterlassen System in inkonsistentem Zustand
        # - Schwierig, individuelle Operationen zu testen

        return results

Korrigierter Code

// Korrigiert: Separate Methoden mit fokussierten Verantwortlichkeiten
public class FixedReportGenerator {

    private final UserRepository userRepository;
    private final ProfileRepository profileRepository;
    private final OrderRepository orderRepository;
    private final PaymentRepository paymentRepository;
    private final ActivityRepository activityRepository;

    public FixedReportGenerator(
            UserRepository userRepository,
            ProfileRepository profileRepository,
            OrderRepository orderRepository,
            PaymentRepository paymentRepository,
            ActivityRepository activityRepository) {
        this.userRepository = userRepository;
        this.profileRepository = profileRepository;
        this.orderRepository = orderRepository;
        this.paymentRepository = paymentRepository;
        this.activityRepository = activityRepository;
    }

    // Korrigiert: Hauptmethode orchestriert, führt Datenzugriff nicht direkt durch
    public Report generateComprehensiveReport(Long userId) {
        // Jeder Aufruf geht an eine fokussierte Repository-Methode
        User user = userRepository.findWithProfile(userId);
        Settings settings = userRepository.findSettings(userId);
        List<Order> orders = orderRepository.findWithItemsByUserId(userId);
        List<Payment> payments = paymentRepository.findByUserId(userId);
        List<Activity> activities = activityRepository.findByUserId(userId);

        return new Report(user, user.getProfile(), settings, orders, payments, activities);
    }
}

// Korrigiert: Fokussierte Repository-Methoden
@Repository
public class UserRepository {

    @PersistenceContext
    private EntityManager em;

    // Korrigiert: Einzelne Abfrage mit JOIN FETCH
    public User findWithProfile(Long userId) {
        return em.createQuery(
            "SELECT u FROM User u LEFT JOIN FETCH u.profile WHERE u.id = :id",
            User.class)
            .setParameter("id", userId)
            .getSingleResult();
    }

    // Korrigiert: Separate fokussierte Methode
    public Settings findSettings(Long userId) {
        return em.createQuery(
            "SELECT s FROM Settings s WHERE s.userId = :userId",
            Settings.class)
            .setParameter("userId", userId)
            .getSingleResult();
    }
}

@Repository
public class OrderRepository {

    @PersistenceContext
    private EntityManager em;

    // Korrigiert: Einzelne Abfrage holt Bestellungen mit Elementen
    public List<Order> findWithItemsByUserId(Long userId) {
        return em.createQuery(
            "SELECT DISTINCT o FROM Order o " +
            "LEFT JOIN FETCH o.items " +
            "WHERE o.userId = :userId",
            Order.class)
            .setParameter("userId", userId)
            .getResultList();
    }
}

// Alternative: Batch-Loading für bessere Leistung verwenden
@Repository
public class OptimizedOrderRepository {

    public List<Order> findWithItemsByUserId(Long userId) {
        // Abfrage 1: Bestellungen abrufen
        List<Order> orders = em.createQuery(
            "SELECT o FROM Order o WHERE o.userId = :userId", Order.class)
            .setParameter("userId", userId)
            .getResultList();

        if (!orders.isEmpty()) {
            List<Long> orderIds = orders.stream()
                .map(Order::getId)
                .collect(toList());

            // Abfrage 2: Batch-Laden von Elementen für alle Bestellungen
            Map<Long, List<OrderItem>> itemsByOrderId = em.createQuery(
                "SELECT i FROM OrderItem i WHERE i.orderId IN :orderIds",
                OrderItem.class)
                .setParameter("orderIds", orderIds)
                .getResultStream()
                .collect(groupingBy(OrderItem::getOrderId));

            // Elemente Bestellungen zuweisen
            orders.forEach(o ->
                o.setItems(itemsByOrderId.getOrDefault(o.getId(), emptyList())));
        }

        return orders;
    }
}
# Korrigiert: Separate Klassen und Methoden für Dateioperationen
from dataclasses import dataclass
from pathlib import Path
from typing import Dict, List, Any


@dataclass
class ProcessingConfig:
    config: Dict
    reference: Dict
    mapping: Dict
    template: str


class ConfigLoader:
    """Fokussierte Klasse zum Laden von Konfigurationsdateien."""

    def __init__(self, input_dir: Path):
        self._input_dir = input_dir

    def load_config(self) -> ProcessingConfig:
        """Lädt alle Konfiguration in einzelner fokussierter Methode."""
        return ProcessingConfig(
            config=self._load_json('config.json'),
            reference=self._load_json('reference.json'),
            mapping=self._load_yaml('mapping.yaml'),
            template=self._load_text('template.html')
        )

    def _load_json(self, filename: str) -> Dict:
        with open(self._input_dir / filename) as f:
            return json.load(f)

    def _load_yaml(self, filename: str) -> Dict:
        with open(self._input_dir / filename) as f:
            return yaml.safe_load(f)

    def _load_text(self, filename: str) -> str:
        return (self._input_dir / filename).read_text()


class DataReader:
    """Fokussierte Klasse zum Lesen von Eingabedaten."""

    def __init__(self, input_dir: Path):
        self._input_dir = input_dir

    def read_data(self) -> List[List[str]]:
        with open(self._input_dir / 'data.csv') as f:
            return list(csv.reader(f))


class ReportWriter:
    """Fokussierte Klasse zum Schreiben von Ausgabedateien."""

    def __init__(self, output_dir: Path):
        self._output_dir = output_dir
        self._output_dir.mkdir(parents=True, exist_ok=True)

    def write_main_output(self, processed: Dict) -> None:
        with open(self._output_dir / 'output.json', 'w') as f:
            json.dump(processed, f)

    def write_report(self, template: str, data: Dict) -> None:
        rendered = self._render_template(template, data)
        with open(self._output_dir / 'report.html', 'w') as f:
            f.write(rendered)

    def write_items(self, items: List[Dict]) -> None:
        for item in items:
            filename = self._output_dir / f"item_{item['id']}.json"
            with open(filename, 'w') as f:
                json.dump(item, f)


class LogWriter:
    """Fokussierte Klasse für Protokollierung."""

    def __init__(self, output_dir: Path):
        self._log_file = output_dir / 'process.log'
        self._summary_file = output_dir / 'summary.csv'

    def write_log(self, log_content: str) -> None:
        with open(self._log_file, 'w') as f:
            f.write(log_content)

    def append_summary(self, item_count: int) -> None:
        with open(self._summary_file, 'a') as f:
            writer = csv.writer(f)
            writer.writerow([datetime.now().isoformat(), item_count])


class FileProcessor:
    """Hauptprozessor, der fokussierte Komponenten orchestriert."""

    def __init__(self, input_dir: str, output_dir: str):
        self._input_path = Path(input_dir)
        self._output_path = Path(output_dir)

        # Korrigiert: Jede Komponente behandelt spezifische Operationen
        self._config_loader = ConfigLoader(self._input_path)
        self._data_reader = DataReader(self._input_path)
        self._report_writer = ReportWriter(self._output_path)
        self._log_writer = LogWriter(self._output_path)

    def process_all_files(self) -> Dict:
        """Korrigiert: Orchestriert Komponenten, jede mit fokussierter Verantwortlichkeit."""

        # Konfiguration laden (2-3 Operationen in fokussierter Klasse)
        config = self._config_loader.load_config()

        # Daten lesen (1 Operation)
        rows = self._data_reader.read_data()

        # Daten verarbeiten (keine I/O)
        processed = self._process_data(rows, config)

        # Ausgaben schreiben (Operationen in fokussierten Klassen)
        self._report_writer.write_main_output(processed)
        self._report_writer.write_report(config.template, processed)
        self._report_writer.write_items(processed.get('items', []))

        # Logs schreiben
        self._log_writer.write_log(self._generate_log())
        self._log_writer.append_summary(len(processed.get('items', [])))

        return processed

    def _process_data(self, rows: List, config: ProcessingConfig) -> Dict:
        """Reine Verarbeitungslogik - keine I/O-Operationen."""
        # Daten mit Konfiguration verarbeiten
        return {'items': []}

    def _generate_log(self) -> str:
        return f"Verarbeitet am {datetime.now()}"

CVE-Beispiele

Diese CWE ist für direkte CVE-Zuordnung als VERBOTEN markiert, da sie ein Codequalitäts-/Wartbarkeitsproblem und keine direkte Sicherheitsschwachstelle darstellt.


Verwandte CWEs

  • CWE-405: Asymmetric Resource Consumption (Eltern)
  • CWE-1060: Excessive Number of Inefficient Server-Side Data Accesses (verwandt)
  • CWE-1073: Non-SQL Invokable Control Element with Excessive Data Accesses (verwandt)

Referenzen

  1. MITRE Corporation. "CWE-1084: Invokable Control Element with Excessive File or Data Access Operations." https://cwe.mitre.org/data/definitions/1084.html

  2. CISQ. "Automated Source Code Quality Measures."

  3. Martin, Robert C. "Clean Code" - Single Responsibility Principle.