├── .github └── workflows │ └── maven-publish.yml ├── .gitignore ├── README.MD ├── doc ├── request1.png └── sample1.png ├── pom.xml └── src └── main ├── java ├── burp │ └── BurpExtender.java └── fr │ └── safepic │ └── burp │ └── script │ ├── JSonParsingTest.java │ ├── LogCallback.java │ ├── PreferenceClass.java │ ├── ScriptModifier.java │ ├── TestUI.java │ ├── js │ ├── AbstractRequestResponseUtil.java │ ├── HttpServiceObj.java │ ├── IssueObj.java │ ├── JavascriptContext.java │ ├── RequestRWUtil.java │ └── ResponseRWUtil.java │ └── ui │ ├── component │ ├── ClickableTabIcon.java │ ├── CloseTabIcon.java │ ├── CloseableTabbedPane.java │ ├── FSJTextArea.java │ ├── FSRTextArea.java │ ├── MyTextArea.java │ └── NewTabIcon.java │ ├── model │ ├── ScriptRef.java │ └── ScriptTableModel.java │ └── panel │ ├── ScriptPanel.java │ ├── ScriptTablePanel.java │ └── TabbedPanel.java └── resources └── sample-menu-item ├── Change IP Port ├── Http.txt └── Https.txt ├── Common Action ├── Prefix URL.txt ├── Set User-Agent.txt └── Set-Method.txt ├── Issues - Burp Pro only ├── Add confidence certain.txt ├── Add confidence firm.txt ├── Add confidence tentative.txt ├── Add severity high.txt ├── Add severity information.txt ├── Add severity medium .txt ├── Add severitylow.txt ├── Add with issue detail.txt └── Add with remediation detail.txt ├── Log ├── Error.txt ├── Info.txt └── Verbose.txt ├── Macro └── Access Macro Json.txt ├── Mark ├── Add Comment.txt └── Highlight in red.txt ├── Request Header ├── Add header.txt ├── Add or replace.txt ├── Remove by name.txt ├── Replace existing header.txt ├── Retrieve all header by name.txt └── Retrieve first header by name.txt ├── RequestBody ├── Replace Json value.txt ├── Replace-Request-By-File-Content.txt ├── retrieve body.txt └── set body.txt ├── Response Header ├── Add header.txt ├── Add or replace.txt ├── Remove by name.txt ├── Replace existing header.txt ├── Retrieve all header by name.txt └── Retrieve first header by name.txt ├── ResponseBody ├── retrieve body.txt └── set body.txt └── Test Value ├── Method.txt ├── Request header value.txt ├── Response header value.txt └── Url value.txt /.github/workflows/maven-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a package using Maven and then publish it to GitHub packages when a release is created 2 | # For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#apache-maven-with-a-settings-path 3 | 4 | name: Maven Package 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | permissions: 15 | contents: read 16 | packages: write 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Set up JDK 11 21 | uses: actions/setup-java@v2 22 | with: 23 | java-version: '11' 24 | distribution: 'adopt' 25 | server-id: github # Value of the distributionManagement/repository/id field of the pom.xml 26 | settings-path: ${{ github.workspace }} # location for the settings.xml file 27 | 28 | - name: Build with Maven 29 | run: mvn -B package --file pom.xml 30 | 31 | - name: Publish to GitHub Packages Apache Maven 32 | run: mvn deploy -s $GITHUB_WORKSPACE/settings.xml 33 | env: 34 | GITHUB_TOKEN: ${{ github.token }} 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/** 2 | target/** 3 | *.iml 4 | DEBUG.txt -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # Burp Scripting Extension 2 | 3 | This extension add the ability to Burp Suite to inspect, modify requests and reponses with Javascript. 4 | Useful if you want to replace header or body depending of other Headers. 5 | 6 | Scripts are dynamically loaded, you can use BurpExtension API, or helper functions. 7 | 8 | 9 | ## Scripting tab 10 | ![Add Request Header](doc/request1.png) 11 | 12 | 13 | ## Contextual code sample 14 | 15 | Right clic in scripting text area in order to obtain code samples : 16 | 17 | ![Add Request Header](doc/sample1.png) -------------------------------------------------------------------------------- /doc/request1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mprunet/burp-scripting/193fea1608150440ce8e8fee34a23fca80b1bfe9/doc/request1.png -------------------------------------------------------------------------------- /doc/sample1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mprunet/burp-scripting/193fea1608150440ce8e8fee34a23fca80b1bfe9/doc/sample1.png -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | fr.safepic.burp 4 | BurpScripting 5 | 0.1-SNAPSHOT 6 | 7 | 11 8 | 11 9 | UTF-8 10 | 11 | 12 | 13 | 14 | org.apache.maven.plugins 15 | maven-shade-plugin 16 | 1.4 17 | 18 | 19 | package 20 | 21 | shade 22 | 23 | 24 | 25 | 26 | net.portswigger.burp.extender:burp-extender-api 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | com.fifesoft 38 | rsyntaxtextarea 39 | 2.6.1 40 | 41 | 46 | 47 | net.portswigger.burp.extender 48 | burp-extender-api 49 | 2.1 50 | 51 | 52 | 53 | org.mozilla 54 | rhino 55 | 1.7.13 56 | 57 | 58 | -------------------------------------------------------------------------------- /src/main/java/burp/BurpExtender.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import fr.safepic.burp.script.ScriptModifier; 4 | import fr.safepic.burp.script.ui.panel.TabbedPanel; 5 | 6 | import javax.swing.*; 7 | import java.awt.*; 8 | 9 | public class BurpExtender implements IBurpExtender { 10 | @Override 11 | public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) { 12 | SwingUtilities.invokeLater(() -> { 13 | TabbedPanel panel = new TabbedPanel(); 14 | callbacks.setExtensionName("ScriptingExtension"); 15 | callbacks.registerExtensionStateListener(panel::unload); 16 | ScriptModifier modifier = new ScriptModifier(callbacks, panel.getScriptListPanel()); 17 | callbacks.registerHttpListener(modifier); 18 | callbacks.registerProxyListener(modifier); 19 | callbacks.registerSessionHandlingAction(modifier); 20 | callbacks.customizeUiComponent(panel.getScriptListPanel()); 21 | callbacks.addSuiteTab(new ITab() { 22 | @Override 23 | public String getTabCaption() { 24 | return "Scripting"; 25 | } 26 | 27 | @Override 28 | public Component getUiComponent() { 29 | return panel; 30 | } 31 | }); 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/fr/safepic/burp/script/JSonParsingTest.java: -------------------------------------------------------------------------------- 1 | package fr.safepic.burp.script; 2 | 3 | import org.mozilla.javascript.Context; 4 | import org.mozilla.javascript.NativeJSON; 5 | import org.mozilla.javascript.Scriptable; 6 | import org.mozilla.javascript.ScriptableObject; 7 | import org.mozilla.javascript.json.JsonParser; 8 | 9 | public class JSonParsingTest { 10 | public static void main(String[] args) { 11 | Context cx = Context.enter(); 12 | try { 13 | 14 | Scriptable scope = cx.initStandardObjects(); 15 | // Pass the Stock Java object to the JavaScript context 16 | 17 | JsonParser jsonParser = new JsonParser(cx, scope); 18 | Object json = jsonParser.parseValue("{\"titi\":\"toto\"}"); 19 | ScriptableObject.putProperty(scope, "json", json); 20 | ScriptableObject.putProperty(scope, "out", Context.javaToJS(System.out, scope)); 21 | // Execute the script 22 | cx.evaluateString(scope, "out.println(json.titi);json.titi='Modified value'", "EvaluationScript", 1, null); 23 | System.out.println(NativeJSON.stringify(cx, scope, json, null, null)); 24 | } catch (Throwable e) { 25 | e.printStackTrace(); 26 | } finally { 27 | Context.exit(); 28 | } 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/fr/safepic/burp/script/LogCallback.java: -------------------------------------------------------------------------------- 1 | package fr.safepic.burp.script; 2 | 3 | import fr.safepic.burp.script.ui.panel.ScriptPanel; 4 | 5 | import java.io.PrintWriter; 6 | import java.io.StringWriter; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public class LogCallback { 11 | private final ScriptPanel panel; 12 | private final PrintWriter stdout; 13 | private final PrintWriter stderr; 14 | private final List messageList = new ArrayList<>(); 15 | private final int level; 16 | 17 | private static class Message { 18 | final boolean error; 19 | final String message; 20 | final Throwable exception; 21 | 22 | public Message(Throwable exception) { 23 | this.error = true; 24 | this.message = null; 25 | this.exception = exception; 26 | } 27 | public Message(boolean error, String message) { 28 | this.error = error; 29 | this.message = message; 30 | this.exception = null; 31 | } 32 | } 33 | 34 | public LogCallback(ScriptPanel panel, PrintWriter stdout, PrintWriter stderr) { 35 | this.panel = panel; 36 | this.stdout = stdout; 37 | this.stderr = stderr; 38 | this.level = panel == null ? 1 : panel.getLogLevel(); 39 | } 40 | 41 | public void flush() { 42 | for (Message msg : messageList) { 43 | if (msg.error) { 44 | if (panel == null) { 45 | if (msg.message != null) { 46 | stderr.println(msg.message); 47 | } else { 48 | msg.exception.printStackTrace(stderr); 49 | } 50 | } else { 51 | if (msg.message != null) { 52 | panel.addLog(htmlEncode("", msg.message, "
")); 53 | } else { 54 | StringWriter sw = new StringWriter(); 55 | try (PrintWriter pw = new PrintWriter(sw)) { 56 | msg.exception.printStackTrace(pw); 57 | pw.flush(); 58 | } 59 | panel.addLog(htmlEncode("Exception : ", sw.toString(), "
")); 60 | } 61 | } 62 | } else { 63 | if (panel == null) { 64 | stdout.println(msg.message); 65 | } else { 66 | panel.addLog(htmlEncode("", msg.message, "
")); 67 | } 68 | } 69 | } 70 | } 71 | 72 | private CharSequence htmlEncode(String prefix, String content, String suffix) { 73 | StringBuilder sb = new StringBuilder(); 74 | sb.append(prefix); 75 | char c; 76 | for (int i = 0; i': 89 | sb.append(">"); 90 | break; 91 | case '<': 92 | sb.append("<"); 93 | break; 94 | case '\n': 95 | sb.append("
"); 96 | break; 97 | default: 98 | sb.append(c); 99 | } 100 | } 101 | sb.append(suffix); 102 | return sb; 103 | 104 | } 105 | 106 | 107 | 108 | public void exception(Throwable ex) { 109 | if (level>=1) { 110 | messageList.add(new Message(ex)); 111 | } 112 | } 113 | 114 | 115 | public void verbose(String s) { 116 | if (level>=4) { 117 | messageList.add(new Message(false, "VERBOSE : " + s)); 118 | } 119 | } 120 | 121 | public void debug(String s) { 122 | if (level>=3) { 123 | messageList.add(new Message(false, "DEBUG : " + s)); 124 | } 125 | } 126 | 127 | public void info(String s) { 128 | if (level>=2) { 129 | messageList.add(new Message(false, "INFO : " + s)); 130 | } 131 | } 132 | 133 | public void error(String s) { 134 | if (level>=1) { 135 | messageList.add(new Message(true, "ERROR : " + s)); 136 | } 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /src/main/java/fr/safepic/burp/script/PreferenceClass.java: -------------------------------------------------------------------------------- 1 | package fr.safepic.burp.script; 2 | 3 | public class PreferenceClass { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/fr/safepic/burp/script/ScriptModifier.java: -------------------------------------------------------------------------------- 1 | package fr.safepic.burp.script; 2 | 3 | import burp.*; 4 | import fr.safepic.burp.script.js.AbstractRequestResponseUtil; 5 | import fr.safepic.burp.script.js.JavascriptContext; 6 | import fr.safepic.burp.script.js.RequestRWUtil; 7 | import fr.safepic.burp.script.js.ResponseRWUtil; 8 | import fr.safepic.burp.script.ui.panel.ScriptTablePanel; 9 | import fr.safepic.burp.script.ui.model.ScriptRef; 10 | import org.mozilla.javascript.Context; 11 | import org.mozilla.javascript.Scriptable; 12 | import org.mozilla.javascript.ScriptableObject; 13 | 14 | import java.io.PrintWriter; 15 | import java.lang.reflect.Method; 16 | import java.lang.reflect.Modifier; 17 | import java.util.Arrays; 18 | import java.util.function.Supplier; 19 | 20 | import static burp.IBurpExtenderCallbacks.TOOL_PROXY; 21 | 22 | public class ScriptModifier implements IHttpListener, IProxyListener, ISessionHandlingAction { 23 | private final ScriptTablePanel panel; 24 | private final PrintWriter stdout; 25 | private final PrintWriter stderr; 26 | private final IBurpExtenderCallbacks callbacks; 27 | private final IExtensionHelpers helpers; 28 | private String requestMethodCache; 29 | private String responseMethodCache; 30 | 31 | 32 | public ScriptModifier(IBurpExtenderCallbacks callbacks, ScriptTablePanel scriptPanel) { 33 | this.stdout = new PrintWriter(callbacks.getStdout(), true); 34 | this.stderr = new PrintWriter(callbacks.getStderr(), true); 35 | this.callbacks = callbacks; 36 | this.helpers = callbacks.getHelpers(); 37 | this.panel = scriptPanel; 38 | } 39 | @Override 40 | public void processProxyMessage(boolean request, IInterceptedProxyMessage iInterceptedProxyMessage) { 41 | callScript(TOOL_PROXY, false, request, iInterceptedProxyMessage::getMessageInfo, null); 42 | } 43 | 44 | 45 | @Override 46 | public void processHttpMessage(int tools, boolean request, IHttpRequestResponse requestResponse) { 47 | if (tools == TOOL_PROXY) { 48 | return; 49 | } 50 | callScript(tools, false, request, ()->requestResponse, null); 51 | } 52 | 53 | 54 | private void classToJavascript(StringBuilder sb, Class clazz, String jsObjectName) { 55 | for (Method m : clazz.getMethods()) { 56 | if (m.getDeclaringClass() != Object.class && (m.getModifiers() & Modifier.PUBLIC) != 0) { 57 | sb.append("function ").append(m.getName()).append("("); 58 | String sep = ""; 59 | for (int i = 0; i< m.getParameterCount();i++) { 60 | sb.append(sep).append("p").append(i); 61 | sep = ","; 62 | } 63 | sb.append(") {\n"); 64 | sb.append(" "); 65 | if (m.getReturnType() != Void.TYPE) { 66 | sb.append("return "); 67 | } 68 | sb.append(jsObjectName).append(".").append(m.getName()).append("("); 69 | sep = ""; 70 | for (int i = 0; i< m.getParameterCount();i++) { 71 | sb.append(sep).append("p").append(i); 72 | sep = ","; 73 | } 74 | sb.append(");\n}\n"); 75 | } 76 | } 77 | 78 | } 79 | 80 | private StringBuilder newRequestScript() { 81 | StringBuilder sb = new StringBuilder(); 82 | if (requestMethodCache == null) { 83 | classToJavascript(sb, RequestRWUtil.class, "burpscriptingextension"); 84 | classToJavascript(sb, LogCallback.class, "log"); 85 | requestMethodCache = sb.toString(); 86 | } else { 87 | sb.append(requestMethodCache); 88 | } 89 | return sb; 90 | } 91 | 92 | private StringBuilder newResponseScript() { 93 | StringBuilder sb = new StringBuilder(); 94 | if (responseMethodCache == null) { 95 | classToJavascript(sb, ResponseRWUtil.class, "burpscriptingextension"); 96 | classToJavascript(sb, LogCallback.class, "log"); 97 | responseMethodCache = sb.toString(); 98 | } else { 99 | sb.append(responseMethodCache); 100 | } 101 | return sb; 102 | } 103 | 104 | private void callScript(int burpTools, boolean fromSession, boolean request, Supplier supplierRequestResponse, IHttpRequestResponse[] macros) { 105 | IHttpRequestResponse requestResponse = null; 106 | AbstractRequestResponseUtil tools = null; 107 | for (ScriptRef scriptRef : panel.getActiveScriptRef()) { 108 | if (scriptRef.isSessionHandling() != fromSession) { 109 | continue; 110 | } 111 | JavascriptContext context = new JavascriptContext(); 112 | if (requestResponse == null) { 113 | requestResponse = supplierRequestResponse.get(); 114 | if (request) { 115 | tools = new RequestRWUtil(callbacks, requestResponse, context); 116 | } else { 117 | tools = new ResponseRWUtil(callbacks, requestResponse, context); 118 | } 119 | } 120 | if (((scriptRef.getTools() & burpTools) == 0 && !scriptRef.isSessionHandling()) 121 | || scriptRef.isScriptError() 122 | || scriptRef.isInScope() && !callbacks.isInScope(tools.request().getUrl())) { 123 | continue; 124 | } 125 | 126 | 127 | // Create and enter a Context. A Context stores information about the execution environment of a script. 128 | LogCallback logCallback = new LogCallback(scriptRef.getPanel(), stdout, stderr); 129 | tools.setLogCallback(logCallback); 130 | Context cx = Context.enter(); 131 | try { 132 | String scriptContent = request ? scriptRef.getScriptRequest() : scriptRef.getScriptResponse(); 133 | if (scriptContent.trim().length() == 0) { 134 | continue; 135 | } 136 | StringBuilder script = request ? newRequestScript() : newResponseScript(); 137 | 138 | Scriptable scope = cx.initStandardObjects(); 139 | // Pass the Stock Java object to the JavaScript context 140 | Object wrappedRequestResponse = Context.javaToJS(requestResponse, scope); 141 | Object wrappedTools = Context.javaToJS(tools, scope); 142 | Object wrappedLog = Context.javaToJS(logCallback, scope); 143 | if (macros != null) { 144 | ResponseRWUtil[] macrosRW = Arrays.stream(macros).map(m->new ResponseRWUtil(callbacks, m, context)) 145 | .toArray(ResponseRWUtil[]::new); 146 | ScriptableObject.putProperty(scope, "macros", Context.javaToJS(macrosRW, scope)); 147 | } 148 | ScriptableObject.putProperty(scope, "helper", Context.javaToJS(callbacks.getHelpers(), scope)); 149 | ScriptableObject.putProperty(scope, "requestResponse", wrappedRequestResponse); 150 | ScriptableObject.putProperty(scope, "burpscriptingextension", wrappedTools); 151 | ScriptableObject.putProperty(scope, "log", wrappedLog); 152 | 153 | script.append(scriptContent); 154 | // Execute the script 155 | context.setCx(cx); 156 | context.setScope(scope); 157 | cx.evaluateString(scope, script.toString(), scriptRef.getName(), 1, null); 158 | tools.commit(); 159 | } catch (Throwable e) { 160 | scriptRef.setScriptError(true); 161 | logCallback.exception(e); 162 | callbacks.issueAlert("Compilation error in script " + scriptRef.getName() + ", script disabled, fix it to reactivate execution"); 163 | logCallback.error("Compilation error, script disabled, you must fix the error"); 164 | panel.notifyScriptDisabled(scriptRef); 165 | } finally { 166 | logCallback.flush(); 167 | // Exit the Context. This removes the association between the Context and the current thread and is an 168 | // essential cleanup action. There should be a call to exit for every call to enter. 169 | Context.exit(); 170 | } 171 | } 172 | 173 | } 174 | 175 | @Override 176 | public String getActionName() { 177 | return "Scripting Extension"; 178 | } 179 | 180 | @Override 181 | public void performAction(IHttpRequestResponse currentRequest, IHttpRequestResponse[] macroItems) { 182 | callScript(IBurpExtenderCallbacks.TOOL_SUITE, true, true, ()->currentRequest, 183 | macroItems); 184 | 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /src/main/java/fr/safepic/burp/script/TestUI.java: -------------------------------------------------------------------------------- 1 | package fr.safepic.burp.script; 2 | 3 | import fr.safepic.burp.script.ui.panel.TabbedPanel; 4 | 5 | import javax.swing.*; 6 | import java.io.BufferedReader; 7 | import java.io.IOException; 8 | import java.io.InputStreamReader; 9 | 10 | public class TestUI { 11 | public static void main(String[] args) throws IOException { 12 | /* try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) { 13 | while(true){*/ 14 | JFrame frame = new JFrame(); 15 | frame.setContentPane(new TabbedPanel()); 16 | frame.setSize(800, 800); 17 | frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 18 | frame.setVisible(true); 19 | /* br.readLine(); 20 | } 21 | }*/ 22 | 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/fr/safepic/burp/script/js/AbstractRequestResponseUtil.java: -------------------------------------------------------------------------------- 1 | package fr.safepic.burp.script.js; 2 | 3 | import burp.IBurpExtenderCallbacks; 4 | import burp.IExtensionHelpers; 5 | import burp.IHttpRequestResponse; 6 | import burp.IRequestInfo; 7 | import fr.safepic.burp.script.LogCallback; 8 | import org.mozilla.javascript.json.JsonParser; 9 | 10 | import java.io.UnsupportedEncodingException; 11 | import java.nio.charset.StandardCharsets; 12 | import java.util.ArrayList; 13 | import java.util.Collections; 14 | import java.util.List; 15 | import java.util.function.Supplier; 16 | import java.util.stream.Collectors; 17 | import java.util.stream.Stream; 18 | 19 | public abstract class AbstractRequestResponseUtil { 20 | 21 | protected LogCallback logCallback; 22 | private final IBurpExtenderCallbacks callbacks; 23 | private final IExtensionHelpers helpers; 24 | private final IHttpRequestResponse requestResponse; 25 | private IRequestInfo request; 26 | private byte[] requestBytes; 27 | protected byte[] requestBody; 28 | private List initialRequestHeader; 29 | private List issues = new ArrayList<>(); 30 | protected final JavascriptContext context; 31 | 32 | 33 | public AbstractRequestResponseUtil(IBurpExtenderCallbacks callbacks, IHttpRequestResponse requestResponse, JavascriptContext context) { 34 | this.callbacks = callbacks; 35 | this.helpers = callbacks.getHelpers(); 36 | this.requestResponse = requestResponse; 37 | this.context = context; 38 | } 39 | 40 | protected IExtensionHelpers helpers() { 41 | return helpers; 42 | } 43 | protected IHttpRequestResponse requestResponse() { 44 | return requestResponse; 45 | } 46 | 47 | 48 | public byte[] requestBytes() { 49 | if (requestBytes == null) { 50 | requestBytes = requestResponse.getRequest(); 51 | } 52 | return requestBytes; 53 | } 54 | 55 | public IRequestInfo request() { 56 | if (request == null) { 57 | request = helpers.analyzeRequest(requestResponse.getHttpService(), requestBytes()); 58 | } 59 | return request; 60 | } 61 | public List initialRequestHeader() { 62 | if (initialRequestHeader == null) { 63 | initialRequestHeader = Collections.unmodifiableList(request().getHeaders()); 64 | } 65 | return initialRequestHeader; 66 | } 67 | 68 | protected List requestHeaders(boolean init) { 69 | return initialRequestHeader(); 70 | } 71 | 72 | 73 | public List getAllRequestHeaders() { 74 | return requestHeaders(true); 75 | } 76 | 77 | protected Stream filterHeaderGetValue(List headers, String header) { 78 | return headers.stream() 79 | .filter(cur->{ 80 | int sep = cur.indexOf(':'); 81 | return (sep != -1 && cur.substring(0, sep).trim().equalsIgnoreCase(header)); 82 | }) 83 | .map(cur->cur.substring(cur.indexOf(':')+1).trim()); 84 | } 85 | 86 | 87 | public String getRequestHeader(String header) { 88 | List requestHeader = requestHeaders(false); 89 | return filterHeaderGetValue(requestHeader, header).findFirst().orElse(null); 90 | 91 | } 92 | 93 | public List getRequestHeaders(String header) { 94 | List requestHeader = requestHeaders(false); 95 | return filterHeaderGetValue(requestHeader, header).collect(Collectors.toList()); 96 | } 97 | 98 | public boolean hasRequestHeader(String header, String value) { 99 | List headers = requestHeaders(false); 100 | return hasHeaderWithValue(header, value, headers); 101 | } 102 | 103 | static protected boolean hasHeaderWithValue(String header, String value, List headers) { 104 | String cur; 105 | for (int i = 1; i headers = requestHeaders(false); 117 | String url = headers.get(0).trim(); 118 | int end = url.indexOf(' '); 119 | return url.substring(0, end); 120 | } 121 | 122 | public String getUrl() { 123 | 124 | List headers = requestHeaders(false); 125 | String url = headers.get(0).trim(); 126 | int begin = url.indexOf(' '); 127 | int end = url.lastIndexOf(' '); 128 | if (begin != -1 && end != -1 && url.substring(end + 1).startsWith("HTTP/")) { 129 | return url.substring(begin + 1, end); 130 | } else { 131 | return null; 132 | } 133 | } 134 | 135 | public String getHref() { 136 | String url = getUrl(); 137 | int idxHRef = url.indexOf("#"); 138 | if (idxHRef != -1) { 139 | return url.substring(idxHRef+1); 140 | } 141 | return ""; 142 | } 143 | 144 | 145 | protected void resetCache() { 146 | this.request = null; 147 | this.requestBytes = null; 148 | this.initialRequestHeader = null; 149 | } 150 | 151 | public void setLogCallback(LogCallback logCallback) { 152 | this.logCallback = logCallback; 153 | } 154 | 155 | public IssueObj addIssue(String name) { 156 | 157 | IssueObj issueObj = new IssueObj( 158 | request().getUrl(), 159 | name, 160 | new IHttpRequestResponse[]{requestResponse}, 161 | requestResponse.getHttpService() 162 | ); 163 | issues.add(issueObj); 164 | return issueObj; 165 | } 166 | protected String decodeBody(byte[] body, String encoding, Supplier contentType) { 167 | if ("undefined".equals(encoding)) { 168 | String type = contentType.get(); 169 | if (type == null) { 170 | logCallback.error("Encoding not specified use ISO-8859-1"); 171 | encoding = "ISO-8859-1"; 172 | } else { 173 | int idxCharset = type.indexOf("charset"); 174 | if (idxCharset != -1) { 175 | encoding = type.substring(idxCharset + 8).split(" ;")[0]; 176 | } else { 177 | logCallback.error("Encoding not specified use ISO-8859-1"); 178 | encoding = "ISO-8859-1"; 179 | } 180 | } 181 | } 182 | try { 183 | return new String(body, encoding); 184 | } catch (UnsupportedEncodingException e) { 185 | logCallback.error("Encoding not supported "+ encoding + " fallback to ISO-8859-1"); 186 | return new String(requestBytes(), StandardCharsets.ISO_8859_1); 187 | } 188 | 189 | } 190 | 191 | public byte[] getRequestBody() { 192 | if (requestBody == null) { 193 | IRequestInfo ri = request(); 194 | requestBody = new byte[requestBytes().length - ri.getBodyOffset()]; 195 | System.arraycopy(requestBytes(), ri.getBodyOffset(), requestBody, 0, requestBody.length); 196 | } 197 | return requestBody; 198 | } 199 | 200 | public String getRequestBodyAsString(String encoding) { 201 | return decodeBody(getRequestBody(), encoding, ()->getRequestHeader("Content-Type")); 202 | } 203 | 204 | public Object getRequestBodyAsJson(String encoding) throws JsonParser.ParseException { 205 | String value = getRequestBodyAsString(encoding); 206 | JsonParser jsonParser = new JsonParser(context.getCx(), context.getScope()); 207 | return jsonParser.parseValue(value); 208 | } 209 | 210 | public void commit() { 211 | for (IssueObj issueObj : issues) { 212 | callbacks.addScanIssue(issueObj); 213 | } 214 | } 215 | 216 | public void setComment(String comment) { 217 | this.requestResponse().setComment(comment); 218 | } 219 | 220 | public String getComment() { 221 | return this.requestResponse().getComment(); 222 | } 223 | 224 | public void setColor(String color) { 225 | this.requestResponse().setHighlight(color); 226 | } 227 | 228 | public String getColor() { 229 | return this.requestResponse().getHighlight(); 230 | } 231 | 232 | } 233 | -------------------------------------------------------------------------------- /src/main/java/fr/safepic/burp/script/js/HttpServiceObj.java: -------------------------------------------------------------------------------- 1 | package fr.safepic.burp.script.js; 2 | 3 | import burp.IHttpService; 4 | 5 | public class HttpServiceObj implements IHttpService { 6 | private final String host; 7 | private final int port; 8 | private final boolean https; 9 | 10 | public HttpServiceObj(boolean https, String host, int port) { 11 | this.host = host; 12 | this.port = port; 13 | this.https = https; 14 | } 15 | 16 | @Override 17 | public String getHost() { 18 | return host; 19 | } 20 | 21 | @Override 22 | public int getPort() { 23 | return port; 24 | } 25 | 26 | @Override 27 | public String getProtocol() { 28 | return https ? "https" : "http"; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/fr/safepic/burp/script/js/IssueObj.java: -------------------------------------------------------------------------------- 1 | package fr.safepic.burp.script.js; 2 | 3 | import burp.IBurpExtenderCallbacks; 4 | import burp.IHttpRequestResponse; 5 | import burp.IHttpService; 6 | import burp.IScanIssue; 7 | 8 | import java.net.URL; 9 | 10 | public class IssueObj implements IScanIssue { 11 | private URL url; 12 | private String issueName; 13 | private int issueType = 0x08000000; 14 | private String severity = "Information"; 15 | private String confidence = "Tentative"; 16 | private String issueBackground; 17 | private String issueDetail; 18 | private String remediationBackground; 19 | private String remediationDetail; 20 | private IHttpRequestResponse[] httpMessages; 21 | private IHttpService httpService; 22 | 23 | public IssueObj(URL url, String name, IHttpRequestResponse[] httpMessages, IHttpService httpService) { 24 | this.url = url; 25 | this.issueName = name; 26 | this.httpMessages = httpMessages; 27 | this.httpService = httpService; 28 | } 29 | 30 | public IssueObj certain() { 31 | confidence = "Certain"; 32 | return this; 33 | } 34 | 35 | public IssueObj firm() { 36 | confidence = "Firm"; 37 | return this; 38 | } 39 | 40 | public IssueObj tentative() { 41 | confidence = "Tentative"; 42 | return this; 43 | } 44 | 45 | public IssueObj high() { 46 | severity = "High"; 47 | return this; 48 | } 49 | 50 | public IssueObj medium() { 51 | severity = "Medium"; 52 | return this; 53 | } 54 | 55 | public IssueObj low() { 56 | severity = "Low"; 57 | return this; 58 | } 59 | 60 | public IssueObj information() { 61 | severity = "Information"; 62 | return this; 63 | } 64 | 65 | public IssueObj falsePositive() { 66 | severity = "False positive"; 67 | return this; 68 | } 69 | 70 | public IssueObj issue(String detail, String background) { 71 | if (!"undefined".equals(detail)) { 72 | this.issueDetail = detail; 73 | } 74 | if (!"undefined".equals(background)) { 75 | this.issueBackground = background; 76 | } 77 | return this; 78 | } 79 | 80 | public IssueObj remediation(String detail, String background) { 81 | if (!"undefined".equals(detail)) { 82 | this.remediationDetail = detail; 83 | } 84 | if (!"undefined".equals(background)) { 85 | this.remediationDetail = background; 86 | } 87 | return this; 88 | } 89 | 90 | @Override 91 | public URL getUrl() { 92 | return url; 93 | } 94 | 95 | @Override 96 | public String getIssueName() { 97 | return issueName; 98 | } 99 | 100 | @Override 101 | public int getIssueType() { 102 | return issueType; 103 | } 104 | 105 | @Override 106 | public String getSeverity() { 107 | return severity; 108 | } 109 | 110 | @Override 111 | public String getConfidence() { 112 | return confidence; 113 | } 114 | 115 | @Override 116 | public String getIssueBackground() { 117 | return null; 118 | } 119 | 120 | @Override 121 | public String getRemediationBackground() { 122 | return null; 123 | } 124 | 125 | @Override 126 | public String getIssueDetail() { 127 | return null; 128 | } 129 | 130 | @Override 131 | public String getRemediationDetail() { 132 | return null; 133 | } 134 | 135 | @Override 136 | public IHttpRequestResponse[] getHttpMessages() { 137 | return httpMessages; 138 | } 139 | 140 | @Override 141 | public IHttpService getHttpService() { 142 | return httpService; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/main/java/fr/safepic/burp/script/js/JavascriptContext.java: -------------------------------------------------------------------------------- 1 | package fr.safepic.burp.script.js; 2 | 3 | import org.mozilla.javascript.Context; 4 | import org.mozilla.javascript.Scriptable; 5 | 6 | public class JavascriptContext { 7 | private Context cx; 8 | private Scriptable scope; 9 | 10 | public void setCx(Context cx) { 11 | this.cx = cx; 12 | } 13 | 14 | public void setScope(Scriptable scope) { 15 | this.scope = scope; 16 | } 17 | 18 | public Context getCx() { 19 | return cx; 20 | } 21 | 22 | public Scriptable getScope() { 23 | return scope; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/fr/safepic/burp/script/js/RequestRWUtil.java: -------------------------------------------------------------------------------- 1 | package fr.safepic.burp.script.js; 2 | 3 | import burp.IBurpExtenderCallbacks; 4 | import burp.IExtensionHelpers; 5 | import burp.IHttpRequestResponse; 6 | import burp.IRequestInfo; 7 | import org.mozilla.javascript.Context; 8 | import org.mozilla.javascript.NativeJSON; 9 | import org.mozilla.javascript.json.JsonParser; 10 | 11 | import java.io.IOException; 12 | import java.nio.file.Files; 13 | import java.nio.file.Paths; 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | import java.util.function.Supplier; 17 | 18 | public class RequestRWUtil extends AbstractRequestResponseUtil { 19 | private List requestHeader; 20 | private boolean requestBodyModifed = false; 21 | 22 | 23 | public RequestRWUtil(IBurpExtenderCallbacks callbacks, IHttpRequestResponse requestResponse, JavascriptContext context) { 24 | super(callbacks, requestResponse, context); 25 | } 26 | 27 | @Override 28 | protected List requestHeaders(boolean init) { 29 | List requestHeader = this.requestHeader; 30 | if (requestHeader == null) { 31 | if (init) { 32 | requestHeader = new ArrayList<>(initialRequestHeader()); 33 | this.requestHeader = requestHeader; 34 | } else { 35 | requestHeader = initialRequestHeader(); 36 | } 37 | 38 | } 39 | return requestHeader; 40 | } 41 | 42 | 43 | /** 44 | * Remove all requests header having the provided name 45 | * @param header: name of the header 46 | */ 47 | public void removeRequestHeader(String header) { 48 | requestHeaders(true); 49 | String cur; 50 | for (int i = 1; i headers = requestHeaders(true); 123 | String oldUrl = headers.get(0); 124 | int begin = oldUrl.indexOf(' '); 125 | int end = oldUrl.lastIndexOf(' '); 126 | headers.set(0, oldUrl.substring(0, begin+1) + url + oldUrl.substring(end)); 127 | } 128 | 129 | public void setMethod(String method) { 130 | List headers = requestHeaders(true); 131 | String oldUrl = headers.get(0); 132 | int end = oldUrl.indexOf(' '); 133 | headers.set(0, method + oldUrl.substring(end)); 134 | } 135 | 136 | /** 137 | * Set a href in the URL 138 | * @param href: the href value 139 | */ 140 | public void setHref(String href) { 141 | String oldUrl = getUrl(); 142 | int idxHRef = oldUrl.indexOf("#"); 143 | String url; 144 | if (idxHRef != -1) { 145 | url = oldUrl.substring(0, idxHRef - 1) + href; 146 | } else { 147 | url = oldUrl += "#" + href; 148 | } 149 | setUrl(url); 150 | logCallback.verbose("Request Url " + oldUrl + " replaced by " + url); 151 | } 152 | 153 | public void setTarget(boolean https, String hostname, int port) { 154 | requestResponse().setHttpService(new HttpServiceObj(https, hostname, port)); 155 | } 156 | 157 | public void setRequestBody(byte[] requestBody) { 158 | this.requestBody = requestBody; 159 | requestBodyModifed = true; 160 | } 161 | 162 | public void setRequestBodyAsString(String body) { 163 | setRequestBody(body.getBytes()); 164 | } 165 | 166 | public void setRequestBodyAsJson(Object json) { 167 | Object jsonStringify = NativeJSON.stringify(context.getCx(), context.getScope(), json, null, null); 168 | setRequestBodyAsString((String)Context.jsToJava(jsonStringify,String.class)); 169 | } 170 | 171 | public Object getRequestBodyAsJson(String encoding) throws JsonParser.ParseException { 172 | String value = getRequestBodyAsString(encoding); 173 | JsonParser jsonParser = new JsonParser(context.getCx(), context.getScope()); 174 | return jsonParser.parseValue(value); 175 | } 176 | 177 | 178 | private boolean isModified() { 179 | return requestBodyModifed || (requestHeader != null && !requestHeader.equals(initialRequestHeader())); 180 | } 181 | 182 | 183 | public void setRequestAsFile(String fileName) throws IOException { 184 | commit(); 185 | byte[] content = Files.readAllBytes(Paths.get(fileName)); 186 | requestResponse().setRequest(content); 187 | } 188 | 189 | /** 190 | * Save the request, useful in case of mixing Burp native method and helper method. 191 | */ 192 | public void commit() { 193 | super.commit(); 194 | if (isModified()) { 195 | requestResponse().setRequest(helpers().buildHttpMessage(requestHeaders(false), getRequestBody())); 196 | resetCache(); 197 | this.requestHeader = null; 198 | this.requestBody = null; 199 | this.requestBodyModifed = false; 200 | logCallback.verbose("Request updated"); 201 | } 202 | } 203 | 204 | } 205 | -------------------------------------------------------------------------------- /src/main/java/fr/safepic/burp/script/js/ResponseRWUtil.java: -------------------------------------------------------------------------------- 1 | package fr.safepic.burp.script.js; 2 | 3 | import burp.*; 4 | import org.mozilla.javascript.Context; 5 | import org.mozilla.javascript.NativeJSON; 6 | import org.mozilla.javascript.json.JsonParser; 7 | 8 | import java.io.IOException; 9 | import java.nio.file.Files; 10 | import java.nio.file.Paths; 11 | import java.util.ArrayList; 12 | import java.util.Collections; 13 | import java.util.List; 14 | import java.util.stream.Collectors; 15 | 16 | public class ResponseRWUtil extends AbstractRequestResponseUtil { 17 | protected IResponseInfo response; 18 | protected byte[] responseBytes; 19 | protected List initialResponseHeader; 20 | protected List responseHeaders; 21 | protected byte[] responseBody; 22 | private boolean responseBodyModified = false; 23 | 24 | 25 | public ResponseRWUtil(IBurpExtenderCallbacks callbacks, IHttpRequestResponse requestResponse, JavascriptContext context) { 26 | super(callbacks, requestResponse, context); 27 | } 28 | 29 | public byte[] responseBytes() { 30 | if (responseBytes == null) { 31 | responseBytes = requestResponse().getResponse(); 32 | } 33 | return responseBytes; 34 | } 35 | 36 | public IResponseInfo response() { 37 | if (response == null) { 38 | response = helpers().analyzeResponse(responseBytes()); 39 | } 40 | return response; 41 | } 42 | public List initialResponseHeader() { 43 | if (initialResponseHeader == null) { 44 | initialResponseHeader = Collections.unmodifiableList(response().getHeaders()); 45 | } 46 | return initialResponseHeader; 47 | } 48 | 49 | protected List responseHeaders(boolean init) { 50 | List responseHeaders = this.responseHeaders; 51 | if (responseHeaders == null) { 52 | if (init) { 53 | responseHeaders = new ArrayList<>(initialResponseHeader()); 54 | this.responseHeaders = responseHeaders; 55 | } else { 56 | responseHeaders = initialResponseHeader(); 57 | } 58 | 59 | } 60 | return responseHeaders; 61 | } 62 | 63 | 64 | public List getAllResponseHeaders() { 65 | return responseHeaders(false); 66 | } 67 | 68 | public String getResponseHeader(String header) { 69 | List responseHeaders = responseHeaders(false); 70 | return filterHeaderGetValue(responseHeaders, header).findFirst().orElse(null); 71 | } 72 | 73 | public List getResponseHeaders(String header) { 74 | List responseHeaders = responseHeaders(false); 75 | return filterHeaderGetValue(responseHeaders, header).collect(Collectors.toList()); 76 | } 77 | 78 | public boolean hasResponseHeader(String header, String value) { 79 | List headers = responseHeaders(false); 80 | return hasHeaderWithValue(header, value, headers); 81 | } 82 | 83 | public void removeResponseHeader(String header) { 84 | List headers = responseHeaders(true); 85 | String cur; 86 | for (int i = 1; i headers = responseHeaders(true); 99 | String oldStatus = headers.get(0); 100 | headers.set(0, "HTTP/1.1 "+s); 101 | logCallback.verbose("Status Header " + oldStatus + " replaced by" + headers.get(0)); 102 | } 103 | 104 | public void setResponseHeader(String header, String value) { 105 | List headers = responseHeaders(true); 106 | boolean set = false; 107 | String cur; 108 | for (int i = 1; i headers = responseHeaders(true); 125 | String cur; 126 | for (int i = 1; i headers = responseHeaders(true); 138 | headers.add(header+": "+value); 139 | logCallback.verbose("Response Header " + header+": "+value + " added"); 140 | } 141 | 142 | public byte[] getResponseBody() { 143 | if (responseBody == null) { 144 | byte[] responseBytes = responseBytes(); 145 | IResponseInfo ri = response(); 146 | responseBody = new byte[responseBytes.length - ri.getBodyOffset()]; 147 | System.arraycopy(responseBytes, ri.getBodyOffset(), responseBody, 0, responseBody.length); 148 | } 149 | return responseBody; 150 | } 151 | 152 | public String getResponseBodyAsString(String encoding) { 153 | return decodeBody(getResponseBody(), encoding, ()->getResponseHeader("Content-Type")); 154 | } 155 | 156 | public Object getResponseBodyAsJson(String encoding) throws JsonParser.ParseException { 157 | String value = getResponseBodyAsString(encoding); 158 | JsonParser jsonParser = new JsonParser(context.getCx(), context.getScope()); 159 | return jsonParser.parseValue(value); 160 | } 161 | 162 | public void setResponseBody(byte[] responseBody) { 163 | this.responseBody = responseBody; 164 | responseBodyModified = true; 165 | } 166 | 167 | public void setResponseBodyAsString(String body) { 168 | setResponseBody(body.getBytes()); 169 | } 170 | 171 | public void setResponseBodyAsJson(Object json) { 172 | Object jsonStringify = NativeJSON.stringify(context.getCx(), context.getScope(), json, null, null); 173 | setResponseBodyAsString((String)Context.jsToJava(jsonStringify,String.class)); 174 | } 175 | 176 | public void setResponseAsFile(String fileName) throws IOException { 177 | commit(); 178 | byte[] content = Files.readAllBytes(Paths.get(fileName)); 179 | requestResponse().setResponse(content); 180 | } 181 | 182 | 183 | private boolean isModified() { 184 | return responseBodyModified || (responseHeaders != null && !responseHeaders.equals(initialResponseHeader)); 185 | } 186 | 187 | 188 | public void commit() { 189 | super.commit(); 190 | if (isModified()) { 191 | requestResponse().setResponse(helpers().buildHttpMessage(responseHeaders(false), getResponseBody())); 192 | this.response = null; 193 | this.responseBytes = null; 194 | this.initialResponseHeader = null; 195 | this.responseHeaders = null; 196 | this.responseBody = null; 197 | this.responseBodyModified = false; 198 | logCallback.verbose("Response updated"); 199 | } 200 | } 201 | 202 | } 203 | -------------------------------------------------------------------------------- /src/main/java/fr/safepic/burp/script/ui/component/ClickableTabIcon.java: -------------------------------------------------------------------------------- 1 | package fr.safepic.burp.script.ui.component; 2 | 3 | import javax.swing.*; 4 | import java.awt.*; 5 | 6 | public abstract class ClickableTabIcon implements Icon { 7 | 8 | private int xPos; 9 | private int yPos; 10 | private int width = 16; 11 | private int height = 16; 12 | private final static int offsetFrame = 2; 13 | protected Icon extraIcon = null; 14 | protected int frameTop; 15 | protected int frameBottom; 16 | protected int frameLeft; 17 | protected int frameRight; 18 | 19 | protected ClickableTabIcon() { 20 | 21 | } 22 | 23 | protected ClickableTabIcon(Icon extraIcon) { 24 | this.extraIcon =extraIcon; 25 | } 26 | 27 | public Color preparePaint(Graphics graphics, int x, int y) { 28 | setXYPos(x, y); 29 | 30 | Color col = graphics.getColor(); 31 | 32 | graphics.setColor(Color.black); 33 | 34 | // top line of rectangle-frame... 35 | graphics.drawLine(frameLeft + 2, frameTop, frameRight - 2, frameTop); 36 | // bottom line of rectangle-frame... 37 | graphics.drawLine(frameLeft + 2, frameBottom, frameRight - 2, frameBottom); 38 | // left line of rectangle-frame... 39 | graphics.drawLine(frameLeft, frameTop + 2, frameLeft, frameBottom - 2); 40 | // right line of rectangle-frame... 41 | graphics.drawLine(frameRight, frameTop + 2, frameRight, frameBottom - 2); 42 | 43 | // rounding 44 | graphics.drawLine(frameLeft + 1, frameTop + 1, frameLeft + 1, frameTop + 1); 45 | graphics.drawLine(frameRight - 1, frameTop + 1, frameRight - 1, frameTop + 1); 46 | graphics.drawLine(frameLeft + 1, frameBottom - 1, frameLeft + 1, frameBottom - 1); 47 | graphics.drawLine(frameRight - 1, frameBottom - 1, frameRight - 1, frameBottom - 1); 48 | return col; 49 | } 50 | 51 | public int getIconWidth() { 52 | return getWidth() + (extraIcon != null ? extraIcon.getIconWidth() : 0); 53 | } 54 | 55 | public int getIconHeight() { 56 | return getHeight(); 57 | } 58 | 59 | /** 60 | * Returns the x-coordinate of the position of this icon. 61 | * 62 | * @return the x-coordinate of the position of this icon. 63 | */ 64 | public int getXPos() { 65 | return xPos; 66 | } 67 | 68 | /** 69 | * Returns the y-coordinate of the position of this icon. 70 | * 71 | * @return the y-coordinate of the position of this icon. 72 | */ 73 | public int getYPos() { 74 | return yPos; 75 | } 76 | 77 | /** 78 | * Returns the width of this icon. 79 | * 80 | * @return the width of this icon. 81 | */ 82 | public int getWidth() { 83 | return width; 84 | } 85 | 86 | /** 87 | * Returns the height of this icon. 88 | * 89 | * @return the height of this icon. 90 | */ 91 | public int getHeight() { 92 | return height; 93 | } 94 | 95 | /** 96 | * Returns the extra-icon, which is to be displayed next to this icon. Might be null. 97 | * 98 | * @return the extra-icon. 99 | */ 100 | public Icon getExtraIcon() { 101 | return extraIcon; 102 | } 103 | 104 | /** 105 | * Sets the x-coordinate of the position of this icon. 106 | * 107 | * @param xPos the x-coordinate of the position of this icon. 108 | */ 109 | protected void setXYPos(int xPos, int yPos) { 110 | this.xPos = xPos; 111 | this.yPos = yPos; 112 | this.frameLeft = xPos + offsetFrame; 113 | this.frameRight = xPos + (width - offsetFrame); 114 | this.frameTop = yPos + offsetFrame; 115 | this.frameBottom = yPos + (height - offsetFrame); 116 | 117 | } 118 | 119 | /** 120 | * Sets the width of this icon. 121 | *

122 | * This method should be called only within the constructor-methods.

123 | * 124 | * @param width the width of this icon. 125 | */ 126 | protected void setWidth(int width) { 127 | this.width = width; 128 | } 129 | 130 | /** 131 | * Sets the height of this icon. 132 | *

133 | * This method should be called only within the constructor-methods.

134 | * 135 | * @param height the height of this icon. 136 | */ 137 | protected void setHeight(int height) { 138 | this.height = height; 139 | } 140 | 141 | /** 142 | * Sets the extra-icon to be displayed next to this icon. 143 | *

144 | * This method should be called only within the constructor-methods.

145 | * 146 | * @param extraIcon the extra icon to display. 147 | */ 148 | protected void setExtraIcon(Icon extraIcon) { 149 | this.extraIcon = extraIcon; 150 | } 151 | 152 | 153 | public Rectangle getBounds() { 154 | return new Rectangle(getXPos(), getYPos(), getWidth(), getHeight()); 155 | } 156 | 157 | } 158 | -------------------------------------------------------------------------------- /src/main/java/fr/safepic/burp/script/ui/component/CloseTabIcon.java: -------------------------------------------------------------------------------- 1 | package fr.safepic.burp.script.ui.component; 2 | 3 | import java.awt.Color; 4 | import java.awt.Component; 5 | import java.awt.Graphics; 6 | import javax.swing.Icon; 7 | 8 | /** 9 | * Draws an icon representing an "X" in a box. The constructor accepts an icon, which will be placed next (right) to the 10 | * 'X' icon drawn by this class. If no extra icon is needed, the empty default constructor can be used, or provide 11 | * "null" as a value for the icon-object. 12 | * 13 | * @author mjoellnir 14 | * @version 1.0 15 | */ 16 | public class CloseTabIcon extends ClickableTabIcon { 17 | private final static int offsetCross1 = 3; 18 | private final static int offsetCross2 = 4; 19 | 20 | /** 21 | * Creates new "X" Icon. 22 | */ 23 | public CloseTabIcon() { 24 | } 25 | 26 | /** 27 | * Creates new "X" Icon with an extra icon next to it. 28 | * 29 | * @param fileIcon the Icon-object to be placed next to this icon. 30 | * @see javax.swing.Icon 31 | */ 32 | public CloseTabIcon(Icon fileIcon) { 33 | super(fileIcon); 34 | } 35 | 36 | @Override 37 | public void paintIcon(Component component, Graphics graphics, int x, int y) { 38 | Color col = preparePaint(graphics, x, y); 39 | 40 | // prepare coordinates for the "X" 41 | int crossTop1 = frameTop + offsetCross1; 42 | int crossBottom1 = frameBottom - offsetCross1; 43 | int crossTop2 = frameTop + offsetCross2; 44 | int crossBottom2 = frameBottom - offsetCross2; 45 | 46 | int crossRight1 = frameRight - offsetCross1; 47 | int crossLeft1 = frameLeft + offsetCross1; 48 | int crossRight2 = frameRight - offsetCross2; 49 | int crossLeft2 = frameLeft + offsetCross2; 50 | 51 | // first diagonal of "X": top left to bottom right... 52 | graphics.drawLine(crossLeft1, crossTop1, crossRight1, crossBottom1); 53 | graphics.drawLine(crossLeft1, crossTop2, crossRight2, crossBottom1); 54 | graphics.drawLine(crossLeft2, crossTop1, crossRight1, crossBottom2); 55 | 56 | // second diagonal of "X": top right to bottom left... 57 | graphics.drawLine(crossRight1, crossTop1, crossLeft1, crossBottom1); 58 | graphics.drawLine(crossRight1, crossTop2, crossLeft2, crossBottom1); 59 | graphics.drawLine(crossRight2, crossTop1, crossLeft1, crossBottom2); 60 | 61 | graphics.setColor(col); 62 | 63 | if (extraIcon != null) { 64 | extraIcon.paintIcon(component, graphics, x + getWidth(), y + 2); 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /src/main/java/fr/safepic/burp/script/ui/component/CloseableTabbedPane.java: -------------------------------------------------------------------------------- 1 | package fr.safepic.burp.script.ui.component; 2 | 3 | import javax.swing.*; 4 | import java.awt.*; 5 | import java.awt.event.MouseEvent; 6 | import java.awt.event.MouseListener; 7 | import java.util.Collections; 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | import java.util.function.Supplier; 11 | 12 | public class CloseableTabbedPane extends JTabbedPane implements MouseListener { 13 | final Map> iconCallback = Collections.synchronizedMap(new HashMap<>()); 14 | final Supplier addListener; 15 | 16 | /** 17 | * Creates a new instance of ClosableTabbedPane 18 | */ 19 | public CloseableTabbedPane(String name, JPanel component, Supplier addListener) { 20 | super(); 21 | this.addListener = addListener; 22 | super.addTab(name, new JScrollPane(component, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER)); 23 | JPanel empty = new JPanel(); 24 | iconCallback.put(empty, addListener); 25 | super.addTab("", null, empty, "Click here to create new script"); 26 | initializeMouseListener(); 27 | setEnabledAt(1, false); 28 | setDisabledIconAt(1, new NewTabIcon()); 29 | /*addChangeListener((l)->{ 30 | if (getTabCount() -1 == getSelectedIndex()) { 31 | addListener.get(); 32 | } 33 | });*/ 34 | } 35 | 36 | /** 37 | * Appends a tab without closing-capabilities, just as the standard JTabbedPane would do. 38 | * 39 | * @see javax.swing.JTabbedPane#addTab(String title, Component component) addTab 40 | */ 41 | @Override 42 | public void addTab(String title, Component component) { 43 | this.addTab(title, null, component, null, null); 44 | } 45 | 46 | @Override 47 | public void addTab(String title, Icon icon, Component component, String tip) { 48 | this.addTab(title, icon, component, tip, null); 49 | } 50 | 51 | @Override 52 | public void addTab(String title, Icon icon, Component component) { 53 | this.addTab(title, icon, component, null, null); 54 | } 55 | 56 | /** 57 | * Appends a tab with or without closing-capabilities, depending on the flag isClosable. If isClosable is true, a 58 | * close-icon ('X') is displayed left of the title. If extraIcon is not null, it will be displayed between the closing 59 | * icon (if present) and the tab's title. The extraIcon will be displayed independently of the closing-icon. 60 | * 61 | * @param title Title of this tab. 62 | * @param component Contents of this tab. 63 | * @param extraIcon Extra icon to be displayed. 64 | * @see javax.swing.JTabbedPane#addTab(String title, Component component) addTab 65 | */ 66 | public void addTab(String title, Icon extraIcon, Component component, String tip, Supplier iconCallback) { 67 | if (iconCallback != null) { 68 | this.iconCallback.put(component, iconCallback); 69 | } 70 | if (extraIcon != null) { 71 | super.insertTab(title, extraIcon, component, tip, getTabCount() - 1); 72 | } else { 73 | super.insertTab(title, null, component, null, getTabCount() - 1); 74 | } 75 | } 76 | 77 | @Override 78 | public void removeTabAt(int index) { 79 | iconCallback.remove(getComponentAt(index)); 80 | super.removeTabAt(index); 81 | } 82 | 83 | @Override 84 | public void mouseClicked(MouseEvent evt) { 85 | int tabIndex = getUI().tabForCoordinate(this, evt.getX(), evt.getY()); 86 | if (tabIndex < 0) { 87 | return; 88 | } 89 | 90 | Icon icon = getIconAt(tabIndex); 91 | if (icon == null) { 92 | icon = getDisabledIconAt(tabIndex); 93 | } 94 | 95 | if (!(icon instanceof ClickableTabIcon)) { 96 | // This tab is not intended to be closeable. 97 | return; 98 | } 99 | 100 | Rectangle rect = ((ClickableTabIcon) icon).getBounds(); 101 | if (rect.contains(evt.getX(), evt.getY())) { 102 | //the tab is being closed 103 | Supplier listener = iconCallback.get(getComponentAt(tabIndex)); 104 | if (listener!=null) { 105 | listener.get(); 106 | } 107 | } 108 | } 109 | 110 | @Override 111 | public void mouseEntered(MouseEvent evt) { 112 | } 113 | 114 | @Override 115 | public void mouseExited(MouseEvent evt) { 116 | } 117 | 118 | @Override 119 | public void mousePressed(MouseEvent evt) { 120 | } 121 | 122 | @Override 123 | public void mouseReleased(MouseEvent evt) { 124 | } 125 | 126 | private void initializeMouseListener() { 127 | addMouseListener(this); 128 | } 129 | } -------------------------------------------------------------------------------- /src/main/java/fr/safepic/burp/script/ui/component/FSJTextArea.java: -------------------------------------------------------------------------------- 1 | package fr.safepic.burp.script.ui.component; 2 | 3 | import javax.swing.*; 4 | import java.awt.*; 5 | 6 | public class FSJTextArea extends JTextArea { 7 | public FSJTextArea() { 8 | } 9 | public FSJTextArea(int rows, int columns) { 10 | super(rows, columns); 11 | } 12 | 13 | public void setFont(Font font) { 14 | super.setFont(new Font("monospaced", Font.PLAIN, 16)); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/fr/safepic/burp/script/ui/component/FSRTextArea.java: -------------------------------------------------------------------------------- 1 | package fr.safepic.burp.script.ui.component; 2 | 3 | import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; 4 | 5 | import java.awt.*; 6 | 7 | public class FSRTextArea extends RSyntaxTextArea { 8 | public FSRTextArea() { 9 | } 10 | public FSRTextArea(int rows, int cols) { 11 | super(rows, cols); 12 | } 13 | public void setFont(Font font) { 14 | super.setFont(new Font("monospaced", Font.PLAIN, 16)); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/fr/safepic/burp/script/ui/component/MyTextArea.java: -------------------------------------------------------------------------------- 1 | package fr.safepic.burp.script.ui.component; 2 | 3 | import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; 4 | import org.fife.ui.rsyntaxtextarea.SyntaxConstants; 5 | import org.fife.ui.rtextarea.RTextScrollPane; 6 | 7 | import javax.swing.*; 8 | import javax.swing.event.DocumentEvent; 9 | import javax.swing.event.DocumentListener; 10 | import java.awt.*; 11 | import java.awt.event.ActionEvent; 12 | import java.util.*; 13 | import java.io.IOException; 14 | import java.net.URI; 15 | import java.nio.charset.StandardCharsets; 16 | import java.nio.file.*; 17 | import java.nio.file.attribute.BasicFileAttributes; 18 | import java.util.List; 19 | 20 | public class MyTextArea extends JPanel { 21 | private final RSyntaxTextArea coloredArea = new FSRTextArea(15, 80); 22 | private final JTextArea normalArea = new FSJTextArea(15, 80); 23 | private final CardLayout layout = new CardLayout(); 24 | private boolean colored = true; 25 | 26 | private static class MenuEntry { 27 | JPopupMenu popup; 28 | JMenu menu; 29 | 30 | public MenuEntry(JMenu menu) { 31 | this.menu = menu; 32 | } 33 | 34 | public MenuEntry(JPopupMenu popup) { 35 | this.popup = popup; 36 | } 37 | 38 | public void add(JMenu child) { 39 | if (popup != null) { 40 | popup.add(child); 41 | } else { 42 | menu.add(child); 43 | } 44 | } 45 | public void add(JMenuItem child) { 46 | if (popup != null) { 47 | popup.add(child); 48 | } else { 49 | menu.add(child); 50 | } 51 | } 52 | 53 | } 54 | 55 | public MyTextArea(String name) { 56 | setName(name); 57 | setLayout(layout); 58 | coloredArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVASCRIPT); 59 | RTextScrollPane coloredAreaSP = new RTextScrollPane(coloredArea); 60 | add(coloredAreaSP, "colored"); 61 | JScrollPane normalAreaSP = new JScrollPane(normalArea); 62 | add(normalAreaSP, "normal"); 63 | layout.show(this, "colored"); 64 | 65 | normalArea.getDocument().addDocumentListener(new ScriptDocumentListener(false)); 66 | coloredArea.getDocument().addDocumentListener(new ScriptDocumentListener(true)); 67 | JPopupMenu coloredAreaMenu = coloredArea.getPopupMenu(); 68 | JPopupMenu normalAreaMenu = new JPopupMenu(); 69 | normalArea.setComponentPopupMenu(normalAreaMenu); 70 | try { 71 | URI uri = MyTextArea.class.getResource("/sample-menu-item").toURI(); 72 | if ("jar".equals(uri.getScheme())) { 73 | try { 74 | FileSystems.getFileSystem(uri); 75 | } catch( FileSystemNotFoundException e ) { 76 | Map env = new HashMap<>(); 77 | env.put("create", "true"); 78 | FileSystems.newFileSystem(uri, env); 79 | } 80 | } 81 | Path myPath = Paths.get(uri); 82 | Stack stack = new Stack<>(); 83 | 84 | List paths = new ArrayList<>(); 85 | SimpleFileVisitor visitor = new SimpleFileVisitor<>() { 86 | @Override 87 | public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { 88 | if (!myPath.equals(dir)) { 89 | MenuEntry parent = stack.peek(); 90 | JMenu child = new JMenu(dir.getFileName().toString()); 91 | parent.add(child); 92 | stack.push(new MenuEntry(child)); 93 | } 94 | return super.preVisitDirectory(dir, attrs); 95 | } 96 | @Override 97 | public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { 98 | paths.add(file); 99 | return super.visitFile(file, attrs); 100 | } 101 | @Override 102 | public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { 103 | return super.visitFileFailed(file, exc); 104 | } 105 | @Override 106 | public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { 107 | paths.stream().sorted(Comparator.comparing(file->file.getFileName().toString())) 108 | .forEach(file->{ 109 | String value = file.getFileName().toString(); 110 | value = value.substring(0, value.length() - 4); 111 | stack.peek().add(new JMenuItem(new AbstractAction(value) { 112 | @Override 113 | public void actionPerformed(ActionEvent e) { 114 | insertSample(file); 115 | } 116 | })); 117 | }); 118 | paths.clear(); 119 | if (!myPath.equals(dir)) { 120 | stack.pop(); 121 | } 122 | return super.postVisitDirectory(dir, exc); 123 | } 124 | }; 125 | 126 | JMenu sample = new JMenu("Code sample"); 127 | coloredAreaMenu.add(sample); 128 | stack.push(new MenuEntry(sample)); 129 | Files.walkFileTree(myPath, visitor); 130 | stack.clear(); 131 | 132 | stack.push(new MenuEntry(normalAreaMenu)); 133 | Files.walkFileTree(myPath, visitor); 134 | 135 | 136 | /* Stream walk = Files.walk(myPath, 2); 137 | Map menus = new 138 | walk.forEach(path -> { 139 | System.out.println(myPath.relativize(path)); 140 | });*/ 141 | } catch (Exception ex) { 142 | ex.printStackTrace(); 143 | } 144 | 145 | 146 | 147 | } 148 | 149 | public void insertSample(Path path) { 150 | try { 151 | if (colored) { 152 | coloredArea.insert(Files.readString(path, StandardCharsets.UTF_8), coloredArea.getCaretPosition()); 153 | } else { 154 | normalArea.insert(Files.readString(path, StandardCharsets.UTF_8), normalArea.getCaretPosition()); 155 | } 156 | }catch (Exception ex) { 157 | ex.printStackTrace(); 158 | } 159 | } 160 | public void addDocumentListener(DocumentListener listener) { 161 | listenerList.add(DocumentListener.class, listener); 162 | } 163 | 164 | 165 | private class ScriptDocumentListener implements DocumentListener { 166 | private final boolean fireWhenColored; 167 | ScriptDocumentListener(boolean fireWhenColored){ 168 | this.fireWhenColored = fireWhenColored; 169 | } 170 | 171 | @Override 172 | public void insertUpdate(DocumentEvent e) { 173 | if (fireWhenColored == colored) { 174 | for (DocumentListener listener : listenerList.getListeners(DocumentListener.class)) { 175 | listener.insertUpdate(e); 176 | } 177 | } 178 | } 179 | 180 | @Override 181 | public void removeUpdate(DocumentEvent e) { 182 | if (fireWhenColored == colored) { 183 | for (DocumentListener listener : listenerList.getListeners(DocumentListener.class)) { 184 | listener.removeUpdate(e); 185 | } 186 | } 187 | } 188 | 189 | @Override 190 | public void changedUpdate(DocumentEvent e) { 191 | if (fireWhenColored == colored) { 192 | for (DocumentListener listener : listenerList.getListeners(DocumentListener.class)) { 193 | listener.changedUpdate(e); 194 | } 195 | } 196 | } 197 | } 198 | 199 | public void setColored(boolean b) { 200 | String currentValue = getText(); 201 | colored = b; 202 | layout.show(this, b ? "colored" : "normal"); 203 | setText(currentValue); 204 | } 205 | 206 | 207 | public String getText() { 208 | return colored ? coloredArea.getText() : normalArea.getText(); 209 | } 210 | 211 | public void setText(String txt) { 212 | if (colored) { 213 | coloredArea.setText(txt); 214 | } else { 215 | normalArea.setText(txt); 216 | } 217 | } 218 | 219 | } 220 | -------------------------------------------------------------------------------- /src/main/java/fr/safepic/burp/script/ui/component/NewTabIcon.java: -------------------------------------------------------------------------------- 1 | package fr.safepic.burp.script.ui.component; 2 | 3 | import javax.swing.*; 4 | import java.awt.*; 5 | 6 | /** 7 | * Draws an icon representing an "X" in a box. The constructor accepts an icon, which will be placed next (right) to the 8 | * 'X' icon drawn by this class. If no extra icon is needed, the empty default constructor can be used, or provide 9 | * "null" as a value for the icon-object. 10 | * 11 | * @author mjoellnir 12 | * @version 1.0 13 | */ 14 | public class NewTabIcon extends ClickableTabIcon { 15 | private final static int offsetCross = 2; 16 | 17 | /** 18 | * Creates new "X" Icon. 19 | */ 20 | public NewTabIcon() { 21 | } 22 | 23 | /** 24 | * Creates new "X" Icon with an extra icon next to it. 25 | * 26 | * @param fileIcon the Icon-object to be placed next to this icon. 27 | * @see Icon 28 | */ 29 | public NewTabIcon(Icon fileIcon) { 30 | super(fileIcon); 31 | } 32 | 33 | @Override 34 | public void paintIcon(Component component, Graphics graphics, int x, int y) { 35 | Color col = preparePaint(graphics, x, y); 36 | // prepare coordinates for the "+" 37 | int horizontalXPos1 = frameLeft + offsetCross; 38 | int horizontalXPos2 = frameRight - offsetCross; 39 | int horizontalYPos= frameTop + (frameBottom - frameTop -2) / 2; 40 | 41 | int verticalYPos1 = frameTop + offsetCross; 42 | int verticalYPos2 = frameBottom - offsetCross; 43 | int verticalXPos= frameLeft + (frameRight - frameLeft -2) / 2; 44 | int yPos = (frameRight - frameLeft - 2) / 2; 45 | int xPos = (frameBottom - frameTop - 2) / 2; 46 | 47 | // Horizontal 48 | graphics.drawLine(horizontalXPos1, horizontalYPos, horizontalXPos2, horizontalYPos); 49 | graphics.drawLine(horizontalXPos1, horizontalYPos+1, horizontalXPos2, horizontalYPos+1); 50 | graphics.drawLine(horizontalXPos1, horizontalYPos+2, horizontalXPos2, horizontalYPos+2); 51 | // Vertical 52 | graphics.drawLine(verticalXPos, verticalYPos1, verticalXPos, verticalYPos2); 53 | graphics.drawLine(verticalXPos+1, verticalYPos1, verticalXPos+1, verticalYPos2); 54 | graphics.drawLine(verticalXPos+2, verticalYPos1, verticalXPos+2, verticalYPos2); 55 | 56 | graphics.setColor(col); 57 | 58 | if (extraIcon != null) { 59 | extraIcon.paintIcon(component, graphics, x + getWidth(), y + 2); 60 | } 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /src/main/java/fr/safepic/burp/script/ui/model/ScriptRef.java: -------------------------------------------------------------------------------- 1 | package fr.safepic.burp.script.ui.model; 2 | 3 | import burp.IBurpExtenderCallbacks; 4 | import fr.safepic.burp.script.ui.panel.ScriptPanel; 5 | 6 | import java.util.Objects; 7 | import java.util.UUID; 8 | import java.util.prefs.BackingStoreException; 9 | import java.util.prefs.Preferences; 10 | 11 | public class ScriptRef { 12 | private String uid = UUID.randomUUID().toString(); 13 | private boolean enabled; 14 | private volatile boolean scriptError; 15 | private boolean inScope = true; 16 | private int tools = IBurpExtenderCallbacks.TOOL_PROXY; 17 | private transient ScriptPanel panel; 18 | private String name; 19 | private String description; 20 | private boolean sessionHandling; 21 | private String scriptRequest; 22 | private String scriptResponse; 23 | private transient ScriptRef backup; 24 | private boolean savedGlobally; 25 | 26 | public ScriptRef() { 27 | 28 | } 29 | 30 | public ScriptRef getBackup() { 31 | return backup; 32 | } 33 | 34 | public ScriptRef backup() { 35 | backup = new ScriptRef(); 36 | backup.enabled = enabled; 37 | backup.name = name; 38 | backup.description = description; 39 | backup.inScope = inScope; 40 | backup.tools = tools; 41 | backup.scriptRequest = scriptRequest; 42 | backup.scriptResponse = scriptResponse; 43 | backup.sessionHandling = sessionHandling; 44 | return backup; 45 | } 46 | 47 | public void restore() { 48 | enabled = backup.enabled; 49 | name = backup.name; 50 | description = backup.description; 51 | inScope = backup.inScope; 52 | tools = backup.tools; 53 | scriptRequest = backup.scriptRequest; 54 | scriptResponse = backup.scriptResponse; 55 | sessionHandling = backup.sessionHandling; 56 | backup = null; 57 | } 58 | 59 | public boolean isEnabled() { 60 | return enabled; 61 | } 62 | 63 | public void setEnabled(boolean enabled) { 64 | this.enabled = enabled; 65 | } 66 | 67 | public ScriptPanel getPanel() { 68 | return panel; 69 | } 70 | 71 | public void setPanel(ScriptPanel panel) { 72 | this.panel = panel; 73 | } 74 | 75 | public String getName() { 76 | return name; 77 | } 78 | 79 | public void setName(String name) { 80 | this.name = name; 81 | } 82 | 83 | public String getScriptRequest() { 84 | return scriptRequest; 85 | } 86 | 87 | public void setScriptRequest(String scriptRequest) { 88 | this.scriptRequest = scriptRequest; 89 | } 90 | 91 | public String getScriptResponse() { 92 | return scriptResponse; 93 | } 94 | 95 | public void setScriptResponse(String scriptResponse) { 96 | this.scriptResponse = scriptResponse; 97 | } 98 | 99 | public String getDescription() { 100 | return description; 101 | } 102 | 103 | public void setDescription(String description) { 104 | this.description = description; 105 | } 106 | 107 | public boolean isInScope() { 108 | return inScope; 109 | } 110 | 111 | public void setInScope(boolean inScope) { 112 | this.inScope = inScope; 113 | } 114 | 115 | public int getTools() { 116 | return tools; 117 | } 118 | 119 | public void setTools(int tools) { 120 | this.tools = tools; 121 | } 122 | 123 | public boolean needsSave() { 124 | if (backup == null) { 125 | return false; 126 | } 127 | if (backup.isInScope() != isInScope()) return true; 128 | if (backup.getTools() != getTools()) return true; 129 | if (!Objects.equals(backup.sessionHandling, sessionHandling)) return true; 130 | if (!Objects.equals(backup.getName(), getName())) return true; 131 | if (!Objects.equals(backup.getDescription(), getDescription())) return true; 132 | if (!Objects.equals(backup.getScriptRequest(), getScriptRequest())) return true; 133 | return !Objects.equals(backup.getScriptResponse(), getScriptResponse()); 134 | } 135 | 136 | 137 | 138 | public void saveData(Preferences pref) throws BackingStoreException { 139 | Preferences node = pref.node(uid); 140 | node.putInt("version", 1); 141 | node.putBoolean("enabled", enabled); 142 | node.putBoolean("inScope", inScope); 143 | node.putInt("tools", tools); 144 | node.put("name", name); 145 | node.putBoolean("sessionHandling", sessionHandling); 146 | node.put("description", description); 147 | node.put("scriptRequest", scriptRequest); 148 | node.put("scriptResponse", scriptResponse); 149 | savedGlobally = true; 150 | node.flush(); 151 | } 152 | 153 | public static ScriptRef restoreData(Preferences node) { 154 | ScriptRef ref = new ScriptRef(); 155 | ref.uid = node.name(); 156 | ref.enabled = node.getBoolean("enabled", false); 157 | ref.inScope = node.getBoolean("inScope", true); 158 | ref.tools = node.getInt("tools", 0); 159 | ref.name = node.get("name", node.name()); 160 | ref.sessionHandling = node.getBoolean("sessionHandling", false); 161 | ref.description = node.get("description", ""); 162 | ref.scriptRequest = node.get("scriptRequest", ""); 163 | ref.scriptResponse = node.get("scriptResponse", ""); 164 | ref.savedGlobally = true; 165 | return ref; 166 | } 167 | 168 | public String getUid() { 169 | return uid; 170 | } 171 | 172 | public boolean isSavedGlobally() { 173 | return savedGlobally; 174 | } 175 | 176 | public boolean isScriptError() { 177 | return scriptError; 178 | } 179 | 180 | public void setScriptError(boolean scriptError) { 181 | this.scriptError = scriptError; 182 | } 183 | 184 | public boolean isSessionHandling() { 185 | return sessionHandling; 186 | } 187 | 188 | public void setSessionHandling(boolean sessionHandling) { 189 | this.sessionHandling = sessionHandling; 190 | } 191 | /* 192 | private boolean local; 193 | private boolean enabled; 194 | private boolean inScope = true; 195 | private int tools = IBurpExtenderCallbacks.TOOL_PROXY; 196 | private transient Script2Panel panel; 197 | private String name; 198 | private String description; 199 | private String scriptRequest; 200 | private String scriptResponse; 201 | private transient ScriptRef backup; 202 | 203 | */ 204 | } 205 | -------------------------------------------------------------------------------- /src/main/java/fr/safepic/burp/script/ui/model/ScriptTableModel.java: -------------------------------------------------------------------------------- 1 | package fr.safepic.burp.script.ui.model; 2 | 3 | import javax.swing.*; 4 | import javax.swing.table.AbstractTableModel; 5 | import javax.swing.table.DefaultTableCellRenderer; 6 | import javax.swing.table.TableCellRenderer; 7 | import java.awt.*; 8 | import java.util.List; 9 | 10 | public class ScriptTableModel extends AbstractTableModel implements TableCellRenderer { 11 | private final DefaultTableCellRenderer DEFAULT_RENDERER = new DefaultTableCellRenderer(); 12 | private volatile Color defaultBackgroundColor; 13 | 14 | private final List scripts; 15 | 16 | public ScriptTableModel(List scripts) { 17 | this.scripts =scripts; 18 | } 19 | 20 | @Override 21 | public boolean isCellEditable(int rowIndex, int columnIndex) { 22 | return columnIndex == 0; 23 | } 24 | 25 | @Override 26 | public String getColumnName(int columnIndex) { 27 | switch (columnIndex) { 28 | case 0: 29 | return "Enabled"; 30 | case 1: 31 | return "Scope Only"; 32 | case 2: 33 | return "Name"; 34 | case 3: 35 | return "Description"; 36 | case 4: 37 | return "State"; 38 | default: 39 | return null; 40 | } 41 | } 42 | 43 | @Override 44 | public int getRowCount() { 45 | return scripts.size(); 46 | } 47 | 48 | @Override 49 | public int getColumnCount() { 50 | return 5; 51 | } 52 | 53 | @Override 54 | public Class getColumnClass(int columnIndex) { 55 | switch (columnIndex) { 56 | case 0: 57 | case 1: 58 | return Boolean.class; 59 | case 2: 60 | case 3: 61 | case 4: 62 | return String.class; 63 | default: 64 | return null; 65 | } 66 | } 67 | 68 | public void fireDataChanged(ScriptRef sr) { 69 | int idx = scripts.indexOf(sr); 70 | for (int i = 0; i contentQueue = new LinkedList<>(); 44 | private boolean contentQueueModified = false; 45 | private final ScriptDocumentListener documentListener = new ScriptDocumentListener(); 46 | private boolean updateObject=true; 47 | private final ScriptRef scriptRef; 48 | 49 | private final JScrollPane scrollPane; 50 | private final BiConsumer scriptRefChangeConsumer; 51 | private final JButton btnRevert = new JButton("Revert"); 52 | private final JButton btnDelete = new JButton("Delete"); 53 | private final JButton btnSave = new JButton("Save"); 54 | private final JComboBox logLevel = new JComboBox<>(new String[]{"NONE", "ERROR","INFO", "DEBUG", "VERBOSE"}); 55 | 56 | public enum Action { 57 | UPDATE, 58 | SAVE, 59 | DELETE 60 | } 61 | 62 | public ScriptPanel(ScriptRef scriptRef, BiConsumer scriptRefChangeConsumer) { 63 | setName(scriptRef.getName()); 64 | scrollPane = new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); 65 | this.scriptRef = scriptRef; 66 | this.scriptRefChangeConsumer = scriptRefChangeConsumer; 67 | this.logLevel.setSelectedIndex(1); 68 | this.logLevel.setToolTipText("Log level"); 69 | } 70 | 71 | private void setValues() { 72 | try { 73 | updateObject=false; 74 | enabled.setSelected(scriptRef.isEnabled()); 75 | tfName.setText(scriptRef.getName()); 76 | // tfDesc.setText(scriptRef.getDescription()); 77 | 78 | requestScript.setText(scriptRef.getScriptRequest()); 79 | responseScript.setText(scriptRef.getScriptResponse()); 80 | cbScope.setSelected(scriptRef.isInScope()); 81 | cbProxy.setSelected((scriptRef.getTools() & IBurpExtenderCallbacks.TOOL_PROXY) != 0); 82 | cbSpider.setSelected((scriptRef.getTools() & IBurpExtenderCallbacks.TOOL_SPIDER) != 0); 83 | cbScanner.setSelected((scriptRef.getTools() & IBurpExtenderCallbacks.TOOL_SCANNER) != 0); 84 | cbIntruder.setSelected((scriptRef.getTools() & IBurpExtenderCallbacks.TOOL_INTRUDER) != 0); 85 | cbRepeater.setSelected((scriptRef.getTools() & IBurpExtenderCallbacks.TOOL_REPEATER) != 0); 86 | cbSequencer.setSelected((scriptRef.getTools() & IBurpExtenderCallbacks.TOOL_SEQUENCER) != 0); 87 | cbExtension.setSelected((scriptRef.getTools() & IBurpExtenderCallbacks.TOOL_EXTENDER) != 0); 88 | cbSessionHandling.setSelected(scriptRef.isSessionHandling()); 89 | cbIntruder.setEnabled(!scriptRef.isSessionHandling()); 90 | cbProxy.setEnabled(!scriptRef.isSessionHandling()); 91 | cbRepeater.setEnabled(!scriptRef.isSessionHandling()); 92 | cbScanner.setEnabled(!scriptRef.isSessionHandling()); 93 | cbSequencer.setEnabled(!scriptRef.isSessionHandling()); 94 | cbExtension.setEnabled(!scriptRef.isSessionHandling()); 95 | cbSpider.setEnabled(!scriptRef.isSessionHandling()); 96 | } finally { 97 | updateObject=true; 98 | } 99 | } 100 | 101 | public void refresh() { 102 | SwingUtilities.invokeLater(this::setValues); 103 | } 104 | 105 | public JScrollPane getScrollPane() { 106 | return scrollPane; 107 | } 108 | 109 | @Override 110 | public void componentShown(ComponentEvent e) { 111 | } 112 | 113 | @Override 114 | public void componentHidden(ComponentEvent e) { 115 | } 116 | 117 | @Override 118 | public void componentMoved(ComponentEvent e) { 119 | 120 | } 121 | 122 | @Override 123 | public void componentResized(ComponentEvent e) { 124 | Dimension parentSize = getParent().getSize(); 125 | Dimension d; 126 | int width = (int)parentSize.getWidth(); 127 | int height = (int)parentSize.getHeight(); 128 | d = new Dimension(width - 10, height - vSplitPane.getY() - 10); 129 | SwingUtilities.invokeLater(()-> ScriptPanel.this.vSplitPane.setPreferredSize(d)); 130 | } 131 | 132 | /*private class ScriptComponentListener implements ComponentListener { 133 | }*/ 134 | 135 | private class ScriptDocumentListener implements DocumentListener { 136 | @Override 137 | public void insertUpdate(DocumentEvent e) { 138 | update(); 139 | } 140 | 141 | @Override 142 | public void removeUpdate(DocumentEvent e) { 143 | update(); 144 | } 145 | 146 | @Override 147 | public void changedUpdate(DocumentEvent e) { 148 | update(); 149 | } 150 | 151 | public void update() { 152 | if (updateObject) { 153 | scriptRef.setName(tfName.getText()); 154 | // scriptRef.setDescription(tfDesc.getText()); 155 | scriptRef.setScriptError(false); 156 | scriptRef.setScriptResponse(responseScript.getText()); 157 | scriptRef.setScriptRequest(requestScript.getText()); 158 | scriptRef.setSessionHandling(cbSessionHandling.isSelected()); 159 | scriptRefChangeConsumer.accept(scriptRef, Action.UPDATE); 160 | } 161 | } 162 | } 163 | 164 | private void changeTools(int tools, boolean enabled) { 165 | if (enabled) { 166 | tools = scriptRef.getTools() | tools; 167 | } else { 168 | tools = scriptRef.getTools() & (tools ^ 0xFFFFFFFF); 169 | } 170 | scriptRef.setTools(tools); 171 | SwingUtilities.invokeLater(()-> scriptRefChangeConsumer.accept(scriptRef, Action.UPDATE)); 172 | } 173 | 174 | public void buildUi() { 175 | int maxColumn = 9; 176 | int line = 0; 177 | setLayout(new GridBagLayout()); 178 | add(enabled, new GridBagConstraints(0, line, 1, 1, 0.0, 0.0 179 | , LINE_START, NONE, new Insets(2, 2, 2, 2), 5, 5)); 180 | add(cbScope, new GridBagConstraints(1, line, 2, 1, 0.0, 0.0 181 | , LINE_START, NONE, new Insets(2, 2, 2, 2), 5, 5)); 182 | add(logLevel, new GridBagConstraints(3, line, 1, 1, 0.0, 0.0 183 | , LINE_START, NONE, new Insets(2, 2, 2, 2), 5, 5)); 184 | 185 | JPanel savePanel = new JPanel(); 186 | savePanel.setLayout(new FlowLayout()); 187 | savePanel.add(btnRevert); 188 | savePanel.add(btnDelete); 189 | savePanel.add(btnSave); 190 | add(savePanel, new GridBagConstraints(4, line, maxColumn - 5, 1, 0.0, 0.0 191 | , LINE_END, NONE, new Insets(2, 2, 2, 2), 5, 5)); 192 | 193 | add(labIntercept, new GridBagConstraints(0, ++line, 1, 1, 0.0, 0.0 194 | , LINE_START, NONE, new Insets(2, 2, 2, 2), 5, 5)); 195 | addComponent(line, 1, cbProxy, cbSpider, cbScanner, cbIntruder, cbRepeater, cbSequencer, cbExtension); 196 | line = addTextFieldWithLabel(maxColumn, line, labelName, tfName); 197 | add(cbSessionHandling, new GridBagConstraints(0, ++line, maxColumn, 1, 0.0, 0.0 198 | , LINE_START, NONE, new Insets(2, 2, 2, 2), 5, 5)); 199 | // line = addTextFieldWithLabel(maxColumn, line, labelDesc, tfDesc); 200 | add(color, new GridBagConstraints(0, ++line, maxColumn, 1, 0.0, 0.0 201 | , LINE_START, NONE, new Insets(2, 2, 2, 2), 5, 5)); 202 | requestScript.setBorder(BorderFactory.createTitledBorder("Request script")); 203 | responseScript.setBorder(BorderFactory.createTitledBorder("Response script")); 204 | errorPane.setContentType("text/html"); 205 | hSplitPane.setResizeWeight( 0.5 ); 206 | vSplitPane.setResizeWeight(0.8); 207 | 208 | add(vSplitPane, new GridBagConstraints(0, ++line, maxColumn, 1, 1.0, 1.0 209 | , CENTER, BOTH, new Insets(2, 2, 2, 2), 5, 5)); 210 | setValues(); 211 | enabled.addItemListener(e->{ 212 | scriptRef.setEnabled(enabled.isSelected()); 213 | scriptRef.setScriptError(false); 214 | scriptRefChangeConsumer.accept(scriptRef, Action.UPDATE); 215 | }); 216 | cbScope.addItemListener(e-> { 217 | scriptRef.setInScope(cbScope.isSelected()); 218 | scriptRefChangeConsumer.accept(scriptRef, Action.UPDATE); 219 | }); 220 | cbSessionHandling.addItemListener(e-> { 221 | scriptRef.setSessionHandling(cbSessionHandling.isSelected()); 222 | SwingUtilities.invokeLater(()->{ 223 | responseScript.setVisible(!cbSessionHandling.isSelected()); 224 | if (!cbSessionHandling.isSelected()) { 225 | hSplitPane.setResizeWeight(0.5); 226 | hSplitPane.resetToPreferredSizes(); 227 | cbIntruder.setEnabled(true); 228 | cbProxy.setEnabled(true); 229 | cbRepeater.setEnabled(true); 230 | cbScanner.setEnabled(true); 231 | cbSequencer.setEnabled(true); 232 | cbExtension.setEnabled(true); 233 | cbSpider.setEnabled(true); 234 | } else { 235 | cbIntruder.setEnabled(false); 236 | cbProxy.setEnabled(false); 237 | cbRepeater.setEnabled(false); 238 | cbScanner.setEnabled(false); 239 | cbSequencer.setEnabled(false); 240 | cbExtension.setEnabled(false); 241 | cbSpider.setEnabled(false); 242 | } 243 | 244 | }); 245 | scriptRefChangeConsumer.accept(scriptRef, Action.UPDATE); 246 | }); 247 | 248 | cbProxy.addItemListener(e->changeTools(IBurpExtenderCallbacks.TOOL_PROXY, cbProxy.isSelected())); 249 | cbSpider.addItemListener(e->changeTools(IBurpExtenderCallbacks.TOOL_SPIDER, cbSpider.isSelected())); 250 | cbScanner.addItemListener(e->changeTools(IBurpExtenderCallbacks.TOOL_SCANNER, cbScanner.isSelected())); 251 | cbIntruder.addItemListener(e->changeTools(IBurpExtenderCallbacks.TOOL_INTRUDER, cbIntruder.isSelected())); 252 | cbRepeater.addItemListener(e->changeTools(IBurpExtenderCallbacks.TOOL_REPEATER, cbRepeater.isSelected())); 253 | cbSequencer.addItemListener(e->changeTools(IBurpExtenderCallbacks.TOOL_SEQUENCER, cbSequencer.isSelected())); 254 | cbExtension.addItemListener(e->changeTools(IBurpExtenderCallbacks.TOOL_EXTENDER, cbExtension.isSelected())); 255 | 256 | tfName.getDocument().addDocumentListener(documentListener); 257 | // tfDesc.getDocument().addDocumentListener(documentListener); 258 | requestScript.addDocumentListener(documentListener); 259 | responseScript.addDocumentListener(documentListener); 260 | 261 | color.addItemListener(e-> SwingUtilities.invokeLater(()-> { 262 | requestScript.setColored(!color.isSelected()); 263 | responseScript.setColored(!color.isSelected()); 264 | })); 265 | 266 | addComponentListener(this); 267 | getParent().addComponentListener(this); 268 | btnRevert.addActionListener(l-> SwingUtilities.invokeLater(()->{ 269 | boolean revert = JOptionPane.showConfirmDialog(this, "Are you sure you want to revert your changes ?", "Confirmation", JOptionPane.YES_NO_OPTION) == JOptionPane.OK_OPTION; 270 | if (revert) { 271 | scriptRef.restore(); 272 | scriptRef.backup(); 273 | setValues(); 274 | scriptRefChangeConsumer.accept(scriptRef, Action.UPDATE); 275 | } 276 | })); 277 | btnSave.addActionListener(l-> SwingUtilities.invokeLater(()->{ 278 | boolean save = JOptionPane.showConfirmDialog(this, "Are you sure you want to save your changes ?", "Confirmation", JOptionPane.YES_NO_OPTION) == JOptionPane.OK_OPTION; 279 | if (save) { 280 | scriptRefChangeConsumer.accept(scriptRef, Action.SAVE); 281 | } 282 | })); 283 | btnDelete.addActionListener(l-> SwingUtilities.invokeLater(()->{ 284 | boolean delete = JOptionPane.showConfirmDialog(this, "Are you sure you want to delete this script ?", "Confirmation", JOptionPane.YES_NO_OPTION) == JOptionPane.OK_OPTION; 285 | if (delete) { 286 | scriptRefChangeConsumer.accept(scriptRef, Action.DELETE); 287 | } 288 | })); 289 | JPopupMenu popupMenu = new JPopupMenu(); 290 | JMenuItem clear = new JMenuItem(new AbstractAction("Clear content") { 291 | @Override 292 | public void actionPerformed(ActionEvent e) { 293 | synchronized (contentQueue) { 294 | contentQueue.clear(); 295 | contentQueueModified = true; 296 | } 297 | refreshLog(); 298 | } 299 | }); 300 | popupMenu.add(clear); 301 | errorPane.setComponentPopupMenu(popupMenu); 302 | } 303 | 304 | private int addTextFieldWithLabel(int maxColumn, int line, JLabel labelName, JTextField tfName) { 305 | add(labelName, new GridBagConstraints(0, ++line, 1, 1, 0.0, 0.0 306 | , LINE_START, NONE, new Insets(2, 2, 2, 2), 5, 5)); 307 | add(tfName, new GridBagConstraints(1, line, maxColumn-2, 1, 1.0, 0.0 308 | , LINE_START, BOTH, new Insets(2, 2, 2, 2), 5, 5)); 309 | return line; 310 | } 311 | 312 | private void addComponent(int line, int column, Component ...components) { 313 | for (Component c: components) { 314 | add(c, new GridBagConstraints(column++, line, 1, 1, 0.0, 0.0 315 | , LINE_START, NONE, new Insets(2, 2, 2, 2), 5, 5)); 316 | } 317 | } 318 | 319 | public int getLogLevel() { 320 | return logLevel.getSelectedIndex(); 321 | } 322 | 323 | public void refreshLog() { 324 | if (contentQueueModified) { 325 | SwingUtilities.invokeLater(()->{ 326 | StringBuilder sb = new StringBuilder(); 327 | sb.append(""); 328 | synchronized (contentQueue) { 329 | contentQueue.forEach(sb::append); 330 | contentQueueModified = false; 331 | } 332 | sb.append(""); 333 | this.errorPane.setText(sb.toString()); 334 | }); 335 | } 336 | } 337 | public void addLog(CharSequence content) { 338 | synchronized (contentQueue) { 339 | while (contentQueue.size()>1000) { 340 | contentQueue.removeFirst(); 341 | } 342 | contentQueue.addLast(content); 343 | contentQueueModified = true; 344 | } 345 | } 346 | 347 | 348 | @Override 349 | public Dimension getPreferredScrollableViewportSize() { 350 | return super.getPreferredSize(); 351 | } 352 | 353 | 354 | @Override 355 | public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { 356 | return 16; 357 | } 358 | 359 | @Override 360 | public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { 361 | return 16; 362 | } 363 | 364 | @Override 365 | public boolean getScrollableTracksViewportWidth() { 366 | return true; 367 | } 368 | 369 | @Override 370 | public boolean getScrollableTracksViewportHeight() { 371 | return false; 372 | } 373 | 374 | } 375 | -------------------------------------------------------------------------------- /src/main/java/fr/safepic/burp/script/ui/panel/ScriptTablePanel.java: -------------------------------------------------------------------------------- 1 | package fr.safepic.burp.script.ui.panel; 2 | 3 | import fr.safepic.burp.script.PreferenceClass; 4 | import fr.safepic.burp.script.ui.component.CloseTabIcon; 5 | import fr.safepic.burp.script.ui.component.CloseableTabbedPane; 6 | import fr.safepic.burp.script.ui.model.ScriptRef; 7 | import fr.safepic.burp.script.ui.model.ScriptTableModel; 8 | 9 | import javax.swing.*; 10 | import java.awt.*; 11 | import java.awt.event.ActionEvent; 12 | import java.awt.event.ActionListener; 13 | import java.awt.event.MouseAdapter; 14 | import java.awt.event.MouseEvent; 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | import java.util.prefs.BackingStoreException; 18 | import java.util.prefs.Preferences; 19 | import java.util.stream.Collectors; 20 | 21 | public class ScriptTablePanel extends JPanel { 22 | private JTable table; 23 | private ScriptTableModel tm; 24 | private final List data = new ArrayList<>(); 25 | private final CloseableTabbedPane tabbedPane = new CloseableTabbedPane("Scripts", this, this::addTab); 26 | private final Timer timer; 27 | 28 | 29 | public ScriptTablePanel() { 30 | setName("Scripts"); 31 | init(); 32 | //tabbedPane.addChangeListener(this::tabChange); 33 | timer = new Timer(1000, e -> { 34 | oldSelectedIndex = tabbedPane.getSelectedIndex(); 35 | Component c = tabbedPane.getComponentAt(oldSelectedIndex); 36 | if (c instanceof JScrollPane) { 37 | c = ((JScrollPane)c).getViewport().getView(); 38 | if (c instanceof ScriptPanel) { 39 | ((ScriptPanel) c).refreshLog(); 40 | } 41 | } 42 | }); 43 | timer.setInitialDelay(5000); 44 | timer.start(); 45 | } 46 | 47 | public Void addTab() { 48 | ScriptRef sr = fakeData("New script " + data.size()); 49 | data.add(sr); 50 | SwingUtilities.invokeLater(()-> tm.fireTableDataChanged()); 51 | addTab(sr); 52 | return null; 53 | } 54 | 55 | public CloseableTabbedPane getTabbedPane() { 56 | return tabbedPane; 57 | } 58 | 59 | private ScriptRef fakeData(String name) { 60 | 61 | ScriptRef scriptRef = new ScriptRef(); 62 | scriptRef.setName(name); 63 | scriptRef.setDescription(""); 64 | scriptRef.setScriptRequest("/* Variables exposed :\n" + 65 | "helper : instance of burp.IExtensionHelpers\n" + 66 | "requestResponse : instance of burp.IHttpRequestResponse\n" + 67 | "Right click to see code sample\n"+ 68 | "*/\n"); 69 | scriptRef.setScriptResponse("/* Variables exposed :\n" + 70 | "helper : instance of burp.IExtensionHelpers\n" + 71 | "requestResponse : instance of burp.IHttpRequestResponse\n" + 72 | "Right click to see code sample\n"+ 73 | "*/\n"); 74 | return scriptRef; 75 | } 76 | 77 | public void init() { 78 | setLayout(new BorderLayout()); 79 | loadData(); 80 | 81 | tm = new ScriptTableModel(data); 82 | table = new JTable(tm); 83 | for (int i = 0; i { 124 | if (sr.getPanel() == null) { 125 | if (!sr.needsSave()) { 126 | sr.backup(); 127 | } 128 | ScriptPanel scriptPanel = new ScriptPanel(sr, ScriptTablePanel.this::onChange); 129 | sr.setPanel(scriptPanel); 130 | tabbedPane.addTab(sr.getName(), new CloseTabIcon(), scriptPanel.getScrollPane(), null, () -> { 131 | SwingUtilities.invokeLater(() -> { 132 | int idx = tabbedPane.indexOfComponent(scriptPanel.getScrollPane()); 133 | sr.setPanel(null); 134 | tabbedPane.removeTabAt(idx); 135 | tabbedPane.setSelectedIndex(0); 136 | }); 137 | return null; 138 | }); 139 | scriptPanel.buildUi(); 140 | } 141 | tabbedPane.setSelectedIndex(tabbedPane.indexOfComponent(sr.getPanel().getScrollPane())); 142 | }); 143 | } 144 | 145 | public void loadData() { 146 | try { 147 | Preferences nodes = getNode(); 148 | for (String child : getNode().childrenNames()) { 149 | data.add(ScriptRef.restoreData(nodes.node(child))); 150 | } 151 | } catch (BackingStoreException ignore) { 152 | } 153 | } 154 | 155 | public void onChange(ScriptRef ref, ScriptPanel.Action action) { 156 | if (action == ScriptPanel.Action.UPDATE) { 157 | SwingUtilities.invokeLater(()-> { 158 | tm.fireDataChanged(ref); 159 | JPanel panel = ref.getPanel(); 160 | if (panel != null) { 161 | int idx = tabbedPane.indexOfComponent(ref.getPanel().getScrollPane()); 162 | tabbedPane.setTitleAt(idx, ref.getName()); 163 | } 164 | }); 165 | } else if (action == ScriptPanel.Action.SAVE) { 166 | try { 167 | ref.saveData(getNode()); 168 | ref.backup(); 169 | SwingUtilities.invokeLater(()-> tm.fireDataChanged(ref)); 170 | } catch (BackingStoreException ignore) { 171 | } 172 | } else if (action == ScriptPanel.Action.DELETE) { 173 | Preferences node = getNode(); 174 | try { 175 | if (node.nodeExists(ref.getUid())) { 176 | node.node(ref.getUid()).removeNode(); 177 | } 178 | node.flush(); 179 | } catch (BackingStoreException ignore) { 180 | } 181 | if (data.contains(ref)) { 182 | data.remove(ref); 183 | SwingUtilities.invokeLater(()-> tm.fireTableDataChanged()); 184 | } 185 | } 186 | } 187 | 188 | private Preferences getNode() { 189 | return Preferences.userNodeForPackage(PreferenceClass.class); 190 | } 191 | 192 | public List getActiveScriptRef() { 193 | return data.stream().filter(ScriptRef::isEnabled).collect(Collectors.toList()); 194 | } 195 | 196 | public void notifyScriptDisabled(ScriptRef ref) { 197 | SwingUtilities.invokeLater(()-> tm.fireDataChanged(ref)); 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/main/java/fr/safepic/burp/script/ui/panel/TabbedPanel.java: -------------------------------------------------------------------------------- 1 | package fr.safepic.burp.script.ui.panel; 2 | 3 | import javax.swing.*; 4 | import java.awt.*; 5 | 6 | public class TabbedPanel extends JPanel { 7 | final ScriptTablePanel refPanel; 8 | 9 | public TabbedPanel() { 10 | setLayout(new BorderLayout()); 11 | refPanel = new ScriptTablePanel(); 12 | add(refPanel.getTabbedPane(), BorderLayout.CENTER); 13 | } 14 | 15 | public void unload() { 16 | 17 | } 18 | 19 | 20 | public ScriptTablePanel getScriptListPanel() { 21 | return refPanel; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Change IP Port/Http.txt: -------------------------------------------------------------------------------- 1 | setTarget(false, 'www.google.com', 80); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Change IP Port/Https.txt: -------------------------------------------------------------------------------- 1 | setTarget(true, 'www.google.com', 443); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Common Action/Prefix URL.txt: -------------------------------------------------------------------------------- 1 | setUrl('/html'+getUrl()); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Common Action/Set User-Agent.txt: -------------------------------------------------------------------------------- 1 | setRequestHeader('User-Agent', 'My value'); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Common Action/Set-Method.txt: -------------------------------------------------------------------------------- 1 | setMethod('POST'); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Issues - Burp Pro only/Add confidence certain.txt: -------------------------------------------------------------------------------- 1 | addIssue('My issue').certain(); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Issues - Burp Pro only/Add confidence firm.txt: -------------------------------------------------------------------------------- 1 | addIssue('My issue').firm(); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Issues - Burp Pro only/Add confidence tentative.txt: -------------------------------------------------------------------------------- 1 | addIssue('My issue').tentative(); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Issues - Burp Pro only/Add severity high.txt: -------------------------------------------------------------------------------- 1 | addIssue('My issue').high(); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Issues - Burp Pro only/Add severity information.txt: -------------------------------------------------------------------------------- 1 | addIssue('My issue'); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Issues - Burp Pro only/Add severity medium .txt: -------------------------------------------------------------------------------- 1 | addIssue('My issue').medium(); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Issues - Burp Pro only/Add severitylow.txt: -------------------------------------------------------------------------------- 1 | addIssue('My issue').low(); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Issues - Burp Pro only/Add with issue detail.txt: -------------------------------------------------------------------------------- 1 | addIssue('My issue').issue("Issue detail", "Issue background"); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Issues - Burp Pro only/Add with remediation detail.txt: -------------------------------------------------------------------------------- 1 | addIssue('My issue').remediation("Remediation detail", "Remediation background"); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Log/Error.txt: -------------------------------------------------------------------------------- 1 | error('Error level trace'); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Log/Info.txt: -------------------------------------------------------------------------------- 1 | info('Info level trace'); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Log/Verbose.txt: -------------------------------------------------------------------------------- 1 | verbose('Verbose level trace'); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Macro/Access Macro Json.txt: -------------------------------------------------------------------------------- 1 | json = macros[0].getResponseBodyAsJson('UTF-8'); 2 | token = json.token; -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Mark/Add Comment.txt: -------------------------------------------------------------------------------- 1 | setComment('My Comment'); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Mark/Highlight in red.txt: -------------------------------------------------------------------------------- 1 | /*red, orange, yellow, green, cyan, blue, pink, magenta, gray*/ 2 | setColor('red'); 3 | -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Request Header/Add header.txt: -------------------------------------------------------------------------------- 1 | addRequestHeader('Cookie', 'My value'); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Request Header/Add or replace.txt: -------------------------------------------------------------------------------- 1 | setRequestHeader('Cookie', 'My value'); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Request Header/Remove by name.txt: -------------------------------------------------------------------------------- 1 | removeRequestHeader('Cookie'); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Request Header/Replace existing header.txt: -------------------------------------------------------------------------------- 1 | updateRequestHeader('Cookie', 'My value'); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Request Header/Retrieve all header by name.txt: -------------------------------------------------------------------------------- 1 | // headerValue is an array 2 | var headerValue = getRequestHeaders('Cookie'); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Request Header/Retrieve first header by name.txt: -------------------------------------------------------------------------------- 1 | var headerValue = getRequestHeader('Cookie'); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/RequestBody/Replace Json value.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Body: 3 | { 4 | "data": "mydata" 5 | } 6 | */ 7 | json = getRequestBodyAsJson('UTF-8'); 8 | json.data = "new data"; 9 | setRequestBodyAsJson(json); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/RequestBody/Replace-Request-By-File-Content.txt: -------------------------------------------------------------------------------- 1 | setRequestAsFile('/home/kali/request.txt'); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/RequestBody/retrieve body.txt: -------------------------------------------------------------------------------- 1 | getRequestBody('utf-8'); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/RequestBody/set body.txt: -------------------------------------------------------------------------------- 1 | setRequestBody('Body'); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Response Header/Add header.txt: -------------------------------------------------------------------------------- 1 | addResponseHeader('Set-Cookie', 'My value'); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Response Header/Add or replace.txt: -------------------------------------------------------------------------------- 1 | setResponseHeader('Set-Cookie', 'My value'); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Response Header/Remove by name.txt: -------------------------------------------------------------------------------- 1 | removeResponseHeader('Set-Cookie'); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Response Header/Replace existing header.txt: -------------------------------------------------------------------------------- 1 | updateResponseHeader('Set-Cookie', 'My value'); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Response Header/Retrieve all header by name.txt: -------------------------------------------------------------------------------- 1 | // headerValue is an array 2 | var headerValue = getResponseHeaders('Set-Cookie'); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Response Header/Retrieve first header by name.txt: -------------------------------------------------------------------------------- 1 | var headerValue = getResponseHeader('Set-Cookie'); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/ResponseBody/retrieve body.txt: -------------------------------------------------------------------------------- 1 | getResponseBody('utf-8'); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/ResponseBody/set body.txt: -------------------------------------------------------------------------------- 1 | setResponseBody('Body'); -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Test Value/Method.txt: -------------------------------------------------------------------------------- 1 | if (getMethod()=='PUT') { 2 | // Your code goes here 3 | } -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Test Value/Request header value.txt: -------------------------------------------------------------------------------- 1 | if (hasRequestHeader('Connection', 'Close')) { 2 | // Your code goes here 3 | } 4 | 5 | -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Test Value/Response header value.txt: -------------------------------------------------------------------------------- 1 | if (hasResponseHeader('Connection', 'Close')) { 2 | // Your code goes here 3 | } -------------------------------------------------------------------------------- /src/main/resources/sample-menu-item/Test Value/Url value.txt: -------------------------------------------------------------------------------- 1 | if (getUrl()=='/test') { 2 | // Your code goes here 3 | } --------------------------------------------------------------------------------