58 Commits

Author SHA1 Message Date
Jean-Baptiste Nahan
de248f9e99 Merge pull request #11 from Mactronique/sf4_php74
bump version Symfony and PHP 7.4
2020-06-10 17:14:10 +02:00
Jean-Baptiste Nahan
8052a36be3 Merge pull request #10 from Mactronique/add_pgsql
add Postgres SQL script
2020-06-10 17:12:34 +02:00
macintoshplus
04d2367a5e add Postgres SQL script 2019-10-29 22:19:23 +01:00
macintoshplus
89bed3d960 bump version Symfony and PHP 7.4 2019-10-11 22:51:04 +02:00
macintoshplus
812b5387f6 remove todo 2019-08-01 23:54:25 +02:00
macintoshplus
f4a8152053 update change log 2019-08-01 23:34:46 +02:00
macintoshplus
6ec2fd5806 Closes #8 2019-08-01 23:31:54 +02:00
macintoshplus
b556ad9568 fix internal and external security checker 2019-08-01 22:49:45 +02:00
Jean-Baptiste Nahan
7d2baabd1a Merge pull request #9 from Mactronique/add_platform_reqs
Add platform reqs from compose.lock
2019-08-01 21:42:39 +02:00
macintoshplus
dcbced5fe4 use only platform in composer.lock 2019-08-01 21:38:38 +02:00
macintoshplus
96662abf51 update deps 2019-07-22 22:18:41 +02:00
macintoshplus
ae89109ab6 start reading platform informations. see #5 #7 2019-06-06 21:20:55 +02:00
macintoshplus
307cfb2f04 Add SQL Example and SQL Script for create database 2019-05-29 22:29:22 +02:00
macintoshplus
612fe7852a use array for json decode 2019-05-28 21:17:17 +02:00
macintoshplus
e7bdbd0790 add alias 2019-05-28 20:49:10 +02:00
macintoshplus
1d4fd0656b fix typo and add docs 2019-05-28 18:45:00 +02:00
macintoshplus
ae1e87c6cc add dbal project provider 2019-05-28 16:49:19 +02:00
macintoshplus
58789c1e0a add config example file 2019-05-28 16:48:48 +02:00
macintoshplus
5d0c92665c fix typo and add docs 2019-05-28 14:46:21 +02:00
macintoshplus
79f9f15a06 fix typo 2019-05-28 14:45:21 +02:00
macintoshplus
254127e34c add MSQSL script for init database 2019-05-28 12:15:34 +02:00
macintoshplus
7d43b3c5ea update copyright 2019-05-28 09:16:24 +02:00
macintoshplus
26d41642c7 add php7.3 2019-05-28 09:16:13 +02:00
macintoshplus
a61a8d8f36 update use composer 2019-05-28 09:08:56 +02:00
macintoshplus
eed13c5f07 update copyright 2019-05-28 09:00:14 +02:00
macintoshplus
3385fbc916 Merge branch 'master' into feature_security_redmine_conf 2019-05-28 08:58:01 +02:00
macintoshplus
52d4c4e99f update copyright 2019-05-28 08:52:15 +02:00
Macintoshplus
bcf7916ba0 add repos info and limit to active project 2018-08-30 22:50:30 +02:00
Macintoshplus
eb9d8fc485 fix config loading from db 2018-08-30 22:43:56 +02:00
Macintoshplus
160cdbce8f fix config loading from db 2018-08-30 22:41:59 +02:00
Macintoshplus
0116f8d256 fixup! fix type for data on insert, update dependencies datas 2018-08-30 22:22:46 +02:00
Macintoshplus
ff01918aee fix type for data on insert, update dependencies datas 2018-08-30 22:15:03 +02:00
Macintoshplus
ac1f1f50a3 add internal and external security checker 2018-05-31 23:52:45 +02:00
Macintoshplus
1b08598a46 include security checker 2018-05-31 23:46:06 +02:00
Macintoshplus
88e575c6f8 #1 2018-05-25 15:19:48 +02:00
Macintoshplus
50382f5b19 config 2018-05-25 15:16:51 +02:00
Macintoshplus
e35bb667ce add upgrade guide 2018-05-25 15:16:35 +02:00
Macintoshplus
9a3a3b3cc1 Add security check and secure private library #1 #3 2018-05-25 15:16:01 +02:00
Macintoshplus
cd881c58f6 move source file to src 2018-05-25 09:07:02 +02:00
Macintoshplus
fb72e329a2 fix test 2018-04-25 12:33:35 +02:00
Macintoshplus
175aa45824 add security persistance #1 2018-04-25 11:51:38 +02:00
Macintoshplus
93bfa1e3f9 update readme 2018-04-25 11:14:54 +02:00
Macintoshplus
fa28a40610 update readme 2018-04-25 11:11:40 +02:00
Macintoshplus
e70da32c3f update readme 2018-04-25 11:10:44 +02:00
Macintoshplus
ea189abd17 add security check and refactor #1 #2 2018-04-25 10:56:41 +02:00
Macintoshplus
27ee261a2e bump version 1.0.0-alpha-3 2018-03-13 13:59:21 +01:00
Macintoshplus
df6efdf0c6 amend copyright mention 2018-03-13 13:59:09 +01:00
Macintoshplus
e075f1f99b bump required php version 5.6+ 2018-03-13 13:53:30 +01:00
Macintoshplus
2eff40b37d add badge Travis and licence 2018-03-13 13:47:33 +01:00
Macintoshplus
534664411a add travis config 2018-03-13 13:40:36 +01:00
Macintoshplus
0406c36867 add atoum for tests 2018-03-13 13:29:39 +01:00
Macintoshplus
05fc077820 update dependencies 2018-01-15 14:44:20 +01:00
Macintoshplus
91c070ee79 update dependencies 2016-11-08 09:24:33 +01:00
Macintoshplus
dae8e8ec82 change docker compose file version 2016-11-08 09:23:38 +01:00
jb
a661046463 update makefile 2016-11-08 09:17:50 +01:00
Macintoshplus
21b914a7ce Add Redmine Cua Persistance 2016-06-13 16:33:38 +02:00
Macintoshplus
92b3345a5c changement de namespace 2016-06-10 15:05:57 +02:00
Macintoshplus
27e3d11329 Dbal Persistance set state removed for all deps before save the result. 2016-05-10 11:51:42 +02:00
42 changed files with 2567 additions and 476 deletions

97
.atoum.php Normal file
View File

@@ -0,0 +1,97 @@
<?php
/*
This file will automatically be included before EACH run.
Use it to configure atoum or anything that needs to be done before EACH run.
More information on documentation:
[en] http://docs.atoum.org/en/latest/chapter3.html#configuration-files
[fr] http://docs.atoum.org/fr/latest/lancement_des_tests.html#fichier-de-configuration
*/
use mageekguy\atoum\reports;
use mageekguy\atoum\reports\coverage;
use mageekguy\atoum\writers\std;
use \mageekguy\atoum;
$report = $script->addDefaultReport();
/*
LOGO
// This will add the atoum logo before each run.
$report->addField(new atoum\report\fields\runner\atoum\logo());
// This will add a green or red logo after each run depending on its status.
$report->addField(new atoum\report\fields\runner\result\logo());
*/
/*
CODE COVERAGE SETUP
// Please replace in next line "Project Name" by your project name and "/path/to/destination/directory" by your destination directory path for html files.
$coverageField = new atoum\report\fields\runner\coverage\html('Project Name', '/path/to/destination/directory');
// Please replace in next line http://url/of/web/site by the root url of your code coverage web site.
$coverageField->setRootUrl('http://url/of/web/site');
$report->addField($coverageField);
*/
/*
TEST EXECUTION SETUP
// Please replace in next line "/path/to/your/tests/units/classes/directory" by your unit test's directory.
$runner->addTestsFromDirectory('path/to/your/tests/units/classes/directory');
*/
/*
TEST GENERATOR SETUP
$testGenerator = new atoum\test\generator();
// Please replace in next line "/path/to/your/tests/units/classes/directory" by your unit test's directory.
$testGenerator->setTestClassesDirectory('path/to/your/tests/units/classes/directory');
// Please replace in next line "your\project\namespace\tests\units" by your unit test's namespace.
$testGenerator->setTestClassNamespace('your\project\namespace\tests\units');
// Please replace in next line "/path/to/your/classes/directory" by your classes directory.
$testGenerator->setTestedClassesDirectory('path/to/your/classes/directory');
// Please replace in next line "your\project\namespace" by your project namespace.
$testGenerator->setTestedClassNamespace('your\project\namespace');
// Please replace in next line "path/to/your/tests/units/runner.php" by path to your unit test's runner.
$testGenerator->setRunnerPath('path/to/your/tests/units/runner.php');
$script->getRunner()->setTestGenerator($testGenerator);
*/
if (!is_dir(__DIR__.'/build/tests') && !mkdir(__DIR__.'/build/tests/', 0777, true) && !is_dir(__DIR__.'/build/tests')) {
throw new \Exception("Unable to make directory ".__DIR__.'/build/tests/', 1);
}
if (!is_dir(__DIR__.'/build/coverage') && !mkdir(__DIR__.'/build/coverage/', 0777, true) && !is_dir(__DIR__.'/build/coverage')) {
throw new \Exception("Unable to make directory ".__DIR__.'/build/coverage/', 1);
}
$xunit = new atoum\reports\asynchronous\xunit();
$runner->addReport($xunit);
$writer = new atoum\writers\file(__DIR__.'/build/tests/atoum.xunit.xml');
$xunit->addWriter($writer);
$clover = new atoum\reports\asynchronous\clover();
$runner->addReport($clover);
$writerClover = new atoum\writers\file(__DIR__.'/build/tests/atoum.clover.xml');
$clover->addWriter($writerClover);
$coverage = new coverage\html();
$coverage->addWriter(new std\out());
$coverage->setOutPutDirectory(__DIR__ . '/build/coverage');
$runner->addReport($coverage);
$script->addTestsFromDirectory(__DIR__.'/tests/Units');

19
.bootstrap.atoum.php Normal file
View File

@@ -0,0 +1,19 @@
<?php
/*
This file will automatically be included before EACH test if -bf/--bootstrap-file argument is not used.
Use it to initialize the tested code, add autoloader, require mandatory file, or anything that needs to be done before EACH test.
More information on documentation:
[en] http://docs.atoum.org/en/latest/chapter3.html#bootstrap-file
[fr] http://docs.atoum.org/fr/latest/lancement_des_tests.html#fichier-de-bootstrap
*/
/*
AUTOLOADER
*/
// composer
require __DIR__ . '/vendor/autoload.php';

8
.gitignore vendored
View File

@@ -1,2 +1,6 @@
vendor/
cua.yml
/vendor/
cua.yml
/build
/*.yml
/*.yaml
!/.travis.yml

49
.travis.yml Normal file
View File

@@ -0,0 +1,49 @@
language: php
php:
- 5.6
- 7.0
- 7.1
- 7.2
- 7.3
- nightly
cache:
directories:
- vendor
sudo: false
env:
matrix:
- COMPOSER_PREFER="--prefer-stable"
- COMPOSER_PREFER="--prefer-lowest"
matrix:
allow_failures:
- php: nightly
- php: 7.4snapshot
- php: 5.6
exclude:
- php: 7.0
env: COMPOSER_PREFER="--prefer-lowest"
- php: 7.1
env: COMPOSER_PREFER="--prefer-lowest"
- php: 7.2
env: COMPOSER_PREFER="--prefer-lowest"
- php: 7.3
env: COMPOSER_PREFER="--prefer-lowest"
- php: 7.1
env: COMPOSER_PREFER="--prefer-stable"
- php: 7.2
env: COMPOSER_PREFER="--prefer-stable"
- php: 7.3
env: COMPOSER_PREFER="--prefer-stable"
- php: 7.4snapshot
env: COMPOSER_PREFER="--prefer-lowest"
- php: nightly
env: COMPOSER_PREFER="--prefer-lowest"
script:
- composer update -o $COMPOSER_PREFER
- vendor/bin/atoum

18
ChangeLog.md Normal file
View File

@@ -0,0 +1,18 @@
Change log
==========
# 2.0.0
* Add security check
* Enable to check one specific project found in the project configuration
* Allow to check one custom project not found in project configuration
* Move source file in src directory
* Add DBAL configuration provider
* Add Redmine configuration provider (experimental)
* Add DBAL Storage for dependencies and security issues
* Add reporting the PHP version and PHP extension dependencies from composer.lock
# 1.0.0
* First version
* Check dependencies update, remove and deprecated

BIN
Cua_Exemple_SQLite.db Normal file

Binary file not shown.

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2016 Mactronique
Copyright (c) 2016-2018 Jean-Baptiste Nahan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,2 +1,37 @@
check:
docker-compose run --rm tool bash -ci 'php5dismod xdebug && composer self-update && ./cua check'
##
## This file is part of Composer Update Analyser package.
##
## @author Jean-Baptiste Nahan <814683+macintoshplus@users.noreply.github.com>
## @copyright 2016-2019 - Jean-Baptiste Nahan
## @license MIT
##
.PHONY: check security console run-tests
check: vendor cua.yml
docker-compose run --rm tool bash -ci 'phpdismod xdebug && php composer.phar self-update && ./cua check $(c)'
security: vendor cua.yml
docker-compose run --rm tool bash -ci 'phpdismod xdebug && ./cua security $(c)'
console:
docker-compose run --rm tool bash
run-tests: vendor
docker-compose run --rm tool bash -ci 'vendor/bin/atoum'
vendor: composer.lock
docker-compose run --rm tool bash -ci 'phpdismod xdebug && php composer.phar self-update && php composer.phar install -o --prefer-dist'
composer.lock: composer.json composer.phar
docker-compose run --rm tool bash -ci 'phpdismod xdebug && php composer.phar self-update && php composer.phar update -o --prefer-dist'
composer.phar:
$(eval EXPECTED_SIGNATURE = "$(shell wget -q -O - https://composer.github.io/installer.sig)")
$(eval ACTUAL_SIGNATURE = "$(shell php -r "copy('https://getcomposer.org/installer', 'composer-setup.php'); echo hash_file('SHA384', 'composer-setup.php');")")
@if [ "$(EXPECTED_SIGNATURE)" != "$(ACTUAL_SIGNATURE)" ]; then echo "Invalid signature"; exit 1; fi
php composer-setup.php
rm composer-setup.php
cua.yml: cua.yml.dist
cp cua.yml.dist cua.yml

View File

@@ -1,27 +0,0 @@
<?php
/**
* This file is part of Composer Update Analyser package.
*
* @author Jean-Baptiste Nahan <jbnahan@gmail.com>
* @copyright 2016 - Jean-Baptiste Nahan
* @license MIT
*/
namespace Mactronique\CUA\Persistence;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Exception\ProcessTimedOutException;
use Symfony\Component\Yaml\Yaml;
interface Persistence {
/**
* @param array $content Content to save
* @param array $config custom config
*/
public function save(array $content, array $config = null);
}

View File

@@ -1,42 +0,0 @@
<?php
/**
* This file is part of Composer Update Analyser package.
*
* @author Jean-Baptiste Nahan <jbnahan@gmail.com>
* @copyright 2016 - Jean-Baptiste Nahan
* @license MIT
*/
namespace Mactronique\CUA\Persistence;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Exception\ProcessTimedOutException;
use Symfony\Component\Yaml\Yaml;
class YamlFile implements Persistence{
/**
* @var string default plath of file
*/
private $filePath;
/**
* @param array $config
*/
public function __construct(array $config){
$this->filePath = $config['path'];
}
/**
* @param array $content Content to save
* @param array $config custom config
*/
public function save(array $content, array $config = null)
{
$content = Yaml::dump($content, 100);
file_put_contents(($config !== null)? $config['path']:$this->filePath, $content);
}
}

214
README.md
View File

@@ -1,14 +1,27 @@
# cua
Composer Update Analyser
Composer Update Analyzer
This utility can read the `composer.lock` for get all library used by your project and run `composer update --dry-run` for get all necessary install, update or remove actions.
[![Build Status](https://travis-ci.org/Mactronique/cua.svg?branch=master)](https://travis-ci.org/Mactronique/cua) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
The output can is a `yaml` file or DBAL table.
This utility can
* Read the `composer.lock` to get all library used by your project and run `composer update --dry-run` to get all necessary install, update or remove actions.
* Run security-checker to get all library actually installed with know security issues. __This feature send the `composer.lock` file at the remote service__.
The output can be a `yaml` file or DBAL table.
## Requirements
* composer
* php 5.5+
* [composer](https://getcomposer.org/)
* [PHP 5.6+](http://php.net)
* [security-checker](https://security.symfony.com/)
## Recommended software
> For Linux only.
* [Docker](https://docs.docker.com/install/)
* [Docker compose](https://docs.docker.com/compose/install/)
* [Make](https://en.wikipedia.org/wiki/Makefile)
## Install
@@ -17,50 +30,191 @@ The output can is a `yaml` file or DBAL table.
## Configure
Create an file `cua.yml` at root cua folder.
Add a file `cua.yml` (or copy the `cual.yml.dist` file) at the root cua folder.
Put content:
Put content :
```
projects:
project_name: /path/to/project
composer_path: /usr/local/bin/composer # the path to composer
composer_path: /usr/local/bin/composer # the path to the composer executable
security_checker_path: /usr/bin/security-checker # The path to security-checker executable
# (set 'internal' if you want use the Internal Security Checker)
persistance:
format: DbalPersistance # Persistence
parameters:
dbname: 'deps' # DBAL Database name
user: 'root' # DBAL database username
password: 'root' # DBALbatabase user password
host: 'localhost' # DBAL server name or ip
driver: 'pdo_mysql' # DBAL Driver name
table_name: 'dependencies' # the table name for DBAL persistance
path: ./all.yml # for YamlFile only
dbname: 'deps' # DBAL Database name
user: 'root' # DBAL database username
password: 'root' # DBALbatabase user password
host: 'localhost' # DBAL server name or IP
driver: 'pdo_mysql' # DBAL Driver name
table_name: 'dependencies' # the table name for DBAL persistence
table_name_security: 'security' # the table name for DBAL persistence
path: ./all.yml # for YamlFile only
path_security: ./all_security.yml # for YamlFile only
project_provider:
type: file
parameters:
path: projects.yml # Location of the file project list of root cua install
```
### DBAL Table creation
Add a file `project.yml` (or copy the `project.yml.dist` file) at the root cua folder.
If you use MySQL for persist, you can use the file `Sql/Create_Table.sql` for create table in your database.
Put content:
```
projects:
'project_name':
path: /path/to/project
check_dependencies: true # Optional, by default : true
lock_path: ./composer.lock # Optional, by default : ./composer.lock. Set the location of composer.lock file from the project path.
check_security: false # Optional, by default : false. Enable this project for command security
php_path: php7.2 # Optional, by default : php. The php executable name (or path) for run security-checker and composer
```
### Store in file
In the `cua.yml` file, set the property `persistance.format` to the `YamlFile`.
Define two properties `path` and `path_security` with the path when you want store the result.
> Note : The content is overwritten for each execution.
Configuration example:
```
[...]
persistance:
format: YamlFile # Persistence
parameters:
path: ./all.yml # The content of library update
path_security: ./all_security.yml # The content of security issues
[...]
```
### Store in Relational Databases (with DBAL)
In the `cua.yml` file, set the property `persistance.format` to the `DbalPersistance`.
All script for creating tables in your database is in this folder `src/Sql/`. If your RDBMS is not present, you cal use on script and update it for your usage.
The parameters are the same for [Doctrine DBAL](https://www.doctrine-project.org/projects/doctrine-dbal/en/2.9/reference/configuration.html) with two additional fields `table_name` and `table_name_security`.
> Note : The PHP used for running the cua application do need the driver for the database.
Configuration example:
```
[...]
persistance:
format: DbalPersistance # Persistence
parameters:
dbname: 'deps' # DBAL Database name
user: 'root' # DBAL database username
password: 'root' # DBALbatabase user password
host: 'localhost' # DBAL server name or IP
driver: 'pdo_mysql' # DBAL Driver name
table_name: 'dependencies' # the table name for the dependencies DBAL persistence
table_name_security: 'security' # the table name for the security DBAL persistence
[...]
```
## Usage
### Use with Redmine Cua Plugin (deprecated)
open console, go to cua root folder and type :
Set the format to `RedmineCuaPersistance` and `table_name` to `cua_dependencies`
In this case, the `project_name` set into the config file do the same project identifier from redmine.
### File Project Provider
This file contains all configuration for each project.
Set the property `project_provider.type` to `file`.
Set the property `project_provider.parameters.path` to the name of file contains all projects configuration.
> Note : The file path is relative to the cua installation folder.
Exemple of configuration:
```
[...]
project_provider:
type: file
parameters:
path: projects.yml # Location of the file project list of root cua install
```
### Dbal Project Provider
You can store the configuration into a database. The SQL script present into the folder `src/Sql` contains the table `projects` description.
The table fields descriptions:
* The field `code` of type string and 10 characters maximum of length is needed and contain a unique identity for the project
* The field `name` of type string and 100 characters maximum of length is needed and contain user-friendly project name
* The field `path` of type string and 255 characters maximum of length is needed and contain the path to the root project folder
* The field `lock_path` of type string and 255 characters maximum of length is needed and contain the relative path (from the root project path) to the `composer.lock` file.
* The field `php_path` of type string and 255 characters maximum of length is needed and contain the PHP executable name or full path.
* The field `private_dependencies` of type text is optional. If defined this field do contain a JSON representation of the array. This array contains all private dependency name. Used only for the security check.
* The field `private_dependencies_strategy` of type string and 10 characters maximum of length is optional, the default value is `remove`. The value can be `remove` or `hash`. This defines the behavior for the private dependency before send the `composer.lock` file for security checks.
* The field `check_dependencies` of type tinyint(1) is optional, if defined to 1, enable the dependency check for this project.
* The field `check_security` of type tinyint(1) is optional, if defined to 1, enable the security check for this project.
* The field `updated_at` of type datetime is needed and contains the last update date and time.
Example of configuration:
```
[...]
project_provider:
type: file
parameters:
working_dir: /sources # The root folder used in prefix for project path
db:
dbname: 'deps' # DBAL Database name
user: 'root' # DBAL database username
password: 'root' # DBALbatabase user password
host: 'localhost' # DBAL server name or IP
driver: 'pdo_mysql' # DBAL Driver name
table_name: projects # The table name for read the project configuration
```
## Check project configuration
Open console, go to the cua root folder and type :
```
php ./cua project:list
```
This command print a table with all project configured and details.
## Usage for check dependencies
Open console, go to the cua root folder and type :
```
php ./cua check
```
This command launch the process for all setting project and store in persistance.
This command launch the process for all setting project and store in persistence.
## Usage for check security
Open console, go to the cua root folder and type :
```
php ./cua security
```
This command launch the process for all setting project and store in persistence.
# Contribute
If you whant contribute, please fork my repo, add feature or fix bugs and create new pull request.
# Todo
[ ] Add script for create table on other Database (postgre, mssql, etc.).
[ ] Get PHP and extentions requirements by project.
If you want to contribute, please fork my repo, add features or fix bugs and create a new pull request.

View File

@@ -1,44 +0,0 @@
<?php
/**
* This file is part of Composer Update Analyser package.
*
* @author Jean-Baptiste Nahan <jbnahan@gmail.com>
* @copyright 2016 - Jean-Baptiste Nahan
* @license MIT
*/
namespace Mactronique\CUA\Service;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Exception\ProcessTimedOutException;
use Symfony\Component\Finder\Finder;
class InstalledLibraryService {
/**
* Construct the service
* @param string $composerPath
*/
public function __construct(LoggerInterface $logger = null){
$this->logger = ($logger === null)? new NullLogger():$logger;
}
public function getInstalledLibrary($projectPath){
$finder = new Finder();
$libraries = [];
$composer = $finder->in($projectPath)->files()->name('composer.lock');
foreach ($composer as $file) {
$json = json_decode($file->getContents(), true);
foreach ($json['packages'] as $value) {
$libraries[$value['name']] = $value['version'];
}
}
return $libraries;
}
}

View File

@@ -1,25 +0,0 @@
## FOR MySQL
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";
CREATE TABLE IF NOT EXISTS `dependencies` (
`id` int(11) NOT NULL,
`project` varchar(50) NOT NULL,
`library` varchar(250) NOT NULL,
`version` varchar(250) NOT NULL,
`state` varchar(20) NOT NULL,
`to_library` varchar(250) DEFAULT NULL,
`to_version` varchar(250) DEFAULT NULL,
`deprecated` tinyint(1) DEFAULT NULL,
`updated_at` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
ALTER TABLE `dependencies`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `idx_proj_lib` (`project`,`library`(191)) USING BTREE,
ADD KEY `idx_plv` (`project`,`library`(191),`version`(191));
ALTER TABLE `dependencies`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;

70
UPGRADE_TO_2.0.md Normal file
View File

@@ -0,0 +1,70 @@
UPGRADE FROM 1.0 to 2.0
=======================
The version 2.0 rewrite the configuration and split the file in two files.
# Rewrite the configuration
Read the `projects` part from the 1.0 cua config file do move into an new file.
This is an example to split the old v1.0 config file:
## v1.0 config file `cua.yml`
```
projects:
project_name: /path/to/project
composer_path: /usr/local/bin/composer # the path to composer
persistance:
format: DbalPersistance # Persistence
parameters:
dbname: 'deps' # DBAL Database name
user: 'root' # DBAL database username
password: 'root' # DBALbatabase user password
host: 'localhost' # DBAL server name or ip
driver: 'pdo_mysql' # DBAL Driver name
table_name: 'dependencies' # the table name for DBAL persistance
path: ./all.yml # for YamlFile only
```
## Generate the v2.0 config file
v2.0 config file `cua.yml` is same as v1.0 without the `projets` configuration key:
```
composer_path: /usr/local/bin/composer # the path to composer
persistance:
format: DbalPersistance # Persistence
parameters:
dbname: 'deps' # DBAL Database name
user: 'root' # DBAL database username
password: 'root' # DBALbatabase user password
host: 'localhost' # DBAL server name or ip
driver: 'pdo_mysql' # DBAL Driver name
table_name: 'dependencies' # the table name for DBAL persistance
path: ./all.yml # for YamlFile only
```
Add the project source for use the projet YAML configuration file:
```
project_provider:
type: file
parameters:
path: projects.yml # Location of the file project list from root cua install
```
## Generate the 2.0 project configuration file
v2.0 project file configuration with the v1.0 `projets` configuration key:
```
projects:
project_name: /path/to/project
```
Il the new file, set the value of the `project name key` to the `path` key like this:
```
projects:
project_name:
path: /path/to/project
```

View File

@@ -1,32 +1,55 @@
{
"name": "matronique/cua",
"type": "library",
"description": "This application can manage many project composer update",
"keywords": ["composer", "update", "dependecies", "management"],
"homepage": "https://github.com/macintoshplus/ComposerUpdateAnalyser",
"license": "MIT",
"authors": [
{
"name": "Jean-Baptiste Nahan",
"email": "jbnahan@gmail.com",
"homepage": "http://nahan.fr"
}
],
"autoload": {
"psr-4": {
"Mactronique\\CUA\\": ""
}
},
"bin": ["cua"],
"require": {
"php": ">=5.5",
"symfony/console": "^3.0",
"symfony/yaml": "^3.0",
"symfony/config": "^3.0",
"psr/log": "^1.0",
"symfony/process": "^3.0",
"symfony/var-dumper": "^3.0",
"symfony/finder": "^3.0",
"doctrine/dbal": "^2.5"
"name": "mactronique/cua",
"type": "project",
"description": "This application can manage many project composer update",
"keywords": [
"composer",
"update",
"dependecies",
"management"
],
"homepage": "https://github.com/Mactronique/cua",
"license": "MIT",
"authors": [
{
"name": "Jean-Baptiste Nahan",
"email": "814683+macintoshplus@users.noreply.github.com"
}
],
"autoload": {
"psr-4": {
"Mactronique\\CUA\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"Mactronique\\CUA\\Tests\\Units\\": "tests/Units"
}
},
"bin": [
"cua"
],
"require": {
"php": ">=5.6 || ^7.0",
"symfony/console": "^3.0 || ^4.0",
"symfony/yaml": "^3.0 || ^4.0",
"symfony/config": "^3.0 || ^4.0",
"psr/log": "^1.0",
"symfony/process": "^3.0 || ^4.0",
"symfony/var-dumper": "^3.0 || ^4.0",
"symfony/finder": "^3.0 || ^4.0",
"doctrine/dbal": "^2.5",
"sensiolabs/security-checker": "^5.0 || ^6.0",
"ext-json": "*"
},
"require-dev": {
"atoum/atoum": "^3.0 || dev-master",
"atoum/reports-extension": "*"
},
"extra": {
"branch-alias": {
"dev-master": "v2.x-dev",
"dev-v1.x": "v1.x-dev"
}
}
}

677
composer.lock generated

File diff suppressed because it is too large Load Diff

8
cua
View File

@@ -1,6 +1,12 @@
#!/usr/bin/env php
<?php
// TestWebService.php
/**
* This file is part of Composer Update Analyser package.
*
* @author Jean-Baptiste Nahan <814683+macintoshplus@users.noreply.github.com>
* @copyright 2016-2019 - Jean-Baptiste Nahan
* @license MIT
*/
require __DIR__.'/vendor/autoload.php';

13
cua.yml.dist Normal file
View File

@@ -0,0 +1,13 @@
composer_path: composer # the path to the composer executable
security_checker_path: security-checker # The path to security-checker executable or 'internal' for use the integrated checker
persistance:
format: YamlFile
parameters:
path: ./all.yml # for YamlFile only
path_security: ./all_security.yml # for YamlFile only
project_provider:
type: file
parameters:
path: projects.yml # Location of the file project list of root cua install

View File

@@ -1,5 +1,10 @@
tool:
image: macintoshplus/composer:latest
volumes:
- ./:/src
- /var/www:/var/www
version: '2'
services:
tool:
image: macintoshplus/php:php72
volumes:
- ./:/sources
- /var/www:/var/www
- /home/jbnahan/.config/composer/vendor/bin/composer:/usr/local/bin/composer
dns:
- 8.8.8.8

9
projects.yml.dist Normal file
View File

@@ -0,0 +1,9 @@
projects:
'cua':
path: ./
check_dependencies: true # Optional, by default : true
lock_path: ./composer.lock # Optional, by default : ./composer.lock. Set the location of composer.lock file from the project path.
check_security: false # Optional, by default : false. Enable this project for command security
php_path: php # Optional, by default : php. The php executable name (or path) for run security-checker and composer
private_dependencies: ~
private_dependencies_strategy: remove

View File

@@ -3,32 +3,32 @@
/**
* This file is part of Composer Update Analyser package.
*
* @author Jean-Baptiste Nahan <jbnahan@gmail.com>
* @copyright 2016 - Jean-Baptiste Nahan
* @author Jean-Baptiste Nahan <814683+macintoshplus@users.noreply.github.com>
* @copyright 2016-2019 - Jean-Baptiste Nahan
* @license MIT
*/
namespace Mactronique\CUA\Command;
use Mactronique\CUA\Service\CheckUpdateService;
use Mactronique\CUA\Service\InstalledLibraryService;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Exception\ProcessTimedOutException;
class CheckCommand extends Command
class CheckDependenciesCommand extends Command
{
protected function configure()
{
$this
->setName('check')
->setDescription('Execute les tests')
->setDescription('Run dependencies check')
->addArgument(
'name',
InputArgument::OPTIONAL,
'Nom du projet à vérifier'
'The project name to check'
)
->addOption(
'composer',
@@ -62,33 +62,57 @@ class CheckCommand extends Command
if (!file_exists($composerPath)) {
throw new \Exception('Invalid composer path '.$composerPath, 1);
}
$service = new \Mactronique\CUA\Service\CheckUpdateService($composerPath);
$service = new CheckUpdateService($composerPath);
$projects = $this->getApplication()->getProjects();
//Chargement du projet via la ligne de commande
$customName = $input->getArgument('name');
// Check one project found in configuration
if (null !== $customName && !$input->getOption('project')) {
if (!isset($projects[$customName])) {
throw new \Exception(sprintf('The name of project %s is not configured', $customName), 1);
}
$output->writeln(sprintf('Check for one specific project <info>"%s"</info>', $customName));
$projects = [$customName => $projects[$customName]];
}
//Load the project config from the commmand line
if ($input->getOption('project')) {
if (null === $input->getArgument('name')) {
if (null === $customName) {
throw new \Exception('Please set the name of project', 1);
}
$projects = [$input->getArgument('name') => $input->getOption('project')];
$customPath = $input->getOption('project');
$output->writeln(
sprintf(
'Check for one custom project <info>"%s"</info> at <info>"%s"</info>',
$customName,
$customPath
)
);
$projects = [$customName => ['path' => $customPath, 'check_dependencies' => true, 'php_path'=>'php']];
}
//Pas de fichier de config donc fichier de sortie obligatoire
if ($input->hasParameterOption(['--no-config'], true) && null === $input->getOption('output')) {
throw new \Exception("Please set the output file option -o or --output", 1);
throw new \Exception('Please set the output file option -o or --output', 1);
}
$outputFile = $input->getOption('output');
$installedService = new \Mactronique\CUA\Service\InstalledLibraryService();
$installedService = new InstalledLibraryService();
foreach ($projects as $projectName => $projectPath) {
foreach ($projects as $projectName => $projectConf) {
$projectPath = $projectConf['path'];
$output->writeln(sprintf('Check <info>%s</info> at <comment>%s</comment>', $projectName, $projectPath));
$resultProject = $service->checkcomposerUpdate($projectPath);
if (!$projectConf['check_dependencies']) {
$output->writeln('<info>Skip</info>');
continue;
}
if($resultProject['error']!= ''){
$output->writeln(sprintf("<error> %s </error>", $resultProject['error']));
$resultProject = $service->checkcomposerUpdate($projectPath, $projectConf['php_path']);
if ($resultProject['error'] != '') {
$output->writeln(sprintf('<error> %s </error>', $resultProject['error']));
}
$output->writeln(sprintf(

View File

@@ -0,0 +1,149 @@
<?php
/**
* This file is part of Composer Update Analyser package.
*
* @author Jean-Baptiste Nahan <814683+macintoshplus@users.noreply.github.com>
* @copyright 2016-2019 - Jean-Baptiste Nahan
* @license MIT
*/
namespace Mactronique\CUA\Command;
use Mactronique\CUA\Service\SecurityCheckService;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class CheckSecurityCommand extends Command
{
protected function configure()
{
$this
->setName('security')
->setDescription('Run security check')
->addArgument(
'name',
InputArgument::OPTIONAL,
'Nom du projet à vérifier'
)
->addOption(
'checker',
null,
InputOption::VALUE_REQUIRED,
'The path to security-checker'
)
->addOption(
'project',
null,
InputOption::VALUE_REQUIRED,
'The path to project'
)
->addOption(
'lock_path',
null,
InputOption::VALUE_REQUIRED,
'The composer.lock path relative to project',
'./composer.lock'
)
->addOption(
'output',
'o',
InputOption::VALUE_REQUIRED,
'The path to output file'
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$securityChecker = $this->getApplication()->getSecurityChecker();
if ($input->getOption('checker')) {
$securityChecker = $input->getOption('checker');
}
if (!file_exists($securityChecker) && $securityChecker !== SecurityCheckService::INTERNAL) {
throw new \Exception('Invalid security-checker path '.$securityChecker, 1);
}
$service = new \Mactronique\CUA\Service\SecurityCheckService($securityChecker);
$projects = $this->getApplication()->getProjects();
//Chargement du projet via la ligne de commande
if ($input->getOption('project')) {
if (null === $input->getArgument('name')) {
throw new \Exception('Please set the name of project', 1);
}
$projects = [$input->getArgument('name') => ['path' => $input->getOption('project'), 'check_security'=> true, 'lock_path'=>$input->getOption('lock_path'), 'php_path'=>'php']];
}
//Pas de fichier de config donc fichier de sortie obligatoire
if ($input->hasParameterOption(['--no-config'], true) && null === $input->getOption('output')) {
throw new \Exception('Please set the output file option -o or --output', 1);
}
$outputFile = $input->getOption('output');
$installedService = new \Mactronique\CUA\Service\InstalledLibraryService();
foreach ($projects as $projectName => $projectConf) {
$projectPath = $projectConf['path'];
$lockPath = $projectConf['lock_path'];
$output->writeln(sprintf('Check Security <info>%s</info> at <comment>%s</comment>', $projectName, $projectPath));
if (!$projectConf['check_security']) {
$output->writeln('<info>Skip</info>');
continue;
}
if (isset($projectConf['private_dependencies']) && count($projectConf['private_dependencies'])) {
$tmpPath = tempnam(sys_get_temp_dir(), 'cua');
$lock = file_get_contents($projectPath.'/'.$lockPath);
foreach ($projectConf['private_dependencies'] as $libraryName) {
$findName = sprintf('"%s"', $libraryName);
if (false === $pos = mb_strpos($lock, $findName)) {
$output->writeln('Private dependency not found in composer.lock : <info>'.$findName.'</info> ');
continue;
}
$result = mb_ereg_replace(''.$findName.'', sprintf('"%s"', md5($libraryName.uniqid())), $lock);
if ($result === false) {
$output->writeln('<error>Error in remplacement of private library</error>');
continue;
}
if (false !== $pos = mb_strpos($result, $findName)) {
$output->writeln('<error>Error : The new lock file contains always the replaced private library '.$findName.'</error>');
}
$lock = $result;
}
file_put_contents($tmpPath, $lock);
$lockPath = $tmpPath;
$output->writeln('Replace lock file by modified version : <info>'.$lockPath.'</info>');
}
$resultProject = $service->checkSecurity($projectPath, $lockPath, $projectConf['php_path']);
if (isset($tmpPath)) {
@unlink($tmpPath);
}
if ($resultProject['error'] != '') {
$output->writeln(sprintf('<error> %s </error>', $resultProject['error']));
continue;
}
$output->writeln(sprintf(
'Result <error> %d </error> dependency with security issue',
count($resultProject['result'])
));
$this->getApplication()->setProjectSecurityResult($projectName, $resultProject['result']);
$this->getApplication()->saveSecurityResult($outputFile);
}
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* This file is part of Composer Update Analyser package.
*
* @author Jean-Baptiste Nahan <814683+macintoshplus@users.noreply.github.com>
* @copyright 2016-2019 - Jean-Baptiste Nahan
* @license MIT
*/
namespace Mactronique\CUA\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
class ProjectListCommand extends Command
{
protected function configure()
{
$this
->setName('project:list')
->setDescription('Get configured project list')
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$projects = $this->getApplication()->getProjects();
//Pas de fichier de config donc fichier de sortie obligatoire
if ($input->hasParameterOption(['--no-config'], true)) {
throw new \Exception('Unable to get the project list without configuration', 1);
}
$outputStyle = new OutputFormatterStyle('red', 'yellow', array());
$output->getFormatter()->setStyle('caution', $outputStyle);
$table = new Table($output);
$table->setHeaders(['Project Name', 'Location', 'Check Dependencies', 'Check Security', 'PHP Bin', 'composer.lock path']);
foreach ($projects as $projectName => $projectConf) {
$projectPath = $projectConf['path'];
if (!is_dir($projectPath)) {
$projectPath = '<error>'.$projectPath.'</error>';
$lock_path = '<caution>'.$projectConf['lock_path'].'</caution>';
} else {
$lock_path = realpath($projectConf['path']).'/'.$projectConf['lock_path'];
$lock_path = file_exists($lock_path) ? $projectConf['lock_path'] : '<error>'.$projectConf['lock_path'].'</error>';
}
$table->addRow([$projectName, $projectPath, $projectConf['check_dependencies'] ? 'true':'<error>false</error>', $projectConf['check_security'] ? 'true':'<error>false</error>', $projectConf['php_path'], $lock_path]);
}
$table->render();
}
}

View File

@@ -3,8 +3,8 @@
/**
* This file is part of Composer Update Analyser package.
*
* @author Jean-Baptiste Nahan <jbnahan@gmail.com>
* @copyright 2016 - Jean-Baptiste Nahan
* @author Jean-Baptiste Nahan <814683+macintoshplus@users.noreply.github.com>
* @copyright 2016-2019 - Jean-Baptiste Nahan
* @license MIT
*/
namespace Mactronique\CUA\Configuration;
@@ -22,13 +22,6 @@ class MainConfiguration implements ConfigurationInterface
// ... add node definitions to the root of the tree
$rootNode
->children()
->arrayNode('projects')
->isRequired()
->cannotBeEmpty()
->useAttributeAsKey('name')
->prototype('scalar')
->end()
->end()
->arrayNode('persistance')
->addDefaultsIfNotSet()
->children()
@@ -43,6 +36,23 @@ class MainConfiguration implements ConfigurationInterface
->scalarNode('composer_path')
->defaultValue('/usr/bin/composer')
->end()
->scalarNode('security_checker_path')
->defaultValue('/usr/bin/security-checker')
->end()
->arrayNode('project_provider')
->addDefaultsIfNotSet()
->children()
->enumNode('type')
->defaultValue('file')
->values(array('file', 'redmine'))
->end()
->arrayNode('parameters')
->defaultValue([])
->prototype('variable')
->end()
->end()
->end()
->end()
->end()
;

View File

@@ -0,0 +1,62 @@
<?php
/**
* This file is part of Composer Update Analyser package.
*
* @author Jean-Baptiste Nahan <814683+macintoshplus@users.noreply.github.com>
* @copyright 2016-2019 - Jean-Baptiste Nahan
* @license MIT
*/
namespace Mactronique\CUA\Configuration;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
class ProjectConfiguration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('main');
// ... add node definitions to the root of the tree
$rootNode
->children()
->arrayNode('projects')
->normalizeKeys(false)
->useAttributeAsKey('name')
->prototype('array')
->addDefaultsIfNotSet()
->children()
->scalarNode('path')
->cannotBeEmpty()
->end()
->scalarNode('lock_path')
->defaultValue('./composer.lock')
->end()
->booleanNode('check_dependencies')
->defaultTrue()
->end()
->booleanNode('check_security')
->defaultFalse()
->end()
->scalarNode('php_path')
->defaultValue('php')
->end()
->arrayNode('private_dependencies')
->prototype('scalar')
->end()
->end()
->enumNode('private_dependencies_strategy')
->defaultValue('remove')
->values(array('remove', 'hash'))
->end()
->end()
->end()
->end()
->end()
;
return $treeBuilder;
}
}

View File

@@ -1,10 +1,20 @@
<?php
/**
* This file is part of Composer Update Analyser package.
*
* @author Jean-Baptiste Nahan <814683+macintoshplus@users.noreply.github.com>
* @copyright 2016-2019 - Jean-Baptiste Nahan
* @license MIT
*/
namespace Mactronique\CUA;
use Symfony\Component\Console\Application;
use Mactronique\CUA\Command\CheckCommand;
use Mactronique\CUA\Configuration\MainConfiguration;
use Mactronique\CUA\Command\CheckDependenciesCommand;
use Mactronique\CUA\Command\CheckSecurityCommand;
use Mactronique\CUA\Command\ProjectListCommand;
use Symfony\Component\Config\Definition\Processor;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputInterface;
@@ -20,12 +30,18 @@ class CuaApplication extends Application
private $persistance;
private $results;
private $projectsProvider;
private $results = [];
private $securityResults = [];
public function __construct()
{
parent::__construct('Composer Update Analyser', '0.1');
$this->add(new CheckCommand());
parent::__construct('Composer Update Analyser', '1.1.0');
$this->add(new CheckDependenciesCommand());
$this->add(new CheckSecurityCommand());
$this->add(new ProjectListCommand());
}
/**
@@ -76,7 +92,7 @@ class CuaApplication extends Application
public function getProjects()
{
return $this->config['projects'];
return $this->projectsProvider->getProjects();
}
public function setProjectResult($projectName, array $content)
@@ -97,14 +113,39 @@ class CuaApplication extends Application
return $this->config['composer_path'];
}
public function definePersistance($name, $parameters){
public function setProjectSecurityResult($projectName, array $content)
{
$this->securityResults[$projectName] = $content;
}
public function saveSecurityResult($path = null)
{
$this->persistance->saveSecurity($this->securityResults, $path);
}
public function getSecurityChecker()
{
return $this->config['security_checker_path'];
}
public function definePersistance($name, $parameters)
{
$className = 'Mactronique\\CUA\\Persistence\\'.$name;
if(!class_exists($className)){
throw new \Exception("Unable to load this persistance class: ".$className, 1);
if (!class_exists($className)) {
throw new \Exception('Unable to load this persistance class: '.$className, 1);
}
$this->persistance = new $className($parameters);
}
protected function defineProjectProvider($config)
{
$className = 'Mactronique\\CUA\\ProjectProvider\\'.ucfirst($config['type']).'Provider';
if (!class_exists($className)) {
throw new \Exception('Unable to load this project provider class: '.$className, 1);
}
$this->projectsProvider = new $className($config['parameters']);
}
/**
* Gets the default input definition.
*
@@ -123,13 +164,15 @@ class CuaApplication extends Application
*/
private function boot(InputInterface $input)
{
$this->config = ['output' => null, 'projects' => [], 'composer_path' => null];
if (!$input->hasParameterOption(['--no-config'], true)) {
$configFile = __DIR__.'/cua.yml';
$this->loadConfigurationFile($configFile);
$this->definePersistance($this->config['persistance']['format'], $this->config['persistance']['parameters']);
$this->config = ['output' => null, 'projects' => [], 'composer_path' => null, 'security_checker_path'=> null];
if ($input->hasParameterOption(['--no-config'], true)) {
return;
}
$configFile = __DIR__.'/../cua.yml';
$this->loadConfigurationFile($configFile);
$this->definePersistance($this->config['persistance']['format'], $this->config['persistance']['parameters']);
$this->defineProjectProvider($this->config['project_provider']);
}
/**

View File

@@ -3,13 +3,14 @@
/**
* This file is part of Composer Update Analyser package.
*
* @author Jean-Baptiste Nahan <jbnahan@gmail.com>
* @copyright 2016 - Jean-Baptiste Nahan
* @author Jean-Baptiste Nahan <814683+macintoshplus@users.noreply.github.com>
* @copyright 2016-2019 - Jean-Baptiste Nahan
* @license MIT
*/
namespace Mactronique\CUA\Persistence;
class DbalPersistance implements Persistence
class DbalPersistence implements Persistence
{
/**
* @var string default plath of file
@@ -18,12 +19,15 @@ class DbalPersistance implements Persistence
private $connexion;
private $updated;
/**
* @param array $config
*/
public function __construct(array $config)
{
$this->config = $config;
$this->updated = [];
}
/**
@@ -36,11 +40,62 @@ class DbalPersistance implements Persistence
$this->connexion->connect();
foreach ($content as $key => $data) {
if (in_array($key, $this->updated)) {
continue;
}
$this->removeAll($key);
$this->installedLib($key, $data['installed']);
$this->installLib($key, $data['install']);
$this->updateLib($key, $data['update']);
$this->removeLib($key, $data['uninstall']);
$this->abandonedLib($key, $data['abandoned']);
$this->updated[] = $key;
}
}
public function saveSecurity(array $content, array $config = null)
{
$this->connexion = \Doctrine\DBAL\DriverManager::getConnection($this->config);
$this->connexion->connect();
foreach ($content as $project => $securitydata) {
$data = ['state' => 'fixed'];
$data['updated_at'] = new \DateTime();
$this->connexion->update($this->config['table_name_security'], $data, ['project' => $project, 'state' => 'open'], ['string', 'datetime', 'string', 'string']);
if (count($securitydata) === 0) {
continue;
}
foreach ($securitydata as $library => $infos) {
$list = json_encode($infos['advisories']);
if ($this->checkSecurityExist($project, $library, $infos['version'])) {
$this->connexion->update(
$this->config['table_name_security'],
[
'details' => $list,
'state' => 'open',
'updated_at' => new \DateTime(),
],
['project' => $project, 'library'=>$library, 'version'=>$infos['version']],
['string', 'string', 'datetime', 'string', 'string', 'string']
);
} else {
$this->connexion->insert(
$this->config['table_name_security'],
[
'details' => $list,
'state' => 'open',
'updated_at' => new \DateTime(),
'project' => $project,
'library'=>$library,
'version'=>$infos['version']
],
['string', 'string', 'datetime', 'string', 'string', 'string']
);
}
}
}
}
@@ -164,4 +219,26 @@ class DbalPersistance implements Persistence
$this->connexion->update($this->config['table_name'], $data, $key, ['string', 'string', 'string', 'string', 'datetime', 'string', 'string']);
}
/**
* Mark all dependency to deleted.
*
* @param string $key
*/
private function removeAll($project)
{
$data = ['state' => 'removed', 'to_library' => null, 'to_version' => null];
$data['updated_at'] = new \DateTime();
$this->connexion->update($this->config['table_name'], $data, ['project' => $project], ['string', 'string', 'string', 'datetime', 'string']);
}
private function checkSecurityExist($project, $library, $version)
{
$result = $this->connexion->executeQuery('SELECT count(*) as nombre FROM '.$this->config['table_name_security'].' WHERE project= ? AND library=? AND version=?', [$project, $library, $version], ['string', 'string', 'string']);
$nb = $result->fetch();
return $nb['nombre'] != 0;
}
}

View File

@@ -0,0 +1,26 @@
<?php
/**
* This file is part of Composer Update Analyser package.
*
* @author Jean-Baptiste Nahan <814683+macintoshplus@users.noreply.github.com>
* @copyright 2016-2019 - Jean-Baptiste Nahan
* @license MIT
*/
namespace Mactronique\CUA\Persistence;
interface Persistence
{
/**
* @param array $content Content to save
* @param array $config custom config
*/
public function save(array $content, array $config = null);
/**
* @param array $content Content to save
* @param array $config custom config
*/
public function saveSecurity(array $content, array $config = null);
}

View File

@@ -0,0 +1,298 @@
<?php
/**
* This file is part of Composer Update Analyser package.
*
* @author Jean-Baptiste Nahan <814683+macintoshplus@users.noreply.github.com>
* @copyright 2016-2019 - Jean-Baptiste Nahan
* @license MIT
*/
namespace Mactronique\CUA\Persistence;
class RedmineCuaPersistence implements Persistence
{
/**
* @var string default plath of file
*/
private $config;
private $connexion;
private $updated;
private $projectCodeCache;
/**
* @param array $config
*/
public function __construct(array $config)
{
$this->config = $config;
$this->updated = [];
$this->projectCodeCache = [];
}
/**
* @param array $content Content to save
* @param array $config custom config
*/
public function save(array $content, array $config = null)
{
$this->connexion = \Doctrine\DBAL\DriverManager::getConnection($this->config);
//Debug des requettes SQL :
//$this->connexion->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger());
$this->connexion->connect();
foreach ($content as $key => $data) {
if (in_array($key, $this->updated)) {
continue;
}
$projectId = $this->convertProjectCode($key);
$this->removeAll($projectId);
$this->installedLib($projectId, $data['installed']);
$this->installLib($projectId, $data['install']);
$this->updateLib($projectId, $data['update']);
$this->removeLib($projectId, $data['uninstall']);
$this->abandonedLib($projectId, $data['abandoned']);
$this->updated[] = $key;
}
}
public function saveSecurity(array $content, array $config = null)
{
$this->connexion = \Doctrine\DBAL\DriverManager::getConnection($this->config);
$this->connexion->connect();
foreach ($content as $project => $securitydata) {
$projectId = $this->convertProjectCode($project);
$data = ['state' => 'fixed'];
$data['updated_at'] = new \DateTime();
$this->connexion->update($this->config['table_name_security'], $data, ['project_id' => $projectId, 'state' => 'open'], ['string', 'datetime', 'integer', 'string']);
if (count($securitydata) === 0) {
continue;
}
foreach ($securitydata as $library => $infos) {
dump($library, $infos);
$list = json_encode($infos['advisories']);
if ($this->checkSecurityExist($projectId, $library, $infos['version'])) {
$this->connexion->update(
$this->config['table_name_security'],
[
'details' => $list,
'state' => 'open',
'updated_at' => new \DateTime(),
],
['project_id' => $projectId, 'library'=>$library, 'version'=>$infos['version']],
['string', 'string', 'datetime', 'integer', 'string', 'string']
);
} else {
$this->connexion->insert(
$this->config['table_name_security'],
[
'details' => $list,
'state' => 'open',
'updated_at' => new \DateTime(),
'project_id' => $projectId,
'library'=>$library,
'version'=>$infos['version']
],
['string', 'string', 'datetime', 'integer', 'string', 'string']
);
}
}
}
}
/**
* Manage the installed library.
*
* @param string $projet
* @param array $installed library list
*/
private function installedLib($project, array $installed)
{
foreach ($installed as $library => $version) {
$dbData = ['project_id' => $project, 'library' => $library, 'version' => $version, 'state' => 'installed', 'to_library' => null, 'to_version' => null];
if ($this->checkExist($project, $library)) {
$this->update($dbData);
continue;
}
$this->insert($dbData);
}
}
/**
* Manage the library to install.
*
* @param string $projet
* @param array $install library list
*/
private function installLib($project, array $install)
{
foreach ($install as $data) {
$dbData = ['project_id' => $project, 'library' => $data['library'], 'version' => $data['version'], 'state' => 'install', 'to_library' => null, 'to_version' => null];
if ($this->checkExist($project, $data['library'])) {
$this->update($dbData);
continue;
}
$this->insert($dbData);
}
}
/**
* Manage the library to update.
*
* @param string $projet
* @param array $update library list
*/
private function updateLib($project, array $update)
{
foreach ($update as $data) {
$dbData = ['project_id' => $project, 'library' => $data['from_library'], 'version' => $data['from_version'], 'state' => 'update', 'to_library' => $data['to_library'], 'to_version' => $data['to_version']];
if ($this->checkExist($project, $data['from_library'])) {
$this->update($dbData);
continue;
}
$this->insert($dbData);
}
}
/**
* Manage the library to remove.
*
* @param string $projet
* @param array $remove library list
*/
private function removeLib($project, array $remove)
{
foreach ($remove as $data) {
$dbData = ['project_id' => $project, 'library' => $data['library'], 'version' => $data['version'], 'state' => 'remove', 'to_library' => null, 'to_version' => null];
if ($this->checkExist($project, $data['library'])) {
$this->update($dbData);
continue;
}
$this->insert($dbData);
}
}
/**
* Manage the abandoned library.
*
* @param string $projet
* @param array $abandonned library list
*/
private function abandonedLib($project, array $abandonned)
{
foreach ($abandonned as $key => $library) {
$this->connexion->update($this->config['table_name'], ['deprecated' => true], ['project_id' => $project, 'library' => $library], ['deprecated' => 'boolean', 'project_id' => 'integer', 'library' => 'string']);
}
}
private function checkExist($project, $library)
{
$result = $this->connexion->executeQuery('SELECT count(*) as nombre FROM '.$this->config['table_name'].' WHERE project_id= ? AND library=?', [$project, $library], ['integer', 'string']);
$nb = $result->fetch(\PDO::FETCH_ASSOC);
return intval($nb['nombre']) != 0;
}
private function checkSecurityExist($project, $library, $version)
{
$result = $this->connexion->executeQuery('SELECT count(*) as nombre FROM '.$this->config['table_name_security'].' WHERE project_id= ? AND library=? AND version=?', [$project, $library, $version], ['integer', 'string', 'string']);
$nb = $result->fetch(\PDO::FETCH_ASSOC);
return intval($nb['nombre']) != 0;
}
/**
* @param array $data list of field (key) with value.
*/
private function insert(array $data)
{
$data['deprecated'] = false;
$data['updated_at'] = new \DateTime();
$this->connexion->insert(
$this->config['table_name'],
$data,
[
'project_id' => 'integer',
'library' => 'string',
'version' => 'string',
'state' => 'string',
'to_library' => 'string',
'to_version' => 'string',
'deprecated' => 'boolean',
'updated_at' => 'datetime',
]);
}
/**
* @param array $data list of field (key) with value.
*/
private function update(array $data)
{
$data['updated_at'] = new \DateTime();
$key['project_id'] = $data['project_id'];
$key['library'] = $data['library'];
unset($data['project_id']);
unset($data['library']);
if (array_key_exists('deprecated', $data)) {
unset($data['deprecated']);
}
$this->connexion->update(
$this->config['table_name'],
$data,
$key,
[
'version' => 'string',
'state' => 'string',
'to_library' => 'string',
'to_version' => 'string',
'updated_at' => 'datetime',
'library' => 'string',
'project_id' => 'integer'
]
);
}
/**
* Mark all dependency to deleted.
*
* @param string $key
*/
private function removeAll($project)
{
$data = ['state' => 'removed', 'to_library' => null, 'to_version' => null];
$data['updated_at'] = new \DateTime();
$this->connexion->update($this->config['table_name'], $data, ['project_id' => $project], ['string', 'string', 'string', 'datetime', 'integer']);
}
/**
* return the index for the project code.
*/
private function convertProjectCode($projectCode)
{
if (array_key_exists($projectCode, $this->projectCodeCache)) {
return $this->projectCodeCache[$projectCode];
}
$result = $this->connexion->executeQuery('SELECT id FROM projects WHERE identifier = ?', [$projectCode], ['string']);
$rows = $result->fetchAll();
if (count($rows) == 0) {
$this->projectCodeCache[$projectCode] = null;
return;
}
$this->projectCodeCache[$projectCode] = intval($rows[0]['id']);
return $this->projectCodeCache[$projectCode];
}
}

View File

@@ -0,0 +1,55 @@
<?php
/**
* This file is part of Composer Update Analyser package.
*
* @author Jean-Baptiste Nahan <814683+macintoshplus@users.noreply.github.com>
* @copyright 2016-2019 - Jean-Baptiste Nahan
* @license MIT
*/
namespace Mactronique\CUA\Persistence;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Exception\ProcessTimedOutException;
use Symfony\Component\Yaml\Yaml;
class YamlFile implements Persistence
{
/**
* @var string default path of file
*/
private $filePath;
/**
* @var string default path of file
*/
private $fileSecurityPath;
/**
* @param array $config
*/
public function __construct(array $config)
{
$this->filePath = $config['path'];
$this->fileSecurityPath = $config['path_security'];
}
/**
* @param array $content Content to save
* @param array $config custom config
*/
public function save(array $content, array $config = null)
{
$content = Yaml::dump($content, 100);
file_put_contents(($config !== null && isset($config['path']))? $config['path']:$this->filePath, $content);
}
public function saveSecurity(array $content, array $config = null)
{
$content = Yaml::dump($content, 100);
file_put_contents(($config !== null && isset($config['path_security']))? $config['path_security']:$this->fileSecurityPath, $content);
}
}

View File

@@ -0,0 +1,83 @@
<?php
/**
* This file is part of Composer Update Analyser package.
*
* @author Jean-Baptiste Nahan <814683+macintoshplus@users.noreply.github.com>
* @copyright 2016-2019 - Jean-Baptiste Nahan
* @license MIT
*/
namespace Mactronique\CUA\ProjectProvider;
use Symfony\Component\Config\Definition\Processor;
use Mactronique\CUA\Configuration\ProjectConfiguration;
class DbalProvider implements ProjectProviderInterface
{
private $config;
private $projects;
public function __construct($config)
{
if (null !== $config || !is_array($config)) {
throw new \Exception("The configuration is not set or not an array", 1);
}
$this->config = $config;
if (!isset($config['working_dir']) || !is_dir($config['working_dir'])) {
throw new \Exception("working_dir configuration key is not set or not exists", 1);
}
if (!isset($config['db']) || !is_array($config['db'])) {
throw new \Exception("db configuration key is not set or is not an array", 1);
}
if (!isset($config['table_name']) || !is_string($config['table_name'])) {
throw new \Exception("table_name configuration key is not set or is not a string", 1);
}
}
public function getProjects()
{
if (null !== $this->projects && is_array($this->projects)) {
return $this->projects;
}
$connexion = \Doctrine\DBAL\DriverManager::getConnection($this->config['db']);
$connexion->connect();
$config = [];
$result = $connexion->executeQuery(sprintf("SELECT code, name, path, check_dependencies, check_security, lock_path, php_path, private_dependencies FROM %s WHERE check_dependencies = '1' or check_security = '1'", $this->config['db']['table_name']));
while ($ligne = $result->fetch(\PDO::FETCH_ASSOC)) {
$conf = [
'path' => $this->config['working_dir'].DIRECTORY_SEPARATOR.$ligne['path'],
'check_dependencies' => boolval($ligne['check_dependencies']),
'check_security' => boolval($ligne['check_security']),
];
if (!empty($ligne['private_dependencies'])) {
$conf['private_dependencies'] = json_decode($ligne['private_dependencies'], true);
// JSON decode silent error
if ($conf['private_dependencies'] === false) {
$conf['private_dependencies'] = null;
}
}
if (!empty($ligne['lock_path'])) {
$conf['lock_path'] = $ligne['lock_path'];
}
if (!empty($ligne['php_path'])) {
$conf['php_path'] = $ligne['php_path'];
}
$config[$ligne['code']] = $conf;
}
$configs = [['projects' => $config]];
$processor = new Processor();
$configuration = new ProjectConfiguration();
$this->projects = $processor->processConfiguration($configuration, $configs)['projects'];
return $this->projects;
}
}

View File

@@ -0,0 +1,48 @@
<?php
/**
* This file is part of Composer Update Analyser package.
*
* @author Jean-Baptiste Nahan <814683+macintoshplus@users.noreply.github.com>
* @copyright 2016-2019 - Jean-Baptiste Nahan
* @license MIT
*/
namespace Mactronique\CUA\ProjectProvider;
use Mactronique\CUA\Configuration\ProjectConfiguration;
use Symfony\Component\Config\Definition\Processor;
use Symfony\Component\Yaml\Yaml;
class FileProvider implements ProjectProviderInterface
{
private $config;
private $projects;
public function __construct($config)
{
$this->config = $config;
if (!isset($config['path'])) {
throw new \Exception("The path of file project is not set !", 1);
}
}
public function getProjects()
{
if (null !== $this->projects && is_array($this->projects)) {
return $this->projects;
}
$filePath = __DIR__.'/../../'.$this->config['path'];
if (!file_exists($filePath)) {
throw new \Exception("Unable to load file : ".$filePath, 1);
}
$config = Yaml::parse(file_get_contents($filePath));
$configs = [$config];
$processor = new Processor();
$configuration = new ProjectConfiguration();
$this->projects = $processor->processConfiguration($configuration, $configs)['projects'];
return $this->projects;
}
}

View File

@@ -0,0 +1,19 @@
<?php
/**
* This file is part of Composer Update Analyser package.
*
* @author Jean-Baptiste Nahan <814683+macintoshplus@users.noreply.github.com>
* @copyright 2016-2019 - Jean-Baptiste Nahan
* @license MIT
*/
namespace Mactronique\CUA\ProjectProvider;
interface ProjectProviderInterface
{
/**
* return the list of project with configuration
* @return array
*/
public function getProjects();
}

View File

@@ -0,0 +1,66 @@
<?php
/**
* This file is part of Composer Update Analyser package.
*
* @author Jean-Baptiste Nahan <814683+macintoshplus@users.noreply.github.com>
* @copyright 2016-2019 - Jean-Baptiste Nahan
* @license MIT
*/
namespace Mactronique\CUA\ProjectProvider;
use Symfony\Component\Config\Definition\Processor;
use Mactronique\CUA\Configuration\ProjectConfiguration;
class RedmineProvider implements ProjectProviderInterface
{
private $config;
private $projects;
public function __construct($config)
{
$this->config = $config;
if (!isset($config['working_dir']) || !is_dir($config['working_dir'])) {
throw new \Exception("working_dir configuration key is not set or not exists", 1);
}
if (!isset($config['db']) || !is_array($config['db'])) {
throw new \Exception("db configuration key is not set or is not an array", 1);
}
}
public function getProjects()
{
if (null !== $this->projects && is_array($this->projects)) {
return $this->projects;
}
$connexion = \Doctrine\DBAL\DriverManager::getConnection($this->config['db']);
$connexion->connect();
$config = [];
$result = $connexion->executeQuery(sprintf("SELECT s.*, p.identifier as pid FROM `%s` s LEFT JOIN `projects` p ON s.project_id = p.id LEFT JOIN `repositories` r ON s.project_id = r.project_id WHERE p.status= '1' and r.is_default = '1'", $this->config['db']['table_name']));
while ($ligne = $result->fetch(\PDO::FETCH_ASSOC)) {
$conf = [
'path' => $this->config['working_dir'].'/'.$ligne['pid'],
'check_dependencies' => boolval($ligne['check_update']),
'check_security' => boolval($ligne['check_security']),
];
if (!empty($ligne['lock_path'])) {
$conf['lock_path'] = $ligne['lock_path'];
}
if (!empty($ligne['php_bin'])) {
$conf['php_path'] = $ligne['php_bin'];
}
$config[$ligne['pid']] = $conf;
}
$configs = [['projects' => $config]];
$processor = new Processor();
$configuration = new ProjectConfiguration();
$this->projects = $processor->processConfiguration($configuration, $configs)['projects'];
return $this->projects;
}
}

View File

@@ -3,8 +3,8 @@
/**
* This file is part of Composer Update Analyser package.
*
* @author Jean-Baptiste Nahan <jbnahan@gmail.com>
* @copyright 2016 - Jean-Baptiste Nahan
* @author Jean-Baptiste Nahan <814683+macintoshplus@users.noreply.github.com>
* @copyright 2016-2019 - Jean-Baptiste Nahan
* @license MIT
*/
namespace Mactronique\CUA\Service;
@@ -15,25 +15,32 @@ use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Exception\ProcessTimedOutException;
class CheckUpdateService
{
class CheckUpdateService {
/**
* @var LoggerInterface|NullLogger
*/
private $logger;
/**
* @var string
*/
private $composerPath;
/**
* @var string
*/
private $composerPath;
/**
* Construct the service
* @param string $composerPath
*/
public function __construct($composerPath, LoggerInterface $logger = null){
$this->composerPath = $composerPath;
$this->logger = ($logger === null)? new NullLogger():$logger;
}
/**
* Construct the service
* @param string $composerPath
*/
public function __construct($composerPath, LoggerInterface $logger = null)
{
$this->composerPath = $composerPath;
$this->logger = ($logger === null)? new NullLogger():$logger;
}
public function checkComposerUpdate($projectPath){
$resultProject = [
public function checkComposerUpdate($projectPath, $php_path)
{
$resultProject = [
'install' => [],
'uninstall' => [],
'update' => [],
@@ -41,17 +48,17 @@ class CheckUpdateService {
'error' => '',
];
$process = new Process($this->composerPath.' update --dry-run --no-ansi');
$process = new Process($php_path.' '.$this->composerPath.' update --dry-run --no-ansi');
$process->setWorkingDirectory($projectPath);
$process->setTimeout(300);
try {
$process->mustRun();
} catch (ProcessFailedException $e) {
$this->logger->error('Process Fail', ['projectPath'=>$projectPath, 'exception'=>$e]);
$this->logger->error('Process Fail', ['projectPath'=>$projectPath, 'exception'=>$e]);
$resultProject['error'] = $process->getErrorOutput();
return $resultProject;
} catch (ProcessTimedOutException $e) {
$this->logger->error('Process time out', ['projectPath'=>$projectPath, 'exception'=>$e]);
$this->logger->error('Process time out', ['projectPath'=>$projectPath, 'exception'=>$e]);
$resultProject['error'] = 'Time out '.$e->getMessage();
return $resultProject;
}
@@ -87,5 +94,5 @@ class CheckUpdateService {
$resultProject['abandoned'] = $abandoned[1];
}
return $resultProject;
}
}
}
}

View File

@@ -0,0 +1,51 @@
<?php
/**
* This file is part of Composer Update Analyser package.
*
* @author Jean-Baptiste Nahan <814683+macintoshplus@users.noreply.github.com>
* @copyright 2016-2019 - Jean-Baptiste Nahan
* @license MIT
*/
namespace Mactronique\CUA\Service;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Symfony\Component\Finder\Finder;
class InstalledLibraryService
{
/**
* Construct the service.
*
* @param string $composerPath
*/
public function __construct(LoggerInterface $logger = null)
{
$this->logger = ($logger === null) ? new NullLogger() : $logger;
}
public function getInstalledLibrary($projectPath)
{
$finder = new Finder();
$libraries = [];
$composer = $finder->in($projectPath)->files()->name('composer.lock');
foreach ($composer as $file) {
$json = json_decode($file->getContents(), true);
foreach ($json['packages'] as $value) {
$libraries[$value['name']] = $value['version'];
}
if (!isset($json['platform'])) {
continue;
}
foreach ($json['platform'] as $platform => $version) {
$libraries[$platform] = $version;
}
}
return $libraries;
}
}

View File

@@ -0,0 +1,127 @@
<?php
/**
* This file is part of Composer Update Analyser package.
*
* @author Jean-Baptiste Nahan <814683+macintoshplus@users.noreply.github.com>
* @copyright 2016-2019 - Jean-Baptiste Nahan
* @license MIT
*/
namespace Mactronique\CUA\Service;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use SensioLabs\Security\SecurityChecker;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Exception\ProcessTimedOutException;
class SecurityCheckService
{
const INTERNAL = 'internal';
/** @var string */
private $securityCheckPath;
/** @var LoggerInterface */
private $logger;
/**
* SecurityCheckService constructor.
* @param string $securityCheckPath
* @param LoggerInterface|null $logger
*/
public function __construct($securityCheckPath, LoggerInterface $logger = null)
{
$this->securityCheckPath = $securityCheckPath;
$this->logger = ($logger === null)? new NullLogger():$logger;
}
/**
* @param string $projectPath
* @param string $lockPath
* @param string $phpPath
* @return array
*/
public function checkSecurity($projectPath, $lockPath, $phpPath)
{
if (strtolower($this->securityCheckPath) === self::INTERNAL){
return $this->checkSecurityInt($projectPath, $lockPath);
}
return $this->checkSecurityExt($projectPath, $lockPath, $phpPath);
}
/**
* Check security with the integrated checker
* @param string $projectPath
* @param string $lockPath
* @return array
*/
private function checkSecurityInt($projectPath, $lockPath)
{
$resultProject = [
'error' => '',
'result' => [],
];
try {
$result = (string) (new SecurityChecker())->check($lockPath);
return $this->processJson($result, $resultProject, $projectPath);
} catch(\Exception $e) {
$resultProject['error'] = $e->getMessage();
}
return $resultProject;
}
/**
* Use external checker
* @param string $projectPath
* @param string $lockPath
* @param string $phpPath
* @return array
*/
private function checkSecurityExt($projectPath, $lockPath, $phpPath)
{
$resultProject = [
'error' => '',
'result' => [],
];
$process = new Process($phpPath.' '.$this->securityCheckPath.' security:check '.$lockPath.' --format=json ');
$process->setWorkingDirectory($projectPath);
$process->setTimeout(3000);
try {
$returnCode = $process->run();
$sortieErr = $process->getErrorOutput();
if (strlen($sortieErr) > 0) {
$resultProject['error'] = 'Result code' . $returnCode. "\n" . $sortieErr;
}
return $this->processJson($process->getOutput(), $resultProject, $projectPath);
} catch (ProcessTimedOutException $e) {
$this->logger->error('Process time out', ['projectPath'=>$projectPath, 'exception'=>$e]);
$resultProject['error'] = 'Time out '.$e->getMessage();
return $resultProject;
}
}
/**
* @param string $jsonString
* @param array $resultProject
* @param string $projectPath
* @return mixed
*/
private function processJson($jsonString, $resultProject, $projectPath)
{
$datas = json_decode($jsonString, true);
if (json_last_error() !== JSON_ERROR_NONE) {
$this->logger->error('Json decode error', ['projectPath'=>$projectPath, 'json_error'=>json_last_error_msg()]);
$resultProject['error'] .= 'Json decode error '.json_last_error_msg();
return $resultProject;
}
$resultProject['result'] = $datas;
return $resultProject;
}
}

View File

@@ -0,0 +1,78 @@
/*
* This file is part of Composer Update Analyser package.
*
* @author Jean-Baptiste Nahan <814683+macintoshplus@users.noreply.github.com>
* @copyright 2016-2019 - Jean-Baptiste Nahan
* @license MIT
*/
--- FOR MSSQL
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
if not exists (select * from sysobjects where name='dependencies' and xtype='U')
CREATE TABLE [dbo].[dependencies](
[id] [int] IDENTITY(1,1) NOT NULL,
[project] [nvarchar](50) NOT NULL,
[library] [nvarchar](250) NOT NULL,
[version] [nvarchar](250) NOT NULL,
[state] [nvarchar](20) NOT NULL,
[to_library] [nvarchar](250) DEFAULT NULL,
[to_version] [nvarchar](250) DEFAULT NULL,
[deprecated] [bit] DEFAULT NULL,
[updated_at] [datetime2](7) NOT NULL,
CONSTRAINT [PK_dependencies] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
if not exists (select * from sysobjects where name='idx_proj_lib' and xtype='UQ')
ALTER TABLE [dbo].[dependencies] ADD CONSTRAINT [idx_proj_lib] UNIQUE NONCLUSTERED
(
[project] ASC,
[library] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
if not exists (select * from sysobjects where name='security' and xtype='U')
CREATE TABLE [dbo].[security](
[id] [int] IDENTITY(1,1) NOT NULL,
[project] [nvarchar](50) NOT NULL,
[library] [nvarchar](255) NOT NULL,
[version] [nvarchar](255) NOT NULL,
[state] [nvarchar](20) NOT NULL,
[details] [text] NOT NULL,
[updated_at] [datetime2](7) NOT NULL,
CONSTRAINT [PK_security] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
if not exists (select * from sysobjects where name='projects' and xtype='U')
CREATE TABLE [dbo].[projects] (
[code] [nvarchar](10) NOT NULL,
[name] [nvarchar](100) NOT NULL,
[path] [nvarchar](255) NOT NULL,
[lock_path] [nvarchar](255) NOT NULL,
[php_path] [nvarchar](255) NOT NULL,
[private_dependencies] [nvarchar](max) DEFAULT NULL,
[private_dependencies_strategy] [nvarchar](10) DEFAULT 'remove',
[check_dependencies] [bit] DEFAULT NULL,
[check_security] [bit] DEFAULT NULL,
[updated_at] [datetime2](7) NOT NULL
CONSTRAINT [PK_project] PRIMARY KEY CLUSTERED
(
[code] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

View File

@@ -0,0 +1,66 @@
##
## This file is part of Composer Update Analyser package.
##
## @author Jean-Baptiste Nahan <814683+macintoshplus@users.noreply.github.com>
## @copyright 2016-2019 - Jean-Baptiste Nahan
## @license MIT
##
## FOR MySQL
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";
CREATE TABLE IF NOT EXISTS `dependencies` (
`id` int(11) NOT NULL,
`project` varchar(50) NOT NULL,
`library` varchar(250) NOT NULL,
`version` varchar(250) NOT NULL,
`state` varchar(20) NOT NULL,
`to_library` varchar(250) DEFAULT NULL,
`to_version` varchar(250) DEFAULT NULL,
`deprecated` tinyint(1) DEFAULT NULL,
`updated_at` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
ALTER TABLE `dependencies`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `idx_proj_lib` (`project`,`library`(191)) USING BTREE,
ADD KEY `idx_plv` (`project`,`library`(191),`version`(191));
ALTER TABLE `dependencies`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
CREATE TABLE IF NOT EXISTS `security` (
`id` int(11) NOT NULL,
`project` varchar(50) NOT NULL,
`library` varchar(255) NOT NULL,
`version` varchar(255) NOT NULL,
`state` varchar(20) NOT NULL,
`details` text NOT NULL,
`updated_at` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
ALTER TABLE `security`
ADD PRIMARY KEY (`id`);
ALTER TABLE `security`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
CREATE TABLE IF NOT EXISTS `projects` (
`code` varchar(10) NOT NULL,
`name` varchar(100) NOT NULL,
`path` varchar(255) NOT NULL,
`lock_path` varchar(255) NOT NULL,
`php_path` varchar(255) NOT NULL,
`private_dependencies` text DEFAULT NULL,
`private_dependencies_strategy` varchar(10) DEFAULT 'remove',
`check_dependencies` tinyint(1) DEFAULT NULL,
`check_security` tinyint(1) DEFAULT NULL,
`updated_at` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
ALTER TABLE `projects`
ADD PRIMARY KEY (`code`);

View File

@@ -0,0 +1,45 @@
CREATE TABLE "dependencies" (
"id" serial NOT NULL PRIMARY KEY,
"project" varchar(50) NOT NULL,
"library" varchar(250) NOT NULL,
"version" varchar(250) NOT NULL,
"state" varchar(20) NOT NULL,
"to_library" varchar(250) NULL,
"to_version" varchar(250) NULL,
"deprecated" boolean NULL,
"updated_at" timestamp NOT NULL
);
CREATE UNIQUE INDEX CONCURRENTLY idx_proj_lib
ON dependencies (project, library);
ALTER TABLE dependencies
ADD CONSTRAINT unique_proj_lib
UNIQUE USING INDEX idx_proj_lib;
CREATE INDEX idx_plv
ON dependencies (project, library, version);
CREATE TABLE "security" (
"id" serial NOT NULL PRIMARY KEY,
"project" varchar(50) NOT NULL,
"library" varchar(250) NOT NULL,
"version" varchar(250) NOT NULL,
"state" varchar(20) NOT NULL,
"details" text NOT NULL,
"updated_at" timestamp NOT NULL
);
CREATE TABLE "projects" (
"code" varchar(10) NOT NULL PRIMARY KEY,
"name" varchar(100) NOT NULL,
"path" varchar(255) NOT NULL,
"lock_path" varchar(255) NOT NULL,
"php_path" varchar(255) NOT NULL,
"private_dependencies" text DEFAULT NULL,
"private_dependencies_strategy" varchar(10) DEFAULT 'remove',
"check_dependencies" boolean DEFAULT NULL,
"check_security" boolean DEFAULT NULL,
"updated_at" timestamp NOT NULL
);

View File

@@ -0,0 +1,36 @@
CREATE TABLE "dependencies" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"project" TEXT NOT NULL,
"library" TEXT NOT NULL,
"version" TEXT NOT NULL,
"state" TEXT NOT NULL,
"to_library" TEXT,
"to_version" TEXT,
"deprecated" INTEGER,
"updated_at" TEXT NOT NULL
);
CREATE TABLE "security" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"projet" TEXT NOT NULL,
"library" TEXT NOT NULL,
"version" TEXT NOT NULL,
"state" TEXT NOT NULL,
"details" TEXT NOT NULL,
"updated_at" TEXT NOT NULL
);
CREATE TABLE "projects" (
"code" TEXT NOT NULL,
"name" TEXT NOT NULL,
"path" TEXT NOT NULL,
"lock_path" TEXT NOT NULL,
"php_path" TEXT NOT NULL,
"private_dependencies" TEXT,
"private_dependencies_strategy" TEXT DEFAULT 'remove',
"check_dependencies" INTEGER,
"check_security" INTEGER,
"updated_at" TEXT NOT NULL,
PRIMARY KEY("code")
);

View File

@@ -0,0 +1,20 @@
<?php
/**
* This file is part of Composer Update Analyser package.
*
* @author Jean-Baptiste Nahan <814683+macintoshplus@users.noreply.github.com>
* @copyright 2016-2019 - Jean-Baptiste Nahan
* @license MIT
*/
namespace Mactronique\CUA\Tests\Units\Persistence;
use atoum;
class YamlFile extends atoum
{
public function testInit()
{
$this->newTestedInstance(['path'=>'here', 'path_security'=>'here_security']);
$this->assert('type')->object($this->testedInstance)->isInstanceOf('Mactronique\CUA\Persistence\Persistence');
}
}