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.
Completed
Branch dev (953bb6)
by Liuta
01:57
created

Xcloner_Encryption   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 347
Duplicated Lines 15.56 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 0
Metric Value
dl 54
loc 347
rs 4.5599
c 0
b 0
f 0
wmc 58
lcom 1
cbo 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 15 3
A get_backup_encryption_key() 0 9 2
A is_encrypted_file() 0 11 2
A get_encrypted_target_backup_file_name() 0 4 1
A get_decrypted_target_backup_file_name() 0 4 1
F encrypt_file() 21 105 20
A verify_encrypted_file() 0 9 2
F decrypt_file() 33 118 25
A get_xcloner_path() 0 7 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Xcloner_Encryption often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Xcloner_Encryption, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: thinkovi
5
 * Date: 2018-11-27
6
 * Time: 12:11
7
 */
8
9
//namespace XCloner;
10
11
class Xcloner_Encryption
12
{
13
    const FILE_ENCRYPTION_BLOCKS = 1024*1024;
14
    const FILE_ENCRYPTION_SUFFIX = ".encrypted";
15
    const FILE_DECRYPTION_SUFFIX = ".decrypted";
16
17
    private $xcloner_settings;
18
    private $logger;
19
    private $verification = false;
20
21
    public function __construct(Xcloner $xcloner_container)
22
    {
23
        $this->xcloner_container = $xcloner_container;
0 ignored issues
show
Bug introduced by
The property xcloner_container does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
24
        if(method_exists($xcloner_container, 'get_xcloner_settings')) {
25
            $this->xcloner_settings = $xcloner_container->get_xcloner_settings();
0 ignored issues
show
Bug introduced by
The method get_xcloner_settings() does not seem to exist on object<Xcloner>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
26
        }else{
27
            $this->xcloner_settings = "";
28
        }
29
30
        if(method_exists($xcloner_container, 'get_xcloner_logger')) {
31
            $this->logger = $xcloner_container->get_xcloner_logger()->withName("xcloner_encryption");
0 ignored issues
show
Bug introduced by
The method get_xcloner_logger() does not seem to exist on object<Xcloner>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
32
        }else{
33
            $this->logger = "";
34
        }
35
    }
36
37
    /**
38
     * Returns the backup encryption key
39
     *
40
     * @return SECURE_AUTH_SALT|null
41
     */
42
    public function get_backup_encryption_key()
43
    {
44
        if(is_object($this->xcloner_settings)) {
45
            return $this->xcloner_settings->get_xcloner_encryption_key();
46
        }
47
48
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by Xcloner_Encryption::get_backup_encryption_key of type SECURE_AUTH_SALT|null.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
49
50
    }
51
52
    /**
53
     * Check if provided filename has encrypted suffix
54
     *
55
     * @param $filename
56
     * @return bool
57
     */
58
    public function is_encrypted_file($filename) {
59
        $fp = fopen($this->get_xcloner_path() .$filename, 'r');
60
        $encryption_length = fread($fp, 16);
61
        fclose($fp);
62
        if(is_numeric($encryption_length)) {
63
            return true;
64
        }
65
66
        return false;
67
68
    }
69
70
    /**
71
     * Returns the filename with encrypted suffix
72
     *
73
     * @param $filename
74
     * @return string
75
     */
76
    public function get_encrypted_target_backup_file_name( $filename ) {
77
78
        return str_replace(self::FILE_DECRYPTION_SUFFIX, "", $filename) . self::FILE_ENCRYPTION_SUFFIX;
79
    }
80
81
    /**
82
     * Returns the filename without encrypted suffix
83
     *
84
     * @param $filename
85
     * @return string
86
     */
87
    public function get_decrypted_target_backup_file_name( $filename ) {
88
89
        return str_replace(self::FILE_ENCRYPTION_SUFFIX, "", $filename) . self::FILE_DECRYPTION_SUFFIX;
90
    }
91
92
    /**
93
     * Encrypt the passed file and saves the result in a new file with ".enc" as suffix.
94
     *
95
     * @param string $source Path to file that should be encrypted
96
     * @param string $dest   File name where the encryped file should be written to.
97
     * @param string $key    The key used for the encryption
98
     * @param int $start   Start position for reading when doing incremental mode.
99
     * @param string $iv   The IV key to use.
100
     * @param bool $verfication   Weather we should we try to verify the decryption.
0 ignored issues
show
Documentation introduced by
There is no parameter named $verfication. Did you maybe mean $verification?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
101
     * @return string|false  Returns the file name that has been created or FALSE if an error occured
102
    */
103
    public function encrypt_file($source, $dest = "" , $key= "", $start = 0, $iv = 0, $verification = true, $recursive = false)
104
    {
105 View Code Duplication
        if(is_object($this->logger)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
106
            $this->logger->info(sprintf('Encrypting file %s at position %d IV %s', $source, $start, base64_encode($iv)));
107
        }
108
109
        //$key = substr(sha1($key, true), 0, 16);
110
        if(!$key){
111
            $key = self::get_backup_encryption_key();
112
        }
113
        $key_digest = openssl_digest ($key, "md5", true);
114
115
        $keep_local = 1;
116
        if(!$dest) {
117
            $dest = $this->get_encrypted_target_backup_file_name($source);
118
            $keep_local = 0;
119
        }
120
121
        if(!$iv || !$start) {
122
            //$iv = openssl_random_pseudo_bytes(16);
123
            $iv = str_pad(self::FILE_ENCRYPTION_BLOCKS, 16, "0000000000000000", STR_PAD_LEFT);
124
        }
125
126
        if( !$start ) {
127
            $fpOut = fopen($this->get_xcloner_path() .$dest, 'w');
128
        }else{
129
            $fpOut = fopen($this->get_xcloner_path() .$dest, 'a');
130
        }
131
132
        if ( $fpOut ) {
133
134
            // Put the initialization vector to the beginning of the file
135
            if(!$start) {
136
                fwrite($fpOut, $iv);
137
            }
138
139
            if (file_exists($this->get_xcloner_path() .$source) &&
140
                $fpIn = fopen($this->get_xcloner_path() .$source, 'rb')) {
141
142
                fseek($fpIn, (int)$start);
143
144
                if (!feof($fpIn)) {
145
146
                    $plaintext = fread($fpIn, 16 * self::FILE_ENCRYPTION_BLOCKS);
147
                    $ciphertext = openssl_encrypt($plaintext, 'AES-128-CBC', $key_digest, OPENSSL_RAW_DATA, $iv);
148
149
                    // Use the first 16 bytes of the ciphertext as the next initialization vector
150
                    $iv = substr($ciphertext, 0, 16);
151
                    //$iv = openssl_random_pseudo_bytes(16);
152
153
                    fwrite($fpOut, $ciphertext);
154
155
                    $start = ftell($fpIn);
156
157
                    fclose($fpOut );
158
159
                    if(!feof($fpIn)) {
160
                        fclose($fpIn);
161
                        //echo "\n NEW:".$key.md5($iv);
162
                        //self::encryptFile($source, $dest, $key, $start, $iv);
163
                        if($recursive ){
164
                            $this->encrypt_file($source, $dest, $key, $start, ($iv), $verification, $recursive);
0 ignored issues
show
Bug introduced by
It seems like $key defined by self::get_backup_encryption_key() on line 111 can also be of type null; however, Xcloner_Encryption::encrypt_file() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
165 View Code Duplication
                        }else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
166
167
                            if(($iv) != base64_decode(base64_encode($iv)))
168
                            {
169
                                throw new \Exception('Could not encode IV for transport');
170
                            }
171
172
                            return array(
173
                                "start" => $start,
174
                                "iv" => base64_encode($iv),
175
                                "target_file" => $dest,
176
                                "finished" => 0
177
                            );
178
                        }
179
                    }
180
181
                }
182
            } else {
183
                if(is_object($this->logger)) {
184
                    $this->logger->error('Unable to read source file for encryption.');
185
                }
186
                throw new \Exception("Unable to read source file for encryption.");
187
            }
188
        } else {
189
            if(is_object($this->logger)) {
190
                $this->logger->error('Unable to write destination file for encryption.');
191
            }
192
            throw new \Exception("Unable to write destination file for encryption.");
193
        }
194
195
        if($verification) {
196
            $this->verify_encrypted_file($dest);
197
        }
198
199
        //we replace the original backup with the encrypted one
200 View Code Duplication
        if( !$keep_local && copy($this->get_xcloner_path() .$dest,
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
201
             $this->get_xcloner_path() .$source) ) {
202
            unlink($this->get_xcloner_path() .$dest);
203
        }
204
205
206
        return array("target_file" => $dest, "finished" => 1);
207
    }
208
209
    public function verify_encrypted_file($file) {
210
        if(is_object($this->logger)) {
211
            $this->logger->info(sprintf('Verifying encrypted file %s', $file));
212
        }
213
214
        $this->verification = true;
215
        $this->decrypt_file($file);
216
        $this->verification = false;
217
    }
218
219
    /**
220
     * Dencrypt the passed file and saves the result in a new file, removing the
221
     * last 4 characters from file name.
222
     *
223
     * @param string $source Path to file that should be decrypted
224
     * @param string $dest   File name where the decryped file should be written to.
225
     * @param string $key    The key used for the decryption (must be the same as for encryption)
226
     * @param int $start   Start position for reading when doing incremental mode.
227
     * @param string $iv   The IV key to use.
228
     * @return string|false  Returns the file name that has been created or FALSE if an error occured
229
     */
230
    public function decrypt_file($source, $dest = "", $key = "", $start = 0, $iv = 0, $recursive = false)
231
    {
232 View Code Duplication
        if(is_object($this->logger)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
233
            $this->logger->info(sprintf('Decrypting file %s at position %d with IV %s', $source, $start, base64_encode($iv)));
234
        }
235
236
        //$key = substr(sha1($key, true), 0, 16);
237
        if(!$key){
238
            $key = self::get_backup_encryption_key();
239
        }
240
241
        $key_digest = openssl_digest ($key, "md5", true);
242
243
        $keep_local = 1;
244
        if(!$dest) {
245
            $dest = $this->get_decrypted_target_backup_file_name($source);
246
            $keep_local = 0;
247
        }
248
249
        if( !$start ) {
250 View Code Duplication
            if($this->verification){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
251
                $fpOut = fopen("php://stdout", 'w');
252
            }else {
253
                $fpOut = fopen($this->get_xcloner_path() . $dest, 'w');
254
            }
255 View Code Duplication
        }else{
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
256
            if($this->verification){
257
                $fpOut = fopen("php://stdout", 'a');
258
            }else {
259
                $fpOut = fopen($this->get_xcloner_path() . $dest, 'a');
260
            }
261
        }
262
263
        if ( $fpOut ) {
264
            if ( file_exists($this->get_xcloner_path() .$source) &&
265
                $fpIn = fopen($this->get_xcloner_path() .$source, 'rb')) {
266
267
                $encryption_length = (int)fread($fpIn, 16);
268
                if(!$encryption_length) {
269
                    $encryption_length = self::FILE_ENCRYPTION_BLOCKS;
270
                }
271
272
                fseek($fpIn, (int)$start);
273
274
                // Get the initialzation vector from the beginning of the file
275
                if(!$iv) {
276
                    $iv = fread($fpIn, 16);
277
                }
278
279
                if (!feof($fpIn)) {
280
281
                    // we have to read one block more for decrypting than for encrypting
282
                    $ciphertext = fread($fpIn, 16 * ($encryption_length + 1));
283
                    $plaintext = openssl_decrypt($ciphertext, 'AES-128-CBC', $key_digest, OPENSSL_RAW_DATA, $iv);
284
285
                    if(!$plaintext){
286
                        unlink($this->get_xcloner_path() . $dest);
287
                        if(is_object($this->logger)) {
288
                            $this->logger->error('Backup decryption failed, please check your provided Encryption Key.');
289
                        }
290
                        throw new \Exception("Backup decryption failed, please check your provided Encryption Key.");
291
                    }
292
293
                    // Use the first 16 bytes of the ciphertext as the next initialization vector
294
                    $iv = substr($ciphertext, 0, 16);
295
296
                    if( !$this->verification) {
297
                        fwrite($fpOut, $plaintext);
298
                    }
299
300
                    $start = ftell($fpIn);
301
302
                    fclose($fpOut );
303
304
                    if(!feof($fpIn)) {
305
                        fclose($fpIn);
306
                        if($this->verification || $recursive) {
307
                            $ciphertext = "";
0 ignored issues
show
Unused Code introduced by
$ciphertext is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
308
                            $plaintext = "";
0 ignored issues
show
Unused Code introduced by
$plaintext is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
309
                            $this->decrypt_file($source, $dest, $key, $start, $iv, $recursive);
0 ignored issues
show
Bug introduced by
It seems like $key defined by self::get_backup_encryption_key() on line 238 can also be of type null; however, Xcloner_Encryption::decrypt_file() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
310 View Code Duplication
                        }else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
311
                            if(($iv) != base64_decode(base64_encode($iv)))
312
                            {
313
                                throw new \Exception('Could not encode IV for transport');
314
                            }
315
316
                            return array(
317
                                "start" => $start,
318
                                "encryption_length" => $encryption_length,
319
                                "iv" => base64_encode($iv),
320
                                "target_file" => $dest,
321
                                "finished" => 0
322
                            );
323
                    }
324
                    }
325
326
                }
327
            } else {
328
                if(is_object($this->logger)) {
329
                    $this->logger->error('Unable to read source file for decryption');
330
                }
331
                throw new \Exception("Unable to read source file for decryption");
332
            }
333
        } else {
334
            if(is_object($this->logger)) {
335
                $this->logger->error('Unable to write destination file for decryption');
336
            }
337
            throw new \Exception("Unable to write destination file for decryption");
338
        }
339
340
        //we replace the original backup with the encrypted one
341 View Code Duplication
        if( !$keep_local && !$this->verification && copy($this->get_xcloner_path()  .$dest,
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
342
            $this->get_xcloner_path()  .$source) ) {
343
            unlink($this->get_xcloner_path()  .$dest);
344
        }
345
346
        return array("target_file" => $dest, "finished" => 1);
347
    }
348
349
    public function get_xcloner_path(){
350
        if(is_object($this->xcloner_settings)) {
351
            return $this->xcloner_settings->get_xcloner_store_path() . DS;
352
        }
353
354
        return null;
355
    }
356
357
}
358
359
360
try {
361
362
    if(isset($argv[1])) {
363
364
        class Xcloner {
365
            public function __construct()
366
            {
367
            }
368
        }
369
        $xcloner_encryption = new Xcloner_Encryption( new Xcloner() );
370
371
        if ($argv[1] == "-e") {
372
            $xcloner_encryption->encrypt_file($argv[2], $argv[2] . ".enc", $argv[4], '', '', '', true);
0 ignored issues
show
Documentation introduced by
'' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
373
        } elseif ($argv[1] == "-d") {
374
            $xcloner_encryption->decrypt_file($argv[2], $argv[2] . ".dec", $argv[4], '', '', true);
375
        }
376
    }
377
}catch(\Exception $e) {
378
    echo "CAUGHT: " . $e->getMessage();
379
}
380