Skip to content

Commit 02a043f

Browse files
committed
Adding new pyscripter components
1 parent 51db72f commit 02a043f

8 files changed

Lines changed: 785 additions & 7 deletions

File tree

BappDescription.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
<p>This extension allows execution of a custom Python script on each HTTP
2-
request and response processed by Burp.</p>
3-
<p>To use, type or paste a Python script into the &quot;Script&quot; tab, and use Burp in
1+
<p>This exension allows execution of custom Python scripts to be used with HTTP request and responses plus handling Macro Items.</p>
2+
<p>To use, type or paste a Python script into the &quot;Python Scripts&quot; tab, and use Burp in
43
the normal way. The script will be executed for each HTTP request and response.
54
The following variables are defined in the context of the script:</p>
65
<ul>
@@ -10,4 +9,5 @@
109
<li>toolFlag</li>
1110
<li>messageIsRequest</li>
1211
<li>messageInfo</li>
12+
<li>macroItems</li>
1313
</ul>

BappManifest.bmf

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ Uuid: eb563ada801346e6bdb7a7d7c5c52583
22
ExtensionType: 2
33
Name: Python Scripter
44
RepoName: python-scripter
5-
ScreenVersion: 1.1
5+
ScreenVersion: 2.0
66
SerialVersion: 2
77
MinPlatformVersion: 0
88
ProOnly: False
9-
Author: Marcin Wielgoszewski
10-
ShortDescription: Allows execution of a custom Python script on each HTTP request and response.
11-
EntryPoint: burpscript.py
9+
Author: Marcin Wielgoszewski, Mike Cromwell
10+
ShortDescription: Allows execution of custom Python scripts to be used with HTTP request and responses plus handling Macro Items.
11+
EntryPoint: python-scripter.py

gui.py

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
from javax.swing import JTabbedPane, JPanel, JButton, JLabel, SwingConstants, JOptionPane, GroupLayout, JCheckBox, JSplitPane
2+
from javax.swing.event import ChangeListener, DocumentListener
3+
from javax.swing.LayoutStyle.ComponentPlacement import RELATED, UNRELATED
4+
from java.awt import BorderLayout, Font, Component
5+
from java.beans import PropertyChangeListener
6+
from org.python.core.util import StringUtil
7+
from uicomponents import BurpUI, TabComponent, TabComponentEditableTabMixin, TabComponentCloseableMixin, TabComponentCloseListener, TabComponentTitleChangedListener
8+
from models import ObservableCollection, Script
9+
from utils import EditorFileAdapter
10+
11+
12+
class ScriptTabbedPane(JTabbedPane):
13+
14+
EMPTY_SCRIPT_TEXT = '''<html><body>
15+
<div style="font-size: 16pt;text-align:center">
16+
You have no Python scripts created.<br/> Please use the add tab (+) button to create a new Python script.
17+
</div></body></html>'''
18+
19+
def __init__(self, extender, callbacks, helpers, scripts):
20+
super(ScriptTabbedPane, self).__init__()
21+
self.scripts = scripts
22+
self.extender = extender
23+
self.callbacks = callbacks
24+
self.helpers = helpers
25+
26+
self.addChangeListener(ScriptTabbedPane.TabsStateChanged())
27+
self.scripts.add_listener(self)
28+
self.create_add_tab()
29+
30+
def create_add_tab(self):
31+
self.add_tab = JButton("+", actionPerformed=self.add_clicked)
32+
self.add_tab.font = Font('Monospaced', Font.PLAIN, 18)
33+
self.add_tab.contentAreaFilled = False
34+
self.add_tab.border = None
35+
36+
self.emptyTab = JPanel(BorderLayout())
37+
self.emptyTab.add(JLabel(ScriptTabbedPane.EMPTY_SCRIPT_TEXT, SwingConstants.CENTER), BorderLayout.CENTER)
38+
39+
self.addTab(None, self.emptyTab)
40+
self.setTabComponentAt(0, self.add_tab)
41+
42+
def add_clicked(self, event):
43+
idx = self.tabCount - 1
44+
title = 'New Script {}'.format(idx + 1)
45+
script = Script(self.extender, self.callbacks, self.helpers, title)
46+
self.scripts.add(script)
47+
48+
def create_script_tab(self, script, idx):
49+
new_tab = ScriptTabComponent(script)
50+
new_tab.tabbed_pane = self
51+
new_tab.addCloseListener(ScriptTabbedPane.ScriptTabCloseListener(self, self.scripts, script))
52+
new_tab.addTitleChangedListener(ScriptTabbedPane.ScriptTabTabTitleChangedListener(script))
53+
new_panel = ScriptPanel(script, self.callbacks)
54+
self.add(new_panel, idx)
55+
self.setTabComponentAt(idx, new_tab)
56+
self.selectedIndex = idx
57+
58+
def collection_changed(self, collection, type, script):
59+
if type == ObservableCollection.ITEM_ADDED:
60+
idx = self.tabCount - 1
61+
self.create_script_tab(script, idx)
62+
63+
64+
class ScriptTabCloseListener(TabComponentCloseListener):
65+
66+
def __init__(self, tabbedpane, scripts, script):
67+
self.tabbed_pane = tabbedpane
68+
self.scripts = scripts
69+
self.script = script
70+
71+
def tabClose(self, event):
72+
result = JOptionPane.showConfirmDialog(None, 'Are you sure you want to close \'{}\' ?'.format(event.source.text),
73+
"Close Tab",
74+
JOptionPane.YES_NO_OPTION,
75+
JOptionPane.QUESTION_MESSAGE)
76+
if result == JOptionPane.YES_OPTION:
77+
idx = self.tabbed_pane.indexOfTabComponent(event.source)
78+
self.tabbed_pane.remove(idx)
79+
self.scripts.remove(self.script)
80+
81+
class ScriptTabTabTitleChangedListener(TabComponentTitleChangedListener):
82+
83+
def __init__(self, script):
84+
self.script = script
85+
86+
def titleChanged(self, event):
87+
self.script.title = event.getTitle()
88+
89+
90+
class TabsStateChanged(ChangeListener):
91+
92+
def stateChanged(self, event):
93+
# prevents the add tab from being selected apart from when there are no other tabs created (i.e. starting from fresh)
94+
# instead the tab next the add tab is selected
95+
tabbed_pane = event.source
96+
if tabbed_pane.tabCount > 1 and tabbed_pane.selectedIndex == tabbed_pane.tabCount - 1:
97+
tabbed_pane.selectedIndex = tabbed_pane.tabCount - 2
98+
99+
100+
class ScriptTabComponent(TabComponentEditableTabMixin, TabComponentCloseableMixin, TabComponent):
101+
102+
def __init__(self, script):
103+
super(ScriptTabComponent, self).__init__()
104+
self.script = script
105+
self.text = self.script.title
106+
self.close_button.font = Font('Dialog', Font.PLAIN, 16)
107+
self.text_field.toolTipText = self.toolTipText = 'Double click to rename, Enter to confirm or Esc to cancel'
108+
109+
110+
111+
class ScriptEditingPanel(JPanel, DocumentListener):
112+
113+
def __init__(self, callbacks, script):
114+
super(ScriptEditingPanel, self).__init__()
115+
self.callbacks = callbacks
116+
self.script = script
117+
self.enabledCheckbox = JCheckBox('Enabled', self.script.enabled, itemStateChanged=self.enabled_changed, alignmentX=Component.LEFT_ALIGNMENT)
118+
self.scriptEditor = callbacks.createTextEditor()
119+
self.scriptEditor.text = script.content
120+
self.scriptText = self.scriptEditor.component
121+
self.compileButton = JButton('Compile', actionPerformed=self.compile, enabled=False)
122+
123+
editingLayout = GroupLayout(self, autoCreateGaps=True, autoCreateContainerGaps=True)
124+
editingLayout.setHorizontalGroup(editingLayout.createParallelGroup()
125+
.addGroup(editingLayout.createSequentialGroup()
126+
.addComponent(self.enabledCheckbox)
127+
.addPreferredGap(UNRELATED)
128+
)
129+
.addGroup(editingLayout.createParallelGroup()
130+
.addComponent(self.scriptText)
131+
.addComponent(self.compileButton)
132+
)
133+
)
134+
135+
editingLayout.setVerticalGroup(editingLayout.createSequentialGroup()
136+
.addGroup(editingLayout.createParallelGroup()
137+
.addComponent(self.enabledCheckbox)
138+
)
139+
.addGroup(editingLayout.createSequentialGroup()
140+
.addComponent(self.scriptText)
141+
.addComponent(self.compileButton)
142+
)
143+
)
144+
self.layout = editingLayout
145+
BurpUI.get_textarea(self.scriptEditor).document.addDocumentListener(self)
146+
self.compile(None)
147+
148+
def enabled_changed(self, event):
149+
self.script.enabled = self.enabledCheckbox.isSelected()
150+
151+
def compile(self, event):
152+
self.script.compile()
153+
self.compileButton.enabled = False
154+
155+
def changedUpdate(self, event):
156+
self._update_content()
157+
self._can_compile(event)
158+
159+
def insertUpdate(self, event):
160+
self._update_content()
161+
self._can_compile(event)
162+
163+
def removeUpdate(self, event):
164+
self._update_content()
165+
self._can_compile(event)
166+
167+
def _update_content(self):
168+
self.script.content = StringUtil.fromBytes(self.scriptEditor.text)
169+
170+
def _can_compile(self, event):
171+
self.compileButton.enabled = False
172+
if event.document.length > 0:
173+
self.compileButton.enabled = self.script.requires_compile
174+
175+
176+
class ScriptOutputPanel(JPanel, PropertyChangeListener):
177+
178+
def __init__(self, callbacks, script):
179+
super(ScriptOutputPanel, self).__init__()
180+
self.callbacks = callbacks
181+
self.script = script
182+
self.script.addPropertyChangeListener(self)
183+
self.tabbedPane = JTabbedPane()
184+
self._create_output_panel()
185+
self._create_error_panel()
186+
self.tabbedPane.addTab('Output', self.outputPanel)
187+
self.tabbedPane.addTab('Errors', self.errorPanel)
188+
self.layout = BorderLayout()
189+
self.add(self.tabbedPane, BorderLayout.CENTER)
190+
self.script.stdout = EditorFileAdapter(self.outputEditor)
191+
self.script.stderr = EditorFileAdapter(self.errorEditor)
192+
193+
def clear_stderr(self, event):
194+
self.errorEditor.text = ''
195+
196+
def clear_stdout(self, event):
197+
self.outputEditor.text = ''
198+
199+
def propertyChange(self, event):
200+
if event.propertyName == Script.Properties.IS_COMPILED:
201+
if event.newValue:
202+
self.errorEditor.text = ''
203+
elif event.propertyName == Script.Properties.COMPILATION_ERROR:
204+
self.errorEditor.text = event.newValue
205+
self.tabbedPane.selectedIndex = 1
206+
207+
def _create_output_panel(self):
208+
self.outputPanel = JPanel()
209+
self.outputEditor = self.callbacks.createTextEditor()
210+
self.outputEditor.editable = False
211+
self.outputText = self.outputEditor.component
212+
self.clearOutputButton = JButton('Clear', actionPerformed=self.clear_stdout)
213+
214+
outputLayout = GroupLayout(self.outputPanel, autoCreateGaps=True, autoCreateContainerGaps=True)
215+
outputLayout.setHorizontalGroup(outputLayout.createParallelGroup()
216+
.addComponent(self.outputText)
217+
.addComponent(self.clearOutputButton)
218+
)
219+
220+
outputLayout.setVerticalGroup(outputLayout.createSequentialGroup()
221+
.addComponent(self.outputText)
222+
.addComponent(self.clearOutputButton)
223+
)
224+
self.outputPanel.layout = outputLayout
225+
226+
def _create_error_panel(self):
227+
self.errorPanel = JPanel()
228+
self.errorEditor = self.callbacks.createTextEditor()
229+
self.errorEditor.editable = False
230+
self.errorText = self.errorEditor.component
231+
self.clearErrorButton = JButton('Clear', actionPerformed=self.clear_stderr)
232+
233+
errorLayout = GroupLayout(self.errorPanel, autoCreateGaps=True, autoCreateContainerGaps=True)
234+
errorLayout.setHorizontalGroup(errorLayout.createParallelGroup()
235+
.addComponent(self.errorText)
236+
.addComponent(self.clearErrorButton)
237+
)
238+
errorLayout.setVerticalGroup(errorLayout.createSequentialGroup()
239+
.addComponent(self.errorText)
240+
.addComponent(self.clearErrorButton)
241+
)
242+
self.errorPanel.layout = errorLayout
243+
244+
245+
class ScriptPanel(JPanel):
246+
247+
def __init__(self, script, callbacks):
248+
self.script = script
249+
self.layout = BorderLayout()
250+
self.editingPanel = ScriptEditingPanel(callbacks, script)
251+
self.outputPanel = ScriptOutputPanel(callbacks, script)
252+
self.splitPane = JSplitPane(JSplitPane.VERTICAL_SPLIT, dividerSize=10)
253+
self.splitPane.topComponent = self.editingPanel
254+
self.splitPane.bottomComponent = self.outputPanel
255+
self.add(self.splitPane, BorderLayout.CENTER)
256+
257+
258+
class GUI(object):
259+
260+
def __init__(self, extender, callbacks, helpers, scripts):
261+
self.panel = JPanel()
262+
self.tabs = ScriptTabbedPane(extender, callbacks, helpers, scripts)
263+
layout = BorderLayout()
264+
self.panel.setLayout(layout)
265+
self.panel.add(self.tabs)
266+
267+
def build(self):
268+
return self.panel

0 commit comments

Comments
 (0)