Skip to content

Commit 16c162b

Browse files
committed
Allow to listen for script output
Add a listener for script output, which provides the script and the output being written. Part of zaproxy#5113 - Allow to differentiate scripts' output
1 parent 937c6e6 commit 16c162b

File tree

2 files changed

+130
-6
lines changed

2 files changed

+130
-6
lines changed

src/org/zaproxy/zap/extension/script/ExtensionScript.java

+90-6
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
import java.util.HashMap;
4141
import java.util.List;
4242
import java.util.Map;
43+
import java.util.Objects;
44+
import java.util.concurrent.CopyOnWriteArrayList;
4345

4446
import javax.script.Invocable;
4547
import javax.script.ScriptContext;
@@ -152,6 +154,15 @@ public class ExtensionScript extends ExtensionAdaptor implements CommandLineList
152154
*/
153155
private List<File> trackedDirs = Collections.synchronizedList(new ArrayList<>());
154156

157+
/**
158+
* The script output listeners added to the extension.
159+
*
160+
* @see #addScriptOutputListener(ScriptOutputListener)
161+
* @see #getWriters(ScriptWrapper)
162+
* @see #removeScriptOutputListener(ScriptOutputListener)
163+
*/
164+
private List<ScriptOutputListener> outputListeners = new CopyOnWriteArrayList<>();
165+
155166
public ExtensionScript() {
156167
super(NAME);
157168
this.setOrder(EXTENSION_ORDER);
@@ -1191,17 +1202,16 @@ public List<ScriptWrapper> getTemplates(ScriptType type) {
11911202
* ever script. It also supports script specific writers.
11921203
*/
11931204
private Writer getWriters(ScriptWrapper script) {
1205+
Writer delegatee = this.writers;
11941206
Writer writer = script.getWriter();
1195-
if (writer == null) {
1196-
// No script specific writer, just return the std one
1197-
return this.writers;
1198-
} else {
1199-
// Return the script specific writer in addition to the std one
1207+
if (writer != null) {
1208+
// Use the script specific writer in addition to the std one
12001209
MultipleWriters scriptWriters = new MultipleWriters();
12011210
scriptWriters.addWriter(writer);
12021211
scriptWriters.addWriter(writers);
1203-
return scriptWriters;
1212+
delegatee = scriptWriters;
12041213
}
1214+
return new ScriptWriter(script, delegatee, outputListeners);
12051215
}
12061216

12071217
/**
@@ -1609,14 +1619,53 @@ public void removeListener(ScriptEventListener listener) {
16091619
this.listeners.remove(listener);
16101620
}
16111621

1622+
/**
1623+
* Adds the given writer.
1624+
* <p>
1625+
* It will be written to each time a script writes some output.
1626+
*
1627+
* @param writer the writer to add.
1628+
* @see #removeWriter(Writer)
1629+
* @see #addScriptOutputListener(ScriptOutputListener)
1630+
*/
16121631
public void addWriter(Writer writer) {
16131632
this.writers.addWriter(writer);
16141633
}
16151634

1635+
/**
1636+
* Removes the given writer.
1637+
*
1638+
* @param writer the writer to remove.
1639+
* @see #addWriter(Writer)
1640+
*/
16161641
public void removeWriter(Writer writer) {
16171642
this.writers.removeWriter(writer);
16181643
}
16191644

1645+
/**
1646+
* Adds the given script output listener.
1647+
*
1648+
* @param listener the listener to add.
1649+
* @since TODO add version
1650+
* @throws NullPointerException if the given listener is {@code null}.
1651+
* @see #removeScriptOutputListener(ScriptOutputListener)
1652+
*/
1653+
public void addScriptOutputListener(ScriptOutputListener listener) {
1654+
outputListeners.add(Objects.requireNonNull(listener, "The parameter listener must not be null."));
1655+
}
1656+
1657+
/**
1658+
* Removes the given script output listener.
1659+
*
1660+
* @param listener the listener to remove.
1661+
* @since TODO add version
1662+
* @throws NullPointerException if the given listener is {@code null}.
1663+
* @see #addScriptOutputListener(ScriptOutputListener)
1664+
*/
1665+
public void removeScriptOutputListener(ScriptOutputListener listener) {
1666+
outputListeners.remove(Objects.requireNonNull(listener, "The parameter listener must not be null."));
1667+
}
1668+
16201669
public ScriptUI getScriptUI() {
16211670
return scriptUI;
16221671
}
@@ -1852,4 +1901,39 @@ public void sessionModeChanged(Mode mode) {
18521901
}
18531902

18541903
}
1904+
1905+
/**
1906+
* A {@code Writer} that notifies {@link ScriptOutputListener}s when writing.
1907+
*/
1908+
private static class ScriptWriter extends Writer {
1909+
1910+
private final ScriptWrapper script;
1911+
private final Writer delegatee;
1912+
private final List<ScriptOutputListener> outputListeners;
1913+
1914+
public ScriptWriter(ScriptWrapper script, Writer delegatee, List<ScriptOutputListener> outputListeners) {
1915+
this.script = Objects.requireNonNull(script);
1916+
this.delegatee = Objects.requireNonNull(delegatee);
1917+
this.outputListeners = Objects.requireNonNull(outputListeners);
1918+
}
1919+
1920+
@Override
1921+
public void write(char[] cbuf, int off, int len) throws IOException {
1922+
delegatee.write(cbuf, off, len);
1923+
if (!outputListeners.isEmpty()) {
1924+
String output = new String(cbuf, off, len);
1925+
outputListeners.forEach(e -> e.output(script, output));
1926+
}
1927+
}
1928+
1929+
@Override
1930+
public void flush() throws IOException {
1931+
delegatee.flush();
1932+
}
1933+
1934+
@Override
1935+
public void close() throws IOException {
1936+
delegatee.close();
1937+
}
1938+
}
18551939
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Zed Attack Proxy (ZAP) and its related class files.
3+
*
4+
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
5+
*
6+
* Copyright 2018 The ZAP Development Team
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*/
20+
package org.zaproxy.zap.extension.script;
21+
22+
/**
23+
* A listener for scripts' output.
24+
*
25+
* @since TODO add version
26+
* @see ExtensionScript#addScriptOutputListener(ScriptOutputListener)
27+
*/
28+
@FunctionalInterface
29+
public interface ScriptOutputListener {
30+
31+
/**
32+
* Called each time a script writes some output.
33+
* <p>
34+
* Can be called concurrently.
35+
*
36+
* @param script the source of the output.
37+
* @param output the new output.
38+
*/
39+
void output(ScriptWrapper script, String output);
40+
}

0 commit comments

Comments
 (0)