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
| Impact | Details |
|---|---|
| Other | Scope: Other Reduce Maintainability - Functions with many operations are hard to understand and modify. |
| Availability | Scope: Availability DoS: Resource Consumption - Multiple operations increase resource usage and potential for leaks. |
| Other | Scope: 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.
Related CWEs
- 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
- MITRE Corporation. "CWE-1084: Invokable Control Element with Excessive File or Data Access Operations." https://cwe.mitre.org/data/definitions/1084.html
- CISQ. "Automated Source Code Quality Measures."
- Martin, Robert C. "Clean Code" - Single Responsibility Principle.