View Javadoc
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.manager;
12  
13  import com.jsql.model.injection.method.AbstractMethodInjection;
14  import com.jsql.model.injection.engine.model.Engine;
15  import com.jsql.util.LogLevelUtil;
16  import com.jsql.util.StringUtil;
17  import com.jsql.util.ThreadUtil;
18  import com.jsql.view.subscriber.SubscriberScan;
19  import com.jsql.view.swing.list.*;
20  import com.jsql.view.swing.manager.util.StateButton;
21  import com.jsql.view.swing.util.I18nViewUtil;
22  import com.jsql.view.swing.util.MediatorHelper;
23  import com.jsql.view.swing.util.UiUtil;
24  import org.apache.commons.lang3.StringUtils;
25  import org.apache.logging.log4j.LogManager;
26  import org.apache.logging.log4j.Logger;
27  import org.json.JSONArray;
28  import org.json.JSONException;
29  
30  import javax.swing.*;
31  import java.awt.*;
32  import java.awt.event.ActionEvent;
33  import java.awt.event.ActionListener;
34  import java.io.BufferedReader;
35  import java.io.IOException;
36  import java.io.InputStreamReader;
37  import java.nio.charset.StandardCharsets;
38  import java.util.List;
39  import java.util.Objects;
40  import java.util.Optional;
41  
42  /**
43   * Manager to display webpages frequently used as backoffice administration.
44   */
45  public class ManagerScan extends AbstractManagerList {
46      
47      private static final Logger LOGGER = LogManager.getRootLogger();
48  
49      public static final String NAME = "listManagerScan";
50  
51      /**
52       * Create admin page finder.
53       */
54      public ManagerScan() {
55          super("swing/list/scan-page.json");
56  
57          this.listPaths.setTransferHandler(null);
58          this.listPaths.setTransferHandler(new ListTransfertHandlerScan());
59          this.listPaths.setCellRenderer(new DefaultListCellRenderer() {
60              @Override
61              public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
62                  JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
63                  ItemList itemList = (ItemList) value;
64                  if (itemList.isVulnerable()) {
65                      label.setIcon(UiUtil.TICK_GREEN.getIcon());
66                  }
67                  return label;
68              }
69          });
70          
71          this.listPaths.setName(ManagerScan.NAME);
72  
73          JPanel lastLine = this.getLastLinePanel();
74          this.add(lastLine, BorderLayout.SOUTH);
75          
76          this.listPaths.addListSelectionListener(e -> {
77              if (this.listPaths.getSelectedValue() == null) {
78                  return;
79              }
80              
81              var beanInjection = ((ItemListScan) this.listPaths.getSelectedValue()).getBeanInjection();
82              MediatorHelper.panelAddressBar().getTextFieldAddress().setText(beanInjection.getUrl());
83              MediatorHelper.panelAddressBar().getTextFieldHeader().setText(beanInjection.getHeader());
84              MediatorHelper.panelAddressBar().getTextFieldRequest().setText(beanInjection.getRequest());
85              
86              String requestType = beanInjection.getRequestType();
87              if (requestType != null && !requestType.isEmpty()) {
88                  MediatorHelper.panelAddressBar().getAtomicRadioMethod().setText(requestType);
89              } else {
90                  MediatorHelper.panelAddressBar().getAtomicRadioMethod().setText(StringUtil.GET);
91              }
92              
93              AbstractMethodInjection method = beanInjection.getMethodInstance();
94              if (method == MediatorHelper.model().getMediatorMethod().getHeader()) {
95                  MediatorHelper.panelAddressBar().getAtomicRadioHeader().setSelected(true);
96              } else if (method == MediatorHelper.model().getMediatorMethod().getRequest()) {
97                  MediatorHelper.panelAddressBar().getAtomicRadioMethod().setSelected(true);
98              } else {
99                  MediatorHelper.panelAddressBar().getAtomicRadioRequest().setSelected(true);
100             }
101         });
102     }
103 
104     @Override
105     public void buildList(String nameFile) {
106         var jsonScan = new StringBuilder();
107         try (
108             var inputStream = UiUtil.class.getClassLoader().getResourceAsStream(nameFile);
109             var inputStreamReader = new InputStreamReader(Objects.requireNonNull(inputStream), StandardCharsets.UTF_8);
110             var reader = new BufferedReader(inputStreamReader)
111         ) {
112             String line;
113             while ((line = reader.readLine()) != null) {
114                 jsonScan.append(line);
115             }
116             var jsonArrayScan = new JSONArray(jsonScan.toString());
117             for (var i = 0 ; i < jsonArrayScan.length() ; i++) {
118                 this.itemsList.add(new ItemListScan(jsonArrayScan.getJSONObject(i)));
119             }
120             this.listPaths = new DnDListScan(this.itemsList);
121         } catch (JSONException | IOException e) {
122             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
123         }
124     }
125 
126     private JPanel getLastLinePanel() {
127         var lastLine = new JPanel();
128         lastLine.setOpaque(false);
129         lastLine.setLayout(new BoxLayout(lastLine, BoxLayout.X_AXIS));
130 
131         this.buildRunButton("SCAN_RUN_BUTTON_LABEL", "SCAN_RUN_BUTTON_TOOLTIP");
132         this.run.addActionListener(new ActionScan());
133 
134         lastLine.add(this.horizontalGlue);
135         lastLine.add(this.progressBar);
136         lastLine.add(this.run);
137         return lastLine;
138     }
139 
140     private class ActionScan implements ActionListener {
141         @Override
142         public void actionPerformed(ActionEvent e) {
143             if (ManagerScan.this.listPaths.getSelectedValuesList().isEmpty()) {
144                 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Select URL(s) to scan");
145                 return;
146             }
147             if (MediatorHelper.model().isInjectingWithoutScan()) {
148                 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Injection already ongoing, either wait or cancel the injection manually");
149                 return;
150             }
151             new Thread(() -> {
152                 if (ManagerScan.this.run.getState() == StateButton.STARTABLE) {
153                     ManagerScan.this.run.setText(I18nViewUtil.valueByKey("SCAN_RUN_BUTTON_STOP"));
154                     ManagerScan.this.run.setState(StateButton.STOPPABLE);
155                     ManagerScan.this.progressBar.setVisible(true);
156                     ManagerScan.this.horizontalGlue.setVisible(false);
157                     DefaultListModel<ItemList> listModel = (DefaultListModel<ItemList>) ManagerScan.this.listPaths.getModel();
158                     for (var i = 0 ; i < listModel.getSize() ; i++) {
159                         listModel.get(i).reset();
160                     }
161                     ManagerScan.this.scan(ManagerScan.this.listPaths.getSelectedValuesList());
162                 } else {
163                     MediatorHelper.model().getResourceAccess().setScanStopped(true);
164                     MediatorHelper.model().setIsStoppedByUser(true);
165                     ManagerScan.this.run.setEnabled(false);
166                     ManagerScan.this.run.setState(StateButton.STOPPING);
167                 }
168             }, "ThreadScan").start();
169         }
170     }
171     
172     /**
173      * Start fast scan of URLs in sequence and display result.
174      * Unplug any existing view and plug a console-like view in order to
175      * respond appropriately to GUI message with simple text result instead of
176      * build complex graphical components during the multi website injections.
177      * At the end of the scan it plugs again the default view.
178      * @param urlsItemList contains a list of String URL
179      */
180     public void scan(List<ItemList> urlsItemList) {
181         MediatorHelper.frame().resetInterface();  // Erase everything in the view from a previous injection
182         
183         // wait for ending of ongoing interaction between two injections
184         ThreadUtil.sleep(500);
185 
186         // Display result only in console
187         MediatorHelper.frame().getSubscriberView().getSubscription().cancel();
188         var subscriberScan = new SubscriberScan();
189         MediatorHelper.model().subscribe(subscriberScan);
190         while (subscriberScan.getSubscription() == null) {  // defensive concurrency check
191             ThreadUtil.sleep(500);
192         }
193 
194         MediatorHelper.model().setIsScanning(true);
195         MediatorHelper.model().getResourceAccess().setScanStopped(false);
196         
197         for (ItemList urlItemList: urlsItemList) {
198             if (  // detect interrupt by user to end intermediate scan
199                 MediatorHelper.model().isStoppedByUser()
200                 || MediatorHelper.model().getResourceAccess().isScanStopped()
201             ) {
202                 break;
203             }
204 
205             var urlItemListScan = (ItemListScan) urlItemList;
206             LOGGER.log(LogLevelUtil.CONSOLE_INFORM, "Scanning {}", urlItemListScan.getBeanInjection().getUrl());
207 
208             Optional<Engine> optionalEngine = MediatorHelper.model().getMediatorEngine().getEngines()
209                 .stream()
210                 .filter(engine -> engine.toString().equalsIgnoreCase(urlItemListScan.getBeanInjection().getEngine()))
211                 .findAny();
212 
213             MediatorHelper.model().getMediatorEngine().setEngineByUser(
214                 optionalEngine.orElse(MediatorHelper.model().getMediatorEngine().getAuto())
215             );
216             MediatorHelper.model().getMediatorUtils().parameterUtil().controlInput(
217                 StringUtils.EMPTY,
218                 urlItemListScan.getBeanInjection().getUrl(),
219                 urlItemListScan.getBeanInjection().getRequest(),
220                 urlItemListScan.getBeanInjection().getHeader(),
221                 urlItemListScan.getBeanInjection().getMethodInstance(),
222                 urlItemListScan.getBeanInjection().getRequestType(),
223                 true
224             );
225 
226             ThreadUtil.sleep(500);
227         }
228         
229         // Get back the default view
230         // Fix #96366: NullPointerException on cancel()
231         if (subscriberScan.getSubscription() != null) {  // defensive concurrency check
232             subscriberScan.getSubscription().cancel();
233         }
234         // Defensive concurrency check: 'IllegalStateException: Duplicate subscribe' when quickly start/stop scanning
235         if (!MediatorHelper.model().isSubscribed(MediatorHelper.frame().getSubscriberView())) {
236             MediatorHelper.model().subscribe(MediatorHelper.frame().getSubscriberView());
237         }
238 
239         MediatorHelper.model().setIsScanning(false);
240         MediatorHelper.model().setIsStoppedByUser(false);
241         MediatorHelper.model().getResourceAccess().setScanStopped(false);
242 
243         this.endProcess();
244     }
245 }