11<?php
22
3+ use Doctrine \Common \Cache \FilesystemCache ;
4+ use DrupalComposer \DrupalSecurityAdvisories \Projects ;
5+ use DrupalComposer \DrupalSecurityAdvisories \UrlHelper ;
6+ use DrupalComposer \DrupalSecurityAdvisories \VersionParser ;
37use GuzzleHttp \Client ;
48use GuzzleHttp \HandlerStack ;
59use Kevinrob \GuzzleCache \CacheMiddleware ;
6- use Doctrine \Common \Cache \FilesystemCache ;
7- use Kevinrob \GuzzleCache \Strategy \PrivateCacheStrategy ;
810use Kevinrob \GuzzleCache \Storage \DoctrineCacheStorage ;
11+ use Kevinrob \GuzzleCache \Strategy \GreedyCacheStrategy ;
912
1013require __DIR__ . '/vendor/autoload.php ' ;
1114
1619$ stack = HandlerStack::create ();
1720$ stack ->push (
1821 new CacheMiddleware (
19- new PrivateCacheStrategy (
22+ new GreedyCacheStrategy (
2023 new DoctrineCacheStorage (
2124 new FilesystemCache (__DIR__ . '/cache ' )
22- )
25+ ),
26+ 3600
2327 )
2428 ),
2529 'cache '
2630);
2731$ client = new Client (['handler ' => $ stack ]);
28-
29- $ data = json_decode ($ client ->get ('https://www.drupal.org/api-d7/node.json?type=project_release&taxonomy_vocabulary_7=100&field_release_build_type=static ' )->getBody ());
30-
31- $ projects = [];
32+ $ projects = new Projects ($ client );
3233$ conflict = [];
3334
34- class UrlHelper {
35-
36- public static function prepareUrl ($ url ) {
37- return str_replace ('https://www.drupal.org/api-d7/node ' , 'https://www.drupal.org/api-d7/node.json ' , $ url );
35+ /**
36+ * @param $url
37+ * @param \GuzzleHttp\Client $client
38+ *
39+ * @return array
40+ */
41+ function fetchAllData ($ url , Client $ client ) {
42+ $ results = [];
43+ $ data = json_decode ($ client ->get ($ url )->getBody ());
44+ while (isset ($ data ) && isset ($ data ->list )) {
45+ $ results = array_merge ($ results , $ data ->list );
46+
47+ if (isset ($ data ->next )) {
48+ $ data = json_decode ($ client ->get (UrlHelper::prepareUrl ($ data ->next ))->getBody ());
49+ }
50+ else {
51+ $ data = NULL ;
52+ }
3853 }
39-
54+ return $ results ;
4055}
4156
42- class VersionParser {
43-
44- public static function getSemVer ($ version , $ isCore ) {
45- $ version = $ isCore ? static ::handleCore ($ version ) : static ::handleContrib ($ version );
46- return static ::isValid ($ version ) ? $ version : FALSE ;
47- }
57+ // Security releases
58+ $ results = fetchAllData ('https://www.drupal.org/api-d7/node.json?type=project_release&taxonomy_vocabulary_7=100&field_release_build_type=static ' , $ client );
59+ foreach ($ results as $ result ) {
60+ $ nid = $ result ->field_release_project ->id ;
61+ $ core = (int ) substr ($ result ->field_release_version , 0 , 1 );
4862
49- public static function handleCore ($ version ) {
50- return $ version ;
63+ // Skip D6 and older.
64+ if ($ core < 7 ) {
65+ continue ;
5166 }
5267
53- public static function handleContrib ($ version ) {
54- list ($ core , $ version ) = explode ('- ' , $ version , 2 );
55- return $ version ;
56- }
68+ $ project = $ projects ->getFromNid ($ nid );
5769
58- public static function isValid ($ version ) {
59- return (strpos ($ version , 'unstable ' ) === FALSE );
70+ if (!$ project ) {
71+ // @todo: log error
72+ continue ;
6073 }
6174
62- }
63-
64- while ( isset ( $ data ) && isset ( $ data -> list )) {
65- $ results = array_merge ( $ results , $ data -> list );
66-
67- if ( isset ( $ data -> next )) {
68- $ data = json_decode ( $ client -> get (UrlHelper:: prepareUrl ( $ data -> next ))-> getBody ()) ;
69- }
70- else {
71- $ data = NULL ;
75+ try {
76+ $ is_core = ( $ project -> field_project_machine_name == ' drupal ' ) ? TRUE : FALSE ;
77+ $ constraint = VersionParser:: generateRangeConstraint ( $ result -> field_release_version , $ is_core );
78+ if (! $ constraint ) {
79+ throw new InvalidArgumentException ( ' Invalid version number. ' );
80+ }
81+ $ conflict [ $ core ][ ' drupal/ ' . $ project -> field_project_machine_name ][] = $ constraint ;
82+ } catch ( \ Exception $ e ) {
83+ // @todo: log exception
84+ continue ;
7285 }
7386}
7487
88+ // Insecure releases
89+ $ results = fetchAllData ('https://www.drupal.org/api-d7/node.json?type=project_release&taxonomy_vocabulary_7=188131&field_release_build_type=static ' , $ client );
7590foreach ($ results as $ result ) {
7691 $ nid = $ result ->field_release_project ->id ;
7792 $ core = (int ) substr ($ result ->field_release_version , 0 , 1 );
@@ -81,24 +96,20 @@ public static function isValid($version) {
8196 continue ;
8297 }
8398
84- try {
85- if (!isset ($ projects [$ nid ])) {
86- $ project = json_decode ($ client ->get ('https://www.drupal.org/api-d7/node.json?nid= ' . $ nid )->getBody ());
87- $ projects [$ nid ] = $ project ->list [0 ];
88- }
89- } catch (\GuzzleHttp \Exception \ServerException $ e ) {
90- // @todo: log exception
99+ $ project = $ projects ->getFromNid ($ nid );
100+
101+ if (!$ project ) {
102+ // @todo: log error
91103 continue ;
92104 }
93105
94106 try {
95- $ project = $ projects [$ nid ];
96107 $ is_core = ($ project ->field_project_machine_name == 'drupal ' ) ? TRUE : FALSE ;
97- $ version = VersionParser::getSemVer ($ result ->field_release_version , $ is_core );
98- if (!$ version ) {
108+ $ constraint = VersionParser::generateExplicitConstraint ($ result ->field_release_version , $ is_core );
109+ if (!$ constraint ) {
99110 throw new InvalidArgumentException ('Invalid version number. ' );
100111 }
101- $ conflict [$ core ]['drupal/ ' . $ project ->field_project_machine_name ][] = ' < ' . $ version ;
112+ $ conflict [$ core ]['drupal/ ' . $ project ->field_project_machine_name ][] = $ constraint ;
102113 } catch (\Exception $ e ) {
103114 // @todo: log exception
104115 continue ;
@@ -121,7 +132,7 @@ public static function isValid($version) {
121132
122133 foreach ($ packages as $ package => $ constraints ) {
123134 natsort ($ constraints );
124- $ composer ['conflict ' ][$ package ] = implode (', ' , $ constraints );
135+ $ composer ['conflict ' ][$ package ] = implode ('| ' , $ constraints );
125136 }
126137
127138 // drupal/core is a subtree split for drupal/drupal and has no own SAs.
0 commit comments