1 /*
2  *
3  * Paros and its related class files.
4  *
5  * Paros is an HTTP/HTTPS proxy for assessing web application security.
6  * Copyright (C) 2003-2004 Chinotec Technologies Company
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the Clarified Artistic License
10  * as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * Clarified Artistic License for more details.
16  *
17  * You should have received a copy of the Clarified Artistic License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20  */
21 // ZAP: 2012/12/19 Code Cleanup: Moved array brackets from variable name to type
22 // ZAP: 2012/12/20 Added listener setter for persistentConnectionListenerList.
23 // ZAP: 2013/01/16 Issue 453: Dynamic loading and unloading of add-ons
24 // ZAP: 2013/04/14 Issue 608: Rename the method ExtensionHook.addSiteMapListner to
25 // addSiteMapListener
26 // ZAP: 2013/05/02 Re-arranged all modifiers into Java coding standard order
27 // ZAP: 2014/03/23 Issue 1022: Proxy - Allow to override a proxied message
28 // ZAP: 2014/10/25 Issue 1062: Added scannerhook to be added by extensions.
29 // ZAP: 2016/04/08 Allow to add ContextDataFactory
30 // ZAP: 2016/05/30 Allow to add AddOnInstallationStatusListener
31 // ZAP: 2016/05/30 Issue 2494: ZAP Proxy is not showing the HTTP CONNECT Request in history tab
32 // ZAP: 2016/08/18 Allow to add ApiImplementor
33 // ZAP: 2017/07/25 Allow to add HttpSenderListener.
34 // ZAP: 2017/11/23 Add an add method for OverrideMessageProxyListener.
35 // ZAP: 2019/06/01 Normalise line endings.
36 // ZAP: 2019/06/05 Normalise format/style.
37 // ZAP: 2020/08/27 Added support for plugable variants
38 package org.parosproxy.paros.extension;
39 
40 import java.util.ArrayList;
41 import java.util.Collections;
42 import java.util.List;
43 import java.util.Vector;
44 import org.parosproxy.paros.common.AbstractParam;
45 import org.parosproxy.paros.core.proxy.ConnectRequestProxyListener;
46 import org.parosproxy.paros.core.proxy.OverrideMessageProxyListener;
47 import org.parosproxy.paros.core.proxy.ProxyListener;
48 import org.parosproxy.paros.core.scanner.ScannerHook;
49 import org.parosproxy.paros.core.scanner.Variant;
50 import org.parosproxy.paros.model.Model;
51 import org.zaproxy.zap.PersistentConnectionListener;
52 import org.zaproxy.zap.extension.AddOnInstallationStatusListener;
53 import org.zaproxy.zap.extension.AddonFilesChangedListener;
54 import org.zaproxy.zap.extension.api.ApiImplementor;
55 import org.zaproxy.zap.model.ContextDataFactory;
56 import org.zaproxy.zap.network.HttpSenderListener;
57 import org.zaproxy.zap.view.SiteMapListener;
58 
59 public class ExtensionHook {
60 
61     /**
62      * The hook for menus.
63      *
64      * <p>Lazily initialised.
65      *
66      * @see #getHookMenu()
67      * @see #getHookMenuNoInit()
68      */
69     private ExtensionHookMenu hookMenu;
70 
71     /**
72      * The hook for view components.
73      *
74      * <p>Lazily initialised.
75      *
76      * @see #getHookView()
77      * @see #getHookViewNoInit()
78      */
79     private ExtensionHookView hookView;
80 
81     private Model model = null;
82     private Vector<OptionsChangedListener> optionsListenerList = new Vector<>();
83 
84     private Vector<ProxyListener> proxyListenerList = new Vector<>();
85 
86     /**
87      * The {@link OverrideMessageProxyListener}s added to this extension hook.
88      *
89      * <p>Lazily initialised.
90      *
91      * @see #addOverrideMessageProxyListener(OverrideMessageProxyListener)
92      * @see #getOverrideMessageProxyListenerList()
93      */
94     private List<OverrideMessageProxyListener> overrideMessageProxyListenersList;
95 
96     /**
97      * The {@link ConnectRequestProxyListener}s added to this extension hook.
98      *
99      * <p>Lazily initialised.
100      *
101      * @see #addConnectionRequestProxyListener(ConnectRequestProxyListener)
102      * @see #getConnectRequestProxyListeners()
103      */
104     private List<ConnectRequestProxyListener> connectRequestProxyListeners;
105 
106     private Vector<SessionChangedListener> sessionListenerList = new Vector<>();
107     private Vector<AbstractParam> optionsParamSetList = new Vector<>();
108     // ZAP: Added support for site map listeners
109     private Vector<SiteMapListener> siteMapListenerList = new Vector<>();
110     // ZAP: Added support for Scanner Hooks
111     private Vector<ScannerHook> scannerHookList = new Vector<>();
112     private Vector<PersistentConnectionListener> persistentConnectionListenerList = new Vector<>();
113     private List<AddonFilesChangedListener> addonFilesChangedListenerList = new ArrayList<>();
114 
115     /**
116      * The {@link ContextDataFactory}s added to this extension hook.
117      *
118      * <p>Lazily initialised.
119      *
120      * @see #addContextDataFactory(ContextDataFactory)
121      * @see #getContextDataFactories()
122      */
123     private List<ContextDataFactory> contextDataFactories;
124 
125     /**
126      * The {@link AddOnInstallationStatusListener}s added to this extension hook.
127      *
128      * <p>Lazily initialised.
129      *
130      * @see #addAddOnInstallationStatusListener(AddOnInstallationStatusListener)
131      * @see #getAddOnInstallationStatusListeners()
132      */
133     private List<AddOnInstallationStatusListener> addOnInstallationStatusListeners;
134 
135     /**
136      * The {@link ApiImplementor}s added to this extension hook.
137      *
138      * <p>Lazily initialised.
139      *
140      * @see #addApiImplementor(ApiImplementor)
141      * @see #getApiImplementors()
142      */
143     private List<ApiImplementor> apiImplementors;
144 
145     /**
146      * The {@link HttpSenderListener}s added to this extension hook.
147      *
148      * <p>Lazily initialised.
149      *
150      * @see #addHttpSenderListener(HttpSenderListener)
151      * @see #getHttpSenderListeners()
152      */
153     private List<HttpSenderListener> httpSenderListeners;
154 
155     /**
156      * The {@link Variant}s added to this extension hook.
157      *
158      * <p>Lazily initialised.
159      *
160      * @see #addVariant(Variant)
161      * @see #getVariants()
162      */
163     private List<Class<? extends Variant>> variants;
164 
165     private ViewDelegate view = null;
166     private CommandLineArgument[] arg = new CommandLineArgument[0];
167 
ExtensionHook(Model model, ViewDelegate view)168     public ExtensionHook(Model model, ViewDelegate view) {
169         this.view = view;
170         this.model = model;
171     }
172 
addOptionsChangedListener(OptionsChangedListener listener)173     public void addOptionsChangedListener(OptionsChangedListener listener) {
174         optionsListenerList.add(listener);
175     }
176 
addOptionsParamSet(AbstractParam paramSet)177     public void addOptionsParamSet(AbstractParam paramSet) {
178         optionsParamSetList.add(paramSet);
179     }
180 
addProxyListener(ProxyListener listener)181     public void addProxyListener(ProxyListener listener) {
182         proxyListenerList.add(listener);
183     }
184 
185     /**
186      * Adds the given {@link ConnectRequestProxyListener} to the extension hook, to be later
187      * notified of CONNECT requests received by the local proxy.
188      *
189      * <p>By default, the {@code ConnectRequestProxyListener}s added are removed from the local
190      * proxy when the extension is unloaded.
191      *
192      * @param listener the {@code ConnectRequestProxyListener} that will be added and then notified
193      * @throws IllegalArgumentException if the given {@code listener} is {@code null}.
194      * @since 2.5.0
195      */
addConnectionRequestProxyListener(ConnectRequestProxyListener listener)196     public void addConnectionRequestProxyListener(ConnectRequestProxyListener listener) {
197         if (listener == null) {
198             throw new IllegalArgumentException("Parameter listener must not be null.");
199         }
200 
201         if (connectRequestProxyListeners == null) {
202             connectRequestProxyListeners = new ArrayList<>();
203         }
204         connectRequestProxyListeners.add(listener);
205     }
206 
207     /**
208      * Gets the {@link ConnectRequestProxyListener}s added to this hook.
209      *
210      * @return an unmodifiable {@code List} containing the added {@code
211      *     ConnectRequestProxyListener}s, never {@code null}.
212      * @since 2.5.0
213      */
getConnectRequestProxyListeners()214     List<ConnectRequestProxyListener> getConnectRequestProxyListeners() {
215         if (connectRequestProxyListeners == null) {
216             return Collections.emptyList();
217         }
218         return Collections.unmodifiableList(connectRequestProxyListeners);
219     }
220 
addSessionListener(SessionChangedListener listener)221     public void addSessionListener(SessionChangedListener listener) {
222         sessionListenerList.add(listener);
223     }
224 
225     /**
226      * @deprecated Replaced by the method {@link #addSiteMapListener(SiteMapListener)}. It will be
227      *     removed in a future release.
228      */
229     @Deprecated
addSiteMapListner(SiteMapListener listener)230     public void addSiteMapListner(SiteMapListener listener) {
231         siteMapListenerList.add(listener);
232     }
233 
addSiteMapListener(SiteMapListener listener)234     public void addSiteMapListener(SiteMapListener listener) {
235         siteMapListenerList.add(listener);
236     }
237 
238     // ZAP: add a scanner hook
addScannerHook(ScannerHook hook)239     public void addScannerHook(ScannerHook hook) {
240         scannerHookList.add(hook);
241     }
242 
addPersistentConnectionListener(PersistentConnectionListener listener)243     public void addPersistentConnectionListener(PersistentConnectionListener listener) {
244         persistentConnectionListenerList.add(listener);
245     }
246 
addCommandLine(CommandLineArgument[] arg)247     public void addCommandLine(CommandLineArgument[] arg) {
248         this.arg = arg;
249     }
250 
addAddonFilesChangedListener(AddonFilesChangedListener listener)251     public void addAddonFilesChangedListener(AddonFilesChangedListener listener) {
252         addonFilesChangedListenerList.add(listener);
253     }
254 
255     /**
256      * Adds the given {@code listener} to the extension hook, to be later notified of changes in the
257      * installation status of the add-ons.
258      *
259      * @param listener the listener that will be added and then notified
260      * @throws IllegalArgumentException if the given {@code listener} is {@code null}.
261      * @since 2.5.0
262      */
addAddOnInstallationStatusListener(AddOnInstallationStatusListener listener)263     public void addAddOnInstallationStatusListener(AddOnInstallationStatusListener listener) {
264         if (listener == null) {
265             throw new IllegalArgumentException("Parameter listener must not be null.");
266         }
267 
268         if (addOnInstallationStatusListeners == null) {
269             addOnInstallationStatusListeners = new ArrayList<>();
270         }
271         addOnInstallationStatusListeners.add(listener);
272     }
273 
274     /**
275      * Gets the {@link AddOnInstallationStatusListener}s added to this hook.
276      *
277      * @return an unmodifiable {@code List} containing the added {@code
278      *     AddOnInstallationStatusListener}s, never {@code null}.
279      * @since 2.5.0
280      */
getAddOnInstallationStatusListeners()281     List<AddOnInstallationStatusListener> getAddOnInstallationStatusListeners() {
282         if (addOnInstallationStatusListeners == null) {
283             return Collections.emptyList();
284         }
285         return Collections.unmodifiableList(addOnInstallationStatusListeners);
286     }
287 
288     /**
289      * Gets the hook for menus.
290      *
291      * @return the hook for menus, never {@code null}.
292      */
getHookMenu()293     public ExtensionHookMenu getHookMenu() {
294         if (hookMenu == null) {
295             hookMenu = new ExtensionHookMenu();
296         }
297         return hookMenu;
298     }
299 
300     /**
301      * Gets the hook for menus, without initialising it.
302      *
303      * @return the hook for menus, might be {@code null}.
304      * @since 2.8.0
305      */
getHookMenuNoInit()306     ExtensionHookMenu getHookMenuNoInit() {
307         return hookMenu;
308     }
309 
310     /**
311      * Gets the hook for view components.
312      *
313      * @return the hook for view components, never {@code null}.
314      */
getHookView()315     public ExtensionHookView getHookView() {
316         if (hookView == null) {
317             hookView = new ExtensionHookView();
318         }
319         return hookView;
320     }
321 
322     /**
323      * Gets the hook for view components, without initialising it.
324      *
325      * @return the hook for view components, might be {@code null}.
326      * @since 2.8.0
327      */
getHookViewNoInit()328     ExtensionHookView getHookViewNoInit() {
329         return hookView;
330     }
331 
332     /** @return Returns the model. */
getModel()333     public Model getModel() {
334         return model;
335     }
336 
337     /** @return Returns the optionsListenerList. */
getOptionsChangedListenerList()338     public Vector<OptionsChangedListener> getOptionsChangedListenerList() {
339         return optionsListenerList;
340     }
341 
getOptionsParamSetList()342     public Vector<AbstractParam> getOptionsParamSetList() {
343         return optionsParamSetList;
344     }
345 
346     /** @return Returns the proxyListenerList. */
getProxyListenerList()347     public Vector<ProxyListener> getProxyListenerList() {
348         return proxyListenerList;
349     }
350 
351     /** @return Returns the sessionListenerList. */
getSessionListenerList()352     public Vector<SessionChangedListener> getSessionListenerList() {
353         return sessionListenerList;
354     }
355 
getSiteMapListenerList()356     public Vector<SiteMapListener> getSiteMapListenerList() {
357         return siteMapListenerList;
358     }
359 
360     // ZAP: get all scannerhooks (used by extensionloader and the scanner)
getScannerHookList()361     public Vector<ScannerHook> getScannerHookList() {
362         return scannerHookList;
363     }
364 
getPersistentConnectionListener()365     public Vector<PersistentConnectionListener> getPersistentConnectionListener() {
366         return persistentConnectionListenerList;
367     }
368 
369     /** @return Returns the view. */
getView()370     public ViewDelegate getView() {
371         return view;
372     }
373 
getCommandLineArgument()374     public CommandLineArgument[] getCommandLineArgument() {
375         return arg;
376     }
377 
getAddonFilesChangedListener()378     public List<AddonFilesChangedListener> getAddonFilesChangedListener() {
379         return addonFilesChangedListenerList;
380     }
381 
382     /**
383      * Adds the given {@code overrideMessageProxyListener} to the extension hook, to be later added
384      * to the {@link org.parosproxy.paros.control.Proxy Proxy}.
385      *
386      * <p>By default, the {@code OverrideMessageProxyListener}s added to this extension hook are
387      * removed from the {@code Proxy} when the extension is unloaded.
388      *
389      * @param overrideMessageProxyListener the {@code OverrideMessageProxyListener} that will be
390      *     added to the {@code Proxy}
391      * @throws IllegalArgumentException if the given {@code overrideMessageProxyListener} is {@code
392      *     null}.
393      * @since 2.7.0
394      */
addOverrideMessageProxyListener( OverrideMessageProxyListener overrideMessageProxyListener)395     public void addOverrideMessageProxyListener(
396             OverrideMessageProxyListener overrideMessageProxyListener) {
397         getOverrideMessageProxyListenerList().add(overrideMessageProxyListener);
398     }
399 
400     /**
401      * Gets the {@link OverrideMessageProxyListener}s added to this hook.
402      *
403      * <p>While it's possible to add the listener with this method it's not recommended, use {@link
404      * #addOverrideMessageProxyListener(OverrideMessageProxyListener)} whenever possible. The
405      * accessibility of this method might change in a future version (to package access).
406      *
407      * @return a {@code List} containing the added {@code OverrideMessageProxyListener}s, never
408      *     {@code null}.
409      * @since 2.3.0
410      */
getOverrideMessageProxyListenerList()411     public List<OverrideMessageProxyListener> getOverrideMessageProxyListenerList() {
412         if (overrideMessageProxyListenersList == null) {
413             overrideMessageProxyListenersList = new ArrayList<>();
414         }
415         return overrideMessageProxyListenersList;
416     }
417 
418     /**
419      * Adds the given {@link ContextDataFactory} to the extension hook, to be later added to the
420      * {@link Model}.
421      *
422      * <p>By default, the {@code ContextDataFactory}s added are removed from the {@code Model} when
423      * the extension is unloaded.
424      *
425      * @param contextDataFactory the {@code ContextDataFactory} that will be added to the {@code
426      *     Model}
427      * @since 2.5.0
428      */
addContextDataFactory(ContextDataFactory contextDataFactory)429     public void addContextDataFactory(ContextDataFactory contextDataFactory) {
430         if (contextDataFactories == null) {
431             contextDataFactories = new ArrayList<>();
432         }
433         contextDataFactories.add(contextDataFactory);
434     }
435 
436     /**
437      * Gets the {@link ContextDataFactory}s added to this hook.
438      *
439      * @return an unmodifiable {@code List} containing the added {@code ContextDataFactory}s, never
440      *     {@code null}.
441      * @since 2.5.0
442      */
getContextDataFactories()443     List<ContextDataFactory> getContextDataFactories() {
444         if (contextDataFactories == null) {
445             return Collections.emptyList();
446         }
447         return Collections.unmodifiableList(contextDataFactories);
448     }
449 
450     /**
451      * Adds the given {@code apiImplementor} to the extension hook, to be later added to the {@link
452      * org.zaproxy.zap.extension.api.API API}.
453      *
454      * <p>By default, the {@code ApiImplementor}s added to this extension hook are removed from the
455      * {@code API} when the extension is unloaded.
456      *
457      * @param apiImplementor the ApiImplementor that will be added to the ZAP API
458      * @throws IllegalArgumentException if the given {@code apiImplementor} is {@code null}.
459      * @since 2.6.0
460      */
addApiImplementor(ApiImplementor apiImplementor)461     public void addApiImplementor(ApiImplementor apiImplementor) {
462         if (apiImplementor == null) {
463             throw new IllegalArgumentException("Parameter apiImplementor must not be null.");
464         }
465 
466         if (apiImplementors == null) {
467             apiImplementors = new ArrayList<>();
468         }
469         apiImplementors.add(apiImplementor);
470     }
471 
472     /**
473      * Gets the {@link ApiImplementor}s added to this hook.
474      *
475      * @return an unmodifiable {@code List} containing the added {@code ApiImplementor}s, never
476      *     {@code null}.
477      * @since 2.6.0
478      */
getApiImplementors()479     List<ApiImplementor> getApiImplementors() {
480         if (apiImplementors == null) {
481             return Collections.emptyList();
482         }
483         return Collections.unmodifiableList(apiImplementors);
484     }
485 
486     /**
487      * Adds the given {@code httpSenderListener} to the extension hook, to be later added to the
488      * {@link org.parosproxy.paros.network.HttpSender HttpSender}.
489      *
490      * <p>By default, the {@code HttpSenderListener}s added to this extension hook are removed from
491      * the {@code HttpSender} when the extension is unloaded.
492      *
493      * @param httpSenderListener the HttpSenderListener that will be added to the {@code HttpSender}
494      * @throws IllegalArgumentException if the given {@code httpSenderListener} is {@code null}.
495      * @since 2.7.0
496      */
addHttpSenderListener(HttpSenderListener httpSenderListener)497     public void addHttpSenderListener(HttpSenderListener httpSenderListener) {
498         if (httpSenderListener == null) {
499             throw new IllegalArgumentException("Parameter httpSenderListener must not be null.");
500         }
501 
502         if (httpSenderListeners == null) {
503             httpSenderListeners = new ArrayList<>();
504         }
505         httpSenderListeners.add(httpSenderListener);
506     }
507 
508     /**
509      * Gets the {@link HttpSenderListener}s added to this hook.
510      *
511      * @return an unmodifiable {@code List} containing the added {@code HttpSenderListener}s, never
512      *     {@code null}.
513      * @since 2.7.0
514      */
getHttpSenderListeners()515     List<HttpSenderListener> getHttpSenderListeners() {
516         if (httpSenderListeners == null) {
517             return Collections.emptyList();
518         }
519         return Collections.unmodifiableList(httpSenderListeners);
520     }
521 
522     /**
523      * Adds the given {@code variant} to the extension hook, to be later added to the {@link
524      * org.parosproxy.paros.model.Model Model}.
525      *
526      * <p>By default, the {@code Variant}s added to this extension hook are removed from the {@code
527      * Model} when the extension is unloaded.
528      *
529      * @param variant the Variant that will be added to the {@code Model}
530      * @throws IllegalArgumentException if the given {@code variant} is {@code null}.
531      * @since 2.10.0
532      */
addVariant(Class<? extends Variant> variant)533     public void addVariant(Class<? extends Variant> variant) {
534         if (variant == null) {
535             throw new IllegalArgumentException("Parameter variant must not be null.");
536         }
537 
538         if (variants == null) {
539             variants = new ArrayList<>();
540         }
541         variants.add(variant);
542     }
543 
544     /**
545      * Gets the {@link Variant}s added to this hook.
546      *
547      * @return an unmodifiable {@code List} containing the added {@code Variant}s, never {@code
548      *     null}.
549      * @since 2.10.0
550      */
getVariants()551     List<Class<? extends Variant>> getVariants() {
552         if (variants == null) {
553             return Collections.emptyList();
554         }
555         return Collections.unmodifiableList(variants);
556     }
557 }
558