This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace Kunstmaan\AdminBundle\Helper\VersionCheck; |
||
4 | |||
5 | use Doctrine\Common\Cache\Cache; |
||
6 | use Doctrine\Common\Cache\CacheProvider; |
||
7 | use Exception; |
||
8 | use GuzzleHttp\Client; |
||
9 | use Kunstmaan\AdminBundle\Helper\VersionCheck\Exception\ParseException; |
||
10 | use Symfony\Component\Cache\Adapter\AdapterInterface; |
||
11 | use Symfony\Component\Cache\Adapter\DoctrineAdapter; |
||
12 | use Symfony\Component\DependencyInjection\ContainerInterface; |
||
13 | use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; |
||
14 | use Symfony\Contracts\Translation\TranslatorInterface; |
||
15 | |||
16 | /** |
||
17 | * Version checker |
||
18 | */ |
||
19 | class VersionChecker |
||
20 | { |
||
21 | public const CACHE_KEY = 'version_check'; |
||
22 | |||
23 | /** |
||
24 | * @var ContainerInterface |
||
25 | */ |
||
26 | private $container; |
||
27 | |||
28 | /** |
||
29 | * @var AdapterInterface |
||
30 | */ |
||
31 | private $cache; |
||
32 | |||
33 | /** |
||
34 | * @var string |
||
35 | */ |
||
36 | private $webserviceUrl; |
||
37 | |||
38 | /** |
||
39 | * @var int |
||
40 | */ |
||
41 | private $cacheTimeframe; |
||
42 | |||
43 | /** |
||
44 | * @var bool |
||
45 | */ |
||
46 | private $enabled; |
||
47 | |||
48 | /** |
||
49 | * @var Client |
||
50 | */ |
||
51 | private $client; |
||
52 | |||
53 | /** |
||
54 | * @var TranslatorInterface|LegacyTranslatorInterface |
||
55 | */ |
||
56 | private $translator; |
||
57 | |||
58 | /** |
||
59 | 7 | * @param CacheProvider|AdapterInterface $cache |
|
60 | */ |
||
61 | 7 | public function __construct(ContainerInterface $container, /* AdapterInterface */$cache, $translator) |
|
62 | 7 | { |
|
63 | $this->container = $container; |
||
64 | |||
65 | 7 | if (!$cache instanceof CacheProvider && !$cache instanceof AdapterInterface) { |
|
66 | // NEXT_MAJOR Add AdapterInterface typehint for the $cache parameter |
||
67 | throw new \InvalidArgumentException(sprintf('The "$cache" parameter should extend from "%s" or implement "%s"', CacheProvider::class, AdapterInterface::class)); |
||
68 | } |
||
69 | 7 | ||
70 | $this->cache = $cache; |
||
0 ignored issues
–
show
|
|||
71 | 7 | if ($cache instanceof CacheProvider) { |
|
72 | 7 | @trigger_error(sprintf('Passing an instance of "%s" as the second argument in "%s" is deprecated since KunstmaanAdminBundle 5.7 and an instance of "%s" will be required in KunstmaanAdminBundle 6.0.', CacheProvider::class, __METHOD__, AdapterInterface::class), E_USER_DEPRECATED); |
|
73 | 7 | ||
74 | 7 | $this->cache = new DoctrineAdapter($cache); |
|
75 | } |
||
76 | |||
77 | // NEXT_MAJOR Add "Symfony\Contracts\Translation\TranslatorInterface" typehint when sf <4.4 support is removed. |
||
78 | if (!$translator instanceof TranslatorInterface && !$translator instanceof LegacyTranslatorInterface) { |
||
79 | throw new \InvalidArgumentException(sprintf('The "$translator" parameter should be instance of "%s" or "%s"', TranslatorInterface::class, LegacyTranslatorInterface::class)); |
||
80 | } |
||
81 | 7 | ||
82 | $this->translator = $translator; |
||
83 | 7 | ||
84 | $this->webserviceUrl = $this->container->getParameter('version_checker.url'); |
||
85 | $this->cacheTimeframe = $this->container->getParameter('version_checker.timeframe'); |
||
86 | $this->enabled = $this->container->getParameter('version_checker.enabled'); |
||
87 | } |
||
88 | |||
89 | /** |
||
90 | * Check that the version check is enabled. |
||
91 | 1 | * |
|
92 | * @return bool |
||
93 | 1 | */ |
|
94 | public function isEnabled() |
||
95 | { |
||
96 | return $this->enabled; |
||
97 | 1 | } |
|
98 | 1 | ||
99 | /** |
||
100 | * Check if we recently did a version check, if not do one now. |
||
101 | 1 | * |
|
102 | * @throws ParseException |
||
103 | */ |
||
104 | public function periodicallyCheck() |
||
105 | { |
||
106 | if (!$this->isEnabled()) { |
||
107 | return; |
||
108 | } |
||
109 | |||
110 | 5 | $cacheItem = $this->cache->getItem(self::CACHE_KEY); |
|
111 | if (!$cacheItem->isHit() || !\is_array($cacheItem->get())) { |
||
112 | 5 | $this->check(); |
|
113 | } |
||
114 | } |
||
115 | |||
116 | 5 | /** |
|
117 | 5 | * Get the version details via webservice. |
|
118 | 5 | * |
|
119 | 5 | * @return mixed a list of bundles if available |
|
120 | 2 | * |
|
121 | * @throws ParseException |
||
122 | 2 | */ |
|
123 | 2 | public function check() |
|
124 | 2 | { |
|
125 | 2 | if (!$this->isEnabled()) { |
|
126 | 2 | return; |
|
127 | } |
||
128 | |||
129 | $host = $this->container->get('request_stack')->getCurrentRequest()->getHttpHost(); |
||
130 | 2 | $console = realpath($this->container->get('kernel')->getProjectDir().'/bin/console'); |
|
131 | 2 | $installed = filectime($console); |
|
132 | 1 | $bundles = $this->parseComposer(); |
|
133 | 1 | $title = $this->container->getParameter('kunstmaan_admin.website_title'); |
|
134 | |||
135 | 1 | $jsonData = json_encode(array( |
|
136 | 'host' => $host, |
||
137 | 'installed' => $installed, |
||
138 | 'bundles' => $bundles, |
||
139 | 'project' => $this->translator->trans($title), |
||
140 | 1 | )); |
|
141 | |||
142 | 1 | try { |
|
143 | 1 | $client = $this->getClient(); |
|
144 | $response = $client->post($this->webserviceUrl, ['body' => $jsonData]); |
||
145 | 1 | $contents = $response->getBody()->getContents(); |
|
146 | $data = json_decode($contents); |
||
147 | |||
148 | if (null === $data) { |
||
149 | return false; |
||
150 | } |
||
151 | |||
152 | 1 | // Save the result in the cache to make sure we don't do the check too often |
|
153 | $cacheItem = $this->cache->getItem(self::CACHE_KEY); |
||
154 | 1 | $cacheItem->expiresAfter($this->cacheTimeframe); |
|
155 | 1 | $cacheItem->set($data); |
|
156 | |||
157 | $this->cache->save($cacheItem); |
||
158 | 1 | ||
159 | return $data; |
||
160 | } catch (Exception $e) { |
||
161 | // We did not receive valid json |
||
162 | return false; |
||
163 | } |
||
164 | } |
||
165 | |||
166 | /** |
||
167 | * @return Client |
||
168 | */ |
||
169 | public function getClient() |
||
170 | { |
||
171 | if (!$this->client) { |
||
172 | $this->client = new Client(array('connect_timeout' => 3, 'timeout' => 1)); |
||
173 | } |
||
174 | |||
175 | return $this->client; |
||
176 | } |
||
177 | |||
178 | /** |
||
179 | * @param Client $client |
||
180 | */ |
||
181 | public function setClient($client) |
||
182 | { |
||
183 | $this->client = $client; |
||
184 | } |
||
185 | |||
186 | /** |
||
187 | * Returns the absolute path to the composer.lock file. |
||
188 | * |
||
189 | 4 | * @return string |
|
190 | */ |
||
191 | 4 | protected function getLockPath() |
|
192 | 4 | { |
|
193 | $kernel = $this->container->get('kernel'); |
||
194 | 4 | $rootPath = $kernel->getProjectDir(); |
|
195 | 4 | ||
196 | 1 | return $rootPath.'/composer.lock'; |
|
197 | } |
||
198 | |||
199 | 3 | /** |
|
200 | 3 | * Returns a list of composer packages. |
|
201 | * |
||
202 | 3 | * @return array |
|
203 | 1 | * |
|
204 | * @throws ParseException |
||
205 | */ |
||
206 | 2 | protected function getPackages() |
|
207 | 1 | { |
|
208 | $translator = $this->container->get('translator'); |
||
209 | $errorMessage = $translator->trans('settings.version.error_parsing_composer'); |
||
210 | |||
211 | 1 | $composerPath = $this->getLockPath(); |
|
212 | if (!file_exists($composerPath)) { |
||
213 | throw new ParseException($translator->trans('settings.version.composer_lock_not_found')); |
||
214 | } |
||
215 | |||
216 | $json = file_get_contents($composerPath); |
||
217 | $result = json_decode($json, true); |
||
218 | |||
219 | if (json_last_error() !== JSON_ERROR_NONE) { |
||
220 | throw new ParseException($errorMessage.' (#'.json_last_error().')'); |
||
221 | 4 | } |
|
222 | |||
223 | 4 | if (\array_key_exists('packages', $result) && \is_array($result['packages'])) { |
|
224 | 4 | return $result['packages']; |
|
225 | 1 | } |
|
226 | 1 | ||
227 | 1 | // No package list in JSON structure |
|
228 | 1 | throw new ParseException($errorMessage); |
|
229 | 1 | } |
|
230 | |||
231 | /** |
||
232 | * Parse the composer.lock file to get the currently used versions of the kunstmaan bundles. |
||
233 | * |
||
234 | 1 | * @return array |
|
235 | * |
||
236 | * @throws ParseException |
||
237 | */ |
||
238 | protected function parseComposer() |
||
239 | { |
||
240 | $bundles = array(); |
||
241 | foreach ($this->getPackages() as $package) { |
||
242 | if (!strncmp($package['name'], 'kunstmaan/', \strlen('kunstmaan/'))) { |
||
243 | $bundles[] = array( |
||
244 | 'name' => $package['name'], |
||
245 | 'version' => $package['version'], |
||
246 | 'reference' => $package['source']['reference'], |
||
247 | ); |
||
248 | } |
||
249 | } |
||
250 | |||
251 | return $bundles; |
||
252 | } |
||
253 | } |
||
254 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.