Issues (14)

Security Analysis    no request data  

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/CanvasArray.php (1 issue)

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
2
3
/** smtech\CanvasPest\CanvasArray */
4
5
namespace smtech\CanvasPest;
6
7
/**
8
 * An object to represent a list of Canvas Objects returned as a response from
9
 * the Canvas API.
10
 *
11
 * @author Seth Battis <[email protected]>
12
 **/
13
class CanvasArray implements \Iterator, \ArrayAccess, \Serializable
14
{
15
    /** The maximum supported number of responses per page */
16
    const MAXIMUM_PER_PAGE = 100;
17
18
    /** @var CanvasPest $api Canvas API (for paging through the array) */
19
    protected $api;
20
21
    /**
22
     * @var string $endpoint API endpoint whose response is represented by this
23
     *      object
24
     **/
25
    private $endpoint = null;
26
27
    /**
28
     * @var CanvasPageLink[] $pagination The canonical (first, last, next,
29
     *      prev, current) pages relative to the current page of responses
30
     **/
31
    private $pagination = [];
32
33
    /**
34
     * @var array Cached pagination per each page response
35
     */
36
    private $paginationPerPage = [];
37
38
    /** @var CanvasObject[] $data Backing store */
39
    private $data = [];
40
41
    /** @var int $page Page number corresponding to current $key */
42
    private $page = null;
43
44
    /** @var int $key Current key-value of iterator */
45
    private $key = null;
46
47
    /**
48
     * Construct a CanvasArray
49
     *
50
     * @param string $jsonResponse A JSON-encoded response array from the
51
     *                             Canvas API
52
     * @param CanvasPest $canvasPest An API object for making pagination calls
53
     **/
54 7
    public function __construct($jsonResponse, $canvasPest)
55
    {
56 7
        $this->api = $canvasPest;
57
58 7
        $this->pagination = $this->parsePageLinks();
59
60
        /* locate ourselves */
61 7
        if (isset($this->pagination[CanvasPageLink::CURRENT])) {
62 7
            $this->page = $this->pagination[CanvasPageLink::CURRENT]->getPageNumber();
63 7
            $this->key = $this->pageNumberToKey($this->page);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->pageNumberToKey($this->page) can also be of type false. However, the property $key is declared as type integer. Maybe add an additional type check?

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 the id property of an instance of the Account 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.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
64 7
            $this->paginationPerPage[$this->page] = $this->pagination;
65 7
        }
66
67
        /* parse the JSON response string */
68 7
        $key = $this->key;
69 7
        foreach (json_decode($jsonResponse, true) as $item) {
70 7
            $this->data[$key++] = new CanvasObject($item);
71 7
        }
72 7
    }
73
74
    /**
75
     * Parse the API response link headers into pagination information
76
     *
77
     * @param  boolean|string[] $headers (Optional, defaults to `$this->api->lastHeader('link')`)
78
     * @return CanvasPageLink[]
79
     */
80 7
    protected function parsePageLinks($headers = false)
81
    {
82 7
        $pagination = [];
83 7
        if (!$headers) {
84 7
            $headers = $this->api->lastHeader('link');
85 7
        }
86
87
        /* parse Canvas page links */
88 7
        if (preg_match_all('%<([^>]*)>\s*;\s*rel="([^"]+)"%', $headers, $links, PREG_SET_ORDER)) {
89 7
            foreach ($links as $link) {
90 7
                $pagination[$link[2]] = new CanvasPageLink($link[1], $link[2]);
91 7
            }
92 7
        }
93
94 7
        return $pagination;
95
    }
96
    /**
97
     * Convert a page number to an array key
98
     *
99
     * @param int $pageNumber 1-indexed page number
100
     *
101
     * @return int|false
102
     **/
103 10
    protected function pageNumberToKey($pageNumber)
104
    {
105 10
        if (isset($this->pagination[CanvasPageLink::CURRENT])) {
106 10
            return ($pageNumber - 1) * $this->pagination[CanvasPageLink::CURRENT]->getPerPage();
107
        }
108
        return false;
109
    }
110
111
    /**
112
     * Request a page of responses from the API
113
     *
114
     * A page of responses will be requested if it appears that that page has
115
     * not yet been loaded (tested by checking if the initial element of the
116
     * page has been initialized in the $data array).
117
     *
118
     * @param int $pageNumber Page number to request
119
     * @param bool $forceRefresh (Optional) Force a refresh of backing data,
120
     *                           even if cached (defaults to `FALSE`)
121
     *
122
     * @return bool `TRUE` if the page is requested, `FALSE` if it is already
123
     *                     cached (and therefore not requested)
124
     **/
125 7
    protected function requestPageNumber($pageNumber, $forceRefresh = false)
126
    {
127 7
        if (!isset($this->data[$this->pageNumberToKey($pageNumber)]) || ($forceRefresh && isset($this->api))) {
128
            // assume one page if no pagination (and already loaded)
129 4
            if (isset($this->pagination[CanvasPageLink::CURRENT])) {
130 4
                $params = $this->pagination[CanvasPageLink::CURRENT]->getParams();
131 4
                $params[CanvasPageLink::PARAM_PAGE_NUMBER] = $pageNumber;
132 4
                $page = $this->api->get($this->pagination[CanvasPageLink::CURRENT]->getEndpoint(), $params);
133 4
                $this->data = array_replace($this->data, $page->data);
134 4
                $pagination = $this->parsePageLinks();
135 4
                $this->paginationPerPage[$pagination[CanvasPageLink::CURRENT]->getPageNumber()] = $pagination;
136 4
                return true;
137
            }
138
        }
139 4
        return false;
140
    }
141
142
    /**
143
     * Request all pages from API
144
     *
145
     * This stores the entire API response locally, in preparation for, most
146
     * likely, serializing this object.
147
     *
148
     * @param bool $forceRefresh (Optional) Force a refresh of backing data,
149
     *                           even if cached (defaults to `FALSE`)
150
     *
151
     * @return void
152
     */
153 7
    protected function requestAllPages($forceRefresh = false)
154
    {
155 7
        $_page = $this->page;
156 7
        $_key = $this->key;
157
158 7
        $nextPageNumber = false;
159 7
        if (isset($this->pagination[CanvasPageLink::NEXT])) {
160 7
            $nextPageNumber = $this->pagination[CanvasPageLink::NEXT]->getPageNumber();
161 7
        }
162
163
        /* welp, here goes... let's hope we have a next page! */
164 7
        while ($nextPageNumber !== false) {
165 7
            $this->requestPageNumber($nextPageNumber, $forceRefresh);
166 7
            if (isset($this->paginationPerPage[$nextPageNumber][CanvasPageLink::NEXT])) {
167 7
                $nextPageNumber = $this->paginationPerPage[$nextPageNumber][CanvasPageLink::NEXT]->getPageNumber();
168 7
            } else {
169 7
                $nextPageNumber = false;
170
            }
171 7
        }
172
173 7
        $this->page = $_page;
174 7
        $this->key = $_key;
175 7
    }
176
177
    /***************************************************************************
178
     * ArrayObject methods
179
     */
180
181
    /**
182
     * Get the number of CanvasObjects in the Canvas response
183
     *
184
     * @return int
185
     *
186
     * @see http://php.net/manual/en/arrayobject.count.php ArrayObject::count
187
     **/
188 1
    public function count()
189
    {
190 1
        $this->requestAllPages();
191 1
        return count($this->data);
192
    }
193
194
    /**
195
     * Creates a copy of the CanvasArray
196
     *
197
     * @return CanvasObject[]
198
     *
199
     * @see http://php.net/manual/en/arrayobject.getarraycopy.php
200
     *      ArrayObject::getArrayCopy
201
     **/
202 1
    public function getArrayCopy()
203
    {
204 1
        $this->requestAllPages();
205 1
        return $this->data;
206
    }
207
208
    /***************************************************************************
209
     * ArrayAccess methods
210
     */
211
212
    /**
213
     * Whether an offset exists
214
     *
215
     * @param int|string $offset
216
     *
217
     * @return bool
218
     *
219
     * @see http://php.net/manual/en/arrayaccess.offsetexists.php
220
     *      ArrayAccess::offsetExists
221
     **/
222 4
    public function offsetExists($offset)
223
    {
224 4
        if (!isset($this->data[$offset])) {
225 4
            $this->requestAllPages();
226 4
        }
227 4
        return isset($this->data[$offset]);
228
    }
229
230
    /**
231
     * Offset to retrieve
232
     *
233
     * @param int|string $offset
234
     *
235
     * @return CanvasObject|null
236
     *
237
     * @see http://php.net/manual/en/arrayaccess.offsetexists.php
238
     *      ArrayAccess::offsetGet
239
     **/
240 2
    public function offsetGet($offset)
241
    {
242 2
        if (!isset($this->data[$offset])) {
243 1
            $this->requestAllPages();
244 1
        }
245 2
        return $this->data[$offset];
246
    }
247
248
    /**
249
     * Assign a value to the specified offset
250
     *
251
     * @deprecated CanvasObject and CanvasArray responses are immutable
252
     *
253
     * @param int|string $offset
254
     * @param CanvasObject $value
255
     *
256
     * @return void
257
     *
258
     * @throws CanvasArray_Exception IMMUTABLE All calls to this method will cause an exception
259
     *
260
     * @see http://php.net/manual/en/arrayaccess.offsetset.php
261
     *      ArrayAccess::offsetSet
262
     **/
263 1
    public function offsetSet($offset, $value)
264
    {
265 1
        throw new CanvasArray_Exception(
266 1
            'Canvas responses are immutable',
267
            CanvasArray_Exception::IMMUTABLE
268 1
        );
269
    }
270
271
    /**
272
     * Unset an offset
273
     *
274
     * @deprecated CanvasObject and CanvasArray responses are immutable
275
     *
276
     * @param int|string $offset
277
     *
278
     * @return void
279
     *
280
     * @throws CanvasArray_Exception IMMUTABLE All calls to this method will
281
     *         cause an exception
282
     *
283
     * @see http://php.net/manual/en/arrayaccess.offsetunset.php
284
     *      ArrayAccess::offsetUnset
285
     **/
286 1
    public function offsetUnset($offset)
287
    {
288 1
        throw new CanvasArray_Exception(
289 1
            'Canvas responses are immutable',
290
            CanvasArray_Exception::IMMUTABLE
291 1
        );
292
    }
293
294
    /**************************************************************************/
295
296
    /**************************************************************************
297
     * Iterator methods
298
     */
299
300
    /**
301
     * Return the current element
302
     *
303
     * @return CanvasObject
304
     *
305
     * @see http://php.net/manual/en/iterator.current.php Iterator::current
306
     **/
307 2
    public function current()
308
    {
309 2
        return $this->data[$this->key];
310
    }
311
312
    /**
313
     * Return the key of the current element
314
     *
315
     * @return int
316
     *
317
     * @see http://php.net/manual/en/iterator.key.php Iterator::key
318
     **/
319 1
    public function key()
320
    {
321 1
        return $this->key;
322
    }
323
324
    /**
325
     * Move forward to next element
326
     *
327
     * @return void
328
     *
329
     * @see http://php.net/manual/en/iterator.next.php Iterator::next
330
     **/
331 2
    public function next()
332
    {
333 2
        $this->key++;
334 2
    }
335
336
    /**
337
     * Rewind the iterator to the first element
338
     *
339
     * @return void
340
     *
341
     * @see http://php.net/manual/en/iterator.rewind.php Iterator::rewind
342
     **/
343 2
    public function rewind()
344
    {
345 2
        $this->key = 0;
346 2
    }
347
348
    /**
349
     * Checks if current position is valid
350
     *
351
     * @return bool
352
     *
353
     * @see http://php.net/manual/en/iterator.valid.php Iterator::valid
354
     **/
355 2
    public function valid()
356
    {
357 2
        return ($this->offsetExists($this->key));
358
    }
359
360
    /**************************************************************************/
361
362
    /***************************************************************************
363
     * Serializable methods
364
     */
365
366
    /**
367
     * String representation of CanvasArray
368
     *
369
     * @return string
370
     *
371
     * @see http://php.net/manual/en/serializable.serialize.php
372
     *      Serializable::serialize()
373
     **/
374 1
    public function serialize()
375
    {
376 1
        $this->requestAllPages();
377 1
        return serialize(
378
            array(
379 1
                'page' => $this->page,
380 1
                'key' => $this->key,
381 1
                'data' => $this->data
382 1
            )
383 1
        );
384
    }
385
386
    /**
387
     * Construct a CanvasArray from its string representation
388
     *
389
     * The data in the unserialized CanvasArray is static and cannot be
390
     * refreshed, as the CanvasPest API connection is _not_ serialized to
391
     * preserve the security of API access tokens.
392
     *
393
     * @param string $data
394
     *
395
     * @return string
396
     *
397
     * @see http://php.net/manual/en/serializable.unserialize.php
398
     *      Serializable::unserialize()
399
     **/
400 1
    public function unserialize($data)
401
    {
402 1
        $_data = unserialize($data);
403 1
        $this->page = $_data['page'];
404 1
        $this->key = $_data['key'];
405 1
        $this->data = $_data['data'];
406 1
        $this->api = null;
407 1
        $this->endpoint = null;
408 1
        $this->pagination = array();
409 1
    }
410
}
411