Invokable Control Element with Excessive File or Data Access Operations

Description

Invokable Control Element with Excessive File or Data Access Operations occurs when a function or method performs too many operations involving file or data manager resources. CISQ recommends a maximum threshold of 7 operations on the same data manager or file within a single invokable control element. When a single function performs many file or database operations, it becomes difficult to understand, maintain, and test. This complexity increases the likelihood of errors, including security vulnerabilities.

Risk

Excessive file or data operations in a single function have security implications. Complex functions with many I/O operations are harder to audit for security issues. Resource management becomes error-prone, increasing the risk of leaks. Error handling for multiple operations is complex and often incomplete. The function is likely violating single responsibility, mixing concerns that should be separated. Performance issues from many operations can lead to denial of service. Transaction boundaries become unclear with many database operations.

Solution

Apply the Single Responsibility Principle - each function should do one thing. Extract related operations into separate, focused methods. Use the repository pattern to encapsulate data access. Batch multiple database operations where possible. Use transactions appropriately for related operations. Consider using unit of work pattern for complex data operations. Implement proper error handling and resource management. Use static analysis tools to detect functions with excessive operations. Refactor large functions into smaller, testable units.

Common Consequences

ImpactDetails
OtherScope: Other

Reduce Maintainability - Functions with many operations are hard to understand and modify.
AvailabilityScope: Availability

DoS: Resource Consumption - Multiple operations increase resource usage and potential for leaks.
OtherScope: Other

Quality Degradation - Complexity increases bug likelihood including security issues.

Example Code

Vulnerable Code

// Vulnerable: Single method with excessive data operations (15+ operations)
public class VulnerableReportGenerator {

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

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

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

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

            // Operation 4: Get user orders
            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: Get user payments
            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: Get user activities
            PreparedStatement ps6 = conn.prepareStatement(
                "SELECT * FROM activities WHERE user_id = ?");
            ps6.setLong(1, userId);
            ResultSet rs6 = ps6.executeQuery();
            List<Activity> activities = mapActivities(rs6);

            // Operations 7-10: Get related data for orders
            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));
            }

            // Operations 11-15: Additional queries
            // ... more database operations ...

            // Vulnerable: 15+ database operations in single method
            // - Hard to understand and maintain
            // - Resource management is complex
            // - Error handling is incomplete
            // - No transaction boundary clarity

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

        } finally {
            conn.close();  // May not close all statements/resultsets
        }
    }
}
# Vulnerable: Function with excessive file operations
class VulnerableFileProcessor:

    def process_all_files(self, input_dir: str, output_dir: str) -> dict:
        """Process all files - too many file operations in one function."""
        results = {}

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

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

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

        # Operation 4: Read mapping file
        with open(f"{input_dir}/mapping.yaml") as f:
            mapping = yaml.safe_load(f)

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

        # Process data using all loaded files...
        processed = self._process(rows, reference, mapping, config)

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

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

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

        # Operations 9-12: Write individual output files
        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: Append to summary file
        with open(f"{output_dir}/summary.csv", 'a') as f:
            writer = csv.writer(f)
            writer.writerow([datetime.now(), len(processed['items'])])

        # Vulnerable: 13+ file operations in single function
        # - Complex error handling needed
        # - Partial failures leave system in inconsistent state
        # - Difficult to test individual operations

        return results

Fixed Code

// Fixed: Separate methods with focused responsibilities
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;
    }

    // Fixed: Main method orchestrates, doesn't do data access directly
    public Report generateComprehensiveReport(Long userId) {
        // Each call is to a focused repository method
        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);
    }
}

// Fixed: Focused repository methods
@Repository
public class UserRepository {

    @PersistenceContext
    private EntityManager em;

    // Fixed: Single query with 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();
    }

    // Fixed: Separate focused method
    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;

    // Fixed: Single query fetches orders with items
    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: Use batch loading for better performance
@Repository
public class OptimizedOrderRepository {

    public List<Order> findWithItemsByUserId(Long userId) {
        // Query 1: Get orders
        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());

            // Query 2: Batch load items for all orders
            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));

            // Assign items to orders
            orders.forEach(o ->
                o.setItems(itemsByOrderId.getOrDefault(o.getId(), emptyList())));
        }

        return orders;
    }
}
# Fixed: Separate classes and methods for file operations
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:
    """Focused class for loading configuration files."""

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

    def load_config(self) -> ProcessingConfig:
        """Load all configuration in single focused method."""
        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:
    """Focused class for reading input data."""

    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:
    """Focused class for writing output files."""

    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:
    """Focused class for logging."""

    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:
    """Main processor orchestrating focused components."""

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

        # Fixed: Each component handles specific operations
        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:
        """Fixed: Orchestrates components, each with focused responsibility."""

        # Load configuration (2-3 operations in focused class)
        config = self._config_loader.load_config()

        # Read data (1 operation)
        rows = self._data_reader.read_data()

        # Process data (no I/O)
        processed = self._process_data(rows, config)

        # Write outputs (operations in focused classes)
        self._report_writer.write_main_output(processed)
        self._report_writer.write_report(config.template, processed)
        self._report_writer.write_items(processed.get('items', []))

        # Write logs
        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:
        """Pure processing logic - no I/O operations."""
        # Process data using configuration
        return {'items': []}

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

CVE Examples

This CWE is marked as PROHIBITED for direct CVE mapping as it represents a code quality/maintainability concern rather than a direct security vulnerability.


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

References

  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.