OpenSCA: Analysis of Composer Dependencies
Published: 2023-09-19 02:46:19

In this article, we will mainly introduce the principle of component composition parsing based on the Composer package manager.

## Introducing Composer

Composer is a dependency management tool for PHP.

Inspired by NPM of Node.js and bundler of Ruby, the design of omposer  has many similarities with these two.

The dependency management file for Composer is composer.json. Developers can specify the version range of each dependency in composer.json or use **composer require /update /remove ${name}** command to manage dependencies.

If there is a composer. json file in a project, you can execute the composer install command to automatically install the dependencies required for the current project and generate a composer. lock file.

**The complete file structure of composer. json is as follows:**

```
{  
    "name": "cakephp/app",  
    "type": "project",  
    "license": "MIT",  
    "require": {      
          "php": ">=7.2",      
          "cakephp/cakephp": "^4.3",      
          "cakephp/migrations": "^3.2",      
          "cakephp/plugin-installer": "^1.3",      
          "mobiledetect/mobiledetectlib": "^2.8"  
           },  
     "require-dev": {     
           "cakephp/bake": "^2.6",      
           "cakephp/cakephp-codesniffer": "^4.5",      
           "cakephp/debug_kit": "^4.5",      
           "josegonzalez/dotenv": "^3.2",      
           "phpunit/phpunit": "~8.5.0 || ^9.3"  
           },
}
```

Here, **name** is the project name. **type** refers to the type of package, which includes four types: **library, project, metapackage**, and **composer-plugin** (library by default).  **license** is the license declared by the project, which can be a string or an array of strings.

**require-dev** refers to the dependency used in the development or testing environment, and **require** refers to the dependency used in the production environment. The dependency is written as "name": "version", and the version can be specified as the exact version or a range.

## Analytical Algorithm

### composer.lock

_composer. lock_ is an automatically generated file that can accurately locate the dependencies and versions used in PHP projects, so the priority is given to analyze  the _composer. lock_ file.

**Structure of composer.lock is as follows：**

```
{  
     "packages": [    
          {      
              "name": "a",      
              "version": "1.1.0",      
              "require": {          
                   "c": "1.1.*"      
                    }    
          },    
          {      
              "name": "b",      
              "version": "1.2.2",      
              "require": {          
                   "c": "^1.0.2"     
                    }    
          },    
          {       
              "name": "c",      
              "version": "1.1.2"    
                    }  
     ],  
     "packages-dev": []
}
```

The **packages** and **packages-dev** fields contain all direct and indirect dependencies used by the project, and record the dependencies between components. **packages** represent the dependencies of the production environment, and **packages-dev** represent the dependencies of the development environment.

**For example：**

```
{  
    "name": "a",  
    "version": "1.1.0",  
    "require": {      
         "c": "1.1.*"  
     }
}
```

● The project depends on component a  in version 1.1.0, which depends on component c  constrained to version 1.1. *.
● Similarly, it can be seen that the project depends on component b in version 1.2.2, and this component depends on component c with a version constraint of ^ 1.0.2.
● Moreover, neither component a nor component b is depended  by any other dependencies. So both components are direct dependencies of the project.

**Note：**

1. 1.1. * represents  the versions in [1.1.0, 1.2.0) 
2. ^ 1.0.2 represents the versions in [1.0.2, 2.0.0) 

**From this, the dependency structure of the current project can be constructed：**


![Solid lines represent direct dependencies & dashed lines represent indirect dependencies](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xi3azkdbylh6f7vk8twv.png)



![Figure: Example of composer.lock detection results](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m8v9zqfdf06ycwbiwz6z.png)

### composer.json

_composer.json_  is a dependency management file managed by developers, which will be parsed when the  _composer.lock_  file cannot be found.

_composer.json_ only contains direct dependencies. During project construction, the required indirect dependencies will be downloaded from the composer repository and built into a _composer.lock_ file. Therefore, the component dependencies of the project can be analyzed by simulating the composer construction process.

**Structure of composer.json  is as follows：**

```
{  
    "name": "foo",  
    "type": "project",  
    "license": "MIT",  
    "require": {
        "a": "^1.1.0",
        "b": "^1.2.0",  
     },  
    "require-dev": {},
}
```
**require** is the direct dependency actually used in the project, and **require-dev** is the direct dependency used during project development.

**For example：**

''a'': ''^ 1.1.0'' represents component a with a dependency version constraint of ^1.1.0.
''b'': ''^ 1.2.0'' represents component b with a dependency version constraint of ^1.2.0.

At this point of analysis, we can summarize the dependency relationship as shown in the following figure：

![Solid lines represent direct dependencies](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o4hwc3f1xz42eutrgi59.png)

Through this dependency relationship, direct dependencies and the version range of the components are visible, but the specific version of the dependency cannot be determined.

In the absence of a _composer.lock_ file, in order to further obtain accurate versions of dependencies and indirect dependencies, it is necessary to download detailed information of the corresponding components from the composer repository.

**For example, the detailed information structure of component a is：**

```
{
  "packages": {
    "a": [
      {
        "version": "1.0.1",
        "require": {
        "c": "^1.0.0"
        }
      }, 
      { 
         "version": "1.1.0", 
         "require": { 
         "c": "^1.1.0" 
          }      
      }    
     ]  
  }
}
```
The **packages** field represents the mapping of component and version information, while the **require** field represents the dependency information of the component.

For this example, the constraint for component a is ^1.1.0, which requires a version in [1.1.0, 2.0.0), so version 1.1.0 is chosen.

**Therefore, the component dependency structure becomes：**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r6kyqsq2yfty5l3lfye3.png)

By hierarchical analyzing in this way, the dependency information of the entire project can be obtained.


![Figure: Example of composer.json detection results](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2s276eacee62983ip6qh.png)