Issues (37)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/Trackers/VisitorTracker.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php namespace Arcanedev\LaravelTracker\Trackers;
2
3
use Arcanedev\LaravelTracker\Contracts\Trackers\VisitorTracker as VisitorTrackerContract;
4
use Arcanedev\LaravelTracker\Support\BindingManager;
5
use Carbon\Carbon;
6
use Illuminate\Support\Arr;
7
use Ramsey\Uuid\Uuid;
8
9
/**
10
 * Class     VisitorTracker
11
 *
12
 * @package  Arcanedev\LaravelTracker\Trackers
13
 * @author   ARCANEDEV <[email protected]>
14
 */
15
class VisitorTracker extends AbstractTracker implements VisitorTrackerContract
16
{
17
    /* -----------------------------------------------------------------
18
     |  Properties
19
     | -----------------------------------------------------------------
20
     */
21
22
    /** @var  array */
23
    private $visitorInfo = [];
24
25
    /* -----------------------------------------------------------------
26
     |  Getters and Setters
27
     | -----------------------------------------------------------------
28
     */
29
30
    /**
31
     * Get the model.
32
     *
33
     * @return \Arcanedev\LaravelTracker\Models\Visitor
34
     */
35 9
    protected function getModel()
36
    {
37 9
        return $this->makeModel(BindingManager::MODEL_VISITOR);
38
    }
39
40
    /**
41
     * Get the session key.
42
     *
43
     * @return string
44
     */
45 9
    private function getSessionKey()
46
    {
47 9
        return $this->getConfig('session.name', 'tracker_session');
48
    }
49
50
    /**
51
     * Set the visitor data.
52
     *
53
     * @param  array  $data
54
     */
55 9
    private function setVisitorData(array $data)
56
    {
57 9
        $this->generateVisitor($data);
58
59 9
        if ($this->createVisitorIfIsUnknown()) {
60
            $this->ensureVisitorDataIsComplete();
61
        }
62 9
    }
63
64
    /**
65
     * Get the visitor id.
66
     *
67
     * @return int
68
     */
69 9
    private function getVisitorId()
70
    {
71 9
        return $this->visitorInfo['id'];
72
    }
73
74
    /**
75
     * Set the visitor id.
76
     *
77
     * @param  mixed  $id
78
     */
79 9
    private function setVisitorId($id)
80
    {
81 9
        $this->visitorInfo['id'] = $id;
82 9
    }
83
84
    /* -----------------------------------------------------------------
85
     |  Main Methods
86
     | -----------------------------------------------------------------
87
     */
88
89
    /**
90
     * Track the visitor.
91
     *
92
     * @param  array  $data
93
     *
94
     * @return int
95
     */
96 9
    public function track(array $data)
97
    {
98 9
        $this->setVisitorData($data);
99
100 9
        return $this->getVisitorId();
101
    }
102
103
    /**
104
     * Check the visitor data.
105
     *
106
     * @param  array  $currentData
107
     * @param  array  $newData
108
     *
109
     * @return array
110
     */
111 6
    public function checkData(array $currentData, array $newData)
112
    {
113 6
        return ($currentData && $newData && $currentData !== $newData)
0 ignored issues
show
Bug Best Practice introduced by
The expression $currentData of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
Bug Best Practice introduced by
The expression $newData of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
114 6
            ? $this->updateData($newData)
115 6
            : $newData;
116
    }
117
118
    /* -----------------------------------------------------------------
119
     |  Other Methods
120
     | -----------------------------------------------------------------
121
     */
122
123
    /**
124
     * Update the visitor data.
125
     *
126
     * @param  array  $data
127
     *
128
     * @return array
129
     */
130 6
    private function updateData(array $data)
131
    {
132 6
        $this->checkIfUserChanged($data);
133
134 6
        return $data;
135
    }
136
137
    /**
138
     * Get the visitor data.
139
     *
140
     * @param  string|null  $column
141
     *
142
     * @return mixed
143
     */
144 9
    private function getVisitorData($column = null)
145
    {
146 9
        $data = $this->session()->get($this->getSessionKey());
147
148 9
        return is_null($column) ? $data : Arr::get($data, $column, null);
149
    }
150
151
    /**
152
     * Check if user changed.
153
     *
154
     * @param  array  $data
155
     */
156 6
    private function checkIfUserChanged(array $data)
157
    {
158 6
        $model = $this->getModel()->newQuery()->find($this->getVisitorData('id'));
159
160
        if (
161 6
            ! is_null($model) &&
162 4
            ! is_null($model->user_id) &&
163 4
            ! is_null($data['user_id']) &&
164 4
            $data['user_id'] !== $model->user_id
165
        ) {
166
            $newVisitor = $this->regenerateSystemVisitor($data);
167
            $model      = $this->findByUuid($newVisitor['uuid']);
168
            $model->update(Arr::except($data, ['id', 'uuid']));
169
        }
170 6
    }
171
172
    /**
173
     * Regenerate visitor data for the system.
174
     *
175
     * @param  array|null  $data
176
     *
177
     * @return array
178
     */
179
    private function regenerateSystemVisitor($data = null)
180
    {
181
        $data = $data ?: $this->getVisitorData();
182
183
        if ($data) {
184
            $this->resetVisitorUuid($data);
185
            $this->createVisitorIfIsUnknown();
186
        }
187
188
        return $this->visitorInfo;
189
    }
190
191
    /**
192
     * Reset the visitor uuid.
193
     *
194
     * @param  array|null  $data
195
     *
196
     * @return array|null
197
     */
198
    private function resetVisitorUuid($data = null)
199
    {
200
        $this->visitorInfo['uuid'] = null;
201
202
        $data = $data ?: $this->visitorInfo;
203
204
        unset($data['uuid']);
205
206
        $this->putSessionData($data);
207
        $this->checkVisitorUuid();
208
209
        return $data;
210
    }
211
212
    /**
213
     * Put the session data.
214
     *
215
     * @param  array  $data
216
     */
217 9
    private function putSessionData(array $data)
218
    {
219 9
        $this->session()->put([
220 9
            $this->getSessionKey() => $data
221
        ]);
222 9
    }
223
224
    /**
225
     * Check the visitor uuid.
226
     */
227 9
    private function checkVisitorUuid()
228
    {
229 9
        if ( ! isset($this->visitorInfo['uuid']) || ! $this->visitorInfo['uuid'])
230 9
            $this->visitorInfo['uuid'] = $this->getVisitorIdFromSystem();
231 9
    }
232
233
    /**
234
     * Get the visitor id from the system.
235
     *
236
     * @return string
237
     */
238 9
    private function getVisitorIdFromSystem()
239
    {
240 9
        return Arr::get($this->getVisitorData(), 'uuid', (string) Uuid::uuid4());
241
    }
242
243
    /**
244
     * Create a new visitor if is unknown.
245
     *
246
     * @return bool
247
     */
248 9
    private function createVisitorIfIsUnknown()
249
    {
250 9
        $model = $this->getModel();
251
252
        /** @var  \Arcanedev\LaravelTracker\Models\Visitor  $visitor */
253 9
        if ($this->isVisitorKnown()) {
254
            $visitor = $model->newQuery()->find($id = $this->getVisitorData($model->getKeyName()));
255
            $visitor->updated_at = Carbon::now();
256
            $visitor->save();
257
258
            $this->setVisitorId($id);
259
260
            return true;
261
        }
262
263 9
        $visitor = $model->newQuery()
264 9
            ->firstOrCreate(Arr::only($this->visitorInfo, ['uuid']), $this->visitorInfo);
265
266 9
        $this->setVisitorId($visitor->getKey());
267 9
        $this->putSessionData($this->visitorInfo);
268
269 9
        return false;
270
    }
271
272
    /**
273
     * Check if the visitor is known.
274
     *
275
     * @return bool
276
     */
277 9
    private function isVisitorKnown()
278
    {
279 9
        if ( ! $this->session()->has($this->getSessionKey()))
280 9
            return false;
281
282
        if ($this->getVisitorData('uuid') != $this->getVisitorIdFromSystem())
283
            return false;
284
285
        if ( ! $this->findByUuid($this->getVisitorData('uuid')))
286
            return false;
287
288
        return true;
289
    }
290
291
    /**
292
     * Find a visitor by its uuid.
293
     *
294
     * @param  string  $uuid
295
     *
296
     * @return \Arcanedev\LaravelTracker\Models\Visitor
297
     */
298
    private function findByUuid($uuid)
299
    {
300
        return $this->getModel()->where('uuid', $uuid)->first();
301
    }
302
303
    /**
304
     * Generate visitor data.
305
     *
306
     * @param  array  $visitorInfo
307
     */
308 9
    private function generateVisitor(array $visitorInfo)
309
    {
310 9
        $this->visitorInfo = $visitorInfo;
311
312 9
        if ( ! $this->checkVisitorDataIsReliable())
313
            $this->regenerateSystemVisitor();
314
315 9
        $this->checkVisitorUuid();
316 9
    }
317
318
    /**
319
     * Check if the visitor data is reliable.
320
     *
321
     * @return bool
322
     */
323 9
    private function checkVisitorDataIsReliable()
324
    {
325 9
        $data = $this->getVisitorData();
326
327 9
        foreach (['user_id', 'client_ip', 'user_agent'] as $key) {
328 9
            if ($this->checkDataIsUnreliable($data, $key)) return false;
329
        }
330
331 9
        return true;
332
    }
333
334
    /**
335
     * Check the data is unreliable.
336
     *
337
     * @param  array|null  $data
338
     * @param  string      $key
339
     *
340
     * @return bool
341
     */
342 9
    private function checkDataIsUnreliable($data, $key)
343
    {
344 9
        return isset($data[$key]) && ($data[$key] !== $this->visitorInfo[$key]);
345
    }
346
347
    /**
348
     * Ensure that the visitor data is complete.
349
     */
350
    private function ensureVisitorDataIsComplete()
351
    {
352
        $visitorData = $this->getVisitorData();
353
        $completed   = true;
354
355
        foreach ($this->visitorInfo as $key => $value) {
356
            if ($visitorData[$key] !== $value) {
357
                /** @var  \Arcanedev\LaravelTracker\Models\Visitor  $model */
358
                if ( ! isset($model))
359
                    $model = $this->getModel()->find($this->getVisitorId());
360
361
                $model->setAttribute($key, $value);
362
                $model->save();
363
364
                $completed = false;
365
            }
366
        }
367
368
        if ( ! $completed)
369
            $this->putSessionData($this->visitorInfo);
370
    }
371
}
372