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