Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Parasoft Jtest 2022.2

Both The Elements of Java Style [Vermeulen 2000] and the JPL Java Coding Standard [Havelund 2010] require that the dependency structure of a package must never contain cycles; that is, it must be representable as a directed acyclic graph (DAG).

Eliminating cycles between packages has several advantages:

  • Testing and maintainability. Cyclic dependencies magnify the repercussions of changes or patches to source code. Reducing the repercussions of changes simplifies testing and improves maintainability. Inability to perform adequate testing because of cyclic dependencies is a frequent source of security vulnerabilities.
  • Reusability. Cyclic dependencies between packages require that the packages be released and upgraded in lockstep. This requirement reduces reusability.
  • Releases and builds. Avoiding cycles also helps to steer the development toward an environment that fosters modularization.
  • Deployment. Avoiding cyclic dependencies between packages reduces coupling between packages. Reduced coupling reduces the frequency of runtime errors such as ClassNotFoundError. This, in turn, simplifies deployment.

Noncompliant Code Example

This noncompliant code example contains packages named account and user that consist of the classes AccountHolder, User, and UserDetails respectively. The class UserDetails extends from AccountHolder because a user is a kind of account holder. The class AccountHolder depends on a nonstatic utility method defined in the User class. Likewise, the UserDetails depends on AccountHolder by extending it.

Code Block
bgColor#ffcccc
package account;
import user.User;
public class AccountHolder {

  private User user;
  public void setUser(User newUser) {user = newUser;}
 
Wiki Markup
Several well-known adages in Object-oriented design suggest that the dependency structure of a package or module must never contain cycles, or in other words, must orchestrate a Directed Acyclic Graph (DAG).  

There are several advantages of eliminating cycles between packages:
* Testing and maintainability: It is preferable to make a change somewhere (or patch) and limit the repercussions to as few packages as possible (ideally just one) as opposed to having to monitor or refine numerous packages. Inability to perform adequate testing because of cyclic dependencies is a frequent source of security vulnerabilities. 
* Reusability: When a new version of a package is released, clients who reuse it do not have to test their existing code bases for compatibility with other packages that this particular package depends on. Sometimes, the reusable package evolves only to accommodate the changes to packages that it depends on.
* Releases and builds: Avoiding cycles also helps to steer the development towards an environment that fosters modularization. Owners of different packages are also redeemed from relying on other _bleeding-edge_ packages.    
* Deployment: By resolving the cycles, deployment is simplified, as runtime errors like the infamous {{ClassNotFoundError}}, are reduced to a minimum by virtue of the toned down coupling between packages.


h2. Noncompliant Code Example

This noncompliant code example features two different packages named {{Account}} and {{User}} that consist of the classes {{AccountHolder}} and {{UserDetails}}, respectively. The class {{UserDetails}} extends from {{AccountHolder}} because a user is a kind of account holder. The class {{AccountHolder}} depends on a few non-static utility methods defined in {{UserDetails}} and must declare and use its instance. Likewise, the {{UserDetails}} depends on {{AccountHolder}} but instead chooses to extend from it. This vicious circle is one recipe for a cyclic dependency.  

{mc} Methods in AccountHolder are designed for inheritance and should not be final {mc}

{code:bgColor=#ffcccc}
package Account;
import User.*;
  public class AccountHolder {
    private UserDetails ud;  // Uses a class defined in package User

    synchronized void depositFunds(String username, double amount) {
      // Use a utility method of UserDetailsUser to check ifwhether username exists
     if if(uduser.exists(username)) { 
        // Deposit the amount
      } 
    }

    protected double getBalance(String accountNumber) { 
      // returnReturn the account balance 
      return 1.0; 
    }
  }

package Useruser;
import Accountaccount.*AccountHolder;
  public class UserDetails extends AccountHolder {
    public synchronized double getUserBalance(String accountNumber) {
      // Use a method of AccountHolder to get the account balance 
      return getBalance(accountNumber); 
    }
}
 
public class User {
  public boolean exists(String username) { 
      // Check whether user exists 
      return true; // Exists
    }
  }
{code}

h2. Compliant Solution

The tight coupling between the classes in the two packages can be weakened by introducing an interface called {{BankApplication}} in a third package, {{Bank}}. The cyclic dependency is eliminated by ensuring that the {{AccountHolder}} does not use an instance of {{UserDetails}}, but instead relies on the interface by importing the {{Bank}} package (not implementing the interface). 

In this compliant solution, such functionality is achieved by adding a parameter of the interface type {{BankApplication}} to the {{depositFunds()}} method. This gives the {{AccountHolder}} a solid contract to bank upon. Additionally, {{UserDetails}} implements the interface and provides concrete implementations of the methods while at the same time, inheriting the other methods from {{AccountHolder}}.  


{code:bgColor=#ccccff}
package Bank}

Compliant Solution

The tight coupling between the classes in the two packages can be weakened by introducing an interface called BankApplication in a third package, bank. The cyclic package dependency is eliminated by ensuring that the AccountHolder does not depend on User but instead relies on the interface by importing the bank package (and not by implementing the interface).

In this compliant solution, such functionality is achieved by adding a parameter of the interface type BankApplication to the depositFunds() method. This solution gives the AccountHolder a solid contract to bank on. Additionally, UserDetails implements the interface and provides concrete implementations of the methods while at the same time inheriting the other methods from AccountHolder.

Code Block
bgColor#ccccff
package bank;
public interface BankApplication {   
  void depositFunds(BankApplication ba, String username, double amount);
  double getBalance(String accountNumber);
  double getUserBalance(String accountNumber);
  boolean exists(String username); 
}

package Accountaccount;
import Bankbank.*BankApplication;  // Import from a third package
class AccountHolder  {	  
  private BankApplication ba;
  public void setBankApplication(BankApplication newBA) {
    ba = newBA;
  }

  public synchronized void depositFunds(BankApplication ba, 
      String username, double amount) {	
    // Use a utility method of UserDetails 
    // to check ifwhether username exists
    if (ba.exists(username)) { 
      // Deposit the amount
    } 
  } 
  public double getBalance(String accountNumber) { 
    // Return the account balance 
    return 1.0; 
  }   
}

package Useruser;
import Accountaccount.*AccountHolder; // One -way dependency
import Bankbank.*BankApplication;    // Import from a third package
public class UserDetails extends AccountHolder 
      implements BankApplication {
  public synchronized double getUserBalance(
      String accountNumber) {
    // Use a method of AccountHolder to get the account balance 
    return getBalance(accountNumber); 
  }
  public boolean exists(String username) { 
    // Check ifwhether user exists
    return true; 
  }
} 

package Implementer;
import Bank.*;
import Account.*;
import User.*;
class BankOperations {
  private BankApplication ba;
  public BankOperations(BankApplication ba) {
    this.ba = ba;
  }

  public void doUserActions() {
    System.out.println(ba.exists("user"));
    System.out.println(ba.getUserBalance("1111"));
  }

  public static void main(String[] args) {
    AccountHolder ac = new AccountHolder(); 
    ac.depositFunds(new UserDetails(), "user", 1.0); // Pass an interface argument
    BankOperations bo = new BankOperations(new UserDetails()); 
    bo.doUserActions(); 
  }
} 
{code}


It might appear that the interface {{BankApplication}} contains superfluous methods such as {{depositFunds()}} and {{getBalance()}}. These methods are present so that if the subclass overrides them, the superclass retains the capability of internally invoking the subclass' methods polymorphically (such as calling {{ba.getBalance()}}, with an overridden implementation of the method in {{UserDetails}}). One side-effect of using this implementation is that methods declared in the interface are required to be public in the classes that define them.

h2. Risk Assessment

Cyclic dependencies between packages can lead to fragile builds. A security vulnerability in a package can easily percolate to several other packages.

|| Recommendation || Severity || Likelihood || Remediation Cost || Priority || Level ||
| MSC08- J | low | probable | medium | {color:green}{*}P4{*}{color} | {color:green}{*}L3{*}{color} |



h3. Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the [CERT website|https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+MSC02-J].

h2. Bibliography

\[[Martin 1996|AA. Bibliography#Martin 96]\] 
\[[Knoernschild 2001|AA. Bibliography#Knoernschild 01]\] Chapter 1: "OO Principles and Patterns, 1.2.5 Acyclic Dependencies Principle"

----
[!The CERT Oracle Secure Coding Standard for Java^button_arrow_left.png!|MSC07-J. Eliminate class initialization cycles]      [!The CERT Oracle Secure Coding Standard for Java^button_arrow_up.png!|49. Miscellaneous (MSC)]      [!The CERT Oracle Secure Coding Standard for Java^button_arrow_right.png!|MSC09-J. Carefully design interfaces before releasing them]

 

The interface BankApplication appears to contain superfluous methods such as depositFunds() and getBalance(). These methods are present so that if the subclass overrides them, the superclass retains the capability of internally invoking the subclass's methods polymorphically (for example, calling ba.getBalance() with an overridden implementation of the method in UserDetails). One consequence of this solution is that methods declared in the interface are required to be public in the classes that define them.

Applicability

Cyclic dependencies between packages can result in fragile builds. A security vulnerability in a package can easily percolate to other packages.

Automated Detection

ToolVersionCheckerDescription
Parasoft Jtest
Include Page
Parasoft_V
Parasoft_V
CERT.DCL60.ACDEnsure that files do not contain cyclical dependencies

Bibliography

[Havelund 2010]JPL Coding Standard, Version 1.1

[Knoernschild 2002]

§1.2.5, "Acyclic Dependencies Principle"

[Martin 1996]

Chapter 1, "OO Principles and Patterns"

[Vermeulen 2000]The Elements of Java Style


...

Image Added Image Added Image Added