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.util; | |
12 | ||
13 | import com.jsql.util.LogLevelUtil; | |
14 | import com.jsql.view.swing.terminal.AbstractExploit; | |
15 | import org.apache.commons.lang3.StringUtils; | |
16 | import org.apache.logging.log4j.LogManager; | |
17 | import org.apache.logging.log4j.Logger; | |
18 | ||
19 | import javax.swing.*; | |
20 | import javax.swing.text.BadLocationException; | |
21 | import javax.swing.text.Element; | |
22 | import java.awt.event.InputEvent; | |
23 | import java.awt.event.KeyAdapter; | |
24 | import java.awt.event.KeyEvent; | |
25 | import java.util.ArrayList; | |
26 | import java.util.List; | |
27 | import java.util.concurrent.atomic.AtomicReference; | |
28 | ||
29 | /** | |
30 | * Keyboard key processing for terminal. | |
31 | */ | |
32 | public class KeyAdapterTerminal extends KeyAdapter { | |
33 | | |
34 | private static final Logger LOGGER = LogManager.getRootLogger(); | |
35 | ||
36 | /** | |
37 | * Terminal where keys are processed. | |
38 | */ | |
39 | private final AbstractExploit terminal; | |
40 | ||
41 | /** | |
42 | * Past commands entered by user. | |
43 | */ | |
44 | private final List<String> commandsHistory = new ArrayList<>(); | |
45 | ||
46 | /** | |
47 | * Current position in array of past commands. | |
48 | */ | |
49 | private int indexCommandsHistory = 0; | |
50 | ||
51 | /** | |
52 | * Create a keyboard processor for a terminal. | |
53 | * @param terminal Terminal where keys are processed | |
54 | */ | |
55 | public KeyAdapterTerminal(AbstractExploit terminal) { | |
56 | this.terminal = terminal; | |
57 | } | |
58 | ||
59 | @Override | |
60 | public void keyPressed(KeyEvent keyEvent) { | |
61 | try { | |
62 | var root = this.terminal.getDocument().getDefaultRootElement(); | |
63 | int caretPosition = this.terminal.getCaretPosition(); | |
64 | int lineNumber = this.terminal.getLineOfOffset(caretPosition); // Get current line | |
65 | ||
66 |
1
1. keyPressed : negated conditional → NO_COVERAGE |
if (this.terminal.getIsEdited().get()) { // Cancel every user keyboard input if another command has just been sent |
67 |
1
1. keyPressed : removed call to java/awt/event/KeyEvent::consume → NO_COVERAGE |
keyEvent.consume(); |
68 | return; | |
69 | } | |
70 | | |
71 | // Get user input | |
72 | var command = new AtomicReference<>(StringUtils.EMPTY); | |
73 |
1
1. keyPressed : removed call to java/util/concurrent/atomic/AtomicReference::set → NO_COVERAGE |
command.set( |
74 | this.terminal.getText( | |
75 | root.getElement(lineNumber).getStartOffset(), | |
76 |
1
1. keyPressed : Replaced integer subtraction with addition → NO_COVERAGE |
root.getElement(lineNumber).getEndOffset() - root.getElement(lineNumber).getStartOffset() |
77 | ) | |
78 | .replace(this.terminal.getPrompt(), StringUtils.EMPTY) | |
79 | .trim() | |
80 | ); | |
81 | | |
82 |
1
1. keyPressed : negated conditional → NO_COVERAGE |
if (keyEvent.getKeyCode() == KeyEvent.VK_ENTER) { // Validate user input ; disable text editing |
83 |
1
1. keyPressed : removed call to com/jsql/view/swing/terminal/util/KeyAdapterTerminal::runCommand → NO_COVERAGE |
this.runCommand(keyEvent, command); |
84 |
1
1. keyPressed : negated conditional → NO_COVERAGE |
} else if (keyEvent.getKeyCode() == KeyEvent.VK_UP) { // Get previous command |
85 |
1
1. keyPressed : removed call to com/jsql/view/swing/terminal/util/KeyAdapterTerminal::appendPreviousCommand → NO_COVERAGE |
this.appendPreviousCommand(keyEvent, root, lineNumber, command); |
86 |
1
1. keyPressed : negated conditional → NO_COVERAGE |
} else if (keyEvent.getKeyCode() == KeyEvent.VK_DOWN) { // Get next command |
87 |
1
1. keyPressed : removed call to com/jsql/view/swing/terminal/util/KeyAdapterTerminal::appendNextCommand → NO_COVERAGE |
this.appendNextCommand(keyEvent, root, lineNumber, command); |
88 |
2
1. keyPressed : negated conditional → NO_COVERAGE 2. keyPressed : negated conditional → NO_COVERAGE |
} else if (keyEvent.getKeyCode() == KeyEvent.VK_LEFT || keyEvent.getKeyCode() == KeyEvent.VK_BACK_SPACE) { // Go to the left until prompt |
89 |
1
1. keyPressed : removed call to com/jsql/view/swing/terminal/util/KeyAdapterTerminal::moveCaretLeft → NO_COVERAGE |
this.moveCaretLeft(keyEvent, caretPosition, lineNumber); |
90 |
1
1. keyPressed : negated conditional → NO_COVERAGE |
} else if (keyEvent.getKeyCode() == KeyEvent.VK_HOME) { // Get to the beginning of the line |
91 |
1
1. keyPressed : removed call to com/jsql/view/swing/terminal/util/KeyAdapterTerminal::moveCaretHome → NO_COVERAGE |
this.moveCaretHome(keyEvent, lineNumber); |
92 |
1
1. keyPressed : negated conditional → NO_COVERAGE |
} else if (this.isKeyNotAllowed(keyEvent, caretPosition)) { |
93 |
1
1. keyPressed : removed call to java/awt/event/KeyEvent::consume → NO_COVERAGE |
keyEvent.consume(); |
94 |
3
1. keyPressed : negated conditional → NO_COVERAGE 2. keyPressed : Replaced bitwise AND with OR → NO_COVERAGE 3. keyPressed : negated conditional → NO_COVERAGE |
} else if (keyEvent.getKeyCode() == KeyEvent.VK_C && (keyEvent.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0) { |
95 |
1
1. keyPressed : removed call to com/jsql/view/swing/terminal/util/KeyAdapterTerminal::cancelCommand → NO_COVERAGE |
this.cancelCommand(keyEvent); |
96 | } | |
97 | } catch (BadLocationException e) { | |
98 | LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e); | |
99 | } | |
100 | } | |
101 | ||
102 | private boolean isKeyNotAllowed(KeyEvent keyEvent, int caretPosition) { | |
103 |
1
1. isKeyNotAllowed : replaced boolean return with true for com/jsql/view/swing/terminal/util/KeyAdapterTerminal::isKeyNotAllowed → NO_COVERAGE |
return |
104 | // Cancel the select all shortcut Ctrl+A | |
105 |
3
1. isKeyNotAllowed : negated conditional → NO_COVERAGE 2. isKeyNotAllowed : Replaced bitwise AND with OR → NO_COVERAGE 3. isKeyNotAllowed : negated conditional → NO_COVERAGE |
keyEvent.getKeyCode() == KeyEvent.VK_A && (keyEvent.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0 |
106 | // Cancel the *beep* sound if deleting while at the end of line | |
107 |
2
1. isKeyNotAllowed : negated conditional → NO_COVERAGE 2. isKeyNotAllowed : negated conditional → NO_COVERAGE |
|| keyEvent.getKeyCode() == KeyEvent.VK_DELETE && caretPosition == this.terminal.getDocument().getLength() |
108 |
4
1. isKeyNotAllowed : Replaced bitwise AND with OR → NO_COVERAGE 2. isKeyNotAllowed : negated conditional → NO_COVERAGE 3. isKeyNotAllowed : Replaced bitwise AND with OR → NO_COVERAGE 4. isKeyNotAllowed : negated conditional → NO_COVERAGE |
|| (keyEvent.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0 && (keyEvent.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) != 0 |
109 |
1
1. isKeyNotAllowed : negated conditional → NO_COVERAGE |
|| keyEvent.getKeyCode() == KeyEvent.VK_PAGE_UP |
110 |
1
1. isKeyNotAllowed : negated conditional → NO_COVERAGE |
|| keyEvent.getKeyCode() == KeyEvent.VK_PAGE_DOWN |
111 |
1
1. isKeyNotAllowed : negated conditional → NO_COVERAGE |
|| keyEvent.getKeyCode() == KeyEvent.VK_TAB; |
112 | } | |
113 | ||
114 | private void cancelCommand(KeyEvent keyEvent) { | |
115 |
1
1. cancelCommand : removed call to java/awt/event/KeyEvent::consume → NO_COVERAGE |
keyEvent.consume(); |
116 |
1
1. cancelCommand : removed call to com/jsql/view/swing/terminal/AbstractExploit::append → NO_COVERAGE |
this.terminal.append("\n"); |
117 |
1
1. cancelCommand : removed call to com/jsql/view/swing/terminal/AbstractExploit::reset → NO_COVERAGE |
this.terminal.reset(); |
118 | } | |
119 | ||
120 | private void moveCaretHome(KeyEvent keyEvent, int lineNumber) throws BadLocationException { | |
121 |
1
1. moveCaretHome : removed call to java/awt/event/KeyEvent::consume → NO_COVERAGE |
keyEvent.consume(); |
122 |
2
1. moveCaretHome : Replaced integer addition with subtraction → NO_COVERAGE 2. moveCaretHome : removed call to com/jsql/view/swing/terminal/AbstractExploit::setCaretPosition → NO_COVERAGE |
this.terminal.setCaretPosition(this.terminal.getLineStartOffset(lineNumber) + this.terminal.getPrompt().length()); |
123 | } | |
124 | ||
125 | private void moveCaretLeft(KeyEvent keyEvent, int caretPosition, int lineNumber) throws BadLocationException { | |
126 |
1
1. moveCaretLeft : Replaced integer subtraction with addition → NO_COVERAGE |
int newCaretPosition = caretPosition - this.terminal.getLineStartOffset(lineNumber); |
127 |
2
1. moveCaretLeft : changed conditional boundary → NO_COVERAGE 2. moveCaretLeft : negated conditional → NO_COVERAGE |
if (newCaretPosition <= this.terminal.getPrompt().length()) { |
128 |
1
1. moveCaretLeft : removed call to java/awt/event/KeyEvent::consume → NO_COVERAGE |
keyEvent.consume(); |
129 | } | |
130 | } | |
131 | ||
132 | private void appendNextCommand( | |
133 | KeyEvent keyEvent, | |
134 | Element root, | |
135 | int lineNumber, | |
136 | AtomicReference<String> command | |
137 | ) throws BadLocationException { | |
138 |
1
1. appendNextCommand : removed call to java/awt/event/KeyEvent::consume → NO_COVERAGE |
keyEvent.consume(); |
139 | | |
140 |
2
1. appendNextCommand : changed conditional boundary → NO_COVERAGE 2. appendNextCommand : negated conditional → NO_COVERAGE |
if (this.indexCommandsHistory < this.commandsHistory.size()) { |
141 |
1
1. appendNextCommand : Replaced integer addition with subtraction → NO_COVERAGE |
this.indexCommandsHistory++; |
142 | } | |
143 | | |
144 |
3
1. appendNextCommand : negated conditional → NO_COVERAGE 2. appendNextCommand : negated conditional → NO_COVERAGE 3. appendNextCommand : changed conditional boundary → NO_COVERAGE |
if (!this.commandsHistory.isEmpty() && this.indexCommandsHistory < this.commandsHistory.size()) { |
145 |
1
1. appendNextCommand : removed call to javax/swing/text/Document::remove → NO_COVERAGE |
this.terminal.getDocument().remove( |
146 |
1
1. appendNextCommand : Replaced integer addition with subtraction → NO_COVERAGE |
root.getElement(lineNumber).getStartOffset() + this.terminal.getPrompt().length(), |
147 |
1
1. appendNextCommand : Replaced integer subtraction with addition → NO_COVERAGE |
command.get().length() - 1 |
148 | ); | |
149 | | |
150 |
1
1. appendNextCommand : removed call to com/jsql/view/swing/terminal/AbstractExploit::append → NO_COVERAGE |
this.terminal.append(this.commandsHistory.get(this.indexCommandsHistory)); |
151 |
1
1. appendNextCommand : removed call to com/jsql/view/swing/terminal/AbstractExploit::setCaretPosition → NO_COVERAGE |
this.terminal.setCaretPosition(this.terminal.getDocument().getLength()); |
152 | } | |
153 | } | |
154 | ||
155 | private void appendPreviousCommand( | |
156 | KeyEvent keyEvent, | |
157 | Element root, | |
158 | int lineNumber, | |
159 | AtomicReference<String> command | |
160 | ) throws BadLocationException { | |
161 |
1
1. appendPreviousCommand : removed call to java/awt/event/KeyEvent::consume → NO_COVERAGE |
keyEvent.consume(); |
162 | | |
163 |
2
1. appendPreviousCommand : changed conditional boundary → NO_COVERAGE 2. appendPreviousCommand : negated conditional → NO_COVERAGE |
if (this.indexCommandsHistory > 0) { |
164 |
1
1. appendPreviousCommand : Replaced integer subtraction with addition → NO_COVERAGE |
this.indexCommandsHistory--; |
165 | } | |
166 | | |
167 |
1
1. appendPreviousCommand : negated conditional → NO_COVERAGE |
if (!this.commandsHistory.isEmpty()) { |
168 | if ( | |
169 |
2
1. appendPreviousCommand : negated conditional → NO_COVERAGE 2. appendPreviousCommand : changed conditional boundary → NO_COVERAGE |
this.commandsHistory.size() > 1 |
170 |
2
1. appendPreviousCommand : Replaced integer subtraction with addition → NO_COVERAGE 2. appendPreviousCommand : negated conditional → NO_COVERAGE |
&& this.indexCommandsHistory == this.commandsHistory.size() - 1 |
171 |
1
1. appendPreviousCommand : negated conditional → NO_COVERAGE |
&& StringUtils.isNotEmpty(command.get()) |
172 | ) { | |
173 |
1
1. appendPreviousCommand : Replaced integer subtraction with addition → NO_COVERAGE |
this.indexCommandsHistory--; |
174 | } | |
175 | | |
176 |
1
1. appendPreviousCommand : removed call to javax/swing/text/Document::remove → NO_COVERAGE |
this.terminal.getDocument().remove( |
177 |
1
1. appendPreviousCommand : Replaced integer addition with subtraction → NO_COVERAGE |
root.getElement(lineNumber).getStartOffset() + this.terminal.getPrompt().length(), |
178 |
1
1. appendPreviousCommand : Replaced integer subtraction with addition → NO_COVERAGE |
command.get().length() - 1 |
179 | ); | |
180 | | |
181 |
1
1. appendPreviousCommand : removed call to com/jsql/view/swing/terminal/AbstractExploit::append → NO_COVERAGE |
this.terminal.append(this.commandsHistory.get(this.indexCommandsHistory)); |
182 |
1
1. appendPreviousCommand : removed call to com/jsql/view/swing/terminal/AbstractExploit::setCaretPosition → NO_COVERAGE |
this.terminal.setCaretPosition(this.terminal.getDocument().getLength()); |
183 | } | |
184 | } | |
185 | ||
186 | private void runCommand(KeyEvent keyEvent, AtomicReference<String> command) { | |
187 |
1
1. runCommand : removed call to java/util/concurrent/atomic/AtomicBoolean::set → NO_COVERAGE |
this.terminal.getIsEdited().set(true); |
188 |
1
1. runCommand : removed call to java/awt/event/KeyEvent::consume → NO_COVERAGE |
keyEvent.consume(); |
189 |
1
1. runCommand : removed call to com/jsql/view/swing/terminal/AbstractExploit::setEditable → NO_COVERAGE |
this.terminal.setEditable(false); |
190 | | |
191 | // Populate cmd list for key up/down | |
192 |
1
1. runCommand : negated conditional → NO_COVERAGE |
if (StringUtils.isNotEmpty(command.get())) { |
193 | this.commandsHistory.add(command.get()); | |
194 | this.indexCommandsHistory = this.commandsHistory.size(); | |
195 | } | |
196 | ||
197 | new SwingWorker<>() { // Thread to give back control to user (SwingUtilities does not) | |
198 | @Override | |
199 | protected Object doInBackground() { | |
200 | // Inside Swing thread to avoid flickering | |
201 |
1
1. doInBackground : removed call to java/lang/Thread::setName → NO_COVERAGE |
Thread.currentThread().setName("SwingWorkerKeyAdapterTerminal"); |
202 | ||
203 | AbstractExploit terminalCommand = KeyAdapterTerminal.this.terminal; | |
204 |
1
1. doInBackground : removed call to com/jsql/view/swing/terminal/AbstractExploit::append → NO_COVERAGE |
terminalCommand.append("\n"); |
205 | | |
206 |
1
1. doInBackground : negated conditional → NO_COVERAGE |
if (StringUtils.isNotEmpty(command.get())) { |
207 |
1
1. doInBackground : removed call to com/jsql/view/swing/terminal/AbstractExploit::setCaretPosition → NO_COVERAGE |
terminalCommand.setCaretPosition(terminalCommand.getDocument().getLength()); |
208 |
1
1. doInBackground : removed call to com/jsql/view/swing/terminal/AbstractExploit::action → NO_COVERAGE |
terminalCommand.action( |
209 | command.get(), | |
210 | terminalCommand.getUuidShell(), | |
211 | terminalCommand.getUrlShell(), | |
212 | terminalCommand.getLoginPassword() | |
213 | ); | |
214 | } else { | |
215 |
1
1. doInBackground : removed call to com/jsql/view/swing/terminal/AbstractExploit::reset → NO_COVERAGE |
terminalCommand.reset(); |
216 | } | |
217 | return null; | |
218 | } | |
219 |
1
1. runCommand : removed call to com/jsql/view/swing/terminal/util/KeyAdapterTerminal$1::execute → NO_COVERAGE |
}.execute(); |
220 | } | |
221 | } | |
Mutations | ||
66 |
1.1 |
|
67 |
1.1 |
|
73 |
1.1 |
|
76 |
1.1 |
|
82 |
1.1 |
|
83 |
1.1 |
|
84 |
1.1 |
|
85 |
1.1 |
|
86 |
1.1 |
|
87 |
1.1 |
|
88 |
1.1 2.2 |
|
89 |
1.1 |
|
90 |
1.1 |
|
91 |
1.1 |
|
92 |
1.1 |
|
93 |
1.1 |
|
94 |
1.1 2.2 3.3 |
|
95 |
1.1 |
|
103 |
1.1 |
|
105 |
1.1 2.2 3.3 |
|
107 |
1.1 2.2 |
|
108 |
1.1 2.2 3.3 4.4 |
|
109 |
1.1 |
|
110 |
1.1 |
|
111 |
1.1 |
|
115 |
1.1 |
|
116 |
1.1 |
|
117 |
1.1 |
|
121 |
1.1 |
|
122 |
1.1 2.2 |
|
126 |
1.1 |
|
127 |
1.1 2.2 |
|
128 |
1.1 |
|
138 |
1.1 |
|
140 |
1.1 2.2 |
|
141 |
1.1 |
|
144 |
1.1 2.2 3.3 |
|
145 |
1.1 |
|
146 |
1.1 |
|
147 |
1.1 |
|
150 |
1.1 |
|
151 |
1.1 |
|
161 |
1.1 |
|
163 |
1.1 2.2 |
|
164 |
1.1 |
|
167 |
1.1 |
|
169 |
1.1 2.2 |
|
170 |
1.1 2.2 |
|
171 |
1.1 |
|
173 |
1.1 |
|
176 |
1.1 |
|
177 |
1.1 |
|
178 |
1.1 |
|
181 |
1.1 |
|
182 |
1.1 |
|
187 |
1.1 |
|
188 |
1.1 |
|
189 |
1.1 |
|
192 |
1.1 |
|
201 |
1.1 |
|
204 |
1.1 |
|
206 |
1.1 |
|
207 |
1.1 |
|
208 |
1.1 |
|
215 |
1.1 |
|
219 |
1.1 |