Invocation of a Control Element at an Unnecessarily Deep Horizontal Layer
Description
Invocation of a Control Element at an Unnecessarily Deep Horizontal Layer occurs when code at one architectural layer invokes code residing at a deeper layer than the adjacent layer, effectively skipping at least one intermediate layer in the hierarchy. The invoked code is not part of a vertical utility layer that is legitimately accessible from any horizontal layer. This violates the principle of layered architecture where each layer should only communicate with its immediate neighbors.
Risk
While primarily an architectural quality issue, improper layer invocation has indirect security implications. When layers are bypassed, security checks implemented at intermediate layers may be circumvented. The architectural violation makes security audits difficult because the actual call paths differ from the expected design. Maintenance becomes error-prone, potentially leading to inconsistent security control implementation. Changes to deep layers may unexpectedly affect upper layers that shouldn't have direct dependencies.
Solution
Enforce strict layered architecture where components only call into adjacent layers. Create explicit interfaces at each layer boundary. Use dependency injection to prevent direct instantiation of deep-layer components. Implement architectural fitness functions in CI/CD to detect layer violations. If cross-layer access is legitimately needed, create vertical utility services that are explicitly designed for such access. Document legitimate exceptions to the layering rules. Use static analysis tools to detect layer boundary violations.
Common Consequences
| Impact | Details |
|---|---|
| Other | Scope: Other Reduce Maintainability - Layer skipping makes the codebase harder to understand, maintain, and modify securely. |
| Other | Scope: Other Quality Degradation - Bypassing intermediate layers may skip intended processing logic including security controls. |
Example Code
Vulnerable Code
// Vulnerable: Controller layer directly accessing data layer, skipping service layer
// Layer 1: Controller (Presentation)
@RestController
public class VulnerableOrderController {
// Vulnerable: Direct injection of repository (data layer)
@Autowired
private OrderRepository orderRepository; // Skips service layer!
@Autowired
private CustomerRepository customerRepository; // Skips service layer!
@GetMapping("/orders/{id}")
public Order getOrder(@PathVariable Long id) {
// Vulnerable: Direct data access
// Bypasses:
// - Authorization checks that should be in service layer
// - Business validation
// - Audit logging
// - Caching logic
return orderRepository.findById(id).orElseThrow();
}
@PostMapping("/orders")
public Order createOrder(@RequestBody OrderRequest request) {
// Vulnerable: Business logic in controller
// Should be in service layer
Customer customer = customerRepository.findById(request.getCustomerId())
.orElseThrow();
// No service-layer validation or authorization
Order order = new Order();
order.setCustomer(customer);
order.setItems(request.getItems());
order.setTotal(calculateTotal(request.getItems()));
return orderRepository.save(order);
}
}
# Vulnerable: View layer accessing database layer directly
# Layer 1: Views (Presentation)
class VulnerableOrderView:
def get_order(self, request, order_id):
# Vulnerable: Direct database access from view
# Skips service/business layer
connection = database.get_connection() # Data layer
cursor = connection.cursor()
# Direct SQL in presentation layer!
cursor.execute(
"SELECT * FROM orders WHERE id = %s",
(order_id,)
)
order = cursor.fetchone()
# No business layer validation
# No authorization check
return render_template("order.html", order=order)
// Vulnerable: UI layer calling infrastructure layer directly
// Layer 1: UI/Presentation
public class VulnerableOrderForm : Form
{
public void LoadOrder(int orderId)
{
// Vulnerable: Direct database access from UI
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
// Skips application and domain layers
var command = new SqlCommand(
"SELECT * FROM Orders WHERE Id = @id", connection);
command.Parameters.AddWithValue("@id", orderId);
var reader = command.ExecuteReader();
// UI directly manipulating data
}
}
public void SaveOrder()
{
// Vulnerable: Direct infrastructure call from UI
// Bypasses all business rules and validation
using (var connection = new SqlConnection(connectionString))
{
// Direct database manipulation without business layer
}
}
}
Fixed Code
// Fixed: Proper layered architecture with each layer calling only adjacent layers
// Layer 1: Controller (Presentation Layer)
@RestController
public class FixedOrderController {
// Fixed: Only depends on service layer (adjacent layer)
private final OrderService orderService;
public FixedOrderController(OrderService orderService) {
this.orderService = orderService;
}
@GetMapping("/orders/{id}")
public ResponseEntity<OrderDTO> getOrder(
@PathVariable Long id,
@AuthenticationPrincipal User user) {
// Fixed: Delegates to service layer
// Service layer handles authorization, validation, etc.
OrderDTO order = orderService.getOrderById(id, user);
return ResponseEntity.ok(order);
}
@PostMapping("/orders")
public ResponseEntity<OrderDTO> createOrder(
@RequestBody @Valid OrderRequest request,
@AuthenticationPrincipal User user) {
// Fixed: All business logic in service layer
OrderDTO order = orderService.createOrder(request, user);
return ResponseEntity.status(HttpStatus.CREATED).body(order);
}
}
// Layer 2: Service Layer (Business Logic)
@Service
public class OrderService {
// Fixed: Only depends on repository layer (adjacent layer)
private final OrderRepository orderRepository;
private final CustomerRepository customerRepository;
private final AuthorizationService authService;
private final AuditService auditService;
@Transactional(readOnly = true)
public OrderDTO getOrderById(Long id, User user) {
Order order = orderRepository.findById(id)
.orElseThrow(() -> new OrderNotFoundException(id));
// Authorization check at service layer
authService.checkOrderAccess(user, order);
// Audit logging at service layer
auditService.logOrderAccess(user, order);
return OrderMapper.toDTO(order);
}
@Transactional
public OrderDTO createOrder(OrderRequest request, User user) {
// Business validation at service layer
validateOrderRequest(request);
// Authorization check
authService.checkCanCreateOrder(user);
// Business logic
Customer customer = customerRepository.findById(request.getCustomerId())
.orElseThrow(() -> new CustomerNotFoundException(request.getCustomerId()));
Order order = OrderFactory.create(customer, request.getItems());
Order saved = orderRepository.save(order);
auditService.logOrderCreation(user, saved);
return OrderMapper.toDTO(saved);
}
}
// Layer 3: Repository Layer (Data Access)
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
// Only data access operations
// Business logic stays in service layer
}
# Fixed: Proper layered architecture in Python
# Layer 1: Views (Presentation Layer)
class FixedOrderView:
def __init__(self, order_service: OrderService):
# Fixed: Only depends on service layer
self.order_service = order_service
def get_order(self, request, order_id):
# Fixed: Delegates to service layer
user = get_current_user(request)
order = self.order_service.get_order(order_id, user)
return render_template("order.html", order=order)
# Layer 2: Service Layer (Business Logic)
class OrderService:
def __init__(self,
order_repository: OrderRepository,
auth_service: AuthorizationService):
# Fixed: Only depends on repository and other services
self.order_repository = order_repository
self.auth_service = auth_service
def get_order(self, order_id: int, user: User) -> OrderDTO:
# Authorization at service layer
order = self.order_repository.find_by_id(order_id)
if not self.auth_service.can_view_order(user, order):
raise UnauthorizedError("Cannot access this order")
return OrderDTO.from_entity(order)
# Layer 3: Repository Layer (Data Access)
class OrderRepository:
def __init__(self, db_session):
self.db = db_session
def find_by_id(self, order_id: int) -> Order:
# Only data access, no business logic
return self.db.query(Order).filter(Order.id == order_id).first()
// Fixed: Clean layered architecture in C#
// Layer 1: Presentation (UI or API)
public class FixedOrderController : Controller
{
// Fixed: Only depends on application service
private readonly IOrderApplicationService _orderService;
public FixedOrderController(IOrderApplicationService orderService)
{
_orderService = orderService;
}
public async Task<IActionResult> GetOrder(int id)
{
var order = await _orderService.GetOrderAsync(id, User.Identity.Name);
return View(order);
}
}
// Layer 2: Application Service
public class OrderApplicationService : IOrderApplicationService
{
// Fixed: Depends on domain services and repositories
private readonly IOrderRepository _orderRepository;
private readonly IAuthorizationService _authService;
public async Task<OrderDto> GetOrderAsync(int id, string username)
{
var order = await _orderRepository.GetByIdAsync(id);
// Authorization at application layer
await _authService.EnsureCanAccessOrderAsync(username, order);
return OrderMapper.ToDto(order);
}
}
// Layer 3: Domain Layer
public class Order
{
// Domain logic only, no infrastructure concerns
public decimal CalculateTotal() { /* ... */ }
}
// Layer 4: Infrastructure (Data Access)
public class OrderRepository : IOrderRepository
{
private readonly DbContext _context;
public async Task<Order> GetByIdAsync(int id)
{
return await _context.Orders.FindAsync(id);
}
}
CVE Examples
This CWE is marked as PROHIBITED for direct CVE mapping as it represents an architectural quality concern rather than a direct security vulnerability.
Related CWEs
- CWE-1061: Insufficient Encapsulation (parent)
- CWE-1227: Encapsulation Issues (category member)
- CWE-1044: Architecture with Number of Horizontal Layers Outside of Expected Range (related)
References
- MITRE Corporation. "CWE-1054: Invocation of a Control Element at an Unnecessarily Deep Horizontal Layer." https://cwe.mitre.org/data/definitions/1054.html
- CISQ. "Automated Source Code Quality Measures."
- Fowler, Martin. "Patterns of Enterprise Application Architecture."