GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Issues (187)

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/DataObjects/QueuedJobDescriptor.php (8 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
2
3
namespace Symbiote\QueuedJobs\DataObjects;
4
5
use SilverStripe\Assets\Filesystem;
6
use SilverStripe\Core\Config\Config;
7
use SilverStripe\Core\Convert;
8
use SilverStripe\Forms\DropdownField;
9
use SilverStripe\Forms\FieldList;
10
use SilverStripe\Forms\LiteralField;
11
use SilverStripe\ORM\DataObject;
12
use SilverStripe\ORM\FieldType\DBField;
13
use SilverStripe\Security\Member;
14
use SilverStripe\Security\Permission;
15
use Symbiote\QueuedJobs\Services\QueuedJob;
16
use Symbiote\QueuedJobs\Services\QueuedJobService;
17
18
/**
19
 * A QueuedJobDescriptor is the stored representation of a piece of work that could take a while to execute,
20
 * because of which it is desirable to not have it executing in parallel to other jobs.
21
 *
22
 * A queued job should always attempt to report how many potential dataobjects will be affected by being executed;
23
 * this will determine which queue it is placed within so that some shorter jobs can execute immediately without needing
24
 * to wait for a potentially long running job.
25
 *
26
 * @property string $JobTitle Name of job
27
 * @property string $Signature Unique identifier for this job instance
28
 * @property string $Implementation Classname of underlying job
29
 * @property string $StartAfter Don't start until this date, if set
30
 * @property string $JobStarted When this job was started
31
 * @property string $JobFinished When this job was finished
32
 * @property int $TotalSteps Number of steps
33
 * @property int $StepsProcessed Number of completed steps
34
 * @property int $LastProcessedCount Number at which StepsProcessed was last checked for stalled jobs
35
 * @property int $ResumeCounts Number of times this job has been resumed
36
 * @property string $SavedJobData serialised data for the job to use as storage
37
 * @property string $SavedJobMessages List of messages saved for this job
38
 * @property string $JobStatus Status of this job
39
 * @property string $JobType Type of job
40
 *
41
 * @method Member RunAs() Member to run this job as
42
 *
43
 * @author Marcus Nyeholt <[email protected]>
44
 * @license BSD http://silverstripe.org/bsd-license/
45
 */
46
class QueuedJobDescriptor extends DataObject
47
{
48
    /**
49
     * {@inheritDoc}
50
     * @var string
51
     */
52
    private static $table_name = 'QueuedJobDescriptor';
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
53
54
    /**
55
     * @var array
56
     */
57
    private static $db = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
58
        'JobTitle' => 'Varchar(255)',
59
        'Signature' => 'Varchar(64)',
60
        'Implementation' => 'Varchar(255)',
61
        'StartAfter' => 'DBDatetime',
62
        'JobStarted' => 'DBDatetime',
63
        'JobRestarted' => 'DBDatetime',
64
        'JobFinished' => 'DBDatetime',
65
        'TotalSteps' => 'Int',
66
        'StepsProcessed' => 'Int',
67
        'LastProcessedCount' => 'Int(-1)', // -1 means never checked, 0 means checked but no work is done
68
        'ResumeCounts' => 'Int',
69
        'SavedJobData' => 'Text',
70
        'SavedJobMessages' => 'Text',
71
        'JobStatus' => 'Varchar(16)',
72
        'JobType' => 'Varchar(16)',
73
    ];
74
75
    /**
76
     * @var array
77
     */
78
    private static $has_one = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
79
        'RunAs' => Member::class,
80
    ];
81
82
    /**
83
     * @var array
84
     */
85
    private static $defaults = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
86
        'JobStatus' => 'New',
87
        'ResumeCounts' => 0,
88
        'LastProcessedCount' => -1 // -1 means never checked, 0 means checked and none were processed
89
    ];
90
91
    /**
92
     * @var array
93
     */
94
    private static $indexes = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
95
        'JobStatus' => true,
96
        'StartAfter' => true,
97
        'Signature' => true,
98
    ];
99
100
    /**
101
     * @var array
102
     */
103
    private static $casting = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
104
        'Messages' => 'HTMLText',
105
    ];
106
107
    /**
108
     * @var array
109
     */
110
    private static $searchable_fields = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
111
        'JobTitle',
112
    ];
113
114
    /**
115
     * @var string
116
     */
117
    private static $default_sort = 'Created DESC';
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
118
119
    public function requireDefaultRecords()
120
    {
121
        parent::requireDefaultRecords();
122
        $this->getJobDir();
123
    }
124
125
    /**
126
     * @return array
127
     */
128
    public function summaryFields()
129
    {
130
        return [
131
            'JobTitle' => _t(__CLASS__ . '.TABLE_TITLE', 'Title'),
132
            'Created' => _t(__CLASS__ . '.TABLE_ADDE', 'Added'),
133
            'JobStarted' => _t(__CLASS__ . '.TABLE_STARTED', 'Started'),
134
            'JobFinished' => _t(__CLASS__ . '.TABLE_FINISHED', 'Finished'),
135
//          'JobRestarted' => _t(__CLASS__ . '.TABLE_RESUMED', 'Resumed'),
136
            'StartAfter' => _t(__CLASS__ . '.TABLE_START_AFTER', 'Start After'),
137
            'JobTypeString' => _t(__CLASS__ . '.JOB_TYPE', 'Job Type'),
138
            'JobStatus' => _t(__CLASS__ . '.TABLE_STATUS', 'Status'),
139
            'LastMessage' => _t(__CLASS__ . '.TABLE_MESSAGES', 'Message'),
140
            'StepsProcessed' => _t(__CLASS__ . '.TABLE_NUM_PROCESSED', 'Done'),
141
            'TotalSteps' => _t(__CLASS__ . '.TABLE_TOTAL', 'Total'),
142
        ];
143
    }
144
145
    /**
146
     * Pause this job, but only if it is waiting, running, or init
147
     *
148
     * @param bool $force Pause this job even if it's not waiting, running, or init
149
     *
150
     * @return bool Return true if this job was paused
151
     */
152
    public function pause($force = false)
153
    {
154
        if (
155
            $force || in_array(
156
                $this->JobStatus,
157
                [QueuedJob::STATUS_WAIT, QueuedJob::STATUS_RUN, QueuedJob::STATUS_INIT]
158
            )
159
        ) {
160
            $this->JobStatus = QueuedJob::STATUS_PAUSED;
161
            $this->write();
162
            return true;
163
        }
164
        return false;
165
    }
166
167
    /**
168
     * Resume this job and schedules it for execution
169
     *
170
     * @param bool $force Resume this job even if it's not paused or broken
171
     *
172
     * @return bool Return true if this job was resumed
173
     */
174
    public function resume($force = false)
175
    {
176
        if ($force || in_array($this->JobStatus, [QueuedJob::STATUS_PAUSED, QueuedJob::STATUS_BROKEN])) {
177
            $this->JobStatus = QueuedJob::STATUS_WAIT;
178
            $this->ResumeCounts++;
179
            $this->write();
180
            QueuedJobService::singleton()->startJob($this);
181
            return true;
182
        }
183
        return false;
184
    }
185
186
    /**
187
     * Restarts this job via a forced resume
188
     */
189
    public function restart()
190
    {
191
        $this->resume(true);
192
    }
193
194
    /**
195
     * Called to indicate that the job is ready to be run on the queue. This is done either as the result of
196
     * creating the job and adding it, or when resuming.
197
     */
198
    public function activateOnQueue()
199
    {
200
        // if it's an immediate job, lets cache it to disk to be picked up later
201
        if (
202
            $this->JobType == QueuedJob::IMMEDIATE
203
            && !Config::inst()->get(QueuedJobService::class, 'use_shutdown_function')
204
        ) {
205
            touch($this->getJobDir() . '/queuedjob-' . $this->ID);
206
        }
207
    }
208
209
    /**
210
     * Gets the path to the queuedjob cache directory
211
     *
212
     * @return string
213
     */
214
    protected function getJobDir()
215
    {
216
        // make sure our temp dir is in place. This is what will be inotify watched
217
        $jobDir = Config::inst()->get(QueuedJobService::class, 'cache_dir');
218
        if ($jobDir[0] !== '/') {
219
            $jobDir = TEMP_FOLDER . '/' . $jobDir;
220
        }
221
222
        if (!is_dir($jobDir)) {
223
            Filesystem::makeFolder($jobDir);
224
        }
225
        return $jobDir;
226
    }
227
228
    public function execute()
229
    {
230
        $service = QueuedJobService::singleton();
231
        $service->runJob($this->ID);
232
    }
233
234
    /**
235
     * Called when the job has completed and we want to cleanup anything the descriptor has lying around
236
     * in caches or the like.
237
     */
238
    public function cleanupJob()
239
    {
240
        // remove the job's temp file if it exists
241
        $tmpFile = $this->getJobDir() . '/queuedjob-' . $this->ID;
242
        if (file_exists($tmpFile)) {
243
            unlink($tmpFile);
244
        }
245
    }
246
247
    public function onBeforeDelete()
248
    {
249
        parent::onBeforeDelete();
250
        $this->cleanupJob();
251
    }
252
253
    /**
254
     * Get all job messages as an HTML unordered list.
255
     *
256
     * @return string|null
257
     */
258
    public function getMessages()
259
    {
260
        if (strlen($this->SavedJobMessages)) {
261
            $messages = @unserialize($this->SavedJobMessages);
262
            if (!empty($messages)) {
263
                return DBField::create_field(
264
                    'HTMLText',
265
                    '<ul><li>' . nl2br(implode('</li><li>', Convert::raw2xml($messages))) . '</li></ul>'
266
                );
267
            }
268
            return '';
269
        }
270
    }
271
272
    /**
273
     * Get the last job message as a raw string
274
     *
275
     * @return string|null
276
     */
277
    public function getLastMessage()
278
    {
279
        if (strlen($this->SavedJobMessages)) {
280
            $msgs = @unserialize($this->SavedJobMessages);
281
            if (is_array($msgs) && sizeof($msgs)) {
282
                return array_pop($msgs);
283
            }
284
        }
285
    }
286
287
    /**
288
     * @return string
289
     */
290
    public function getTitle()
291
    {
292
        return $this->JobTitle;
293
    }
294
295
    /**
296
     * Return a string representation of the numeric JobType
297
     * @return string
298
     */
299
    public function getJobTypeString()
300
    {
301
        $map = $this->getJobTypeValues();
302
        return isset($map[$this->JobType]) ? $map[$this->JobType] : '(Unknown)';
303
    }
304
305
306
    /**
307
     * Return a map of numeric JobType values to localisable string representations.
308
     * @return array
309
     */
310
    public function getJobTypeValues()
311
    {
312
        return [
313
            QueuedJob::IMMEDIATE => _t(__CLASS__ . '.TYPE_IMMEDIATE', 'Immediate'),
314
            QueuedJob::QUEUED => _t(__CLASS__ . '.TYPE_QUEUED', 'Queued'),
315
            QueuedJob::LARGE => _t(__CLASS__ . '.TYPE_LARGE', 'Large'),
316
        ];
317
    }
318
319
    /**
320
     * @return FieldList
321
     */
322
    public function getCMSFields()
323
    {
324
        $fields = parent::getCMSFields();
325
        $fields->replaceField(
326
            'JobType',
327
            new DropdownField('JobType', $this->fieldLabel('JobType'), $this->getJobTypeValues())
328
        );
329
        $statuses = [
330
            QueuedJob::STATUS_NEW,
331
            QueuedJob::STATUS_INIT,
332
            QueuedJob::STATUS_RUN,
333
            QueuedJob::STATUS_WAIT,
334
            QueuedJob::STATUS_COMPLETE,
335
            QueuedJob::STATUS_PAUSED,
336
            QueuedJob::STATUS_CANCELLED,
337
            QueuedJob::STATUS_BROKEN,
338
        ];
339
        $fields->replaceField(
340
            'JobStatus',
341
            DropdownField::create('JobStatus', $this->fieldLabel('JobStatus'), array_combine($statuses, $statuses))
342
        );
343
344
        $fields->removeByName('SavedJobData');
345
        $fields->removeByName('SavedJobMessages');
346
347
        if (strlen($this->SavedJobMessages)) {
348
            $fields->addFieldToTab('Root.Messages', LiteralField::create('Messages', $this->getMessages()));
349
        }
350
351
        if (Permission::check('ADMIN')) {
352
            return $fields;
353
        }
354
355
        // Readonly CMS view is a lot more useful for debugging than no view at all
356
        return $fields->makeReadonly();
357
    }
358
}
359