1 | /******************************************************************************* | |
2 | * Copyhacked (H) 2012-2025. | |
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 it | |
6 | * every time possible with every body. | |
7 | * | |
8 | * Contributors: | |
9 | * ron190 at ymail dot com - initial implementation | |
10 | ******************************************************************************/ | |
11 | package com.jsql.view.swing.terminal; | |
12 | ||
13 | import com.jsql.util.LogLevelUtil; | |
14 | import com.jsql.view.swing.terminal.util.BlockCaret; | |
15 | import com.jsql.view.swing.terminal.util.KeyAdapterTerminal; | |
16 | import com.jsql.view.swing.util.MediatorHelper; | |
17 | import com.jsql.view.swing.util.UiUtil; | |
18 | import org.apache.commons.lang3.StringUtils; | |
19 | import org.apache.logging.log4j.LogManager; | |
20 | import org.apache.logging.log4j.Logger; | |
21 | ||
22 | import javax.swing.*; | |
23 | import javax.swing.text.BadLocationException; | |
24 | import javax.swing.text.Style; | |
25 | import javax.swing.text.StyleConstants; | |
26 | import java.awt.*; | |
27 | import java.awt.event.MouseMotionListener; | |
28 | import java.net.MalformedURLException; | |
29 | import java.net.URI; | |
30 | import java.net.URISyntaxException; | |
31 | import java.net.URL; | |
32 | import java.util.UUID; | |
33 | import java.util.concurrent.atomic.AtomicBoolean; | |
34 | ||
35 | /** | |
36 | * A Terminal completely built from swing text pane. | |
37 | */ | |
38 | public abstract class AbstractExploit extends JTextPane { | |
39 | | |
40 | /** | |
41 | * Log4j logger sent to view. | |
42 | */ | |
43 | private static final Logger LOGGER = LogManager.getRootLogger(); | |
44 | ||
45 | /** | |
46 | * True if terminal is processing command. | |
47 | */ | |
48 | private final AtomicBoolean isEdited = new AtomicBoolean(false); | |
49 | ||
50 | /** | |
51 | * Server name or IP to display on prompt. | |
52 | */ | |
53 | private final String host; | |
54 | ||
55 | /** | |
56 | * User and password for database. | |
57 | */ | |
58 | private String[] loginPassword = null; | |
59 | private final UUID uuidShell; | |
60 | private final String urlShell; | |
61 | ||
62 | /** | |
63 | * Style used for coloring text. | |
64 | */ | |
65 | private final transient Style style = this.addStyle("Necrophagist's next album is 2014.", null); | |
66 | ||
67 | /** | |
68 | * Length of prompt. | |
69 | */ | |
70 | private String prompt = StringUtils.EMPTY; | |
71 | ||
72 | /** | |
73 | * Text to display next caret. | |
74 | */ | |
75 | private final String labelShell; | |
76 | | |
77 | /** | |
78 | * Build a shell instance. | |
79 | * @param uuidShell Unique identifier to discriminate beyond multiple opened terminals | |
80 | * @param urlShell URL of current shell | |
81 | * @param labelShell Type of shell to display on prompt | |
82 | */ | |
83 | protected AbstractExploit(UUID uuidShell, String urlShell, String labelShell) throws MalformedURLException, URISyntaxException { | |
84 | this(uuidShell, urlShell, labelShell, true); | |
85 | } | |
86 | protected AbstractExploit(UUID uuidShell, String urlShell, String labelShell, boolean isAddingPrompt) throws MalformedURLException, URISyntaxException { | |
87 | this.uuidShell = uuidShell; | |
88 | this.urlShell = urlShell; | |
89 | this.labelShell = labelShell; | |
90 | ||
91 | URL url; | |
92 |
1
1. <init> : negated conditional → NO_COVERAGE |
if (StringUtils.isEmpty(urlShell)) { // udf |
93 | url = new URI(MediatorHelper.model().getMediatorUtils().getConnectionUtil().getUrlByUser()).toURL(); | |
94 | } else { | |
95 | url = new URI(urlShell).toURL(); | |
96 | } | |
97 | this.host = url.getHost(); | |
98 | ||
99 |
1
1. <init> : removed call to com/jsql/view/swing/terminal/AbstractExploit::setFont → NO_COVERAGE |
this.setFont(new Font(UiUtil.FONT_NAME_MONO_NON_ASIAN, Font.PLAIN, UIManager.getFont("TextArea.font").getSize())); |
100 |
1
1. <init> : removed call to com/jsql/view/swing/terminal/AbstractExploit::setCaret → NO_COVERAGE |
this.setCaret(new BlockCaret()); |
101 |
1
1. <init> : removed call to com/jsql/view/swing/terminal/AbstractExploit::setBackground → NO_COVERAGE |
this.setBackground(Color.BLACK); |
102 |
1
1. <init> : removed call to com/jsql/view/swing/terminal/AbstractExploit::setForeground → NO_COVERAGE |
this.setForeground(Color.LIGHT_GRAY); |
103 | ||
104 |
1
1. <init> : negated conditional → NO_COVERAGE |
if (isAddingPrompt) { |
105 |
1
1. <init> : removed call to com/jsql/view/swing/terminal/AbstractExploit::displayPrompt → NO_COVERAGE |
this.displayPrompt(true); |
106 | } else { | |
107 |
1
1. <init> : removed call to com/jsql/view/swing/terminal/AbstractExploit::append → NO_COVERAGE |
this.append("Waiting for reverse connection...\n"); |
108 | } | |
109 | ||
110 |
1
1. <init> : removed call to com/jsql/view/swing/terminal/AbstractExploit::setCursor → NO_COVERAGE |
this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); |
111 |
1
1. <init> : removed call to com/jsql/view/swing/terminal/AbstractExploit::setTransferHandler → NO_COVERAGE |
this.setTransferHandler(null); |
112 |
1
1. <init> : removed call to com/jsql/view/swing/terminal/AbstractExploit::setHighlighter → NO_COVERAGE |
this.setHighlighter(null); |
113 | ||
114 |
1
1. <init> : removed call to com/jsql/view/swing/terminal/AbstractExploit::addMouseListener → NO_COVERAGE |
this.addMouseListener(new EmptyFocusCopy(this)); |
115 |
1
1. <init> : removed call to com/jsql/view/swing/terminal/AbstractExploit::addKeyListener → NO_COVERAGE |
this.addKeyListener(new KeyAdapterTerminal(this)); |
116 | } | |
117 | ||
118 | /** | |
119 | * Run when cmd is validated. | |
120 | * @param cmd Command to execute | |
121 | * @param terminalID Unique ID for terminal instance | |
122 | * @param wbhPath URL of shell | |
123 | * @param arg Additional parameters (User and password for SQLShell) | |
124 | */ | |
125 | public abstract void action(String cmd, UUID terminalID, String wbhPath, String... arg); | |
126 | | |
127 | /** | |
128 | * Update terminal and use default behavior. | |
129 | */ | |
130 | public void reset() { | |
131 |
1
1. reset : removed call to com/jsql/view/swing/terminal/AbstractExploit::reset → NO_COVERAGE |
this.reset(true); |
132 | } | |
133 | public void reset(boolean isPromptVisible) { | |
134 |
1
1. reset : removed call to java/util/concurrent/atomic/AtomicBoolean::set → NO_COVERAGE |
this.isEdited.set(false); |
135 |
1
1. reset : removed call to com/jsql/view/swing/terminal/AbstractExploit::setEditable → NO_COVERAGE |
this.setEditable(true); |
136 |
1
1. reset : negated conditional → NO_COVERAGE |
if (isPromptVisible) { |
137 |
1
1. reset : removed call to com/jsql/view/swing/terminal/AbstractExploit::displayPrompt → NO_COVERAGE |
this.displayPrompt(false); |
138 | } | |
139 |
1
1. reset : removed call to com/jsql/view/swing/terminal/AbstractExploit::setCaretPosition → NO_COVERAGE |
this.setCaretPosition(this.getDocument().getLength()); |
140 |
1
1. reset : removed call to com/jsql/view/swing/terminal/AbstractExploit::setCursor → NO_COVERAGE |
this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); |
141 | } | |
142 | ||
143 | /** | |
144 | * Add a text at the end of textpane. | |
145 | * @param string Text to add | |
146 | */ | |
147 | public void append(String string) { | |
148 | try { | |
149 | var doc = this.getDocument(); | |
150 |
1
1. append : removed call to javax/swing/text/Document::insertString → NO_COVERAGE |
doc.insertString(doc.getLength(), string, null); |
151 | } catch (BadLocationException e) { | |
152 | LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e); | |
153 | } | |
154 | } | |
155 | | |
156 | /** | |
157 | * Append prompt to textpane, measure prompt the first time is used. | |
158 | * @param isAddingPrompt Should we measure prompt length? | |
159 | */ | |
160 | public void displayPrompt(boolean isAddingPrompt) { | |
161 |
1
1. displayPrompt : removed call to javax/swing/text/StyleConstants::setUnderline → NO_COVERAGE |
StyleConstants.setUnderline(this.style, true); |
162 |
1
1. displayPrompt : removed call to com/jsql/view/swing/terminal/AbstractExploit::appendPrompt → NO_COVERAGE |
this.appendPrompt("jsql", Color.LIGHT_GRAY, isAddingPrompt); |
163 |
1
1. displayPrompt : removed call to javax/swing/text/StyleConstants::setUnderline → NO_COVERAGE |
StyleConstants.setUnderline(this.style, false); |
164 | ||
165 |
1
1. displayPrompt : removed call to com/jsql/view/swing/terminal/AbstractExploit::appendPrompt → NO_COVERAGE |
this.appendPrompt(StringUtils.SPACE + this.labelShell, Color.LIGHT_GRAY, isAddingPrompt); |
166 |
1
1. displayPrompt : removed call to com/jsql/view/swing/terminal/AbstractExploit::appendPrompt → NO_COVERAGE |
this.appendPrompt("[", new Color(0x32BF32), isAddingPrompt); |
167 |
1
1. displayPrompt : removed call to com/jsql/view/swing/terminal/AbstractExploit::appendPrompt → NO_COVERAGE |
this.appendPrompt(this.host, new Color(0xBFBF19), isAddingPrompt); |
168 |
1
1. displayPrompt : removed call to com/jsql/view/swing/terminal/AbstractExploit::appendPrompt → NO_COVERAGE |
this.appendPrompt("]", new Color(0x32BF32), isAddingPrompt); |
169 |
1
1. displayPrompt : removed call to com/jsql/view/swing/terminal/AbstractExploit::appendPrompt → NO_COVERAGE |
this.appendPrompt(" >", new Color(0xBF6464), isAddingPrompt); |
170 |
1
1. displayPrompt : removed call to com/jsql/view/swing/terminal/AbstractExploit::appendPrompt → NO_COVERAGE |
this.appendPrompt(StringUtils.SPACE, Color.LIGHT_GRAY, isAddingPrompt); |
171 | } | |
172 | ||
173 | /** | |
174 | * Add a colored string to the textpane, measure prompt at the same time. | |
175 | * @param string Text to append | |
176 | * @param color Color of text | |
177 | * @param isAddingPrompt Should we measure prompt length? | |
178 | */ | |
179 | private void appendPrompt(String string, Color color, boolean isAddingPrompt) { | |
180 | try { | |
181 |
1
1. appendPrompt : removed call to javax/swing/text/StyleConstants::setForeground → NO_COVERAGE |
StyleConstants.setForeground(this.style, color); |
182 |
1
1. appendPrompt : removed call to javax/swing/text/StyledDocument::insertString → NO_COVERAGE |
this.getStyledDocument().insertString(this.getStyledDocument().getLength(), string, this.style); |
183 |
1
1. appendPrompt : negated conditional → NO_COVERAGE |
if (isAddingPrompt) { |
184 | this.prompt += string; | |
185 | } | |
186 | } catch (BadLocationException e) { | |
187 | LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e); | |
188 | } | |
189 | } | |
190 | ||
191 | /** | |
192 | * NoWrap. | |
193 | */ | |
194 | @Override | |
195 | public boolean getScrollableTracksViewportWidth() { | |
196 |
3
1. getScrollableTracksViewportWidth : negated conditional → NO_COVERAGE 2. getScrollableTracksViewportWidth : replaced boolean return with true for com/jsql/view/swing/terminal/AbstractExploit::getScrollableTracksViewportWidth → NO_COVERAGE 3. getScrollableTracksViewportWidth : changed conditional boundary → NO_COVERAGE |
return this.getUI().getPreferredSize(this).width <= this.getParent().getSize().width; |
197 | } | |
198 | ||
199 | /** | |
200 | * Cancel every mouse movement processing like drag/drop. | |
201 | */ | |
202 | @Override | |
203 | public synchronized void addMouseMotionListener(MouseMotionListener l) { | |
204 | // Do nothing | |
205 | } | |
206 | ||
207 | /** | |
208 | * Get index of line for current offset (generally cursor position). | |
209 | * @param offset Position on the line | |
210 | * @return Index of the line | |
211 | */ | |
212 | public int getLineOfOffset(int offset) throws BadLocationException { | |
213 | var errorMsg = "Can't translate offset to line"; | |
214 | var doc = this.getDocument(); | |
215 | | |
216 |
2
1. getLineOfOffset : negated conditional → NO_COVERAGE 2. getLineOfOffset : changed conditional boundary → NO_COVERAGE |
if (offset < 0) { |
217 | throw new BadLocationException(errorMsg, -1); | |
218 |
2
1. getLineOfOffset : changed conditional boundary → NO_COVERAGE 2. getLineOfOffset : negated conditional → NO_COVERAGE |
} else if (offset > doc.getLength()) { |
219 |
1
1. getLineOfOffset : Replaced integer addition with subtraction → NO_COVERAGE |
throw new BadLocationException(errorMsg, doc.getLength() + 1); |
220 | } else { | |
221 | var map = doc.getDefaultRootElement(); | |
222 |
1
1. getLineOfOffset : replaced int return with 0 for com/jsql/view/swing/terminal/AbstractExploit::getLineOfOffset → NO_COVERAGE |
return map.getElementIndex(offset); |
223 | } | |
224 | } | |
225 | ||
226 | /** | |
227 | * Get position of the beginning of the line. | |
228 | * @param line Index of the line | |
229 | * @return Offset of line | |
230 | */ | |
231 | public int getLineStartOffset(int line) throws BadLocationException { | |
232 | var map = this.getDocument().getDefaultRootElement(); | |
233 | | |
234 |
2
1. getLineStartOffset : negated conditional → NO_COVERAGE 2. getLineStartOffset : changed conditional boundary → NO_COVERAGE |
if (line < 0) { |
235 | throw new BadLocationException("Negative line", -1); | |
236 |
2
1. getLineStartOffset : changed conditional boundary → NO_COVERAGE 2. getLineStartOffset : negated conditional → NO_COVERAGE |
} else if (line >= map.getElementCount()) { |
237 |
1
1. getLineStartOffset : Replaced integer addition with subtraction → NO_COVERAGE |
throw new BadLocationException("No such line", this.getDocument().getLength() + 1); |
238 | } else { | |
239 | var lineElem = map.getElement(line); | |
240 |
1
1. getLineStartOffset : replaced int return with 0 for com/jsql/view/swing/terminal/AbstractExploit::getLineStartOffset → NO_COVERAGE |
return lineElem.getStartOffset(); |
241 | } | |
242 | } | |
243 | ||
244 | | |
245 | // Getter and setter | |
246 | | |
247 | public AtomicBoolean getIsEdited() { | |
248 |
1
1. getIsEdited : replaced return value with null for com/jsql/view/swing/terminal/AbstractExploit::getIsEdited → NO_COVERAGE |
return this.isEdited; |
249 | } | |
250 | ||
251 | public UUID getUuidShell() { | |
252 |
1
1. getUuidShell : replaced return value with null for com/jsql/view/swing/terminal/AbstractExploit::getUuidShell → NO_COVERAGE |
return this.uuidShell; |
253 | } | |
254 | ||
255 | public String getUrlShell() { | |
256 |
1
1. getUrlShell : replaced return value with "" for com/jsql/view/swing/terminal/AbstractExploit::getUrlShell → NO_COVERAGE |
return this.urlShell; |
257 | } | |
258 | ||
259 | public String getPrompt() { | |
260 |
1
1. getPrompt : replaced return value with "" for com/jsql/view/swing/terminal/AbstractExploit::getPrompt → NO_COVERAGE |
return this.prompt; |
261 | } | |
262 | ||
263 | public String[] getLoginPassword() { | |
264 |
1
1. getLoginPassword : replaced return value with null for com/jsql/view/swing/terminal/AbstractExploit::getLoginPassword → NO_COVERAGE |
return this.loginPassword; |
265 | } | |
266 | ||
267 | public void setLoginPassword(String[] loginPassword) { | |
268 | this.loginPassword = loginPassword; | |
269 | } | |
270 | } | |
Mutations | ||
92 |
1.1 |
|
99 |
1.1 |
|
100 |
1.1 |
|
101 |
1.1 |
|
102 |
1.1 |
|
104 |
1.1 |
|
105 |
1.1 |
|
107 |
1.1 |
|
110 |
1.1 |
|
111 |
1.1 |
|
112 |
1.1 |
|
114 |
1.1 |
|
115 |
1.1 |
|
131 |
1.1 |
|
134 |
1.1 |
|
135 |
1.1 |
|
136 |
1.1 |
|
137 |
1.1 |
|
139 |
1.1 |
|
140 |
1.1 |
|
150 |
1.1 |
|
161 |
1.1 |
|
162 |
1.1 |
|
163 |
1.1 |
|
165 |
1.1 |
|
166 |
1.1 |
|
167 |
1.1 |
|
168 |
1.1 |
|
169 |
1.1 |
|
170 |
1.1 |
|
181 |
1.1 |
|
182 |
1.1 |
|
183 |
1.1 |
|
196 |
1.1 2.2 3.3 |
|
216 |
1.1 2.2 |
|
218 |
1.1 2.2 |
|
219 |
1.1 |
|
222 |
1.1 |
|
234 |
1.1 2.2 |
|
236 |
1.1 2.2 |
|
237 |
1.1 |
|
240 |
1.1 |
|
248 |
1.1 |
|
252 |
1.1 |
|
256 |
1.1 |
|
260 |
1.1 |
|
264 |
1.1 |