HaE For Java
7
.classpath
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="lib" path="lib/json.jar"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
17
.project
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>HaE</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
235
HaE/HaE.py
@@ -1,235 +0,0 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
# Author: Vulkey_Chen
|
||||
# Blog: gh0st.cn
|
||||
# Team: MSTSEC
|
||||
|
||||
import json, re, jsbeautifier
|
||||
|
||||
from burp import IBurpExtender, ITab, IHttpListener, IMessageEditorTabFactory, IMessageEditorTab
|
||||
from javax.swing import JPanel, JLabel, JButton, JTextArea, JTextField, JCheckBox, JTabbedPane, JScrollPane, SwingConstants
|
||||
from java.awt import BorderLayout
|
||||
|
||||
from java.io import PrintWriter
|
||||
|
||||
# color list
|
||||
colors = ['red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'pink', 'magenta', 'gray']
|
||||
|
||||
# config
|
||||
configFile = "config.json"
|
||||
|
||||
# 获取配置文件内容
|
||||
def getConfig():
|
||||
config = ""
|
||||
with open(configFile, 'r') as content:
|
||||
config = json.load(content)
|
||||
return config
|
||||
|
||||
# 寻找内容
|
||||
def findContent(info, message):
|
||||
info = getConfig()
|
||||
results = {}
|
||||
for i in info:
|
||||
regex = re.compile(info[i]['regex'])
|
||||
regexRes = regex.findall(message)
|
||||
if regexRes != []:
|
||||
results[i] = ','.join(list(set(regexRes)))
|
||||
return results
|
||||
|
||||
class BurpExtender(IBurpExtender, ITab,IHttpListener, IMessageEditorTabFactory):
|
||||
def registerExtenderCallbacks(self, callbacks):
|
||||
self._callbacks = callbacks
|
||||
self._helpers = callbacks.getHelpers()
|
||||
callbacks.setExtensionName("HaE(Highlighter and Extractor)")
|
||||
self._stdout = PrintWriter(callbacks.getStdout(), True)
|
||||
callbacks.registerHttpListener(self)
|
||||
callbacks.registerMessageEditorTabFactory(self)
|
||||
print 'HaE(Highlighter and Extractor)\nAuthor: Vulkey_Chen\nBlog: gh0st.cn\nTeam: MSTSEC'
|
||||
self._callbacks.customizeUiComponent(self.getUiComponent())
|
||||
self._callbacks.addSuiteTab(self)
|
||||
self.endColors = []
|
||||
|
||||
def getTabCaption(self):
|
||||
return 'HaE'
|
||||
|
||||
def createNewInstance(self, controller, editable):
|
||||
return MarkINFOTab(self, controller, editable)
|
||||
|
||||
def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo):
|
||||
if messageIsRequest:
|
||||
return
|
||||
content = messageInfo.getResponse()
|
||||
# content 为响应正文信息
|
||||
info = getConfig()
|
||||
results = findContent(info, content)
|
||||
colorList = []
|
||||
if results != {}:
|
||||
for i in results:
|
||||
if info[i]['highlight'] == 1 :
|
||||
if info[i]['color'] == 'red':
|
||||
messageInfo.setHighlight(info[i]['color'])
|
||||
break
|
||||
else:
|
||||
colorList.append(info[i]['color'])
|
||||
|
||||
if not messageInfo.getHighlight():
|
||||
colorsList = [colors.index(i) for i in colorList]
|
||||
colorsList.sort()
|
||||
# print(colorsList)
|
||||
self.helper(colorsList)
|
||||
endColor = [colors.index(x) for x in self.endColors]
|
||||
# print(endColor)
|
||||
messageInfo.setHighlight(colors[min(endColor)])
|
||||
|
||||
# 颜色升级
|
||||
def helper(self, mylist):
|
||||
l = len(mylist)
|
||||
i = 0
|
||||
stack = []
|
||||
while i < l:
|
||||
if not stack:
|
||||
stack.append(mylist[i])
|
||||
i += 1
|
||||
else:
|
||||
if mylist[i] != stack[-1]:
|
||||
stack.append(mylist[i])
|
||||
i += 1
|
||||
else:
|
||||
stack[-1] -= 1
|
||||
i += 1
|
||||
|
||||
if len(stack) == len(set(stack)):
|
||||
self.endColors = [colors[i] for i in stack]
|
||||
else:
|
||||
self.helper(stack)
|
||||
|
||||
def addConfig(self, event):
|
||||
nameText = self.nameTextField.getText()
|
||||
regexText = self.regexTextField.getText()
|
||||
colorText = self.colorTextField.getText()
|
||||
isHighlight = int(self.highlightCheckBox.isSelected())
|
||||
isExtract = int(self.extractCheckBox.isSelected())
|
||||
if colorText in colors:
|
||||
# 获取配置文件信息
|
||||
content = open(configFile, 'r')
|
||||
dicts = json.load(content)
|
||||
content.close()
|
||||
# 读写判断
|
||||
if nameText in dicts:
|
||||
self.tipString.setText("Name is existed!")
|
||||
elif not(isHighlight or isExtract):
|
||||
self.tipString.setText("Highlight or Extract?")
|
||||
else:
|
||||
dicts[nameText] = {"regex": regexText, "highlight": isHighlight, "extract": isExtract, "color": colorText}
|
||||
with open(configFile, 'w') as configContent:
|
||||
configContent.write(jsbeautifier.beautify(json.dumps(dicts)))
|
||||
self.tipString.setText("Save Successfully!")
|
||||
else:
|
||||
self.tipString.setText("Not in colors list.")
|
||||
|
||||
def reloadConfig(self, event):
|
||||
# 重新载入配置文件
|
||||
with open(configFile, 'r') as content:
|
||||
self.configTextArea.setText(content.read())
|
||||
|
||||
def saveConfig(self, event):
|
||||
# 保存配置文件
|
||||
text = self.configTextArea.getText()
|
||||
if text != "":
|
||||
with open(configFile, 'w') as content:
|
||||
content.write(text)
|
||||
self.reloadConfig()
|
||||
|
||||
def getUiComponent(self):
|
||||
self.HaEPanel = JPanel()
|
||||
self.HaEPanel.setBorder(None)
|
||||
self.HaEPanel.setLayout(BorderLayout(0, 0))
|
||||
self.panel = JPanel()
|
||||
self.HaEPanel.add(self.panel, BorderLayout.NORTH)
|
||||
self.panel.setLayout(BorderLayout(0, 0))
|
||||
self.tabbedPane = JTabbedPane(JTabbedPane.TOP)
|
||||
self.panel.add(self.tabbedPane, BorderLayout.CENTER)
|
||||
self.setPanel = JPanel()
|
||||
self.tabbedPane.addTab("Set", None, self.setPanel, None)
|
||||
self.setPanel.setLayout(BorderLayout(0, 0))
|
||||
self.setPanel_1 = JPanel()
|
||||
self.setPanel.add(self.setPanel_1, BorderLayout.NORTH)
|
||||
self.nameString = JLabel("Name")
|
||||
self.setPanel_1.add(self.nameString)
|
||||
self.nameTextField = JTextField()
|
||||
self.setPanel_1.add(self.nameTextField)
|
||||
self.nameTextField.setColumns(10)
|
||||
self.regexString = JLabel("Regex")
|
||||
self.setPanel_1.add(self.regexString)
|
||||
self.regexTextField = JTextField()
|
||||
self.setPanel_1.add(self.regexTextField)
|
||||
self.regexTextField.setColumns(10)
|
||||
self.extractCheckBox = JCheckBox("Extract")
|
||||
self.setPanel_1.add(self.extractCheckBox)
|
||||
self.highlightCheckBox = JCheckBox("Highlight")
|
||||
self.setPanel_1.add(self.highlightCheckBox)
|
||||
self.setPanel_2 = JPanel()
|
||||
self.setPanel.add(self.setPanel_2)
|
||||
self.colorString = JLabel("Color")
|
||||
self.setPanel_2.add(self.colorString)
|
||||
self.colorTextField = JTextField()
|
||||
self.setPanel_2.add(self.colorTextField)
|
||||
self.colorTextField.setColumns(5)
|
||||
self.addBottun = JButton("Add", actionPerformed=self.addConfig)
|
||||
self.setPanel_2.add(self.addBottun)
|
||||
self.tipString = JLabel("");
|
||||
self.setPanel_2.add(self.tipString)
|
||||
self.configPanel = JPanel()
|
||||
self.tabbedPane.addTab("Config", None, self.configPanel, None)
|
||||
self.configPanel.setLayout(BorderLayout(0, 0))
|
||||
self.configTextArea = JTextArea()
|
||||
# self.configTextArea.setEnabled(False)
|
||||
self.configTextArea.setTabSize(4)
|
||||
self.configTextArea.setLineWrap(True)
|
||||
self.configTextArea.setRows(20)
|
||||
self.configPanel.add(self.configTextArea, BorderLayout.SOUTH)
|
||||
self.scrollPane = JScrollPane(self.configTextArea)
|
||||
self.configPanel.add(self.scrollPane, BorderLayout.SOUTH)
|
||||
self.reloadButton = JButton("Reload", actionPerformed=self.reloadConfig)
|
||||
self.configPanel.add(self.reloadButton, BorderLayout.NORTH)
|
||||
self.saveButton = JButton("Save", actionPerformed=self.saveConfig)
|
||||
self.configPanel.add(self.saveButton, BorderLayout.CENTER)
|
||||
return self.HaEPanel
|
||||
|
||||
class MarkINFOTab(IMessageEditorTab):
|
||||
def __init__(self, extender, controller, editable):
|
||||
self._extender = extender
|
||||
self._helpers = extender._helpers
|
||||
self._editable = editable
|
||||
self._txtInput = extender._callbacks.createTextEditor()
|
||||
self._txtInput.setEditable(editable)
|
||||
|
||||
def getTabCaption(self):
|
||||
return "MarkINFO"
|
||||
|
||||
def getUiComponent(self):
|
||||
return self._txtInput.getComponent()
|
||||
|
||||
# 非响应 没有匹配到不返回Tab标签页
|
||||
def isEnabled(self, content, isRequest):
|
||||
info = getConfig()
|
||||
if not isRequest:
|
||||
contents = findContent(info, content)
|
||||
if contents != {}:
|
||||
for i in contents:
|
||||
if info[i]['extract'] == 1 :
|
||||
return True
|
||||
|
||||
# 设置Tab的内容
|
||||
def setMessage(self, content, isRequest):
|
||||
# 判断是否有内容
|
||||
if content:
|
||||
if not isRequest:
|
||||
info = getConfig()
|
||||
contents = findContent(info, content)
|
||||
result = ""
|
||||
for i in contents:
|
||||
if info[i]['extract'] == 1 :
|
||||
result += "[{}] {}\n".format(i,contents[i])
|
||||
self._txtInput.setText(result)
|
||||
else:
|
||||
return False
|
||||
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"Email": {
|
||||
"color": "yellow",
|
||||
"highlight": 1,
|
||||
"regex": "[\\w-]+(?:\\.[\\w-]+)*@(?:[\\w](?:[\\w-]*[\\w])?\\.)+[\\w](?:[\\w-]*[\\w])?",
|
||||
"extract": 1
|
||||
},
|
||||
"Chinese IDCard": {
|
||||
"color": "orange",
|
||||
"highlight": 1,
|
||||
"regex": "[1-9]\\d{5}(?:19|20)\\d\\d(?:0[1-9]|1[012])(?:0[1-9]|[12]\\d|3[01])\\d{3}(?:\\d|X)",
|
||||
"extract": 1
|
||||
},
|
||||
"Chinese Mobile": {
|
||||
"color": "yellow",
|
||||
"highlight": 1,
|
||||
"regex": "[^0-9]+(1[3-9]\\d{9})[^0-9]+",
|
||||
"extract": 1
|
||||
},
|
||||
"Internal IP Address": {
|
||||
"color": "yellow",
|
||||
"highlight": 1,
|
||||
"regex": "(?:10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})|(?:172\\.(?:(?:1[6-9])|(?:2\\d)|(?:3[01]))\\.\\d{1,3}\\.\\d{1,3})|(?:192\\.168\\.\\d{1,3}\\.\\d{1,3})",
|
||||
"extract": 1
|
||||
}
|
||||
}
|
||||
@@ -1,440 +0,0 @@
|
||||
from __future__ import print_function
|
||||
import sys
|
||||
import os
|
||||
import platform
|
||||
import io
|
||||
import getopt
|
||||
import re
|
||||
import string
|
||||
import errno
|
||||
import copy
|
||||
import glob
|
||||
from jsbeautifier.__version__ import __version__
|
||||
from jsbeautifier.javascript.options import BeautifierOptions
|
||||
from jsbeautifier.javascript.beautifier import Beautifier
|
||||
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
|
||||
# Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
|
||||
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation files
|
||||
# (the "Software"), to deal in the Software without restriction,
|
||||
# including without limitation the rights to use, copy, modify, merge,
|
||||
# publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
# and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
#
|
||||
# Originally written by Einar Lielmanis et al.,
|
||||
# Conversion to python by Einar Lielmanis, einar@beautifier.io,
|
||||
# Parsing improvement for brace-less and semicolon-less statements
|
||||
# by Liam Newman <bitwiseman@beautifier.io>
|
||||
# Python is not my native language, feel free to push things around.
|
||||
#
|
||||
# Use either from command line (script displays its usage when run
|
||||
# without any parameters),
|
||||
#
|
||||
#
|
||||
# or, alternatively, use it as a module:
|
||||
#
|
||||
# import jsbeautifier
|
||||
# res = jsbeautifier.beautify('your javascript string')
|
||||
# res = jsbeautifier.beautify_file('some_file.js')
|
||||
#
|
||||
# you may specify some options:
|
||||
#
|
||||
# opts = jsbeautifier.default_options()
|
||||
# opts.indent_size = 2
|
||||
# res = jsbeautifier.beautify('some javascript', opts)
|
||||
#
|
||||
#
|
||||
# Here are the available options: (read source)
|
||||
|
||||
|
||||
class MissingInputStreamError(Exception):
|
||||
pass
|
||||
|
||||
def default_options():
|
||||
return BeautifierOptions()
|
||||
|
||||
|
||||
def beautify(string, opts=default_options()):
|
||||
b = Beautifier()
|
||||
return b.beautify(string, opts)
|
||||
|
||||
|
||||
def set_file_editorconfig_opts(filename, js_options):
|
||||
from editorconfig import get_properties, EditorConfigError
|
||||
try:
|
||||
_ecoptions = get_properties(os.path.abspath(filename))
|
||||
|
||||
if _ecoptions.get("indent_style") == "tab":
|
||||
js_options.indent_with_tabs = True
|
||||
elif _ecoptions.get("indent_style") == "space":
|
||||
js_options.indent_with_tabs = False
|
||||
|
||||
if _ecoptions.get("indent_size"):
|
||||
js_options.indent_size = int(_ecoptions["indent_size"])
|
||||
|
||||
if _ecoptions.get("max_line_length"):
|
||||
if _ecoptions.get("max_line_length") == "off":
|
||||
js_options.wrap_line_length = 0
|
||||
else:
|
||||
js_options.wrap_line_length = int(
|
||||
_ecoptions["max_line_length"])
|
||||
|
||||
if _ecoptions.get("insert_final_newline") == 'true':
|
||||
js_options.end_with_newline = True
|
||||
elif _ecoptions.get("insert_final_newline") == 'false':
|
||||
js_options.end_with_newline = False
|
||||
|
||||
if _ecoptions.get("end_of_line"):
|
||||
if _ecoptions["end_of_line"] == "cr":
|
||||
js_options.eol = '\r'
|
||||
elif _ecoptions["end_of_line"] == "lf":
|
||||
js_options.eol = '\n'
|
||||
elif _ecoptions["end_of_line"] == "crlf":
|
||||
js_options.eol = '\r\n'
|
||||
|
||||
except EditorConfigError:
|
||||
# do not error on bad editor config
|
||||
print("Error loading EditorConfig. Ignoring.", file=sys.stderr)
|
||||
|
||||
def beautify_file(file_name, opts=default_options()):
|
||||
input_string = ''
|
||||
if file_name == '-': # stdin
|
||||
if sys.stdin.isatty():
|
||||
raise MissingInputStreamError()
|
||||
|
||||
stream = sys.stdin
|
||||
if platform.platform().lower().startswith('windows'):
|
||||
if sys.version_info.major >= 3:
|
||||
# for python 3 on windows this prevents conversion
|
||||
stream = io.TextIOWrapper(sys.stdin.buffer, newline='')
|
||||
elif platform.architecture()[0] == '32bit':
|
||||
# for python 2 x86 on windows this prevents conversion
|
||||
import msvcrt
|
||||
msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
|
||||
else:
|
||||
raise Exception('Pipe to stdin not supported on Windows with Python 2.x 64-bit.')
|
||||
|
||||
input_string = stream.read()
|
||||
|
||||
# if you pipe an empty string, that is a failure
|
||||
if input_string == '':
|
||||
raise MissingInputStreamError()
|
||||
else:
|
||||
stream = io.open(file_name, 'rt', newline='', encoding='UTF-8')
|
||||
input_string = stream.read()
|
||||
|
||||
return beautify(input_string, opts)
|
||||
|
||||
|
||||
def usage(stream=sys.stdout):
|
||||
|
||||
print("jsbeautifier.py@" + __version__ + """
|
||||
|
||||
Javascript beautifier (https://beautifier.io/)
|
||||
|
||||
Usage: jsbeautifier.py [options] <infile>
|
||||
|
||||
<infile> can be "-", which means stdin.
|
||||
|
||||
Input options:
|
||||
|
||||
-i, --stdin Read input from stdin
|
||||
|
||||
Output options:
|
||||
|
||||
-s, --indent-size=NUMBER Indentation size. (default 4).
|
||||
-c, --indent-char=CHAR Character to indent with. (default space).
|
||||
-e, --eol=STRING Character(s) to use as line terminators.
|
||||
(default first newline in file, otherwise "\\n")
|
||||
-t, --indent-with-tabs Indent with tabs, overrides -s and -c
|
||||
-d, --disable-preserve-newlines Do not preserve existing line breaks.
|
||||
-P, --space-in-paren Add padding spaces within paren, ie. f( a, b )
|
||||
-E, --space-in-empty-paren Add a single space inside empty paren, ie. f( )
|
||||
-j, --jslint-happy More jslint-compatible output
|
||||
-a, --space-after-anon-function Add a space before an anonymous function's parens, ie. function ()
|
||||
--space-after-named-function Add a space before a named function's parens, i.e. function example ()
|
||||
-b, --brace-style=collapse Brace style (collapse, expand, end-expand, none)(,preserve-inline)
|
||||
-k, --keep-array-indentation Keep array indentation.
|
||||
-r, --replace Write output in-place, replacing input
|
||||
-o, --outfile=FILE Specify a file to output to (default stdout)
|
||||
-f, --keep-function-indentation Do not re-indent function bodies defined in var lines.
|
||||
-x, --unescape-strings Decode printable chars encoded in \\xNN notation.
|
||||
-X, --e4x Pass E4X xml literals through untouched
|
||||
-C, --comma-first Put commas at the beginning of new line instead of end.
|
||||
-O, --operator-position=STRING Set operator position (before-newline, after-newline, preserve-newline)
|
||||
-w, --wrap-line-length Attempt to wrap line when it exceeds this length.
|
||||
NOTE: Line continues until next wrap point is found.
|
||||
-n, --end-with-newline End output with newline
|
||||
--indent-empty-lines Keep indentation on empty lines
|
||||
--templating List of templating languages (auto,none,django,erb,handlebars,php) ["auto"] auto = none in JavaScript, all in html
|
||||
--editorconfig Enable setting configuration from EditorConfig
|
||||
|
||||
Rarely needed options:
|
||||
|
||||
--eval-code evaluate code if a JS interpreter is
|
||||
installed. May be useful with some obfuscated
|
||||
script but poses a potential security issue.
|
||||
|
||||
-l, --indent-level=NUMBER Initial indentation level. (default 0).
|
||||
|
||||
-h, --help, --usage Prints this help statement.
|
||||
-v, --version Show the version
|
||||
|
||||
""", file=stream)
|
||||
if stream == sys.stderr:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
def mkdir_p(path):
|
||||
try:
|
||||
if path:
|
||||
os.makedirs(path)
|
||||
except OSError as exc: # Python >2.5
|
||||
if exc.errno == errno.EEXIST and os.path.isdir(path):
|
||||
pass
|
||||
else:
|
||||
raise Exception()
|
||||
|
||||
|
||||
def isFileDifferent(filepath, expected):
|
||||
try:
|
||||
return (
|
||||
''.join(
|
||||
io.open(
|
||||
filepath,
|
||||
'rt',
|
||||
newline='').readlines()) != expected)
|
||||
except BaseException:
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
argv = sys.argv[1:]
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(argv, "f:s:c:e:o:rdEPjab:kil:xhtvXnCO:w:m:",
|
||||
['file=', 'indent-size=', 'indent-char=', 'eol=', 'outfile=', 'replace', 'disable-preserve-newlines',
|
||||
'space-in-paren', 'space-in-empty-paren', 'jslint-happy', 'space-after-anon-function',
|
||||
'brace-style=', 'indent-level=', 'unescape-strings',
|
||||
'help', 'usage', 'stdin', 'eval-code', 'indent-with-tabs', 'keep-function-indentation', 'version',
|
||||
'e4x', 'end-with-newline', 'comma-first', 'operator-position=', 'wrap-line-length', 'editorconfig', 'space-after-named-function',
|
||||
'keep-array-indentation', 'indent-empty-lines', 'templating'])
|
||||
except getopt.GetoptError as ex:
|
||||
print(ex, file=sys.stderr)
|
||||
return usage(sys.stderr)
|
||||
|
||||
js_options = default_options()
|
||||
|
||||
filepath_params = []
|
||||
filepath_params.extend(args)
|
||||
|
||||
outfile_param = 'stdout'
|
||||
replace = False
|
||||
|
||||
for opt, arg in opts:
|
||||
if opt in ('--file', '-f'):
|
||||
filepath_params.append(arg)
|
||||
elif opt in ('--keep-array-indentation', '-k'):
|
||||
js_options.keep_array_indentation = True
|
||||
elif opt in ('--keep-function-indentation'):
|
||||
js_options.keep_function_indentation = True
|
||||
elif opt in ('--outfile', '-o'):
|
||||
outfile_param = arg
|
||||
elif opt in ('--replace', '-r'):
|
||||
replace = True
|
||||
elif opt in ('--indent-size', '-s'):
|
||||
js_options.indent_size = int(arg)
|
||||
elif opt in ('--indent-char', '-c'):
|
||||
js_options.indent_char = arg
|
||||
elif opt in ('--eol', '-e'):
|
||||
js_options.eol = arg
|
||||
elif opt in ('--indent-with-tabs', '-t'):
|
||||
js_options.indent_with_tabs = True
|
||||
elif opt in ('--disable-preserve-newlines', '-d'):
|
||||
js_options.preserve_newlines = False
|
||||
elif opt in ('--max-preserve-newlines', '-m'):
|
||||
js_options.max_preserve_newlines = int(arg)
|
||||
elif opt in ('--space-in-paren', '-P'):
|
||||
js_options.space_in_paren = True
|
||||
elif opt in ('--space-in-empty-paren', '-E'):
|
||||
js_options.space_in_empty_paren = True
|
||||
elif opt in ('--jslint-happy', '-j'):
|
||||
js_options.jslint_happy = True
|
||||
elif opt in ('--space-after-anon-function', '-a'):
|
||||
js_options.space_after_anon_function = True
|
||||
elif opt in ('--space-after-named-function'):
|
||||
js_options.space_after_named_function = True
|
||||
elif opt in ('--eval-code'):
|
||||
js_options.eval_code = True
|
||||
elif opt in ('--brace-style', '-b'):
|
||||
js_options.brace_style = arg
|
||||
elif opt in ('--unescape-strings', '-x'):
|
||||
js_options.unescape_strings = True
|
||||
elif opt in ('--e4x', '-X'):
|
||||
js_options.e4x = True
|
||||
elif opt in ('--end-with-newline', '-n'):
|
||||
js_options.end_with_newline = True
|
||||
elif opt in ('--comma-first', '-C'):
|
||||
js_options.comma_first = True
|
||||
elif opt in ('--operator-position', '-O'):
|
||||
js_options.operator_position = arg
|
||||
elif opt in ('--wrap-line-length ', '-w'):
|
||||
js_options.wrap_line_length = int(arg)
|
||||
elif opt in ('--indent-empty-lines'):
|
||||
js_options.indent_empty_lines = True
|
||||
elif opt in ('--templating'):
|
||||
js_options.templating = arg.split(',')
|
||||
elif opt in ('--stdin', '-i'):
|
||||
# stdin is the default if no files are passed
|
||||
filepath_params = []
|
||||
elif opt in ('--editorconfig'):
|
||||
js_options.editorconfig = True
|
||||
elif opt in ('--version', '-v'):
|
||||
return print(__version__)
|
||||
elif opt in ('--help', '--usage', '-h'):
|
||||
return usage()
|
||||
|
||||
try:
|
||||
filepaths = []
|
||||
if not filepath_params or (
|
||||
len(filepath_params) == 1 and filepath_params[0] == '-'):
|
||||
# default to stdin
|
||||
filepath_params = []
|
||||
filepaths.append('-')
|
||||
|
||||
|
||||
for filepath_param in filepath_params:
|
||||
# ignore stdin setting if files are specified
|
||||
if '-' == filepath_param:
|
||||
continue
|
||||
|
||||
# Check if each literal filepath exists
|
||||
if os.path.isfile(filepath_param):
|
||||
filepaths.append(filepath_param)
|
||||
elif '*' in filepath_param or '?' in filepath_param:
|
||||
# handle globs
|
||||
# empty result is okay
|
||||
if sys.version_info.major == 2 or (
|
||||
sys.version_info.major == 3 and
|
||||
sys.version_info.minor <= 4):
|
||||
if '**' in filepath_param:
|
||||
raise Exception('Recursive globs not supported on Python <= 3.4.')
|
||||
filepaths.extend(glob.glob(filepath_param))
|
||||
else:
|
||||
filepaths.extend(glob.glob(filepath_param, recursive=True))
|
||||
else:
|
||||
# not a glob and not a file
|
||||
raise OSError(errno.ENOENT, os.strerror(errno.ENOENT),
|
||||
filepath_param)
|
||||
|
||||
if len(filepaths) > 1:
|
||||
replace = True
|
||||
elif filepaths and filepaths[0] == '-':
|
||||
replace = False
|
||||
|
||||
# remove duplicates
|
||||
filepaths = set(filepaths)
|
||||
|
||||
for filepath in filepaths:
|
||||
if not replace:
|
||||
outfile = outfile_param
|
||||
else:
|
||||
outfile = filepath
|
||||
|
||||
# Editorconfig used only on files, not stdin
|
||||
if getattr(js_options, 'editorconfig'):
|
||||
editorconfig_filepath = filepath
|
||||
|
||||
if editorconfig_filepath == '-':
|
||||
if outfile != 'stdout':
|
||||
editorconfig_filepath = outfile
|
||||
else:
|
||||
fileType = 'js'
|
||||
editorconfig_filepath = 'stdin.' + fileType
|
||||
|
||||
# debug("EditorConfig is enabled for ", editorconfig_filepath);
|
||||
js_options = copy.copy(js_options)
|
||||
set_file_editorconfig_opts(editorconfig_filepath, js_options)
|
||||
|
||||
pretty = beautify_file(filepath, js_options)
|
||||
|
||||
if outfile == 'stdout':
|
||||
stream = sys.stdout
|
||||
|
||||
# python automatically converts newlines in text to "\r\n" when on windows
|
||||
# switch to binary to prevent this
|
||||
if platform.platform().lower().startswith('windows'):
|
||||
if sys.version_info.major >= 3:
|
||||
# for python 3 on windows this prevents conversion
|
||||
stream = io.TextIOWrapper(sys.stdout.buffer, newline='')
|
||||
elif platform.architecture()[0] == '32bit':
|
||||
# for python 2 x86 on windows this prevents conversion
|
||||
import msvcrt
|
||||
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
|
||||
else:
|
||||
raise Exception('Pipe to stdout not supported on Windows with Python 2.x 64-bit.')
|
||||
|
||||
stream.write(pretty)
|
||||
else:
|
||||
if isFileDifferent(outfile, pretty):
|
||||
mkdir_p(os.path.dirname(outfile))
|
||||
|
||||
# python automatically converts newlines in text to "\r\n" when on windows
|
||||
# set newline to empty to prevent this
|
||||
with io.open(outfile, 'wt', newline='', encoding='UTF-8') as f:
|
||||
print('beautified ' + outfile, file=sys.stdout)
|
||||
try:
|
||||
f.write(pretty)
|
||||
except TypeError:
|
||||
# This is not pretty, but given how we did the version import
|
||||
# it is the only way to do this without having setup.py
|
||||
# fail on a missing six dependency.
|
||||
six = __import__("six")
|
||||
f.write(six.u(pretty))
|
||||
else:
|
||||
print('beautified ' + outfile + ' - unchanged', file=sys.stdout)
|
||||
|
||||
|
||||
except MissingInputStreamError:
|
||||
print(
|
||||
"Must pipe input or define at least one file.\n",
|
||||
file=sys.stderr)
|
||||
usage(sys.stderr)
|
||||
return 1
|
||||
|
||||
except UnicodeError as ex:
|
||||
print("Error while decoding input or encoding output:",
|
||||
file=sys.stderr)
|
||||
print(ex, file=sys.stderr)
|
||||
return 1
|
||||
|
||||
except Exception as ex:
|
||||
print(ex, file=sys.stderr)
|
||||
return 1
|
||||
|
||||
# Success
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1 +0,0 @@
|
||||
__version__ = '1.11.0'
|
||||
@@ -1 +0,0 @@
|
||||
# Empty file :)
|
||||
@@ -1,53 +0,0 @@
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation files
|
||||
# (the "Software"), to deal in the Software without restriction,
|
||||
# including without limitation the rights to use, copy, modify, merge,
|
||||
# publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
# and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import re
|
||||
|
||||
|
||||
class Directives:
|
||||
|
||||
def __init__(self, start_block_pattern, end_block_pattern):
|
||||
|
||||
self.__directives_block_pattern = re.compile(start_block_pattern + r' beautify( \w+[:]\w+)+ ' + end_block_pattern)
|
||||
self.__directive_pattern = re.compile(r' (\w+)[:](\w+)')
|
||||
|
||||
self.__directives_end_ignore_pattern = re.compile(start_block_pattern + r'\sbeautify\signore:end\s' + end_block_pattern)
|
||||
|
||||
def get_directives(self, text):
|
||||
if not self.__directives_block_pattern.match(text):
|
||||
return None
|
||||
|
||||
directives = {}
|
||||
directive_match = self.__directive_pattern.search(text)
|
||||
|
||||
while directive_match:
|
||||
directives[directive_match.group(1)] = directive_match.group(2)
|
||||
directive_match = self.__directive_pattern.search(
|
||||
text, directive_match.end())
|
||||
|
||||
|
||||
return directives
|
||||
|
||||
def readIgnored(self, input):
|
||||
return input.readUntilAfter(self.__directives_end_ignore_pattern)
|
||||
@@ -1,136 +0,0 @@
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation files
|
||||
# (the "Software"), to deal in the Software without restriction,
|
||||
# including without limitation the rights to use, copy, modify, merge,
|
||||
# publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
# and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import re
|
||||
|
||||
|
||||
class InputScanner:
|
||||
def __init__(self, input_string):
|
||||
self.__six = __import__("six")
|
||||
if input_string is None:
|
||||
input_string = ''
|
||||
self.__input = input_string
|
||||
self.__input_length = len(self.__input)
|
||||
self.__position = 0
|
||||
|
||||
def restart(self):
|
||||
self.__position = 0
|
||||
|
||||
def back(self):
|
||||
if self.__position > 0:
|
||||
self.__position -= 1
|
||||
|
||||
def hasNext(self):
|
||||
return self.__position < self.__input_length
|
||||
|
||||
def next(self):
|
||||
val = None
|
||||
if self.hasNext():
|
||||
val = self.__input[self.__position]
|
||||
self.__position += 1
|
||||
|
||||
return val
|
||||
|
||||
def peek(self, index=0):
|
||||
val = None
|
||||
index += self.__position
|
||||
if index >= 0 and index < self.__input_length:
|
||||
val = self.__input[index]
|
||||
|
||||
return val
|
||||
|
||||
def test(self, pattern, index=0):
|
||||
index += self.__position
|
||||
return index >= 0 and index < self.__input_length and bool(
|
||||
pattern.match(self.__input, index))
|
||||
|
||||
def testChar(self, pattern, index=0):
|
||||
# test one character regex match
|
||||
val = self.peek(index)
|
||||
return val is not None and bool(pattern.match(val))
|
||||
|
||||
def match(self, pattern):
|
||||
pattern_match = None
|
||||
if self.hasNext():
|
||||
pattern_match = pattern.match(self.__input, self.__position)
|
||||
if bool(pattern_match):
|
||||
self.__position = pattern_match.end(0)
|
||||
return pattern_match
|
||||
|
||||
def read(self, starting_pattern, until_pattern=None, until_after=False):
|
||||
val = ''
|
||||
pattern_match = None
|
||||
if bool(starting_pattern):
|
||||
pattern_match = self.match(starting_pattern)
|
||||
if bool(pattern_match):
|
||||
val = pattern_match.group(0)
|
||||
|
||||
if bool(until_pattern) and \
|
||||
(bool(pattern_match) or not bool(starting_pattern)):
|
||||
val += self.readUntil(until_pattern, until_after)
|
||||
|
||||
return val
|
||||
|
||||
def readUntil(self, pattern, include_match=False):
|
||||
val = ''
|
||||
pattern_match = None
|
||||
match_index = self.__position
|
||||
if self.hasNext():
|
||||
pattern_match = pattern.search(self.__input, self.__position)
|
||||
if bool(pattern_match):
|
||||
if include_match:
|
||||
match_index = pattern_match.end(0)
|
||||
else:
|
||||
match_index = pattern_match.start(0)
|
||||
else:
|
||||
match_index = self.__input_length
|
||||
|
||||
val = self.__input[self.__position:match_index]
|
||||
self.__position = match_index
|
||||
|
||||
return val
|
||||
|
||||
def readUntilAfter(self, pattern):
|
||||
return self.readUntil(pattern, True)
|
||||
|
||||
def get_regexp(self, pattern, match_from=False):
|
||||
result = None
|
||||
# strings are converted to regexp
|
||||
if isinstance(pattern, self.__six.string_types) and pattern != '':
|
||||
result = re.compile(pattern)
|
||||
elif pattern is not None:
|
||||
result = re.compile(pattern.pattern)
|
||||
return result
|
||||
|
||||
# css beautifier legacy helpers
|
||||
def peekUntilAfter(self, pattern):
|
||||
start = self.__position
|
||||
val = self.readUntilAfter(pattern)
|
||||
self.__position = start
|
||||
return val
|
||||
|
||||
def lookBack(self, testVal):
|
||||
start = self.__position - 1
|
||||
return start >= len(testVal) and \
|
||||
self.__input[start - len(testVal):start].lower() == testVal
|
||||
@@ -1,216 +0,0 @@
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation files
|
||||
# (the "Software"), to deal in the Software without restriction,
|
||||
# including without limitation the rights to use, copy, modify, merge,
|
||||
# publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
# and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import copy
|
||||
import re
|
||||
from collections import namedtuple
|
||||
|
||||
|
||||
class Options:
|
||||
def __init__(self, options=None, merge_child_field=None):
|
||||
self.css = None
|
||||
self.js = None
|
||||
self.html = None
|
||||
|
||||
self.raw_options = _mergeOpts(options, merge_child_field)
|
||||
|
||||
# Support passing the source text back with no change
|
||||
self.disabled = self._get_boolean('disabled')
|
||||
|
||||
self.eol = self._get_characters('eol', 'auto')
|
||||
self.end_with_newline = self._get_boolean('end_with_newline')
|
||||
self.indent_size = self._get_number('indent_size', 4)
|
||||
self.indent_char = self._get_characters('indent_char', ' ')
|
||||
self.indent_level = self._get_number('indent_level')
|
||||
|
||||
self.preserve_newlines = self._get_boolean('preserve_newlines', True)
|
||||
self.max_preserve_newlines = self._get_number(
|
||||
'max_preserve_newlines', 32786)
|
||||
|
||||
if not self.preserve_newlines:
|
||||
self.max_preserve_newlines = 0
|
||||
|
||||
self.indent_with_tabs = self._get_boolean(
|
||||
'indent_with_tabs', self.indent_char == '\t')
|
||||
if self.indent_with_tabs:
|
||||
self.indent_char = '\t'
|
||||
|
||||
# indent_size behavior changed after 1.8.6
|
||||
# It used to be that indent_size would be
|
||||
# set to 1 for indent_with_tabs. That is no longer needed and
|
||||
# actually doesn't make sense - why not use spaces? Further,
|
||||
# that might produce unexpected behavior - tabs being used
|
||||
# for single-column alignment. So, when indent_with_tabs is true
|
||||
# and indent_size is 1, reset indent_size to 4.
|
||||
if self.indent_size == 1:
|
||||
self.indent_size = 4
|
||||
|
||||
# Backwards compat with 1.3.x
|
||||
self.wrap_line_length = self._get_number(
|
||||
'wrap_line_length', self._get_number('max_char'))
|
||||
|
||||
self.indent_empty_lines = self._get_boolean('indent_empty_lines')
|
||||
|
||||
|
||||
# valid templating languages ['django', 'erb', 'handlebars', 'php']
|
||||
# For now, 'auto' = all off for javascript, all on for html (and inline javascript).
|
||||
# other values ignored
|
||||
self.templating = self._get_selection_list('templating',
|
||||
['auto', 'none', 'django', 'erb', 'handlebars', 'php'], ['auto'])
|
||||
|
||||
|
||||
def _get_array(self, name, default_value=[]):
|
||||
option_value = getattr(self.raw_options, name, default_value)
|
||||
result = []
|
||||
if isinstance(option_value, list):
|
||||
result = copy.copy(option_value)
|
||||
elif isinstance(option_value, str):
|
||||
result = re.compile(r"[^a-zA-Z0-9_/\-]+").split(option_value)
|
||||
|
||||
return result
|
||||
|
||||
def _get_boolean(self, name, default_value=False):
|
||||
option_value = getattr(self.raw_options, name, default_value)
|
||||
result = False
|
||||
try:
|
||||
result = bool(option_value)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
return result
|
||||
|
||||
def _get_characters(self, name, default_value=''):
|
||||
option_value = getattr(self.raw_options, name, default_value)
|
||||
result = ''
|
||||
if isinstance(option_value, str):
|
||||
result = option_value.replace('\\r', '\r').replace(
|
||||
'\\n', '\n').replace('\\t', '\t')
|
||||
|
||||
return result
|
||||
|
||||
def _get_number(self, name, default_value=0):
|
||||
option_value = getattr(self.raw_options, name, default_value)
|
||||
result = 0
|
||||
try:
|
||||
result = int(option_value)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
return result
|
||||
|
||||
def _get_selection(self, name, selection_list, default_value=None):
|
||||
result = self._get_selection_list(name, selection_list, default_value)
|
||||
if len(result) != 1:
|
||||
raise ValueError(
|
||||
"Invalid Option Value: The option '" + name + "' can only be one of the following values:\n" +
|
||||
str(selection_list) +
|
||||
"\nYou passed in: '" +
|
||||
str(getattr(self.raw_options, name, None)) +
|
||||
"'")
|
||||
|
||||
return result[0]
|
||||
|
||||
def _get_selection_list(self, name, selection_list, default_value=None):
|
||||
if not selection_list:
|
||||
raise ValueError("Selection list cannot be empty.")
|
||||
|
||||
default_value = default_value or [selection_list[0]]
|
||||
|
||||
if not self._is_valid_selection(default_value, selection_list):
|
||||
raise ValueError("Invalid Default Value!")
|
||||
|
||||
result = self._get_array(name, default_value)
|
||||
if not self._is_valid_selection(result, selection_list):
|
||||
raise ValueError(
|
||||
"Invalid Option Value: The option '" + name + "' can contain only the following values:\n" +
|
||||
str(selection_list) +
|
||||
"\nYou passed in: '" +
|
||||
str(getattr(self.raw_options, name, None)) +
|
||||
"'")
|
||||
|
||||
return result
|
||||
|
||||
def _is_valid_selection(self, result, selection_list):
|
||||
if len(result) == 0 or len(selection_list) == 0:
|
||||
return False
|
||||
|
||||
for item in result:
|
||||
if item not in selection_list:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
# merges child options up with the parent options object
|
||||
# Example: obj = {a: 1, b: {a: 2}}
|
||||
# mergeOpts(obj, 'b')
|
||||
#
|
||||
# Returns: {a: 2}
|
||||
|
||||
|
||||
def _mergeOpts(options, childFieldName):
|
||||
if options is None:
|
||||
options = {}
|
||||
|
||||
if isinstance(options, tuple):
|
||||
options = dict(options)
|
||||
|
||||
options = _normalizeOpts(options)
|
||||
finalOpts = copy.copy(options)
|
||||
if isinstance(options, dict):
|
||||
local = finalOpts.get(childFieldName, None)
|
||||
if local:
|
||||
del(finalOpts[childFieldName])
|
||||
for key in local:
|
||||
finalOpts[key] = local[key]
|
||||
finalOpts = namedtuple("CustomOptions", finalOpts.keys())(
|
||||
*finalOpts.values())
|
||||
|
||||
if isinstance(options, Options):
|
||||
local = getattr(finalOpts, childFieldName, None)
|
||||
if local:
|
||||
delattr(finalOpts, childFieldName)
|
||||
for key in local:
|
||||
setattr(finalOpts, key, local[key])
|
||||
|
||||
return finalOpts
|
||||
|
||||
|
||||
def _normalizeOpts(options):
|
||||
convertedOpts = copy.copy(options)
|
||||
if isinstance(convertedOpts, dict):
|
||||
option_keys = list(convertedOpts.keys())
|
||||
for key in option_keys:
|
||||
if '-' in key:
|
||||
del convertedOpts[key]
|
||||
convertedOpts[key.replace('-', '_')] = options[key]
|
||||
else:
|
||||
option_keys = list(getattr(convertedOpts, '__dict__', {}))
|
||||
for key in option_keys:
|
||||
if '-' in key:
|
||||
delattr(convertedOpts, key)
|
||||
setattr(convertedOpts, key.replace(
|
||||
'-', '_'), getattr(options, key, None))
|
||||
|
||||
return convertedOpts
|
||||
@@ -1,348 +0,0 @@
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation files
|
||||
# (the "Software"), to deal in the Software without restriction,
|
||||
# including without limitation the rights to use, copy, modify, merge,
|
||||
# publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
# and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import re
|
||||
import math
|
||||
|
||||
# Using object instead of string to allow for later expansion of info
|
||||
# about each line
|
||||
|
||||
__all__ = ["Output"]
|
||||
|
||||
|
||||
class OutputLine:
|
||||
def __init__(self, parent):
|
||||
self.__parent = parent
|
||||
self.__character_count = 0
|
||||
self.__indent_count = -1
|
||||
self.__alignment_count = 0
|
||||
self.__wrap_point_index = 0
|
||||
self.__wrap_point_character_count = 0
|
||||
self.__wrap_point_indent_count = -1
|
||||
self.__wrap_point_alignment_count = 0
|
||||
|
||||
self.__items = []
|
||||
|
||||
def clone_empty(self):
|
||||
line = OutputLine(self.__parent)
|
||||
line.set_indent(self.__indent_count, self.__alignment_count)
|
||||
return line
|
||||
|
||||
def item(self, index):
|
||||
return self.__items[index]
|
||||
|
||||
def is_empty(self):
|
||||
return len(self.__items) == 0
|
||||
|
||||
def set_indent(self, indent=0, alignment=0):
|
||||
if self.is_empty():
|
||||
self.__indent_count = indent
|
||||
self.__alignment_count = alignment
|
||||
self.__character_count = self.__parent.get_indent_size(
|
||||
self.__indent_count, self.__alignment_count)
|
||||
|
||||
def _set_wrap_point(self):
|
||||
if self.__parent.wrap_line_length:
|
||||
self.__wrap_point_index = len(self.__items)
|
||||
self.__wrap_point_character_count = self.__character_count
|
||||
self.__wrap_point_indent_count = \
|
||||
self.__parent.next_line.__indent_count
|
||||
self.__wrap_point_alignment_count = \
|
||||
self.__parent.next_line.__alignment_count
|
||||
|
||||
def _should_wrap(self):
|
||||
return self.__wrap_point_index and \
|
||||
self.__character_count > \
|
||||
self.__parent.wrap_line_length and \
|
||||
self.__wrap_point_character_count > \
|
||||
self.__parent.next_line.__character_count
|
||||
|
||||
|
||||
def _allow_wrap(self):
|
||||
if self._should_wrap():
|
||||
self.__parent.add_new_line()
|
||||
next = self.__parent.current_line
|
||||
next.set_indent(self.__wrap_point_indent_count,
|
||||
self.__wrap_point_alignment_count)
|
||||
next.__items = self.__items[self.__wrap_point_index:]
|
||||
self.__items = self.__items[:self.__wrap_point_index]
|
||||
|
||||
next.__character_count += self.__character_count - \
|
||||
self.__wrap_point_character_count
|
||||
self.__character_count = self.__wrap_point_character_count
|
||||
|
||||
if next.__items[0] == " ":
|
||||
next.__items.pop(0)
|
||||
next.__character_count -= 1
|
||||
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def last(self):
|
||||
if not self.is_empty():
|
||||
return self.__items[-1]
|
||||
|
||||
return None
|
||||
|
||||
def push(self, item):
|
||||
self.__items.append(item)
|
||||
last_newline_index = item.rfind('\n')
|
||||
if last_newline_index != -1:
|
||||
self.__character_count = len(item) - last_newline_index
|
||||
else:
|
||||
self.__character_count += len(item)
|
||||
|
||||
def pop(self):
|
||||
item = None
|
||||
if not self.is_empty():
|
||||
item = self.__items.pop()
|
||||
self.__character_count -= len(item)
|
||||
return item
|
||||
|
||||
def _remove_indent(self):
|
||||
if self.__indent_count > 0:
|
||||
self.__indent_count -= 1
|
||||
self.__character_count -= self.__parent.indent_size
|
||||
|
||||
def _remove_wrap_indent(self):
|
||||
if self.__wrap_point_indent_count > 0:
|
||||
self.__wrap_point_indent_count -= 1
|
||||
|
||||
def trim(self):
|
||||
while self.last() == ' ':
|
||||
self.__items.pop()
|
||||
self.__character_count -= 1
|
||||
|
||||
def toString(self):
|
||||
result = ''
|
||||
if self.is_empty():
|
||||
if self.__parent.indent_empty_lines:
|
||||
result = self.__parent.get_indent_string(self.__indent_count)
|
||||
else:
|
||||
result = self.__parent.get_indent_string(
|
||||
self.__indent_count, self.__alignment_count)
|
||||
result += ''.join(self.__items)
|
||||
return result
|
||||
|
||||
|
||||
class IndentStringCache:
|
||||
def __init__(self, options, base_string):
|
||||
self.__cache = ['']
|
||||
self.__indent_size = options.indent_size
|
||||
self.__indent_string = options.indent_char
|
||||
if not options.indent_with_tabs:
|
||||
self.__indent_string = options.indent_char * options.indent_size
|
||||
|
||||
# Set to null to continue support of auto detection of base indent
|
||||
base_string = base_string or ''
|
||||
if options.indent_level > 0:
|
||||
base_string = options.indent_level * self.__indent_string
|
||||
|
||||
self.__base_string = base_string
|
||||
self.__base_string_length = len(base_string)
|
||||
|
||||
def get_indent_size(self, indent, column=0):
|
||||
result = self.__base_string_length
|
||||
if indent < 0:
|
||||
result = 0
|
||||
result += indent * self.__indent_size
|
||||
result += column
|
||||
return result
|
||||
|
||||
def get_indent_string(self, indent_level, column=0):
|
||||
result = self.__base_string
|
||||
if indent_level < 0:
|
||||
indent_level = 0
|
||||
result = ''
|
||||
column += indent_level * self.__indent_size
|
||||
self.__ensure_cache(column)
|
||||
result += self.__cache[column]
|
||||
return result
|
||||
|
||||
def __ensure_cache(self, column):
|
||||
while column >= len(self.__cache):
|
||||
self.__add_column()
|
||||
|
||||
def __add_column(self):
|
||||
column = len(self.__cache)
|
||||
indent = 0
|
||||
result = ''
|
||||
if self.__indent_size and column >= self.__indent_size:
|
||||
indent = int(math.floor(column / self.__indent_size))
|
||||
column -= indent * self.__indent_size
|
||||
result = indent * self.__indent_string
|
||||
if column:
|
||||
result += column * ' '
|
||||
self.__cache.append(result)
|
||||
|
||||
|
||||
class Output:
|
||||
def __init__(self, options, baseIndentString=''):
|
||||
|
||||
self.__indent_cache = IndentStringCache(options, baseIndentString)
|
||||
self.raw = False
|
||||
self._end_with_newline = options.end_with_newline
|
||||
self.indent_size = options.indent_size
|
||||
self.wrap_line_length = options.wrap_line_length
|
||||
self.indent_empty_lines = options.indent_empty_lines
|
||||
self.__lines = []
|
||||
self.previous_line = None
|
||||
self.current_line = None
|
||||
self.next_line = OutputLine(self)
|
||||
self.space_before_token = False
|
||||
self.non_breaking_space = False
|
||||
self.previous_token_wrapped = False
|
||||
# initialize
|
||||
self.__add_outputline()
|
||||
|
||||
def __add_outputline(self):
|
||||
self.previous_line = self.current_line
|
||||
self.current_line = self.next_line.clone_empty()
|
||||
self.__lines.append(self.current_line)
|
||||
|
||||
def get_line_number(self):
|
||||
return len(self.__lines)
|
||||
|
||||
def get_indent_string(self, indent, column=0):
|
||||
return self.__indent_cache.get_indent_string(indent, column)
|
||||
|
||||
def get_indent_size(self, indent, column=0):
|
||||
return self.__indent_cache.get_indent_size(indent, column)
|
||||
|
||||
def is_empty(self):
|
||||
return self.previous_line is None and self.current_line.is_empty()
|
||||
|
||||
def add_new_line(self, force_newline=False):
|
||||
# never newline at the start of file
|
||||
# otherwise, newline only if we didn't just add one or we're forced
|
||||
if self.is_empty() or \
|
||||
(not force_newline and self.just_added_newline()):
|
||||
return False
|
||||
|
||||
# if raw output is enabled, don't print additional newlines,
|
||||
# but still return True as though you had
|
||||
if not self.raw:
|
||||
self.__add_outputline()
|
||||
return True
|
||||
|
||||
def get_code(self, eol):
|
||||
self.trim(True)
|
||||
|
||||
# handle some edge cases where the last tokens
|
||||
# has text that ends with newline(s)
|
||||
last_item = self.current_line.pop()
|
||||
if last_item:
|
||||
if last_item[-1] == '\n':
|
||||
last_item = re.sub(r'[\n]+$', '', last_item)
|
||||
self.current_line.push(last_item)
|
||||
|
||||
if self._end_with_newline:
|
||||
self.__add_outputline()
|
||||
|
||||
sweet_code = "\n".join(line.toString() for line in self.__lines)
|
||||
|
||||
if not eol == '\n':
|
||||
sweet_code = sweet_code.replace('\n', eol)
|
||||
|
||||
return sweet_code
|
||||
|
||||
def set_wrap_point(self):
|
||||
self.current_line._set_wrap_point()
|
||||
|
||||
def set_indent(self, indent=0, alignment=0):
|
||||
# Next line stores alignment values
|
||||
self.next_line.set_indent(indent, alignment)
|
||||
|
||||
# Never indent your first output indent at the start of the file
|
||||
if len(self.__lines) > 1:
|
||||
self.current_line.set_indent(indent, alignment)
|
||||
return True
|
||||
self.current_line.set_indent()
|
||||
return False
|
||||
|
||||
def add_raw_token(self, token):
|
||||
for _ in range(token.newlines):
|
||||
self.__add_outputline()
|
||||
|
||||
self.current_line.set_indent(-1)
|
||||
self.current_line.push(token.whitespace_before)
|
||||
self.current_line.push(token.text)
|
||||
self.space_before_token = False
|
||||
self.non_breaking_space = False
|
||||
self.previous_token_wrapped = False
|
||||
|
||||
def add_token(self, printable_token):
|
||||
self.__add_space_before_token()
|
||||
self.current_line.push(printable_token)
|
||||
self.space_before_token = False
|
||||
self.non_breaking_space = False
|
||||
self.previous_token_wrapped = self.current_line._allow_wrap()
|
||||
|
||||
def __add_space_before_token(self):
|
||||
if self.space_before_token and not self.just_added_newline():
|
||||
if not self.non_breaking_space:
|
||||
self.set_wrap_point()
|
||||
self.current_line.push(' ')
|
||||
self.space_before_token = False
|
||||
|
||||
def remove_indent(self, index):
|
||||
while index < len(self.__lines):
|
||||
self.__lines[index]._remove_indent()
|
||||
index += 1
|
||||
self.current_line._remove_wrap_indent()
|
||||
|
||||
def trim(self, eat_newlines=False):
|
||||
self.current_line.trim()
|
||||
|
||||
while eat_newlines and len(
|
||||
self.__lines) > 1 and self.current_line.is_empty():
|
||||
self.__lines.pop()
|
||||
self.current_line = self.__lines[-1]
|
||||
self.current_line.trim()
|
||||
|
||||
if len(self.__lines) > 1:
|
||||
self.previous_line = self.__lines[-2]
|
||||
else:
|
||||
self.previous_line = None
|
||||
|
||||
def just_added_newline(self):
|
||||
return self.current_line.is_empty()
|
||||
|
||||
def just_added_blankline(self):
|
||||
return self.is_empty() or \
|
||||
(self.current_line.is_empty() and self.previous_line.is_empty())
|
||||
|
||||
def ensure_empty_line_above(self, starts_with, ends_with):
|
||||
index = len(self.__lines) - 2
|
||||
while index >= 0:
|
||||
potentialEmptyLine = self.__lines[index]
|
||||
if potentialEmptyLine.is_empty():
|
||||
break
|
||||
elif not potentialEmptyLine.item(0).startswith(starts_with) and \
|
||||
potentialEmptyLine.item(-1) != ends_with:
|
||||
self.__lines.insert(index + 1, OutputLine(self))
|
||||
self.previous_line = self.__lines[-2]
|
||||
break
|
||||
index -= 1
|
||||
@@ -1,82 +0,0 @@
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation files
|
||||
# (the "Software"), to deal in the Software without restriction,
|
||||
# including without limitation the rights to use, copy, modify, merge,
|
||||
# publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
# and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
__all__ = ["Pattern"]
|
||||
|
||||
class Pattern:
|
||||
def __init__(self, input_scanner, parent=None):
|
||||
self._input = input_scanner
|
||||
self._starting_pattern = None
|
||||
self._match_pattern = None
|
||||
self._until_pattern = None
|
||||
self._until_after = False
|
||||
|
||||
if parent is not None:
|
||||
self._starting_pattern = self._input.get_regexp(parent._starting_pattern)
|
||||
self._match_pattern = self._input.get_regexp(parent._match_pattern)
|
||||
self._until_pattern = self._input.get_regexp(parent._until_pattern)
|
||||
self._until_after = parent._until_after
|
||||
|
||||
def read(self):
|
||||
result = self._input.read(self._starting_pattern)
|
||||
if (self._starting_pattern is None) or result:
|
||||
result += self._input.read(self._match_pattern,
|
||||
self._until_pattern, self._until_after)
|
||||
return result
|
||||
|
||||
def read_match(self):
|
||||
return self._input.match(self._match_pattern)
|
||||
|
||||
def until_after(self, pattern):
|
||||
result = self._create()
|
||||
result._until_after = True
|
||||
result._until_pattern = self._input.get_regexp(pattern)
|
||||
result._update()
|
||||
return result
|
||||
|
||||
def until(self, pattern):
|
||||
result = self._create()
|
||||
result._until_after = False
|
||||
result._until_pattern = self._input.get_regexp(pattern)
|
||||
result._update()
|
||||
return result
|
||||
|
||||
def starting_with(self, pattern):
|
||||
result = self._create()
|
||||
result._starting_pattern = self._input.get_regexp(pattern)
|
||||
result._update()
|
||||
return result
|
||||
|
||||
def matching(self, pattern):
|
||||
result = self._create()
|
||||
result._match_pattern = self._input.get_regexp(pattern)
|
||||
result._update()
|
||||
return result
|
||||
|
||||
def _create(self):
|
||||
return Pattern(self._input, self)
|
||||
|
||||
def _update(self):
|
||||
pass
|
||||
|
||||
@@ -1,177 +0,0 @@
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation files
|
||||
# (the "Software"), to deal in the Software without restriction,
|
||||
# including without limitation the rights to use, copy, modify, merge,
|
||||
# publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
# and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import copy
|
||||
from ..core.pattern import Pattern
|
||||
|
||||
__all__ = ["TemplatablePattern"]
|
||||
|
||||
class TemplateNames:
|
||||
def __init__(self):
|
||||
self.django = False
|
||||
self.erb = False
|
||||
self.handlebars = False
|
||||
self.php = False
|
||||
|
||||
class TemplatePatterns:
|
||||
def __init__(self, input_scanner):
|
||||
pattern = Pattern(input_scanner)
|
||||
self.handlebars_comment = pattern.starting_with(r'{{!--').until_after(r'--}}')
|
||||
self.handlebars_unescaped = pattern.starting_with(r'{{{').until_after(r'}}}')
|
||||
self.handlebars = pattern.starting_with(r'{{').until_after(r'}}')
|
||||
self.php = pattern.starting_with(r'<\?(?:[=]|php)').until_after(r'\?>')
|
||||
self.erb = pattern.starting_with(r'<%[^%]').until_after(r'[^%]%>')
|
||||
# django coflicts with handlebars a bit.
|
||||
self.django = pattern.starting_with(r'{%').until_after(r'%}')
|
||||
self.django_value = pattern.starting_with(r'{{').until_after(r'}}')
|
||||
self.django_comment = pattern.starting_with(r'{#').until_after(r'#}')
|
||||
|
||||
class TemplatablePattern(Pattern):
|
||||
|
||||
def __init__(self, input_scanner, parent=None):
|
||||
Pattern.__init__(self, input_scanner, parent)
|
||||
self.__template_pattern = None
|
||||
self._disabled = TemplateNames()
|
||||
self._excluded = TemplateNames()
|
||||
|
||||
if parent is not None:
|
||||
self.__template_pattern = \
|
||||
self._input.get_regexp(parent.__template_pattern)
|
||||
self._disabled = copy.copy(parent._disabled)
|
||||
self._excluded = copy.copy(parent._excluded)
|
||||
|
||||
self.__patterns = TemplatePatterns(input_scanner)
|
||||
|
||||
def _create(self):
|
||||
return TemplatablePattern(self._input, self)
|
||||
|
||||
def _update(self):
|
||||
self.__set_templated_pattern()
|
||||
|
||||
def read_options(self, options):
|
||||
result = self._create()
|
||||
for language in ['django', 'erb', 'handlebars', 'php']:
|
||||
setattr(result._disabled, language,
|
||||
not (language in options.templating))
|
||||
result._update()
|
||||
return result
|
||||
|
||||
def disable(self, language):
|
||||
result = self._create()
|
||||
setattr(result._disabled, language, True)
|
||||
result._update()
|
||||
return result
|
||||
|
||||
def exclude(self, language):
|
||||
result = self._create()
|
||||
setattr(result._excluded, language, True)
|
||||
result._update()
|
||||
return result
|
||||
|
||||
def read(self):
|
||||
result = ''
|
||||
if bool(self._match_pattern):
|
||||
result = self._input.read(self._starting_pattern)
|
||||
else:
|
||||
result = self._input.read(self._starting_pattern,
|
||||
self.__template_pattern)
|
||||
|
||||
next = self._read_template()
|
||||
|
||||
while (bool(next)):
|
||||
if self._match_pattern is not None:
|
||||
next += self._input.read(self._match_pattern)
|
||||
else:
|
||||
next += self._input.readUntil(self.__template_pattern)
|
||||
|
||||
result += next
|
||||
next = self._read_template()
|
||||
|
||||
if self._until_after:
|
||||
result += self._input.readUntilAfter(self._until_after)
|
||||
|
||||
return result
|
||||
|
||||
def __set_templated_pattern(self):
|
||||
items = list()
|
||||
|
||||
if not self._disabled.php:
|
||||
items.append(self.__patterns.php._starting_pattern.pattern)
|
||||
|
||||
if not self._disabled.handlebars:
|
||||
items.append(self.__patterns.handlebars._starting_pattern.pattern)
|
||||
|
||||
if not self._disabled.erb:
|
||||
items.append(self.__patterns.erb._starting_pattern.pattern)
|
||||
|
||||
if not self._disabled.django:
|
||||
items.append(self.__patterns.django._starting_pattern.pattern)
|
||||
items.append(self.__patterns.django_value._starting_pattern.pattern)
|
||||
items.append(self.__patterns.django_comment._starting_pattern.pattern)
|
||||
|
||||
if self._until_pattern:
|
||||
items.append(self._until_pattern.pattern)
|
||||
|
||||
self.__template_pattern = self._input.get_regexp(
|
||||
r'(?:' + '|'.join(items) + ')')
|
||||
|
||||
def _read_template(self):
|
||||
resulting_string = ''
|
||||
c = self._input.peek()
|
||||
if c == '<':
|
||||
peek1 = self._input.peek(1)
|
||||
if not self._disabled.php and \
|
||||
not self._excluded.php and \
|
||||
peek1 == '?':
|
||||
resulting_string = resulting_string or \
|
||||
self.__patterns.php.read()
|
||||
|
||||
if not self._disabled.erb and \
|
||||
not self._excluded.erb and \
|
||||
peek1 == '%':
|
||||
resulting_string = resulting_string or \
|
||||
self.__patterns.erb.read()
|
||||
elif c == '{':
|
||||
if not self._disabled.handlebars and \
|
||||
not self._excluded.handlebars:
|
||||
resulting_string = resulting_string or \
|
||||
self.__patterns.handlebars_comment.read()
|
||||
resulting_string = resulting_string or \
|
||||
self.__patterns.handlebars_unescaped.read()
|
||||
resulting_string = resulting_string or \
|
||||
self.__patterns.handlebars.read()
|
||||
if not self._disabled.django:
|
||||
# django coflicts with handlebars a bit.
|
||||
if not self._excluded.django and \
|
||||
not self._excluded.handlebars:
|
||||
resulting_string = resulting_string or \
|
||||
self.__patterns.django_value.read()
|
||||
if not self._excluded.django:
|
||||
|
||||
resulting_string = resulting_string or \
|
||||
self.__patterns.django_comment.read()
|
||||
resulting_string = resulting_string or \
|
||||
self.__patterns.django.read()
|
||||
|
||||
return resulting_string
|
||||
@@ -1,43 +0,0 @@
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation files
|
||||
# (the "Software"), to deal in the Software without restriction,
|
||||
# including without limitation the rights to use, copy, modify, merge,
|
||||
# publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
# and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
|
||||
class Token:
|
||||
def __init__(
|
||||
self,
|
||||
type,
|
||||
text,
|
||||
newlines=0,
|
||||
whitespace_before=''):
|
||||
self.type = type
|
||||
self.text = text
|
||||
self.comments_before = None
|
||||
self.newlines = newlines
|
||||
self.whitespace_before = whitespace_before
|
||||
self.parent = None
|
||||
self.next = None
|
||||
self.previous = None
|
||||
self.opened = None
|
||||
self.closed = None
|
||||
self.directives = None
|
||||
@@ -1,135 +0,0 @@
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation files
|
||||
# (the "Software"), to deal in the Software without restriction,
|
||||
# including without limitation the rights to use, copy, modify, merge,
|
||||
# publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
# and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import re
|
||||
from ..core.inputscanner import InputScanner
|
||||
from ..core.token import Token
|
||||
from ..core.tokenstream import TokenStream
|
||||
from ..core.pattern import Pattern
|
||||
from ..core.whitespacepattern import WhitespacePattern
|
||||
|
||||
__all__ = ["TOKEN", "Tokenizer", "TokenizerPatterns", "TokenTypes"]
|
||||
|
||||
class TokenTypes:
|
||||
START = 'TK_START'
|
||||
RAW = 'TK_RAW'
|
||||
EOF = 'TK_EOF'
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
TOKEN = TokenTypes()
|
||||
|
||||
class TokenizerPatterns:
|
||||
def __init__(self, input_scanner):
|
||||
self.whitespace = WhitespacePattern(input_scanner)
|
||||
|
||||
|
||||
|
||||
class Tokenizer:
|
||||
|
||||
def __init__(self, input_string, options):
|
||||
self._input = InputScanner(input_string)
|
||||
self._options = options
|
||||
self.__tokens = None
|
||||
|
||||
self._patterns = TokenizerPatterns(self._input)
|
||||
|
||||
def tokenize(self):
|
||||
self._input.restart()
|
||||
self.__tokens = TokenStream()
|
||||
|
||||
current = None
|
||||
previous = Token(TOKEN.START,'')
|
||||
open_token = None
|
||||
open_stack = []
|
||||
comments = TokenStream()
|
||||
|
||||
while previous.type != TOKEN.EOF:
|
||||
current = self.__get_next_token_with_comments(previous, open_token)
|
||||
|
||||
if self._is_opening(current):
|
||||
open_stack.append(open_token)
|
||||
open_token = current
|
||||
elif open_token is not None and \
|
||||
self._is_closing(current, open_token):
|
||||
current.opened = open_token
|
||||
open_token.closed = current
|
||||
open_token = open_stack.pop()
|
||||
current.parent = open_token
|
||||
|
||||
self.__tokens.add(current)
|
||||
previous = current
|
||||
return self.__tokens
|
||||
|
||||
def __get_next_token_with_comments(self, previous, open_token):
|
||||
current = self._get_next_token(previous, open_token)
|
||||
|
||||
if self._is_comment(current):
|
||||
comments = TokenStream()
|
||||
while self._is_comment(current):
|
||||
comments.add(current)
|
||||
current = self._get_next_token(previous, open_token)
|
||||
|
||||
if not comments.isEmpty():
|
||||
current.comments_before = comments
|
||||
comments = TokenStream()
|
||||
|
||||
current.parent = open_token
|
||||
current.previous = previous
|
||||
previous.next = current
|
||||
|
||||
return current
|
||||
|
||||
def _is_first_token(self):
|
||||
return self.__tokens.isEmpty()
|
||||
|
||||
def _reset(self):
|
||||
pass
|
||||
|
||||
def _get_next_token(self, previous_token, open_token):
|
||||
self._readWhitespace()
|
||||
resulting_string = self._input.read(re.compile(r'.+'))
|
||||
if resulting_string:
|
||||
return self._create_token(TOKEN.RAW, resulting_string)
|
||||
else:
|
||||
return self._create_token(TOKEN.EOF, '')
|
||||
|
||||
def _is_comment(self, current_token):
|
||||
return False
|
||||
|
||||
def _is_opening(self, current_token):
|
||||
return False
|
||||
|
||||
def _is_closing(self, current_token, open_token):
|
||||
return False
|
||||
|
||||
def _create_token(self, token_type, text):
|
||||
token = Token(token_type, text,
|
||||
self._patterns.whitespace.newline_count,
|
||||
self._patterns.whitespace.whitespace_before_token)
|
||||
return token
|
||||
|
||||
def _readWhitespace(self):
|
||||
return self._patterns.whitespace.read()
|
||||
@@ -1,74 +0,0 @@
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation files
|
||||
# (the "Software"), to deal in the Software without restriction,
|
||||
# including without limitation the rights to use, copy, modify, merge,
|
||||
# publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
# and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import re
|
||||
from ..core.inputscanner import InputScanner
|
||||
from ..core.token import Token
|
||||
|
||||
class TokenStream:
|
||||
|
||||
def __init__(self, parent_token=None):
|
||||
self.__tokens = []
|
||||
self.__tokens_length = len(self.__tokens)
|
||||
self.__position = 0
|
||||
self.__parent_token = parent_token
|
||||
|
||||
def restart(self):
|
||||
self.__position = 0
|
||||
|
||||
def isEmpty(self):
|
||||
return self.__tokens_length == 0
|
||||
|
||||
def hasNext(self):
|
||||
return self.__position < self.__tokens_length
|
||||
|
||||
def next(self):
|
||||
if self.hasNext():
|
||||
val = self.__tokens[self.__position]
|
||||
self.__position += 1
|
||||
return val
|
||||
else:
|
||||
raise StopIteration
|
||||
|
||||
def peek(self, index=0):
|
||||
val = None
|
||||
index += self.__position
|
||||
if index >= 0 and index < self.__tokens_length:
|
||||
val = self.__tokens[index]
|
||||
|
||||
return val
|
||||
|
||||
def add(self, token):
|
||||
if self.__parent_token:
|
||||
token.parent = self.__parent_token
|
||||
|
||||
self.__tokens.append(token)
|
||||
self.__tokens_length += 1
|
||||
|
||||
def __iter__(self):
|
||||
self.restart()
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
return self.next()
|
||||
@@ -1,78 +0,0 @@
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation files
|
||||
# (the "Software"), to deal in the Software without restriction,
|
||||
# including without limitation the rights to use, copy, modify, merge,
|
||||
# publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
# and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import re
|
||||
from ..core.pattern import Pattern
|
||||
|
||||
__all__ = ["WhitespacePattern"]
|
||||
|
||||
|
||||
class WhitespacePattern(Pattern):
|
||||
def __init__(self, input_scanner, parent=None):
|
||||
Pattern.__init__(self, input_scanner, parent)
|
||||
|
||||
if parent is not None:
|
||||
self._newline_regexp = \
|
||||
self._input.get_regexp(parent._newline_regexp)
|
||||
else:
|
||||
self.__set_whitespace_patterns('', '')
|
||||
|
||||
self.newline_count = 0
|
||||
self.whitespace_before_token = ''
|
||||
|
||||
def __set_whitespace_patterns(self, whitespace_chars, newline_chars):
|
||||
whitespace_chars += '\\t '
|
||||
newline_chars += '\\n\\r'
|
||||
|
||||
self._match_pattern = self._input.get_regexp(
|
||||
'[' + whitespace_chars + newline_chars + ']+')
|
||||
self._newline_regexp = self._input.get_regexp(
|
||||
'\\r\\n|[' + newline_chars + ']')
|
||||
|
||||
|
||||
def read(self):
|
||||
self.newline_count = 0
|
||||
self.whitespace_before_token = ''
|
||||
|
||||
resulting_string = self._input.read(self._match_pattern)
|
||||
if resulting_string == ' ':
|
||||
self.whitespace_before_token = ' '
|
||||
elif bool(resulting_string):
|
||||
lines = self._newline_regexp.split(resulting_string)
|
||||
self.newline_count = len(lines) - 1
|
||||
self.whitespace_before_token = lines[-1]
|
||||
|
||||
return resulting_string
|
||||
|
||||
|
||||
def matching(self, whitespace_chars, newline_chars):
|
||||
result = self._create()
|
||||
result.__set_whitespace_patterns(whitespace_chars, newline_chars)
|
||||
result._update()
|
||||
return result
|
||||
|
||||
def _create(self):
|
||||
return WhitespacePattern(self._input, self)
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
# Empty file :)
|
||||
@@ -1,75 +0,0 @@
|
||||
import re
|
||||
|
||||
# This section of code was translated to python from acorn (javascript).
|
||||
#
|
||||
# Acorn was written by Marijn Haverbeke and released under an MIT
|
||||
# license. The Unicode regexps (for identifiers and whitespace) were
|
||||
# taken from [Esprima](http://esprima.org) by Ariya Hidayat.
|
||||
#
|
||||
# Git repositories for Acorn are available at
|
||||
#
|
||||
# http://marijnhaverbeke.nl/git/acorn
|
||||
# https://github.com/marijnh/acorn.git
|
||||
|
||||
# This is not pretty, but given how we did the version import
|
||||
# it is the only way to do this without having setup.py fail on a missing
|
||||
# six dependency.
|
||||
six = __import__("six")
|
||||
|
||||
# ## Character categories
|
||||
|
||||
# acorn used char codes to squeeze the last bit of performance out
|
||||
# Beautifier is okay without that, so we're using regex
|
||||
# permit #(23), $ (36), and @ (64). @ is used in ES7 decorators.
|
||||
# 65 through 91 are uppercase letters.
|
||||
# permit _ (95).
|
||||
# 97 through 123 are lowercase letters.
|
||||
_baseASCIIidentifierStartChars = six.u(r"\x23\x24\x40\x41-\x5a\x5f\x61-\x7a")
|
||||
|
||||
# inside an identifier @ is not allowed but 0-9 are.
|
||||
_baseASCIIidentifierChars = six.u(r"\x24\x30-\x39\x41-\x5a\x5f\x61-\x7a")
|
||||
|
||||
# Big ugly regular expressions that match characters in the
|
||||
# whitespace, identifier, and identifier-start categories. These
|
||||
# are only applied when a character is found to actually have a
|
||||
# code point above 128.
|
||||
# IMPORTANT: These strings must be run through six to handle \u chars
|
||||
_nonASCIIidentifierStartChars = six.u(r"\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc")
|
||||
_nonASCIIidentifierChars = six.u(r"\u0300-\u036f\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u0620-\u0649\u0672-\u06d3\u06e7-\u06e8\u06fb-\u06fc\u0730-\u074a\u0800-\u0814\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0840-\u0857\u08e4-\u08fe\u0900-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962-\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09d7\u09df-\u09e0\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5f-\u0b60\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2-\u0ce3\u0ce6-\u0cef\u0d02\u0d03\u0d46-\u0d48\u0d57\u0d62-\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e34-\u0e3a\u0e40-\u0e45\u0e50-\u0e59\u0eb4-\u0eb9\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f41-\u0f47\u0f71-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1029\u1040-\u1049\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u170e-\u1710\u1720-\u1730\u1740-\u1750\u1772\u1773\u1780-\u17b2\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1920-\u192b\u1930-\u193b\u1951-\u196d\u19b0-\u19c0\u19c8-\u19c9\u19d0-\u19d9\u1a00-\u1a15\u1a20-\u1a53\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1b46-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1bb0-\u1bb9\u1be6-\u1bf3\u1c00-\u1c22\u1c40-\u1c49\u1c5b-\u1c7d\u1cd0-\u1cd2\u1d00-\u1dbe\u1e01-\u1f15\u200c\u200d\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2d81-\u2d96\u2de0-\u2dff\u3021-\u3028\u3099\u309a\ua640-\ua66d\ua674-\ua67d\ua69f\ua6f0-\ua6f1\ua7f8-\ua800\ua806\ua80b\ua823-\ua827\ua880-\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8f3-\ua8f7\ua900-\ua909\ua926-\ua92d\ua930-\ua945\ua980-\ua983\ua9b3-\ua9c0\uaa00-\uaa27\uaa40-\uaa41\uaa4c-\uaa4d\uaa50-\uaa59\uaa7b\uaae0-\uaae9\uaaf2-\uaaf3\uabc0-\uabe1\uabec\uabed\uabf0-\uabf9\ufb20-\ufb28\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f")
|
||||
#_nonASCIIidentifierStart = re.compile("[" + _nonASCIIidentifierStartChars + "]")
|
||||
#_nonASCIIidentifier = re.compile("[" + _nonASCIIidentifierStartChars + _nonASCIIidentifierChars + "]")
|
||||
|
||||
_identifierStart = six.u(r"(?:\\u[0-9a-fA-F]{4}|[") + \
|
||||
_baseASCIIidentifierStartChars + \
|
||||
_nonASCIIidentifierStartChars + \
|
||||
six.u("])")
|
||||
_identifierChars = six.u(r"(?:\\u[0-9a-fA-F]{4}|[") + \
|
||||
_baseASCIIidentifierChars + \
|
||||
_nonASCIIidentifierStartChars + \
|
||||
_nonASCIIidentifierChars + \
|
||||
six.u("])*")
|
||||
|
||||
identifier = re.compile(_identifierStart + _identifierChars)
|
||||
|
||||
identifierStart = re.compile(_identifierStart)
|
||||
identifierMatch = re.compile(six.u(r"(?:\\u[0-9a-fA-F]{4}|[") + \
|
||||
_baseASCIIidentifierChars + \
|
||||
_nonASCIIidentifierStartChars + \
|
||||
_nonASCIIidentifierChars + \
|
||||
six.u("])+"))
|
||||
|
||||
_nonASCIIwhitespace = re.compile(
|
||||
six.u(r"[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]"))
|
||||
|
||||
# Whether a single character denotes a newline.
|
||||
# IMPORTANT: This string must be run through six to handle \u chars
|
||||
newline = re.compile(six.u(r"[\n\r\u2028\u2029]"))
|
||||
|
||||
# Matches a whole line break (where CRLF is considered a single
|
||||
# line break). Used to count lines.
|
||||
|
||||
# in javascript, these two differ
|
||||
# in python they are the same, different methods are called on them
|
||||
# IMPORTANT: This string must be run through six to handle \u chars
|
||||
lineBreak = re.compile(six.u(r"\r\n|[\n\r\u2028\u2029]"))
|
||||
allLineBreaks = lineBreak
|
||||
@@ -1,94 +0,0 @@
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation files
|
||||
# (the "Software"), to deal in the Software without restriction,
|
||||
# including without limitation the rights to use, copy, modify, merge,
|
||||
# publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
# and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
|
||||
from ..core.options import Options as BaseOptions
|
||||
|
||||
OPERATOR_POSITION = [
|
||||
'before-newline',
|
||||
'after-newline',
|
||||
'preserve-newline'
|
||||
]
|
||||
|
||||
class BeautifierOptions(BaseOptions):
|
||||
def __init__(self, options=None):
|
||||
BaseOptions.__init__(self, options, 'js')
|
||||
|
||||
self.css = None
|
||||
self.js = None
|
||||
self.html = None
|
||||
|
||||
# compatibility, re
|
||||
|
||||
raw_brace_style = getattr(self.raw_options, 'brace_style', None)
|
||||
if raw_brace_style == "expand-strict": # graceful handling of deprecated option
|
||||
setattr(self.raw_options, 'brace_style', "expand")
|
||||
elif raw_brace_style == "collapse-preserve-inline": # graceful handling of deprecated option
|
||||
setattr(self.raw_options, 'brace_style', "collapse,preserve-inline")
|
||||
# elif bool(self.raw_options.braces_on_own_line): # graceful handling of deprecated option
|
||||
# raw_brace_style = "expand": "collapse"
|
||||
# elif raw_brace_style is None: # Nothing exists to set it
|
||||
# setattr(self.raw_options, 'brace_style', "collapse")
|
||||
|
||||
# preserve-inline in delimited string will trigger brace_preserve_inline, everything
|
||||
# else is considered a brace_style and the last one only will have an effect
|
||||
|
||||
brace_style_split = self._get_selection_list('brace_style', ['collapse', 'expand', 'end-expand', 'none', 'preserve-inline'])
|
||||
|
||||
# preserve-inline in delimited string will trigger brace_preserve_inline
|
||||
# Everything else is considered a brace_style and the last one only will
|
||||
# have an effect
|
||||
# specify defaults in case one half of meta-option is missing
|
||||
self.brace_preserve_inline = False
|
||||
self.brace_style = "collapse"
|
||||
|
||||
for bs in brace_style_split:
|
||||
if bs == "preserve-inline":
|
||||
self.brace_preserve_inline = True
|
||||
else:
|
||||
self.brace_style = bs
|
||||
|
||||
self.unindent_chained_methods = self._get_boolean('unindent_chained_methods')
|
||||
self.break_chained_methods = self._get_boolean('break_chained_methods')
|
||||
self.space_in_paren = self._get_boolean('space_in_paren')
|
||||
self.space_in_empty_paren = self._get_boolean('space_in_empty_paren')
|
||||
self.jslint_happy = self._get_boolean('jslint_happy')
|
||||
self.space_after_anon_function = self._get_boolean('space_after_anon_function')
|
||||
self.space_after_named_function = self._get_boolean('space_after_named_function')
|
||||
self.keep_array_indentation = self._get_boolean('keep_array_indentation')
|
||||
self.space_before_conditional = self._get_boolean('space_before_conditional', True)
|
||||
self.unescape_strings = self._get_boolean('unescape_strings')
|
||||
self.e4x = self._get_boolean('e4x')
|
||||
self.comma_first = self._get_boolean('comma_first')
|
||||
self.operator_position = self._get_selection('operator_position', OPERATOR_POSITION)
|
||||
|
||||
# For testing of beautify preserve:start directive
|
||||
self.test_output_raw = False
|
||||
self.editorconfig = False
|
||||
|
||||
# force opts.space_after_anon_function to true if opts.jslint_happy
|
||||
if self.jslint_happy:
|
||||
self.space_after_anon_function = True
|
||||
|
||||
self.eval_code = False
|
||||
@@ -1,562 +0,0 @@
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation files
|
||||
# (the "Software"), to deal in the Software without restriction,
|
||||
# including without limitation the rights to use, copy, modify, merge,
|
||||
# publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
# and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import re
|
||||
from ..core.inputscanner import InputScanner
|
||||
from ..core.tokenizer import TokenTypes as BaseTokenTypes
|
||||
from ..core.tokenizer import Tokenizer as BaseTokenizer
|
||||
from ..core.tokenizer import TokenizerPatterns as BaseTokenizerPatterns
|
||||
from ..core.directives import Directives
|
||||
|
||||
from ..core.pattern import Pattern
|
||||
from ..core.templatablepattern import TemplatablePattern
|
||||
|
||||
|
||||
__all__ = ["TOKEN", "Tokenizer", "TokenTypes"]
|
||||
|
||||
class TokenTypes(BaseTokenTypes):
|
||||
START_EXPR = 'TK_START_EXPR'
|
||||
END_EXPR = 'TK_END_EXPR'
|
||||
START_BLOCK = 'TK_START_BLOCK'
|
||||
END_BLOCK = 'TK_END_BLOCK'
|
||||
WORD = 'TK_WORD'
|
||||
RESERVED = 'TK_RESERVED'
|
||||
SEMICOLON = 'TK_SEMICOLON'
|
||||
STRING = 'TK_STRING'
|
||||
EQUALS = 'TK_EQUALS'
|
||||
OPERATOR = 'TK_OPERATOR'
|
||||
COMMA = 'TK_COMMA'
|
||||
BLOCK_COMMENT = 'TK_BLOCK_COMMENT'
|
||||
COMMENT = 'TK_COMMENT'
|
||||
DOT = 'TK_DOT'
|
||||
UNKNOWN = 'TK_UNKNOWN'
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
TOKEN = TokenTypes()
|
||||
|
||||
dot_pattern = re.compile(r'[^\d\.]')
|
||||
|
||||
number_pattern = re.compile(
|
||||
r'0[xX][0123456789abcdefABCDEF]*|0[oO][01234567]*|0[bB][01]*|\d+n|(?:\.\d+|\d+\.?\d*)(?:[eE][+-]?\d+)?')
|
||||
digit = re.compile(r'[0-9]')
|
||||
|
||||
|
||||
positionable_operators = frozenset(
|
||||
(">>> === !== " +
|
||||
"<< && >= ** != == <= >> || |> " +
|
||||
"< / - + > : & % ? ^ | *").split(' '))
|
||||
|
||||
punct = (">>>= " +
|
||||
"... >>= <<= === >>> !== **= " +
|
||||
"=> ^= :: /= << <= == && -= >= >> != -- += ** || ++ %= &= *= |= |> " +
|
||||
"= ! ? > < : / ^ - + * & % ~ |")
|
||||
|
||||
punct = re.compile(r'([-[\]{}()*+?.,\\^$|#])').sub(r'\\\1', punct)
|
||||
# ?. but not if followed by a number
|
||||
punct = '\\?\\.(?!\\d) ' + punct
|
||||
punct = punct.replace(' ', '|')
|
||||
|
||||
punct_pattern = re.compile(punct)
|
||||
|
||||
# Words which always should start on a new line
|
||||
line_starters = frozenset(
|
||||
('continue,try,throw,return,var,let,const,if,switch,case,default,for,' +
|
||||
'while,break,function,import,export').split(','))
|
||||
reserved_words = line_starters | frozenset(['do',
|
||||
'in',
|
||||
'of',
|
||||
'else',
|
||||
'get',
|
||||
'set',
|
||||
'new',
|
||||
'catch',
|
||||
'finally',
|
||||
'typeof',
|
||||
'yield',
|
||||
'async',
|
||||
'await',
|
||||
'from',
|
||||
'as'])
|
||||
|
||||
reserved_word_pattern = re.compile(r'^(?:' + '|'.join(reserved_words) + r')$')
|
||||
|
||||
directives_core = Directives(r'/\*', r'\*/')
|
||||
|
||||
xmlRegExp = re.compile(
|
||||
r'[\s\S]*?<(\/?)([-a-zA-Z:0-9_.]+|{[\s\S]+?}|!\[CDATA\[[\s\S]*?\]\])(\s+{[\s\S]+?}|\s+[-a-zA-Z:0-9_.]+|\s+[-a-zA-Z:0-9_.]+\s*=\s*(\'[^\']*\'|"[^"]*"|{[\s\S]+?}))*\s*(/?)\s*>')
|
||||
|
||||
class TokenizerPatterns(BaseTokenizerPatterns):
|
||||
def __init__(self, input_scanner, acorn, options):
|
||||
BaseTokenizerPatterns.__init__(self, input_scanner)
|
||||
|
||||
# This is not pretty, but given how we did the version import
|
||||
# it is the only way to do this without having setup.py fail on a missing
|
||||
# six dependency.
|
||||
six = __import__("six")
|
||||
|
||||
# IMPORTANT: This string must be run through six to handle \u chars
|
||||
self.whitespace = self.whitespace.matching(
|
||||
six.u(r'\u00A0\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff'),
|
||||
six.u(r'\u2028\u2029'))
|
||||
|
||||
pattern = Pattern(input_scanner)
|
||||
templatable = TemplatablePattern(input_scanner) \
|
||||
.read_options(options)
|
||||
|
||||
self.identifier = templatable.starting_with(acorn.identifier \
|
||||
).matching(acorn.identifierMatch)
|
||||
self.number = pattern.matching(number_pattern)
|
||||
self.punct = pattern.matching(punct_pattern)
|
||||
self.comment = pattern.starting_with(r'//').until(
|
||||
six.u(r'[\n\r\u2028\u2029]'))
|
||||
self.block_comment = pattern.starting_with(r'/\*').until_after(r'\*/')
|
||||
self.html_comment_start = pattern.matching(r'<!--')
|
||||
self.html_comment_end = pattern.matching(r'-->')
|
||||
self.include = pattern.starting_with(r'#include' \
|
||||
).until_after(acorn.lineBreak)
|
||||
self.shebang = pattern.starting_with(r'#!' \
|
||||
).until_after(acorn.lineBreak)
|
||||
|
||||
self.xml = pattern.matching(xmlRegExp)
|
||||
|
||||
self.single_quote = templatable.until(six.u(r"['\\\n\r\u2028\u2029]"))
|
||||
self.double_quote = templatable.until(six.u(r'["\\\n\r\u2028\u2029]'))
|
||||
self.template_text = templatable.until(r'[`\\$]')
|
||||
self.template_expression = templatable.until(r'[`}\\]')
|
||||
|
||||
|
||||
|
||||
class Tokenizer(BaseTokenizer):
|
||||
positionable_operators = positionable_operators
|
||||
line_starters = line_starters
|
||||
|
||||
def __init__(self, input_string, opts):
|
||||
BaseTokenizer.__init__(self, input_string, opts)
|
||||
|
||||
import jsbeautifier.javascript.acorn as acorn
|
||||
self.acorn = acorn
|
||||
|
||||
self.in_html_comment = False
|
||||
self.has_char_escapes = False
|
||||
|
||||
self._patterns = TokenizerPatterns(self._input, self.acorn, opts)
|
||||
|
||||
|
||||
def _reset(self):
|
||||
self.in_html_comment = False
|
||||
|
||||
def _is_comment(self, current_token):
|
||||
return current_token.type == TOKEN.COMMENT or \
|
||||
current_token.type == TOKEN.BLOCK_COMMENT or \
|
||||
current_token.type == TOKEN.UNKNOWN
|
||||
|
||||
|
||||
def _is_opening(self, current_token):
|
||||
return current_token.type == TOKEN.START_BLOCK or current_token.type == TOKEN.START_EXPR
|
||||
|
||||
def _is_closing(self, current_token, open_token):
|
||||
return (current_token.type == TOKEN.END_BLOCK or current_token.type == TOKEN.END_EXPR) and \
|
||||
(open_token is not None and (
|
||||
(current_token.text == ']' and open_token.text == '[') or
|
||||
(current_token.text == ')' and open_token.text == '(') or
|
||||
(current_token.text == '}' and open_token.text == '{')))
|
||||
|
||||
def _get_next_token(self, previous_token, open_token):
|
||||
token = None
|
||||
self._readWhitespace()
|
||||
|
||||
c = self._input.peek()
|
||||
if c is None:
|
||||
token = self._create_token(TOKEN.EOF, '')
|
||||
|
||||
token = token or self._read_non_javascript(c)
|
||||
token = token or self._read_string(c)
|
||||
token = token or self._read_word(previous_token)
|
||||
token = token or self._read_singles(c)
|
||||
token = token or self._read_comment(c)
|
||||
token = token or self._read_regexp(c, previous_token)
|
||||
token = token or self._read_xml(c, previous_token)
|
||||
token = token or self._read_punctuation()
|
||||
token = token or self._create_token(TOKEN.UNKNOWN, self._input.next())
|
||||
|
||||
return token
|
||||
|
||||
def _read_singles(self, c):
|
||||
token = None
|
||||
|
||||
if c == '(' or c == '[':
|
||||
token = self._create_token(TOKEN.START_EXPR, c)
|
||||
elif c == ')' or c == ']':
|
||||
token = self._create_token(TOKEN.END_EXPR, c)
|
||||
elif c == '{':
|
||||
token = self._create_token(TOKEN.START_BLOCK, c)
|
||||
elif c == '}':
|
||||
token = self._create_token(TOKEN.END_BLOCK, c)
|
||||
elif c == ';':
|
||||
token = self._create_token(TOKEN.SEMICOLON, c)
|
||||
elif c == '.' and self._input.peek(1) is not None and \
|
||||
bool(dot_pattern.match(self._input.peek(1))):
|
||||
token = self._create_token(TOKEN.DOT, c)
|
||||
elif c == ',':
|
||||
token = self._create_token(TOKEN.COMMA, c)
|
||||
|
||||
if token is not None:
|
||||
self._input.next()
|
||||
|
||||
return token
|
||||
|
||||
def _read_word(self, previous_token):
|
||||
resulting_string = self._patterns.identifier.read()
|
||||
|
||||
if bool(resulting_string):
|
||||
resulting_string = re.sub(self.acorn.allLineBreaks, '\n', resulting_string)
|
||||
if not (previous_token.type == TOKEN.DOT or (
|
||||
previous_token.type == TOKEN.RESERVED and (
|
||||
previous_token.text == 'set' or previous_token.text == 'get')
|
||||
)) and reserved_word_pattern.match(resulting_string):
|
||||
if resulting_string == 'in' or resulting_string == 'of':
|
||||
# in and of are operators, need to hack
|
||||
return self._create_token(TOKEN.OPERATOR, resulting_string)
|
||||
|
||||
return self._create_token(TOKEN.RESERVED, resulting_string)
|
||||
|
||||
return self._create_token(TOKEN.WORD, resulting_string)
|
||||
|
||||
resulting_string = self._patterns.number.read()
|
||||
if resulting_string != '':
|
||||
return self._create_token(TOKEN.WORD, resulting_string)
|
||||
|
||||
def _read_comment(self, c):
|
||||
token = None
|
||||
if c == '/':
|
||||
comment = ''
|
||||
if self._input.peek(1) == '*': # peek /* .. */ comment
|
||||
comment = self._patterns.block_comment.read()
|
||||
|
||||
directives = directives_core.get_directives(comment)
|
||||
if directives and directives.get('ignore') == 'start':
|
||||
comment += directives_core.readIgnored(self._input)
|
||||
comment = re.sub(self.acorn.allLineBreaks, '\n', comment)
|
||||
token = self._create_token(TOKEN.BLOCK_COMMENT, comment)
|
||||
token.directives = directives
|
||||
|
||||
elif self._input.peek(1) == '/': # peek // comment
|
||||
comment = self._patterns.comment.read()
|
||||
token = self._create_token(TOKEN.COMMENT, comment)
|
||||
|
||||
return token
|
||||
|
||||
|
||||
def _read_string(self, c):
|
||||
if c == '`' or c == "'" or c == '"':
|
||||
resulting_string = self._input.next()
|
||||
self.has_char_escapes = False
|
||||
|
||||
if c == '`':
|
||||
resulting_string += self.parse_string('`', True, '${')
|
||||
else:
|
||||
resulting_string += self.parse_string(c)
|
||||
|
||||
if self.has_char_escapes and self._options.unescape_strings:
|
||||
resulting_string = self.unescape_string(resulting_string)
|
||||
|
||||
if self._input.peek() == c:
|
||||
resulting_string += self._input.next()
|
||||
|
||||
resulting_string = re.sub(
|
||||
self.acorn.allLineBreaks, '\n', resulting_string)
|
||||
|
||||
return self._create_token(TOKEN.STRING, resulting_string)
|
||||
|
||||
return None
|
||||
|
||||
def _read_regexp(self, c, previous_token):
|
||||
|
||||
if c == '/' and self.allowRegExOrXML(previous_token):
|
||||
# handle regexp
|
||||
resulting_string = self._input.next()
|
||||
esc = False
|
||||
|
||||
in_char_class = False
|
||||
while self._input.hasNext() and \
|
||||
(esc or in_char_class or self._input.peek() != c) and \
|
||||
not self._input.testChar(self.acorn.newline):
|
||||
resulting_string += self._input.peek()
|
||||
if not esc:
|
||||
esc = self._input.peek() == '\\'
|
||||
if self._input.peek() == '[':
|
||||
in_char_class = True
|
||||
elif self._input.peek() == ']':
|
||||
in_char_class = False
|
||||
else:
|
||||
esc = False
|
||||
self._input.next()
|
||||
|
||||
if self._input.peek() == c:
|
||||
resulting_string += self._input.next()
|
||||
|
||||
if c == '/':
|
||||
# regexps may have modifiers /regexp/MOD, so fetch those too
|
||||
# Only [gim] are valid, but if the user puts in garbage, do
|
||||
# what we can to take it.
|
||||
resulting_string += self._input.read(
|
||||
self.acorn.identifier)
|
||||
|
||||
return self._create_token(TOKEN.STRING, resulting_string)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def _read_xml(self, c, previous_token):
|
||||
if self._options.e4x and c == "<" and self.allowRegExOrXML(previous_token):
|
||||
# handle e4x xml literals
|
||||
xmlStr = ""
|
||||
match = self._patterns.xml.read_match()
|
||||
if match and not match.group(1):
|
||||
rootTag = match.group(2)
|
||||
rootTag = re.sub(r'^{\s+', '{', re.sub(r'\s+}$', '}', rootTag))
|
||||
isCurlyRoot = rootTag.startswith('{')
|
||||
depth = 0
|
||||
while bool(match):
|
||||
isEndTag = match.group(1)
|
||||
tagName = match.group(2)
|
||||
isSingletonTag = (
|
||||
match.groups()[-1] != "") or (match.group(2)[0:8] == "![CDATA[")
|
||||
if not isSingletonTag and (tagName == rootTag or (
|
||||
isCurlyRoot and re.sub(r'^{\s+', '{', re.sub(r'\s+}$', '}', tagName)))):
|
||||
if isEndTag:
|
||||
depth -= 1
|
||||
else:
|
||||
depth += 1
|
||||
|
||||
xmlStr += match.group(0)
|
||||
if depth <= 0:
|
||||
break
|
||||
|
||||
match = self._patterns.xml.read_match()
|
||||
|
||||
# if we didn't close correctly, keep unformatted.
|
||||
if not match:
|
||||
xmlStr += self._input.match(re.compile(r'[\s\S]*')).group(0)
|
||||
|
||||
xmlStr = re.sub(self.acorn.allLineBreaks, '\n', xmlStr)
|
||||
return self._create_token(TOKEN.STRING, xmlStr)
|
||||
|
||||
return None
|
||||
|
||||
def _read_non_javascript(self, c):
|
||||
resulting_string = ''
|
||||
|
||||
if c == '#':
|
||||
|
||||
# she-bang
|
||||
if self._is_first_token():
|
||||
resulting_string = self._patterns.shebang.read()
|
||||
if resulting_string:
|
||||
return self._create_token(TOKEN.UNKNOWN, resulting_string.strip() + '\n')
|
||||
|
||||
# handles extendscript #includes
|
||||
resulting_string = self._patterns.include.read()
|
||||
|
||||
if resulting_string:
|
||||
return self._create_token(TOKEN.UNKNOWN, resulting_string.strip() + '\n')
|
||||
|
||||
c = self._input.next()
|
||||
|
||||
# Spidermonkey-specific sharp variables for circular references
|
||||
# https://developer.mozilla.org/En/Sharp_variables_in_JavaScript
|
||||
# http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp
|
||||
# around line 1935
|
||||
sharp = '#'
|
||||
if self._input.hasNext() and self._input.testChar(digit):
|
||||
while True:
|
||||
c = self._input.next()
|
||||
sharp += c
|
||||
if (not self._input.hasNext()) or c == '#' or c == '=':
|
||||
break
|
||||
if c == '#':
|
||||
pass
|
||||
elif self._input.peek() == '[' and self._input.peek(1) == ']':
|
||||
sharp += '[]'
|
||||
self._input.next()
|
||||
self._input.next()
|
||||
elif self._input.peek() == '{' and self._input.peek(1) == '}':
|
||||
sharp += '{}'
|
||||
self._input.next()
|
||||
self._input.next()
|
||||
|
||||
return self._create_token(TOKEN.WORD, sharp)
|
||||
|
||||
self._input.back()
|
||||
|
||||
elif c == '<' and self._is_first_token():
|
||||
|
||||
if self._patterns.html_comment_start.read():
|
||||
c = '<!--'
|
||||
while self._input.hasNext() and not self._input.testChar(self.acorn.newline):
|
||||
c += self._input.next()
|
||||
|
||||
self.in_html_comment = True
|
||||
return self._create_token(TOKEN.COMMENT, c)
|
||||
|
||||
elif c == '-' and self.in_html_comment and \
|
||||
self._patterns.html_comment_end.read():
|
||||
self.in_html_comment = False
|
||||
return self._create_token(TOKEN.COMMENT, '-->')
|
||||
|
||||
return None
|
||||
|
||||
def _read_punctuation(self):
|
||||
token = None
|
||||
resulting_string = self._patterns.punct.read()
|
||||
if resulting_string != '':
|
||||
if resulting_string == '=':
|
||||
token = self._create_token(TOKEN.EQUALS, resulting_string)
|
||||
elif resulting_string == '?.':
|
||||
token = self._create_token(TOKEN.DOT, resulting_string)
|
||||
else:
|
||||
token = self._create_token(TOKEN.OPERATOR, resulting_string)
|
||||
|
||||
return token
|
||||
|
||||
__regexTokens = { TOKEN.COMMENT, TOKEN.START_EXPR, TOKEN.START_BLOCK,
|
||||
TOKEN.START, TOKEN.END_BLOCK, TOKEN.OPERATOR,
|
||||
TOKEN.EQUALS, TOKEN.EOF, TOKEN.SEMICOLON, TOKEN.COMMA }
|
||||
def allowRegExOrXML(self, previous_token):
|
||||
return (previous_token.type == TOKEN.RESERVED and previous_token.text in {'return', 'case', 'throw', 'else', 'do', 'typeof', 'yield'}) or \
|
||||
(previous_token.type == TOKEN.END_EXPR and previous_token.text == ')' and
|
||||
previous_token.opened.previous.type == TOKEN.RESERVED and previous_token.opened.previous.text in {'if', 'while', 'for'}) or \
|
||||
(previous_token.type in self.__regexTokens )
|
||||
|
||||
def parse_string(
|
||||
self,
|
||||
delimiter,
|
||||
allow_unescaped_newlines=False,
|
||||
start_sub=None):
|
||||
if delimiter == '\'':
|
||||
pattern = self._patterns.single_quote
|
||||
elif delimiter == '"':
|
||||
pattern = self._patterns.double_quote
|
||||
elif delimiter == '`':
|
||||
pattern = self._patterns.template_text
|
||||
elif delimiter == '}':
|
||||
pattern = self._patterns.template_expression
|
||||
resulting_string = pattern.read()
|
||||
next = ''
|
||||
while self._input.hasNext():
|
||||
next = self._input.next()
|
||||
if next == delimiter or \
|
||||
(not allow_unescaped_newlines and
|
||||
self.acorn.newline.match(next)):
|
||||
self._input.back()
|
||||
break
|
||||
elif next == '\\' and self._input.hasNext():
|
||||
current_char = self._input.peek()
|
||||
if current_char == 'x' or current_char == 'u':
|
||||
self.has_char_escapes = True
|
||||
elif current_char == '\r' and self._input.peek(1) == '\n':
|
||||
self._input.next()
|
||||
|
||||
next += self._input.next()
|
||||
elif start_sub is not None:
|
||||
if start_sub == '${' and next == '$' and \
|
||||
self._input.peek() == '{':
|
||||
next += self._input.next()
|
||||
|
||||
if start_sub == next:
|
||||
if delimiter == '`':
|
||||
next += self.parse_string(
|
||||
'}', allow_unescaped_newlines, '`')
|
||||
else:
|
||||
next += self.parse_string(
|
||||
'`', allow_unescaped_newlines, '${')
|
||||
|
||||
if self._input.hasNext():
|
||||
next += self._input.next()
|
||||
|
||||
next += pattern.read()
|
||||
resulting_string += next
|
||||
return resulting_string
|
||||
|
||||
|
||||
def unescape_string(self, s):
|
||||
# You think that a regex would work for this
|
||||
# return s.replace(/\\x([0-9a-f]{2})/gi, function(match, val) {
|
||||
# return String.fromCharCode(parseInt(val, 16));
|
||||
# })
|
||||
# However, dealing with '\xff', '\\xff', '\\\xff' makes this more fun.
|
||||
out = self.acorn.six.u('')
|
||||
escaped = 0
|
||||
|
||||
input_scan = InputScanner(s)
|
||||
matched = None
|
||||
|
||||
while input_scan.hasNext():
|
||||
# Keep any whitespace, non-slash characters
|
||||
# also keep slash pairs.
|
||||
matched = input_scan.match(re.compile(r'([\s]|[^\\]|\\\\)+'))
|
||||
|
||||
if matched:
|
||||
out += matched.group(0)
|
||||
|
||||
if input_scan.peek() != '\\':
|
||||
continue
|
||||
|
||||
input_scan.next()
|
||||
if input_scan.peek() == 'x':
|
||||
matched = input_scan.match(re.compile(r'x([0-9A-Fa-f]{2})'))
|
||||
elif input_scan.peek() == 'u':
|
||||
matched = input_scan.match(re.compile(r'u([0-9A-Fa-f]{4})'))
|
||||
else:
|
||||
out += '\\'
|
||||
if input_scan.hasNext():
|
||||
out += input_scan.next()
|
||||
continue
|
||||
|
||||
# If there's some error decoding, return the original string
|
||||
if not matched:
|
||||
return s
|
||||
|
||||
escaped = int(matched.group(1), 16)
|
||||
|
||||
if escaped > 0x7e and escaped <= 0xff and matched.group(
|
||||
0).startswith('x'):
|
||||
# we bail out on \x7f..\xff,
|
||||
# leaving whole string escaped,
|
||||
# as it's probably completely binary
|
||||
return s
|
||||
elif escaped >= 0x00 and escaped < 0x20:
|
||||
# leave 0x00...0x1f escaped
|
||||
out += '\\' + matched.group(0)
|
||||
continue
|
||||
elif escaped == 0x22 or escaped == 0x27 or escaped == 0x5c:
|
||||
# single-quote, apostrophe, backslash - escape these
|
||||
out += ('\\' + chr(escaped))
|
||||
else:
|
||||
out += self.acorn.six.unichr(escaped)
|
||||
|
||||
return out
|
||||
@@ -1 +0,0 @@
|
||||
# Empty file :)
|
||||
@@ -1 +0,0 @@
|
||||
# Empty file :)
|
||||
@@ -1,48 +0,0 @@
|
||||
import re
|
||||
import unittest
|
||||
import jsbeautifier
|
||||
|
||||
|
||||
class TestJSBeautifierIndentation(unittest.TestCase):
|
||||
def test_tabs(self):
|
||||
test_fragment = self.decodesto
|
||||
|
||||
self.options.indent_with_tabs = 1
|
||||
test_fragment('{tabs()}', "{\n\ttabs()\n}")
|
||||
|
||||
def test_function_indent(self):
|
||||
test_fragment = self.decodesto
|
||||
|
||||
self.options.indent_with_tabs = 1
|
||||
self.options.keep_function_indentation = 1
|
||||
test_fragment(
|
||||
'var foo = function(){ bar() }();',
|
||||
"var foo = function() {\n\tbar()\n}();")
|
||||
|
||||
self.options.tabs = 1
|
||||
self.options.keep_function_indentation = 0
|
||||
test_fragment(
|
||||
'var foo = function(){ baz() }();',
|
||||
"var foo = function() {\n\tbaz()\n}();")
|
||||
|
||||
def decodesto(self, input, expectation=None):
|
||||
self.assertEqual(
|
||||
jsbeautifier.beautify(input, self.options), expectation or input)
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
options = jsbeautifier.default_options()
|
||||
options.indent_size = 4
|
||||
options.indent_char = ' '
|
||||
options.preserve_newlines = True
|
||||
options.jslint_happy = False
|
||||
options.keep_array_indentation = False
|
||||
options.brace_style = 'collapse'
|
||||
options.indent_level = 0
|
||||
|
||||
cls.options = options
|
||||
cls.wrapregex = re.compile('^(.+)$', re.MULTILINE)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,72 +0,0 @@
|
||||
#
|
||||
# General code for JSBeautifier unpackers infrastructure. See README.specs
|
||||
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
|
||||
#
|
||||
|
||||
"""General code for JSBeautifier unpackers infrastructure."""
|
||||
|
||||
import pkgutil
|
||||
import re
|
||||
from jsbeautifier.unpackers import evalbased
|
||||
|
||||
# NOTE: AT THE MOMENT, IT IS DEACTIVATED FOR YOUR SECURITY: it runs js!
|
||||
BLACKLIST = ['jsbeautifier.unpackers.evalbased']
|
||||
|
||||
|
||||
class UnpackingError(Exception):
|
||||
"""Badly packed source or general error. Argument is a
|
||||
meaningful description."""
|
||||
pass
|
||||
|
||||
|
||||
def getunpackers():
|
||||
"""Scans the unpackers dir, finds unpackers and add them to UNPACKERS list.
|
||||
An unpacker will be loaded only if it is a valid python module (name must
|
||||
adhere to naming conventions) and it is not blacklisted (i.e. inserted
|
||||
into BLACKLIST."""
|
||||
path = __path__
|
||||
prefix = __name__ + '.'
|
||||
unpackers = []
|
||||
interface = ['unpack', 'detect', 'PRIORITY']
|
||||
for _importer, modname, _ispkg in pkgutil.iter_modules(path, prefix):
|
||||
if 'tests' not in modname and modname not in BLACKLIST:
|
||||
try:
|
||||
module = __import__(modname, fromlist=interface)
|
||||
except ImportError:
|
||||
raise UnpackingError('Bad unpacker: %s' % modname)
|
||||
else:
|
||||
unpackers.append(module)
|
||||
|
||||
return sorted(unpackers, key=lambda mod: mod.PRIORITY)
|
||||
|
||||
|
||||
UNPACKERS = getunpackers()
|
||||
|
||||
|
||||
def run(source, evalcode=False):
|
||||
"""Runs the applicable unpackers and return unpacked source as a string."""
|
||||
for unpacker in [mod for mod in UNPACKERS if mod.detect(source)]:
|
||||
source = unpacker.unpack(source)
|
||||
if evalcode and evalbased.detect(source):
|
||||
source = evalbased.unpack(source)
|
||||
return source
|
||||
|
||||
|
||||
def filtercomments(source):
|
||||
"""NOT USED: strips trailing comments and put them at the top."""
|
||||
trailing_comments = []
|
||||
comment = True
|
||||
|
||||
while comment:
|
||||
if re.search(r'^\s*\/\*', source):
|
||||
comment = source[0, source.index('*/') + 2]
|
||||
elif re.search(r'^\s*\/\/', source):
|
||||
comment = re.search(r'^\s*\/\/', source).group(0)
|
||||
else:
|
||||
comment = None
|
||||
|
||||
if comment:
|
||||
source = re.sub(r'^\s+', '', source[len(comment):])
|
||||
trailing_comments.append(comment)
|
||||
|
||||
return '\n'.join(trailing_comments) + source
|
||||
@@ -1,43 +0,0 @@
|
||||
#
|
||||
# Unpacker for eval() based packers, a part of javascript beautifier
|
||||
# by Einar Lielmanis <einar@beautifier.io>
|
||||
#
|
||||
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
|
||||
#
|
||||
# usage:
|
||||
#
|
||||
# if detect(some_string):
|
||||
# unpacked = unpack(some_string)
|
||||
#
|
||||
|
||||
"""Unpacker for eval() based packers: runs JS code and returns result.
|
||||
Works only if a JS interpreter (e.g. Mozilla's Rhino) is installed and
|
||||
properly set up on host."""
|
||||
|
||||
from subprocess import PIPE, Popen
|
||||
|
||||
PRIORITY = 3
|
||||
|
||||
|
||||
def detect(source):
|
||||
"""Detects if source is likely to be eval() packed."""
|
||||
return source.strip().lower().startswith('eval(function(')
|
||||
|
||||
|
||||
def unpack(source):
|
||||
"""Runs source and return resulting code."""
|
||||
return jseval('print %s;' % source[4:]) if detect(source) else source
|
||||
|
||||
# In case of failure, we'll just return the original, without crashing on user.
|
||||
|
||||
|
||||
def jseval(script):
|
||||
"""Run code in the JS interpreter and return output."""
|
||||
try:
|
||||
interpreter = Popen(['js'], stdin=PIPE, stdout=PIPE)
|
||||
except OSError:
|
||||
return script
|
||||
result, errors = interpreter.communicate(script)
|
||||
if interpreter.poll() or errors:
|
||||
return script
|
||||
return result
|
||||
@@ -1,61 +0,0 @@
|
||||
#
|
||||
# simple unpacker/deobfuscator for scripts messed up with
|
||||
# javascriptobfuscator.com
|
||||
#
|
||||
# written by Einar Lielmanis <einar@beautifier.io>
|
||||
# rewritten in Python by Stefano Sanfilippo <a.little.coder@gmail.com>
|
||||
#
|
||||
# Will always return valid javascript: if `detect()` is false, `code` is
|
||||
# returned, unmodified.
|
||||
#
|
||||
# usage:
|
||||
#
|
||||
# if javascriptobfuscator.detect(some_string):
|
||||
# some_string = javascriptobfuscator.unpack(some_string)
|
||||
#
|
||||
|
||||
"""deobfuscator for scripts messed up with JavascriptObfuscator.com"""
|
||||
|
||||
import re
|
||||
|
||||
PRIORITY = 1
|
||||
|
||||
|
||||
def smartsplit(code):
|
||||
"""Split `code` at " symbol, only if it is not escaped."""
|
||||
strings = []
|
||||
pos = 0
|
||||
while pos < len(code):
|
||||
if code[pos] == '"':
|
||||
word = '' # new word
|
||||
pos += 1
|
||||
while pos < len(code):
|
||||
if code[pos] == '"':
|
||||
break
|
||||
if code[pos] == '\\':
|
||||
word += '\\'
|
||||
pos += 1
|
||||
word += code[pos]
|
||||
pos += 1
|
||||
strings.append('"%s"' % word)
|
||||
pos += 1
|
||||
return strings
|
||||
|
||||
|
||||
def detect(code):
|
||||
"""Detects if `code` is JavascriptObfuscator.com packed."""
|
||||
# prefer `is not` idiom, so that a true boolean is returned
|
||||
return (re.search(r'^var _0x[a-f0-9]+ ?\= ?\[', code) is not None)
|
||||
|
||||
|
||||
def unpack(code):
|
||||
"""Unpacks JavascriptObfuscator.com packed code."""
|
||||
if detect(code):
|
||||
matches = re.search(r'var (_0x[a-f\d]+) ?\= ?\[(.*?)\];', code)
|
||||
if matches:
|
||||
variable = matches.group(1)
|
||||
dictionary = smartsplit(matches.group(2))
|
||||
code = code[len(matches.group(0)):]
|
||||
for key, value in enumerate(dictionary):
|
||||
code = code.replace(r'%s[%s]' % (variable, key), value)
|
||||
return code
|
||||
@@ -1,90 +0,0 @@
|
||||
#
|
||||
# deobfuscator for scripts messed up with myobfuscate.com
|
||||
# by Einar Lielmanis <einar@beautifier.io>
|
||||
#
|
||||
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
|
||||
#
|
||||
# usage:
|
||||
#
|
||||
# if detect(some_string):
|
||||
# unpacked = unpack(some_string)
|
||||
#
|
||||
|
||||
# CAVEAT by Einar Lielmanis
|
||||
|
||||
#
|
||||
# You really don't want to obfuscate your scripts there: they're tracking
|
||||
# your unpackings, your script gets turned into something like this,
|
||||
# as of 2011-08-26:
|
||||
#
|
||||
# var _escape = 'your_script_escaped';
|
||||
# var _111 = document.createElement('script');
|
||||
# _111.src = 'http://api.www.myobfuscate.com/?getsrc=ok' +
|
||||
# '&ref=' + encodeURIComponent(document.referrer) +
|
||||
# '&url=' + encodeURIComponent(document.URL);
|
||||
# var 000 = document.getElementsByTagName('head')[0];
|
||||
# 000.appendChild(_111);
|
||||
# document.write(unescape(_escape));
|
||||
#
|
||||
|
||||
"""Deobfuscator for scripts messed up with MyObfuscate.com"""
|
||||
|
||||
import re
|
||||
import base64
|
||||
|
||||
# Python 2 retrocompatibility
|
||||
# pylint: disable=F0401
|
||||
# pylint: disable=E0611
|
||||
try:
|
||||
from urllib import unquote
|
||||
except ImportError:
|
||||
from urllib.parse import unquote
|
||||
|
||||
from jsbeautifier.unpackers import UnpackingError
|
||||
|
||||
PRIORITY = 1
|
||||
|
||||
CAVEAT = """//
|
||||
// Unpacker warning: be careful when using myobfuscate.com for your projects:
|
||||
// scripts obfuscated by the free online version call back home.
|
||||
//
|
||||
|
||||
"""
|
||||
|
||||
SIGNATURE = (
|
||||
r'["\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F'
|
||||
r'\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x61\x62\x63\x64\x65'
|
||||
r'\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75'
|
||||
r'\x76\x77\x78\x79\x7A\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x2B'
|
||||
r'\x2F\x3D","","\x63\x68\x61\x72\x41\x74","\x69\x6E\x64\x65\x78'
|
||||
r'\x4F\x66","\x66\x72\x6F\x6D\x43\x68\x61\x72\x43\x6F\x64\x65","'
|
||||
r'\x6C\x65\x6E\x67\x74\x68"]')
|
||||
|
||||
|
||||
def detect(source):
|
||||
"""Detects MyObfuscate.com packer."""
|
||||
return SIGNATURE in source
|
||||
|
||||
|
||||
def unpack(source):
|
||||
"""Unpacks js code packed with MyObfuscate.com"""
|
||||
if not detect(source):
|
||||
return source
|
||||
payload = unquote(_filter(source))
|
||||
match = re.search(r"^var _escape\='<script>(.*)<\/script>'",
|
||||
payload, re.DOTALL)
|
||||
polished = match.group(1) if match else source
|
||||
return CAVEAT + polished
|
||||
|
||||
|
||||
def _filter(source):
|
||||
"""Extracts and decode payload (original file) from `source`"""
|
||||
try:
|
||||
varname = re.search(r'eval\(\w+\(\w+\((\w+)\)\)\);', source).group(1)
|
||||
reverse = re.search(r"var +%s *\= *'(.*)';" % varname, source).group(1)
|
||||
except AttributeError:
|
||||
raise UnpackingError('Malformed MyObfuscate data.')
|
||||
try:
|
||||
return base64.b64decode(reverse[::-1].encode('utf8')).decode('utf8')
|
||||
except TypeError:
|
||||
raise UnpackingError('MyObfuscate payload is not base64-encoded.')
|
||||
@@ -1,149 +0,0 @@
|
||||
#
|
||||
# Unpacker for Dean Edward's p.a.c.k.e.r, a part of javascript beautifier
|
||||
# by Einar Lielmanis <einar@beautifier.io>
|
||||
#
|
||||
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
|
||||
#
|
||||
# usage:
|
||||
#
|
||||
# if detect(some_string):
|
||||
# unpacked = unpack(some_string)
|
||||
#
|
||||
|
||||
"""Unpacker for Dean Edward's p.a.c.k.e.r"""
|
||||
|
||||
import re
|
||||
import string
|
||||
from jsbeautifier.unpackers import UnpackingError
|
||||
|
||||
PRIORITY = 1
|
||||
|
||||
|
||||
def detect(source):
|
||||
global beginstr
|
||||
global endstr
|
||||
beginstr = ''
|
||||
endstr = ''
|
||||
begin_offset = -1
|
||||
"""Detects whether `source` is P.A.C.K.E.R. coded."""
|
||||
mystr = re.search('eval[ ]*\([ ]*function[ ]*\([ ]*p[ ]*,[ ]*a[ ]*,[ ]*c['
|
||||
' ]*,[ ]*k[ ]*,[ ]*e[ ]*,[ ]*', source)
|
||||
if(mystr):
|
||||
begin_offset = mystr.start()
|
||||
beginstr = source[:begin_offset]
|
||||
if(begin_offset != -1):
|
||||
""" Find endstr"""
|
||||
source_end = source[begin_offset:]
|
||||
if(source_end.split("')))", 1)[0] == source_end):
|
||||
try:
|
||||
endstr = source_end.split("}))", 1)[1]
|
||||
except IndexError:
|
||||
endstr = ''
|
||||
else:
|
||||
endstr = source_end.split("')))", 1)[1]
|
||||
return (mystr is not None)
|
||||
|
||||
|
||||
def unpack(source):
|
||||
"""Unpacks P.A.C.K.E.R. packed js code."""
|
||||
payload, symtab, radix, count = _filterargs(source)
|
||||
|
||||
if count != len(symtab):
|
||||
raise UnpackingError('Malformed p.a.c.k.e.r. symtab.')
|
||||
|
||||
try:
|
||||
unbase = Unbaser(radix)
|
||||
except TypeError:
|
||||
raise UnpackingError('Unknown p.a.c.k.e.r. encoding.')
|
||||
|
||||
def lookup(match):
|
||||
"""Look up symbols in the synthetic symtab."""
|
||||
word = match.group(0)
|
||||
return symtab[unbase(word)] or word
|
||||
|
||||
source = re.sub(r'\b\w+\b', lookup, payload)
|
||||
return _replacestrings(source)
|
||||
|
||||
|
||||
def _filterargs(source):
|
||||
"""Juice from a source file the four args needed by decoder."""
|
||||
juicers = [
|
||||
(r"}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\), *(\d+), *(.*)\)\)"),
|
||||
(r"}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\)"),
|
||||
]
|
||||
for juicer in juicers:
|
||||
args = re.search(juicer, source, re.DOTALL)
|
||||
if args:
|
||||
a = args.groups()
|
||||
if a[1] == "[]":
|
||||
a = list(a)
|
||||
a[1] = 62
|
||||
a = tuple(a)
|
||||
try:
|
||||
return a[0], a[3].split('|'), int(a[1]), int(a[2])
|
||||
except ValueError:
|
||||
raise UnpackingError('Corrupted p.a.c.k.e.r. data.')
|
||||
|
||||
# could not find a satisfying regex
|
||||
raise UnpackingError(
|
||||
'Could not make sense of p.a.c.k.e.r data (unexpected code structure)')
|
||||
|
||||
|
||||
def _replacestrings(source):
|
||||
global beginstr
|
||||
global endstr
|
||||
"""Strip string lookup table (list) and replace values in source."""
|
||||
match = re.search(r'var *(_\w+)\=\["(.*?)"\];', source, re.DOTALL)
|
||||
|
||||
if match:
|
||||
varname, strings = match.groups()
|
||||
startpoint = len(match.group(0))
|
||||
lookup = strings.split('","')
|
||||
variable = '%s[%%d]' % varname
|
||||
for index, value in enumerate(lookup):
|
||||
source = source.replace(variable % index, '"%s"' % value)
|
||||
return source[startpoint:]
|
||||
return beginstr + source + endstr
|
||||
|
||||
|
||||
class Unbaser(object):
|
||||
"""Functor for a given base. Will efficiently convert
|
||||
strings to natural numbers."""
|
||||
ALPHABET = {
|
||||
62: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
||||
95: (' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
'[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~')
|
||||
}
|
||||
|
||||
def __init__(self, base):
|
||||
self.base = base
|
||||
|
||||
# fill elements 37...61, if necessary
|
||||
if 36 < base < 62:
|
||||
if not hasattr(self.ALPHABET, self.ALPHABET[62][:base]):
|
||||
self.ALPHABET[base] = self.ALPHABET[62][:base]
|
||||
# attrs = self.ALPHABET
|
||||
# print ', '.join("%s: %s" % item for item in attrs.items())
|
||||
# If base can be handled by int() builtin, let it do it for us
|
||||
if 2 <= base <= 36:
|
||||
self.unbase = lambda string: int(string, base)
|
||||
else:
|
||||
# Build conversion dictionary cache
|
||||
try:
|
||||
self.dictionary = dict(
|
||||
(cipher, index) for index, cipher in enumerate(
|
||||
self.ALPHABET[base]))
|
||||
except KeyError:
|
||||
raise TypeError('Unsupported base encoding.')
|
||||
|
||||
self.unbase = self._dictunbaser
|
||||
|
||||
def __call__(self, string):
|
||||
return self.unbase(string)
|
||||
|
||||
def _dictunbaser(self, string):
|
||||
"""Decodes a value to an integer."""
|
||||
ret = 0
|
||||
for index, cipher in enumerate(string[::-1]):
|
||||
ret += (self.base ** index) * self.dictionary[cipher]
|
||||
return ret
|
||||
@@ -1,2 +0,0 @@
|
||||
# Empty file :)
|
||||
# pylint: disable=C0111
|
||||
@@ -1,54 +0,0 @@
|
||||
#
|
||||
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
|
||||
#
|
||||
|
||||
"""Tests for JavaScriptObfuscator unpacker."""
|
||||
|
||||
import unittest
|
||||
from jsbeautifier.unpackers.javascriptobfuscator import (
|
||||
unpack, detect, smartsplit)
|
||||
|
||||
# pylint: disable=R0904
|
||||
|
||||
|
||||
class TestJavascriptObfuscator(unittest.TestCase):
|
||||
"""JavascriptObfuscator.com test case."""
|
||||
|
||||
def test_smartsplit(self):
|
||||
"""Test smartsplit() function."""
|
||||
split = smartsplit
|
||||
|
||||
def equals(data, result): return self.assertEqual(split(data), result)
|
||||
|
||||
equals('', [])
|
||||
equals('"a", "b"', ['"a"', '"b"'])
|
||||
equals('"aaa","bbbb"', ['"aaa"', '"bbbb"'])
|
||||
equals('"a", "b\\\""', ['"a"', '"b\\\""'])
|
||||
|
||||
def test_detect(self):
|
||||
"""Test detect() function."""
|
||||
def positive(source): return self.assertTrue(detect(source))
|
||||
|
||||
def negative(source): return self.assertFalse(detect(source))
|
||||
|
||||
negative('')
|
||||
negative('abcd')
|
||||
negative('var _0xaaaa')
|
||||
positive('var _0xaaaa = ["a", "b"]')
|
||||
positive('var _0xaaaa=["a", "b"]')
|
||||
positive('var _0x1234=["a","b"]')
|
||||
|
||||
def test_unpack(self):
|
||||
"""Test unpack() function."""
|
||||
def decodeto(
|
||||
ob, original): return self.assertEqual(
|
||||
unpack(ob), original)
|
||||
|
||||
decodeto('var _0x8df3=[];var a=10;', 'var a=10;')
|
||||
decodeto('var _0xb2a7=["\x74\x27\x65\x73\x74"];var i;for(i=0;i<10;++i)'
|
||||
'{alert(_0xb2a7[0]);} ;', 'var i;for(i=0;i<10;++i){alert'
|
||||
'("t\'est");} ;')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,43 +0,0 @@
|
||||
#
|
||||
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
|
||||
#
|
||||
|
||||
"""Tests for MyObfuscate unpacker."""
|
||||
|
||||
import unittest
|
||||
import os
|
||||
from jsbeautifier.unpackers.myobfuscate import detect, unpack
|
||||
from jsbeautifier.unpackers.tests import __path__ as path
|
||||
|
||||
INPUT = os.path.join(path[0], 'test-myobfuscate-input.js')
|
||||
OUTPUT = os.path.join(path[0], 'test-myobfuscate-output.js')
|
||||
|
||||
# pylint: disable=R0904
|
||||
|
||||
|
||||
class TestMyObfuscate(unittest.TestCase):
|
||||
# pylint: disable=C0103
|
||||
"""MyObfuscate obfuscator testcase."""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""Load source files (encoded and decoded version) for tests."""
|
||||
with open(INPUT, 'r') as data:
|
||||
cls.input = data.read()
|
||||
with open(OUTPUT, 'r') as data:
|
||||
cls.output = data.read()
|
||||
|
||||
def test_detect(self):
|
||||
"""Test detect() function."""
|
||||
def detected(source): return self.assertTrue(detect(source))
|
||||
|
||||
detected(self.input)
|
||||
|
||||
def test_unpack(self):
|
||||
"""Test unpack() function."""
|
||||
def check(inp, out): return self.assertEqual(unpack(inp), out)
|
||||
|
||||
check(self.input, self.output)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,56 +0,0 @@
|
||||
#
|
||||
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
|
||||
#
|
||||
|
||||
"""Tests for P.A.C.K.E.R. unpacker."""
|
||||
|
||||
import unittest
|
||||
from jsbeautifier.unpackers.packer import detect, unpack
|
||||
|
||||
# pylint: disable=R0904
|
||||
|
||||
|
||||
class TestPacker(unittest.TestCase):
|
||||
"""P.A.C.K.E.R. testcase."""
|
||||
|
||||
def test_detect(self):
|
||||
"""Test detect() function."""
|
||||
def positive(source): return self.assertTrue(detect(source))
|
||||
|
||||
def negative(source): return self.assertFalse(detect(source))
|
||||
|
||||
negative('')
|
||||
negative('var a = b')
|
||||
positive('eval(function(p,a,c,k,e,r')
|
||||
positive('eval ( function(p, a, c, k, e, r')
|
||||
|
||||
def test_unpack(self):
|
||||
"""Test unpack() function."""
|
||||
def check(inp, out):
|
||||
return detect(inp) and self.assertEqual(unpack(inp), out)
|
||||
|
||||
check("eval(function(p,a,c,k,e,r){e=String;if(!''.replace(/^/,String)"
|
||||
"){while(c--)r[c]=k[c]||c;k=[function(e){return r[e]}];e="
|
||||
"function(){return'\\\\w+'};c=1};while(c--)if(k[c])p=p.replace("
|
||||
"new RegExp('\\\\b'+e(c)+'\\\\b','g'),k[c]);return p}('0 2=1',"
|
||||
"62,3,'var||a'.split('|'),0,{}))", 'var a=1')
|
||||
|
||||
check("function test (){alert ('This is a test!')}; "
|
||||
"eval(function(p,a,c,k,e,r){e=String;if(!''.replace(/^/,String))"
|
||||
"{while(c--)r[c]=k[c]||c;k=[function(e){return r[e]}];e=function"
|
||||
"(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp("
|
||||
"'\\b'+e(c)+'\\b','g'),k[c]);return p}('0 2=1',3,3,"
|
||||
"'var||a'.split('|'),0,{}))",
|
||||
"function test (){alert ('This is a test!')}; var a=1")
|
||||
|
||||
check("eval(function(p,a,c,k,e,d){e=function(c){return c.toString(36)};if(!''.replace(/^/,String)){while(c--){d[c.toString(a)]=k[c]||c.toString(a)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('2 0=\"4 3!\";2 1=0.5(/b/6);a.9(\"8\").7=1;',12,12,'str|n|var|W3Schools|Visit|search|i|innerHTML|demo|getElementById|document|w3Schools'.split('|'),0,{}))",
|
||||
"var str=\"Visit W3Schools!\";var n=str.search(/w3Schools/i);document.getElementById(\"demo\").innerHTML=n;")
|
||||
|
||||
check("a=b;\r\nwhile(1){\ng=h;{return'\\w+'};break;eval(function(p,a,c,k,e,d){e=function(c){return c.toString(36)};if(!''.replace(/^/,String)){while(c--){d[c.toString(a)]=k[c]||c.toString(a)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('$(5).4(3(){$(\'.1\').0(2);$(\'.6\').0(d);$(\'.7\').0(b);$(\'.a\').0(8);$(\'.9\').0(c)});',14,14,'html|r5e57|8080|function|ready|document|r1655|rc15b|8888|r39b0|r6ae9|3128|65309|80'.split('|'),0,{}))c=abx;",
|
||||
"a=b;\r\nwhile(1){\ng=h;{return'\\w+'};break;$(document).ready(function(){$('.r5e57').html(8080);$('.r1655').html(80);$('.rc15b').html(3128);$('.r6ae9').html(8888);$('.r39b0').html(65309)});c=abx;")
|
||||
|
||||
check("eval(function(p,a,c,k,e,r){e=function(c){return c.toString(36)};if('0'.replace(0,e)==0){while(c--)r[e(c)]=k[c];k=[function(e){return r[e]||e}];e=function(){return'[0-9ab]'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('$(5).a(6(){ $(\'.8\').0(1); $(\'.b\').0(4); $(\'.9\').0(2); $(\'.7\').0(3)})',[],12,'html|52136|555|65103|8088|document|function|r542c|r8ce6|rb0de|ready|rfab0'.split('|'),0,{}))",
|
||||
"$(document).ready(function(){ $('.r8ce6').html(52136); $('.rfab0').html(8088); $('.rb0de').html(555); $('.r542c').html(65103)})")
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,45 +0,0 @@
|
||||
#
|
||||
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
|
||||
#
|
||||
|
||||
"""Tests for urlencoded unpacker."""
|
||||
|
||||
import unittest
|
||||
|
||||
from jsbeautifier.unpackers.urlencode import detect, unpack
|
||||
|
||||
# pylint: disable=R0904
|
||||
|
||||
|
||||
class TestUrlencode(unittest.TestCase):
|
||||
"""urlencode test case."""
|
||||
|
||||
def test_detect(self):
|
||||
"""Test detect() function."""
|
||||
def encoded(source): return self.assertTrue(detect(source))
|
||||
|
||||
def unencoded(source): return self.assertFalse(detect(source))
|
||||
|
||||
unencoded('')
|
||||
unencoded('var a = b')
|
||||
encoded('var%20a+=+b')
|
||||
encoded('var%20a=b')
|
||||
encoded('var%20%21%22')
|
||||
|
||||
def test_unpack(self):
|
||||
"""Test unpack function."""
|
||||
def equals(
|
||||
source,
|
||||
result): return self.assertEqual(
|
||||
unpack(source),
|
||||
result)
|
||||
|
||||
equals('', '')
|
||||
equals('abcd', 'abcd')
|
||||
equals('var a = b', 'var a = b')
|
||||
equals('var%20a=b', 'var a=b')
|
||||
equals('var%20a+=+b', 'var a = b')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,36 +0,0 @@
|
||||
#
|
||||
# Trivial bookmarklet/escaped script detector for the javascript beautifier
|
||||
# written by Einar Lielmanis <einar@beautifier.io>
|
||||
# rewritten in Python by Stefano Sanfilippo <a.little.coder@gmail.com>
|
||||
#
|
||||
# Will always return valid javascript: if `detect()` is false, `code` is
|
||||
# returned, unmodified.
|
||||
#
|
||||
# usage:
|
||||
#
|
||||
# some_string = urlencode.unpack(some_string)
|
||||
#
|
||||
|
||||
"""Bookmarklet/escaped script unpacker."""
|
||||
|
||||
# Python 2 retrocompatibility
|
||||
# pylint: disable=F0401
|
||||
# pylint: disable=E0611
|
||||
try:
|
||||
from urllib import unquote_plus
|
||||
except ImportError:
|
||||
from urllib.parse import unquote_plus
|
||||
|
||||
PRIORITY = 0
|
||||
|
||||
|
||||
def detect(code):
|
||||
"""Detects if a scriptlet is urlencoded."""
|
||||
# the fact that script doesn't contain any space, but has %20 instead
|
||||
# should be sufficient check for now.
|
||||
return ' ' not in code and ('%20' in code or code.count('%') > 3)
|
||||
|
||||
|
||||
def unpack(code):
|
||||
"""URL decode `code` source string."""
|
||||
return unquote_plus(code) if detect(code) else code
|
||||
980
HaE/six.py
@@ -1,980 +0,0 @@
|
||||
# Copyright (c) 2010-2020 Benjamin Peterson
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
"""Utilities for writing code that runs on Python 2 and 3"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import functools
|
||||
import itertools
|
||||
import operator
|
||||
import sys
|
||||
import types
|
||||
|
||||
__author__ = "Benjamin Peterson <benjamin@python.org>"
|
||||
__version__ = "1.14.0"
|
||||
|
||||
|
||||
# Useful for very coarse version differentiation.
|
||||
PY2 = sys.version_info[0] == 2
|
||||
PY3 = sys.version_info[0] == 3
|
||||
PY34 = sys.version_info[0:2] >= (3, 4)
|
||||
|
||||
if PY3:
|
||||
string_types = str,
|
||||
integer_types = int,
|
||||
class_types = type,
|
||||
text_type = str
|
||||
binary_type = bytes
|
||||
|
||||
MAXSIZE = sys.maxsize
|
||||
else:
|
||||
string_types = basestring,
|
||||
integer_types = (int, long)
|
||||
class_types = (type, types.ClassType)
|
||||
text_type = unicode
|
||||
binary_type = str
|
||||
|
||||
if sys.platform.startswith("java"):
|
||||
# Jython always uses 32 bits.
|
||||
MAXSIZE = int((1 << 31) - 1)
|
||||
else:
|
||||
# It's possible to have sizeof(long) != sizeof(Py_ssize_t).
|
||||
class X(object):
|
||||
|
||||
def __len__(self):
|
||||
return 1 << 31
|
||||
try:
|
||||
len(X())
|
||||
except OverflowError:
|
||||
# 32-bit
|
||||
MAXSIZE = int((1 << 31) - 1)
|
||||
else:
|
||||
# 64-bit
|
||||
MAXSIZE = int((1 << 63) - 1)
|
||||
del X
|
||||
|
||||
|
||||
def _add_doc(func, doc):
|
||||
"""Add documentation to a function."""
|
||||
func.__doc__ = doc
|
||||
|
||||
|
||||
def _import_module(name):
|
||||
"""Import module, returning the module after the last dot."""
|
||||
__import__(name)
|
||||
return sys.modules[name]
|
||||
|
||||
|
||||
class _LazyDescr(object):
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def __get__(self, obj, tp):
|
||||
result = self._resolve()
|
||||
setattr(obj, self.name, result) # Invokes __set__.
|
||||
try:
|
||||
# This is a bit ugly, but it avoids running this again by
|
||||
# removing this descriptor.
|
||||
delattr(obj.__class__, self.name)
|
||||
except AttributeError:
|
||||
pass
|
||||
return result
|
||||
|
||||
|
||||
class MovedModule(_LazyDescr):
|
||||
|
||||
def __init__(self, name, old, new=None):
|
||||
super(MovedModule, self).__init__(name)
|
||||
if PY3:
|
||||
if new is None:
|
||||
new = name
|
||||
self.mod = new
|
||||
else:
|
||||
self.mod = old
|
||||
|
||||
def _resolve(self):
|
||||
return _import_module(self.mod)
|
||||
|
||||
def __getattr__(self, attr):
|
||||
_module = self._resolve()
|
||||
value = getattr(_module, attr)
|
||||
setattr(self, attr, value)
|
||||
return value
|
||||
|
||||
|
||||
class _LazyModule(types.ModuleType):
|
||||
|
||||
def __init__(self, name):
|
||||
super(_LazyModule, self).__init__(name)
|
||||
self.__doc__ = self.__class__.__doc__
|
||||
|
||||
def __dir__(self):
|
||||
attrs = ["__doc__", "__name__"]
|
||||
attrs += [attr.name for attr in self._moved_attributes]
|
||||
return attrs
|
||||
|
||||
# Subclasses should override this
|
||||
_moved_attributes = []
|
||||
|
||||
|
||||
class MovedAttribute(_LazyDescr):
|
||||
|
||||
def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
|
||||
super(MovedAttribute, self).__init__(name)
|
||||
if PY3:
|
||||
if new_mod is None:
|
||||
new_mod = name
|
||||
self.mod = new_mod
|
||||
if new_attr is None:
|
||||
if old_attr is None:
|
||||
new_attr = name
|
||||
else:
|
||||
new_attr = old_attr
|
||||
self.attr = new_attr
|
||||
else:
|
||||
self.mod = old_mod
|
||||
if old_attr is None:
|
||||
old_attr = name
|
||||
self.attr = old_attr
|
||||
|
||||
def _resolve(self):
|
||||
module = _import_module(self.mod)
|
||||
return getattr(module, self.attr)
|
||||
|
||||
|
||||
class _SixMetaPathImporter(object):
|
||||
|
||||
"""
|
||||
A meta path importer to import six.moves and its submodules.
|
||||
|
||||
This class implements a PEP302 finder and loader. It should be compatible
|
||||
with Python 2.5 and all existing versions of Python3
|
||||
"""
|
||||
|
||||
def __init__(self, six_module_name):
|
||||
self.name = six_module_name
|
||||
self.known_modules = {}
|
||||
|
||||
def _add_module(self, mod, *fullnames):
|
||||
for fullname in fullnames:
|
||||
self.known_modules[self.name + "." + fullname] = mod
|
||||
|
||||
def _get_module(self, fullname):
|
||||
return self.known_modules[self.name + "." + fullname]
|
||||
|
||||
def find_module(self, fullname, path=None):
|
||||
if fullname in self.known_modules:
|
||||
return self
|
||||
return None
|
||||
|
||||
def __get_module(self, fullname):
|
||||
try:
|
||||
return self.known_modules[fullname]
|
||||
except KeyError:
|
||||
raise ImportError("This loader does not know module " + fullname)
|
||||
|
||||
def load_module(self, fullname):
|
||||
try:
|
||||
# in case of a reload
|
||||
return sys.modules[fullname]
|
||||
except KeyError:
|
||||
pass
|
||||
mod = self.__get_module(fullname)
|
||||
if isinstance(mod, MovedModule):
|
||||
mod = mod._resolve()
|
||||
else:
|
||||
mod.__loader__ = self
|
||||
sys.modules[fullname] = mod
|
||||
return mod
|
||||
|
||||
def is_package(self, fullname):
|
||||
"""
|
||||
Return true, if the named module is a package.
|
||||
|
||||
We need this method to get correct spec objects with
|
||||
Python 3.4 (see PEP451)
|
||||
"""
|
||||
return hasattr(self.__get_module(fullname), "__path__")
|
||||
|
||||
def get_code(self, fullname):
|
||||
"""Return None
|
||||
|
||||
Required, if is_package is implemented"""
|
||||
self.__get_module(fullname) # eventually raises ImportError
|
||||
return None
|
||||
get_source = get_code # same as get_code
|
||||
|
||||
_importer = _SixMetaPathImporter(__name__)
|
||||
|
||||
|
||||
class _MovedItems(_LazyModule):
|
||||
|
||||
"""Lazy loading of moved objects"""
|
||||
__path__ = [] # mark as package
|
||||
|
||||
|
||||
_moved_attributes = [
|
||||
MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
|
||||
MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
|
||||
MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"),
|
||||
MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
|
||||
MovedAttribute("intern", "__builtin__", "sys"),
|
||||
MovedAttribute("map", "itertools", "builtins", "imap", "map"),
|
||||
MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"),
|
||||
MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"),
|
||||
MovedAttribute("getoutput", "commands", "subprocess"),
|
||||
MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"),
|
||||
MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"),
|
||||
MovedAttribute("reduce", "__builtin__", "functools"),
|
||||
MovedAttribute("shlex_quote", "pipes", "shlex", "quote"),
|
||||
MovedAttribute("StringIO", "StringIO", "io"),
|
||||
MovedAttribute("UserDict", "UserDict", "collections"),
|
||||
MovedAttribute("UserList", "UserList", "collections"),
|
||||
MovedAttribute("UserString", "UserString", "collections"),
|
||||
MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
|
||||
MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
|
||||
MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"),
|
||||
MovedModule("builtins", "__builtin__"),
|
||||
MovedModule("configparser", "ConfigParser"),
|
||||
MovedModule("collections_abc", "collections", "collections.abc" if sys.version_info >= (3, 3) else "collections"),
|
||||
MovedModule("copyreg", "copy_reg"),
|
||||
MovedModule("dbm_gnu", "gdbm", "dbm.gnu"),
|
||||
MovedModule("dbm_ndbm", "dbm", "dbm.ndbm"),
|
||||
MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread" if sys.version_info < (3, 9) else "_thread"),
|
||||
MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
|
||||
MovedModule("http_cookies", "Cookie", "http.cookies"),
|
||||
MovedModule("html_entities", "htmlentitydefs", "html.entities"),
|
||||
MovedModule("html_parser", "HTMLParser", "html.parser"),
|
||||
MovedModule("http_client", "httplib", "http.client"),
|
||||
MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"),
|
||||
MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"),
|
||||
MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"),
|
||||
MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"),
|
||||
MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"),
|
||||
MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
|
||||
MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
|
||||
MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
|
||||
MovedModule("cPickle", "cPickle", "pickle"),
|
||||
MovedModule("queue", "Queue"),
|
||||
MovedModule("reprlib", "repr"),
|
||||
MovedModule("socketserver", "SocketServer"),
|
||||
MovedModule("_thread", "thread", "_thread"),
|
||||
MovedModule("tkinter", "Tkinter"),
|
||||
MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
|
||||
MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
|
||||
MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
|
||||
MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
|
||||
MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
|
||||
MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"),
|
||||
MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
|
||||
MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
|
||||
MovedModule("tkinter_colorchooser", "tkColorChooser",
|
||||
"tkinter.colorchooser"),
|
||||
MovedModule("tkinter_commondialog", "tkCommonDialog",
|
||||
"tkinter.commondialog"),
|
||||
MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
|
||||
MovedModule("tkinter_font", "tkFont", "tkinter.font"),
|
||||
MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
|
||||
MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
|
||||
"tkinter.simpledialog"),
|
||||
MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"),
|
||||
MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"),
|
||||
MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"),
|
||||
MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
|
||||
MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"),
|
||||
MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"),
|
||||
]
|
||||
# Add windows specific modules.
|
||||
if sys.platform == "win32":
|
||||
_moved_attributes += [
|
||||
MovedModule("winreg", "_winreg"),
|
||||
]
|
||||
|
||||
for attr in _moved_attributes:
|
||||
setattr(_MovedItems, attr.name, attr)
|
||||
if isinstance(attr, MovedModule):
|
||||
_importer._add_module(attr, "moves." + attr.name)
|
||||
del attr
|
||||
|
||||
_MovedItems._moved_attributes = _moved_attributes
|
||||
|
||||
moves = _MovedItems(__name__ + ".moves")
|
||||
_importer._add_module(moves, "moves")
|
||||
|
||||
|
||||
class Module_six_moves_urllib_parse(_LazyModule):
|
||||
|
||||
"""Lazy loading of moved objects in six.moves.urllib_parse"""
|
||||
|
||||
|
||||
_urllib_parse_moved_attributes = [
|
||||
MovedAttribute("ParseResult", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("SplitResult", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("parse_qs", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("parse_qsl", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("urldefrag", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("urljoin", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("urlparse", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("urlsplit", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("urlunparse", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("urlunsplit", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("quote", "urllib", "urllib.parse"),
|
||||
MovedAttribute("quote_plus", "urllib", "urllib.parse"),
|
||||
MovedAttribute("unquote", "urllib", "urllib.parse"),
|
||||
MovedAttribute("unquote_plus", "urllib", "urllib.parse"),
|
||||
MovedAttribute("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"),
|
||||
MovedAttribute("urlencode", "urllib", "urllib.parse"),
|
||||
MovedAttribute("splitquery", "urllib", "urllib.parse"),
|
||||
MovedAttribute("splittag", "urllib", "urllib.parse"),
|
||||
MovedAttribute("splituser", "urllib", "urllib.parse"),
|
||||
MovedAttribute("splitvalue", "urllib", "urllib.parse"),
|
||||
MovedAttribute("uses_fragment", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("uses_netloc", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("uses_params", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("uses_query", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("uses_relative", "urlparse", "urllib.parse"),
|
||||
]
|
||||
for attr in _urllib_parse_moved_attributes:
|
||||
setattr(Module_six_moves_urllib_parse, attr.name, attr)
|
||||
del attr
|
||||
|
||||
Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes
|
||||
|
||||
_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"),
|
||||
"moves.urllib_parse", "moves.urllib.parse")
|
||||
|
||||
|
||||
class Module_six_moves_urllib_error(_LazyModule):
|
||||
|
||||
"""Lazy loading of moved objects in six.moves.urllib_error"""
|
||||
|
||||
|
||||
_urllib_error_moved_attributes = [
|
||||
MovedAttribute("URLError", "urllib2", "urllib.error"),
|
||||
MovedAttribute("HTTPError", "urllib2", "urllib.error"),
|
||||
MovedAttribute("ContentTooShortError", "urllib", "urllib.error"),
|
||||
]
|
||||
for attr in _urllib_error_moved_attributes:
|
||||
setattr(Module_six_moves_urllib_error, attr.name, attr)
|
||||
del attr
|
||||
|
||||
Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes
|
||||
|
||||
_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"),
|
||||
"moves.urllib_error", "moves.urllib.error")
|
||||
|
||||
|
||||
class Module_six_moves_urllib_request(_LazyModule):
|
||||
|
||||
"""Lazy loading of moved objects in six.moves.urllib_request"""
|
||||
|
||||
|
||||
_urllib_request_moved_attributes = [
|
||||
MovedAttribute("urlopen", "urllib2", "urllib.request"),
|
||||
MovedAttribute("install_opener", "urllib2", "urllib.request"),
|
||||
MovedAttribute("build_opener", "urllib2", "urllib.request"),
|
||||
MovedAttribute("pathname2url", "urllib", "urllib.request"),
|
||||
MovedAttribute("url2pathname", "urllib", "urllib.request"),
|
||||
MovedAttribute("getproxies", "urllib", "urllib.request"),
|
||||
MovedAttribute("Request", "urllib2", "urllib.request"),
|
||||
MovedAttribute("OpenerDirector", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"),
|
||||
MovedAttribute("ProxyHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("BaseHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"),
|
||||
MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("FileHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("FTPHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("UnknownHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"),
|
||||
MovedAttribute("urlretrieve", "urllib", "urllib.request"),
|
||||
MovedAttribute("urlcleanup", "urllib", "urllib.request"),
|
||||
MovedAttribute("URLopener", "urllib", "urllib.request"),
|
||||
MovedAttribute("FancyURLopener", "urllib", "urllib.request"),
|
||||
MovedAttribute("proxy_bypass", "urllib", "urllib.request"),
|
||||
MovedAttribute("parse_http_list", "urllib2", "urllib.request"),
|
||||
MovedAttribute("parse_keqv_list", "urllib2", "urllib.request"),
|
||||
]
|
||||
for attr in _urllib_request_moved_attributes:
|
||||
setattr(Module_six_moves_urllib_request, attr.name, attr)
|
||||
del attr
|
||||
|
||||
Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes
|
||||
|
||||
_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"),
|
||||
"moves.urllib_request", "moves.urllib.request")
|
||||
|
||||
|
||||
class Module_six_moves_urllib_response(_LazyModule):
|
||||
|
||||
"""Lazy loading of moved objects in six.moves.urllib_response"""
|
||||
|
||||
|
||||
_urllib_response_moved_attributes = [
|
||||
MovedAttribute("addbase", "urllib", "urllib.response"),
|
||||
MovedAttribute("addclosehook", "urllib", "urllib.response"),
|
||||
MovedAttribute("addinfo", "urllib", "urllib.response"),
|
||||
MovedAttribute("addinfourl", "urllib", "urllib.response"),
|
||||
]
|
||||
for attr in _urllib_response_moved_attributes:
|
||||
setattr(Module_six_moves_urllib_response, attr.name, attr)
|
||||
del attr
|
||||
|
||||
Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes
|
||||
|
||||
_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"),
|
||||
"moves.urllib_response", "moves.urllib.response")
|
||||
|
||||
|
||||
class Module_six_moves_urllib_robotparser(_LazyModule):
|
||||
|
||||
"""Lazy loading of moved objects in six.moves.urllib_robotparser"""
|
||||
|
||||
|
||||
_urllib_robotparser_moved_attributes = [
|
||||
MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"),
|
||||
]
|
||||
for attr in _urllib_robotparser_moved_attributes:
|
||||
setattr(Module_six_moves_urllib_robotparser, attr.name, attr)
|
||||
del attr
|
||||
|
||||
Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes
|
||||
|
||||
_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"),
|
||||
"moves.urllib_robotparser", "moves.urllib.robotparser")
|
||||
|
||||
|
||||
class Module_six_moves_urllib(types.ModuleType):
|
||||
|
||||
"""Create a six.moves.urllib namespace that resembles the Python 3 namespace"""
|
||||
__path__ = [] # mark as package
|
||||
parse = _importer._get_module("moves.urllib_parse")
|
||||
error = _importer._get_module("moves.urllib_error")
|
||||
request = _importer._get_module("moves.urllib_request")
|
||||
response = _importer._get_module("moves.urllib_response")
|
||||
robotparser = _importer._get_module("moves.urllib_robotparser")
|
||||
|
||||
def __dir__(self):
|
||||
return ['parse', 'error', 'request', 'response', 'robotparser']
|
||||
|
||||
_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"),
|
||||
"moves.urllib")
|
||||
|
||||
|
||||
def add_move(move):
|
||||
"""Add an item to six.moves."""
|
||||
setattr(_MovedItems, move.name, move)
|
||||
|
||||
|
||||
def remove_move(name):
|
||||
"""Remove item from six.moves."""
|
||||
try:
|
||||
delattr(_MovedItems, name)
|
||||
except AttributeError:
|
||||
try:
|
||||
del moves.__dict__[name]
|
||||
except KeyError:
|
||||
raise AttributeError("no such move, %r" % (name,))
|
||||
|
||||
|
||||
if PY3:
|
||||
_meth_func = "__func__"
|
||||
_meth_self = "__self__"
|
||||
|
||||
_func_closure = "__closure__"
|
||||
_func_code = "__code__"
|
||||
_func_defaults = "__defaults__"
|
||||
_func_globals = "__globals__"
|
||||
else:
|
||||
_meth_func = "im_func"
|
||||
_meth_self = "im_self"
|
||||
|
||||
_func_closure = "func_closure"
|
||||
_func_code = "func_code"
|
||||
_func_defaults = "func_defaults"
|
||||
_func_globals = "func_globals"
|
||||
|
||||
|
||||
try:
|
||||
advance_iterator = next
|
||||
except NameError:
|
||||
def advance_iterator(it):
|
||||
return it.next()
|
||||
next = advance_iterator
|
||||
|
||||
|
||||
try:
|
||||
callable = callable
|
||||
except NameError:
|
||||
def callable(obj):
|
||||
return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
|
||||
|
||||
|
||||
if PY3:
|
||||
def get_unbound_function(unbound):
|
||||
return unbound
|
||||
|
||||
create_bound_method = types.MethodType
|
||||
|
||||
def create_unbound_method(func, cls):
|
||||
return func
|
||||
|
||||
Iterator = object
|
||||
else:
|
||||
def get_unbound_function(unbound):
|
||||
return unbound.im_func
|
||||
|
||||
def create_bound_method(func, obj):
|
||||
return types.MethodType(func, obj, obj.__class__)
|
||||
|
||||
def create_unbound_method(func, cls):
|
||||
return types.MethodType(func, None, cls)
|
||||
|
||||
class Iterator(object):
|
||||
|
||||
def next(self):
|
||||
return type(self).__next__(self)
|
||||
|
||||
callable = callable
|
||||
_add_doc(get_unbound_function,
|
||||
"""Get the function out of a possibly unbound function""")
|
||||
|
||||
|
||||
get_method_function = operator.attrgetter(_meth_func)
|
||||
get_method_self = operator.attrgetter(_meth_self)
|
||||
get_function_closure = operator.attrgetter(_func_closure)
|
||||
get_function_code = operator.attrgetter(_func_code)
|
||||
get_function_defaults = operator.attrgetter(_func_defaults)
|
||||
get_function_globals = operator.attrgetter(_func_globals)
|
||||
|
||||
|
||||
if PY3:
|
||||
def iterkeys(d, **kw):
|
||||
return iter(d.keys(**kw))
|
||||
|
||||
def itervalues(d, **kw):
|
||||
return iter(d.values(**kw))
|
||||
|
||||
def iteritems(d, **kw):
|
||||
return iter(d.items(**kw))
|
||||
|
||||
def iterlists(d, **kw):
|
||||
return iter(d.lists(**kw))
|
||||
|
||||
viewkeys = operator.methodcaller("keys")
|
||||
|
||||
viewvalues = operator.methodcaller("values")
|
||||
|
||||
viewitems = operator.methodcaller("items")
|
||||
else:
|
||||
def iterkeys(d, **kw):
|
||||
return d.iterkeys(**kw)
|
||||
|
||||
def itervalues(d, **kw):
|
||||
return d.itervalues(**kw)
|
||||
|
||||
def iteritems(d, **kw):
|
||||
return d.iteritems(**kw)
|
||||
|
||||
def iterlists(d, **kw):
|
||||
return d.iterlists(**kw)
|
||||
|
||||
viewkeys = operator.methodcaller("viewkeys")
|
||||
|
||||
viewvalues = operator.methodcaller("viewvalues")
|
||||
|
||||
viewitems = operator.methodcaller("viewitems")
|
||||
|
||||
_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.")
|
||||
_add_doc(itervalues, "Return an iterator over the values of a dictionary.")
|
||||
_add_doc(iteritems,
|
||||
"Return an iterator over the (key, value) pairs of a dictionary.")
|
||||
_add_doc(iterlists,
|
||||
"Return an iterator over the (key, [values]) pairs of a dictionary.")
|
||||
|
||||
|
||||
if PY3:
|
||||
def b(s):
|
||||
return s.encode("latin-1")
|
||||
|
||||
def u(s):
|
||||
return s
|
||||
unichr = chr
|
||||
import struct
|
||||
int2byte = struct.Struct(">B").pack
|
||||
del struct
|
||||
byte2int = operator.itemgetter(0)
|
||||
indexbytes = operator.getitem
|
||||
iterbytes = iter
|
||||
import io
|
||||
StringIO = io.StringIO
|
||||
BytesIO = io.BytesIO
|
||||
del io
|
||||
_assertCountEqual = "assertCountEqual"
|
||||
if sys.version_info[1] <= 1:
|
||||
_assertRaisesRegex = "assertRaisesRegexp"
|
||||
_assertRegex = "assertRegexpMatches"
|
||||
_assertNotRegex = "assertNotRegexpMatches"
|
||||
else:
|
||||
_assertRaisesRegex = "assertRaisesRegex"
|
||||
_assertRegex = "assertRegex"
|
||||
_assertNotRegex = "assertNotRegex"
|
||||
else:
|
||||
def b(s):
|
||||
return s
|
||||
# Workaround for standalone backslash
|
||||
|
||||
def u(s):
|
||||
return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape")
|
||||
unichr = unichr
|
||||
int2byte = chr
|
||||
|
||||
def byte2int(bs):
|
||||
return ord(bs[0])
|
||||
|
||||
def indexbytes(buf, i):
|
||||
return ord(buf[i])
|
||||
iterbytes = functools.partial(itertools.imap, ord)
|
||||
import StringIO
|
||||
StringIO = BytesIO = StringIO.StringIO
|
||||
_assertCountEqual = "assertItemsEqual"
|
||||
_assertRaisesRegex = "assertRaisesRegexp"
|
||||
_assertRegex = "assertRegexpMatches"
|
||||
_assertNotRegex = "assertNotRegexpMatches"
|
||||
_add_doc(b, """Byte literal""")
|
||||
_add_doc(u, """Text literal""")
|
||||
|
||||
|
||||
def assertCountEqual(self, *args, **kwargs):
|
||||
return getattr(self, _assertCountEqual)(*args, **kwargs)
|
||||
|
||||
|
||||
def assertRaisesRegex(self, *args, **kwargs):
|
||||
return getattr(self, _assertRaisesRegex)(*args, **kwargs)
|
||||
|
||||
|
||||
def assertRegex(self, *args, **kwargs):
|
||||
return getattr(self, _assertRegex)(*args, **kwargs)
|
||||
|
||||
|
||||
def assertNotRegex(self, *args, **kwargs):
|
||||
return getattr(self, _assertNotRegex)(*args, **kwargs)
|
||||
|
||||
|
||||
if PY3:
|
||||
exec_ = getattr(moves.builtins, "exec")
|
||||
|
||||
def reraise(tp, value, tb=None):
|
||||
try:
|
||||
if value is None:
|
||||
value = tp()
|
||||
if value.__traceback__ is not tb:
|
||||
raise value.with_traceback(tb)
|
||||
raise value
|
||||
finally:
|
||||
value = None
|
||||
tb = None
|
||||
|
||||
else:
|
||||
def exec_(_code_, _globs_=None, _locs_=None):
|
||||
"""Execute code in a namespace."""
|
||||
if _globs_ is None:
|
||||
frame = sys._getframe(1)
|
||||
_globs_ = frame.f_globals
|
||||
if _locs_ is None:
|
||||
_locs_ = frame.f_locals
|
||||
del frame
|
||||
elif _locs_ is None:
|
||||
_locs_ = _globs_
|
||||
exec("""exec _code_ in _globs_, _locs_""")
|
||||
|
||||
exec_("""def reraise(tp, value, tb=None):
|
||||
try:
|
||||
raise tp, value, tb
|
||||
finally:
|
||||
tb = None
|
||||
""")
|
||||
|
||||
|
||||
if sys.version_info[:2] > (3,):
|
||||
exec_("""def raise_from(value, from_value):
|
||||
try:
|
||||
raise value from from_value
|
||||
finally:
|
||||
value = None
|
||||
""")
|
||||
else:
|
||||
def raise_from(value, from_value):
|
||||
raise value
|
||||
|
||||
|
||||
print_ = getattr(moves.builtins, "print", None)
|
||||
if print_ is None:
|
||||
def print_(*args, **kwargs):
|
||||
"""The new-style print function for Python 2.4 and 2.5."""
|
||||
fp = kwargs.pop("file", sys.stdout)
|
||||
if fp is None:
|
||||
return
|
||||
|
||||
def write(data):
|
||||
if not isinstance(data, basestring):
|
||||
data = str(data)
|
||||
# If the file has an encoding, encode unicode with it.
|
||||
if (isinstance(fp, file) and
|
||||
isinstance(data, unicode) and
|
||||
fp.encoding is not None):
|
||||
errors = getattr(fp, "errors", None)
|
||||
if errors is None:
|
||||
errors = "strict"
|
||||
data = data.encode(fp.encoding, errors)
|
||||
fp.write(data)
|
||||
want_unicode = False
|
||||
sep = kwargs.pop("sep", None)
|
||||
if sep is not None:
|
||||
if isinstance(sep, unicode):
|
||||
want_unicode = True
|
||||
elif not isinstance(sep, str):
|
||||
raise TypeError("sep must be None or a string")
|
||||
end = kwargs.pop("end", None)
|
||||
if end is not None:
|
||||
if isinstance(end, unicode):
|
||||
want_unicode = True
|
||||
elif not isinstance(end, str):
|
||||
raise TypeError("end must be None or a string")
|
||||
if kwargs:
|
||||
raise TypeError("invalid keyword arguments to print()")
|
||||
if not want_unicode:
|
||||
for arg in args:
|
||||
if isinstance(arg, unicode):
|
||||
want_unicode = True
|
||||
break
|
||||
if want_unicode:
|
||||
newline = unicode("\n")
|
||||
space = unicode(" ")
|
||||
else:
|
||||
newline = "\n"
|
||||
space = " "
|
||||
if sep is None:
|
||||
sep = space
|
||||
if end is None:
|
||||
end = newline
|
||||
for i, arg in enumerate(args):
|
||||
if i:
|
||||
write(sep)
|
||||
write(arg)
|
||||
write(end)
|
||||
if sys.version_info[:2] < (3, 3):
|
||||
_print = print_
|
||||
|
||||
def print_(*args, **kwargs):
|
||||
fp = kwargs.get("file", sys.stdout)
|
||||
flush = kwargs.pop("flush", False)
|
||||
_print(*args, **kwargs)
|
||||
if flush and fp is not None:
|
||||
fp.flush()
|
||||
|
||||
_add_doc(reraise, """Reraise an exception.""")
|
||||
|
||||
if sys.version_info[0:2] < (3, 4):
|
||||
# This does exactly the same what the :func:`py3:functools.update_wrapper`
|
||||
# function does on Python versions after 3.2. It sets the ``__wrapped__``
|
||||
# attribute on ``wrapper`` object and it doesn't raise an error if any of
|
||||
# the attributes mentioned in ``assigned`` and ``updated`` are missing on
|
||||
# ``wrapped`` object.
|
||||
def _update_wrapper(wrapper, wrapped,
|
||||
assigned=functools.WRAPPER_ASSIGNMENTS,
|
||||
updated=functools.WRAPPER_UPDATES):
|
||||
for attr in assigned:
|
||||
try:
|
||||
value = getattr(wrapped, attr)
|
||||
except AttributeError:
|
||||
continue
|
||||
else:
|
||||
setattr(wrapper, attr, value)
|
||||
for attr in updated:
|
||||
getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
|
||||
wrapper.__wrapped__ = wrapped
|
||||
return wrapper
|
||||
_update_wrapper.__doc__ = functools.update_wrapper.__doc__
|
||||
|
||||
def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS,
|
||||
updated=functools.WRAPPER_UPDATES):
|
||||
return functools.partial(_update_wrapper, wrapped=wrapped,
|
||||
assigned=assigned, updated=updated)
|
||||
wraps.__doc__ = functools.wraps.__doc__
|
||||
|
||||
else:
|
||||
wraps = functools.wraps
|
||||
|
||||
|
||||
def with_metaclass(meta, *bases):
|
||||
"""Create a base class with a metaclass."""
|
||||
# This requires a bit of explanation: the basic idea is to make a dummy
|
||||
# metaclass for one level of class instantiation that replaces itself with
|
||||
# the actual metaclass.
|
||||
class metaclass(type):
|
||||
|
||||
def __new__(cls, name, this_bases, d):
|
||||
if sys.version_info[:2] >= (3, 7):
|
||||
# This version introduced PEP 560 that requires a bit
|
||||
# of extra care (we mimic what is done by __build_class__).
|
||||
resolved_bases = types.resolve_bases(bases)
|
||||
if resolved_bases is not bases:
|
||||
d['__orig_bases__'] = bases
|
||||
else:
|
||||
resolved_bases = bases
|
||||
return meta(name, resolved_bases, d)
|
||||
|
||||
@classmethod
|
||||
def __prepare__(cls, name, this_bases):
|
||||
return meta.__prepare__(name, bases)
|
||||
return type.__new__(metaclass, 'temporary_class', (), {})
|
||||
|
||||
|
||||
def add_metaclass(metaclass):
|
||||
"""Class decorator for creating a class with a metaclass."""
|
||||
def wrapper(cls):
|
||||
orig_vars = cls.__dict__.copy()
|
||||
slots = orig_vars.get('__slots__')
|
||||
if slots is not None:
|
||||
if isinstance(slots, str):
|
||||
slots = [slots]
|
||||
for slots_var in slots:
|
||||
orig_vars.pop(slots_var)
|
||||
orig_vars.pop('__dict__', None)
|
||||
orig_vars.pop('__weakref__', None)
|
||||
if hasattr(cls, '__qualname__'):
|
||||
orig_vars['__qualname__'] = cls.__qualname__
|
||||
return metaclass(cls.__name__, cls.__bases__, orig_vars)
|
||||
return wrapper
|
||||
|
||||
|
||||
def ensure_binary(s, encoding='utf-8', errors='strict'):
|
||||
"""Coerce **s** to six.binary_type.
|
||||
|
||||
For Python 2:
|
||||
- `unicode` -> encoded to `str`
|
||||
- `str` -> `str`
|
||||
|
||||
For Python 3:
|
||||
- `str` -> encoded to `bytes`
|
||||
- `bytes` -> `bytes`
|
||||
"""
|
||||
if isinstance(s, text_type):
|
||||
return s.encode(encoding, errors)
|
||||
elif isinstance(s, binary_type):
|
||||
return s
|
||||
else:
|
||||
raise TypeError("not expecting type '%s'" % type(s))
|
||||
|
||||
|
||||
def ensure_str(s, encoding='utf-8', errors='strict'):
|
||||
"""Coerce *s* to `str`.
|
||||
|
||||
For Python 2:
|
||||
- `unicode` -> encoded to `str`
|
||||
- `str` -> `str`
|
||||
|
||||
For Python 3:
|
||||
- `str` -> `str`
|
||||
- `bytes` -> decoded to `str`
|
||||
"""
|
||||
if not isinstance(s, (text_type, binary_type)):
|
||||
raise TypeError("not expecting type '%s'" % type(s))
|
||||
if PY2 and isinstance(s, text_type):
|
||||
s = s.encode(encoding, errors)
|
||||
elif PY3 and isinstance(s, binary_type):
|
||||
s = s.decode(encoding, errors)
|
||||
return s
|
||||
|
||||
|
||||
def ensure_text(s, encoding='utf-8', errors='strict'):
|
||||
"""Coerce *s* to six.text_type.
|
||||
|
||||
For Python 2:
|
||||
- `unicode` -> `unicode`
|
||||
- `str` -> `unicode`
|
||||
|
||||
For Python 3:
|
||||
- `str` -> `str`
|
||||
- `bytes` -> decoded to `str`
|
||||
"""
|
||||
if isinstance(s, binary_type):
|
||||
return s.decode(encoding, errors)
|
||||
elif isinstance(s, text_type):
|
||||
return s
|
||||
else:
|
||||
raise TypeError("not expecting type '%s'" % type(s))
|
||||
|
||||
|
||||
def python_2_unicode_compatible(klass):
|
||||
"""
|
||||
A class decorator that defines __unicode__ and __str__ methods under Python 2.
|
||||
Under Python 3 it does nothing.
|
||||
|
||||
To support Python 2 and 3 with a single code base, define a __str__ method
|
||||
returning text and apply this decorator to the class.
|
||||
"""
|
||||
if PY2:
|
||||
if '__str__' not in klass.__dict__:
|
||||
raise ValueError("@python_2_unicode_compatible cannot be applied "
|
||||
"to %s because it doesn't define __str__()." %
|
||||
klass.__name__)
|
||||
klass.__unicode__ = klass.__str__
|
||||
klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
|
||||
return klass
|
||||
|
||||
|
||||
# Complete the moves implementation.
|
||||
# This code is at the end of this module to speed up module loading.
|
||||
# Turn this module into a package.
|
||||
__path__ = [] # required for PEP 302 and PEP 451
|
||||
__package__ = __name__ # see PEP 366 @ReservedAssignment
|
||||
if globals().get("__spec__") is not None:
|
||||
__spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable
|
||||
# Remove other six meta path importers, since they cause problems. This can
|
||||
# happen if six is removed from sys.modules and then reloaded. (Setuptools does
|
||||
# this for some reason.)
|
||||
if sys.meta_path:
|
||||
for i, importer in enumerate(sys.meta_path):
|
||||
# Here's some real nastiness: Another "instance" of the six module might
|
||||
# be floating around. Therefore, we can't use isinstance() to check for
|
||||
# the six meta path importer, since the other six instance will have
|
||||
# inserted an importer with different class.
|
||||
if (type(importer).__name__ == "_SixMetaPathImporter" and
|
||||
importer.name == __name__):
|
||||
del sys.meta_path[i]
|
||||
break
|
||||
del i, importer
|
||||
# Finally, add the importer to the meta path import hook.
|
||||
sys.meta_path.append(_importer)
|
||||
168
README.md
@@ -1,122 +1,70 @@
|
||||
# HaE - 信息高亮与提取者
|
||||
# HaE - Highlighter and Extractor
|
||||
|
||||
## 前言
|
||||
## 介绍
|
||||
|
||||
HaE(Highlight and Extractor),是基于MarkINFO插件(地址:https://github.com/gh0stkey/BurpSuite-Extender-MarkInfo )的基础进行重构。
|
||||
**HaE**是基于 `BurpSuite` 插件 `JavaAPI` 开发的请求高亮标记与信息提取的辅助型插件。
|
||||
|
||||
用处:
|
||||
- 高亮标记请求,针对高亮的请求进行深度挖掘
|
||||
- 敏感信息泄露发现
|
||||

|
||||
|
||||
## 设计想法
|
||||
该插件可以通过自定义正则的方式匹配**响应报文**,可以自行决定符合该自定义正则匹配的相应请求是否需要高亮标记、信息提取。
|
||||
|
||||
语言:Python
|
||||
|
||||

|
||||
|
||||
功能:
|
||||
- 自定义正则
|
||||
- 自定义高亮颜色
|
||||
- 自定义高亮或提取
|
||||
|
||||
## 设计过程
|
||||
|
||||
### 可视化界面
|
||||
|
||||
UI设计(基于Eclipse可视化设计),基于Java Swing
|
||||
|
||||

|
||||
|
||||
然后将Java代码转换为Python代码即可(**有很多坑~**)
|
||||
|
||||
使用BurpSuite接口:`ITab`创建Tab
|
||||
|
||||

|
||||
|
||||
### 高亮颜色
|
||||
|
||||
将BurpSuite的所有高亮颜色集成:(仅支持:`red, orange, yellow, green, cyan, blue, pink, magenta, gray`)
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### 配置文件格式
|
||||
|
||||
选用JSON格式,格式为
|
||||
|
||||
```
|
||||
name: {"regex": regexText, "highlight": isHighlight, "extract": isExtract, "color": colorText}
|
||||
```
|
||||
|
||||
### 颜色优先级和升级
|
||||
|
||||
定义Colors变量:
|
||||
|
||||
`colors = ['red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'pink', 'magenta', 'gray']`
|
||||
|
||||
利用下标的方式进行优先级排序,当满足2个同颜色条件则以优先级顺序上升颜色。(例如:**两个正则,颜色为橘黄色,该请求两个正则都匹配到了,那么将升级为红色**)
|
||||
注:`HaE`的使用,对测试人员来说需要基本的正则表达式基础,由于`Java`正则表达式的库并没有`Python`的优雅或方便,在使用正则的,HaE要求使用者必须使用`()`将所需提取的表达式内容包含;例如你要匹配一个**Shiro应用**的响应报文,正常匹配规则为`rememberMe=delete`,如果你要提取这段内容的话就需要变成`(rememberMe=delete)`。
|
||||
|
||||
## 使用方法
|
||||
|
||||
贴一些案例,仅供参考,还有更多玩法,可以自我琢磨~
|
||||
插件装载:`Extender - Extensions - Add - Select File - Next`
|
||||
|
||||
### 环境设置
|
||||
初次装载`HaE`会初始化配置文件,默认配置文件内置一个正则:`Email`,初始化的配置文件会放在与`BurpSuite Jar`包同级目录下。
|
||||
|
||||
进入Extender - Options - Python Environment
|
||||

|
||||
|
||||

|
||||
除了初始化的配置文件外,还有`init.hae`,该文件用于存储配置文件路径;`HaE`支持自定义配置文件路径,你可以通过点击`Select File`按钮进行选择自定义配置文件。
|
||||
|
||||
载入Jython的Jar包以及载入python的包路径。
|
||||

|
||||
|
||||
加载插件,选择HaE.py文件:
|
||||
HaE支持三个动作:
|
||||
|
||||

|
||||
1. 重载规则(Reload):当你不使用HaE UI界面去修改配置文件内的规则时,而是直接基于配置文件进行修改规则时可使用;
|
||||
2. 新建规则(New):新建规则会自动添加一行表格数据,单击或双击进行修改数据即可自动保存;
|
||||
3. 删除规则(Delete):单击选中某条规则时,按下该按钮即可删除规则。
|
||||
|
||||
加载成功:
|
||||
注:HaE的操作都是基于表单UI的方式,操作即会自动保存。
|
||||
|
||||

|
||||
## 插件优点
|
||||
|
||||
1. 多选项自定义控制适配需求;
|
||||
2. 多颜色高亮分类,将BurpSuite的所有高亮颜色集成:`red, orange, yellow, green, cyan, blue, pink, magenta, gray`;
|
||||
3. 颜色升级算法:利用下标的方式进行优先级排序,当满足2个同颜色条件则以优先级顺序上升颜色。(例如:**两个正则,颜色为橘黄色,该请求两个正则都匹配到了,那么将升级为红色**)
|
||||
4. 简单的配置文件格式选用JSON格式,格式为
|
||||
```
|
||||
{name: {"loaded": isLoaded:,"regex": regexText, "highlight": isHighlight, "extract": isExtract, "color": colorText}}
|
||||
```
|
||||
5. 内置简单缓存,在“多正则、大数据”的场景下减少卡顿现象。
|
||||
|
||||
## 实际使用
|
||||
|
||||
使用 RGPerson 生成测试数据,放入网站根目录文件中:
|
||||
|
||||

|
||||
|
||||
访问该地址,在`Proxy - HTTP History`中可以看见高亮请求,响应标签页中含有`MarkINFO`标签,其中将匹配到的信息提取了出来。
|
||||
|
||||

|
||||
|
||||
|
||||
### RUN IT
|
||||
## 正则优化
|
||||
|
||||
#### 添加自定义正则
|
||||
|
||||
```
|
||||
名字:Email
|
||||
|
||||
正则:[\\w-]+(?:\\.[\\w-]+)*@(?:[\\w](?:[\\w-]*[\\w])?\\.)+[\\w](?:[\\w-]*[\\w])?
|
||||
|
||||
高亮颜色:red
|
||||
|
||||
是否高亮和提取:是
|
||||
```
|
||||
|
||||
转到HaE标签页,进行设置,点击Add按钮即可添加
|
||||
|
||||

|
||||
|
||||
HaE - Config查看是否进行配置,点击Reload按钮:
|
||||
|
||||

|
||||
|
||||
#### 高亮请求
|
||||
|
||||
在Proxy - HTTP History中可以看见高亮请求,响应标签页中含有`MarkINFO`标签,其中将匹配到的邮箱提取了出来
|
||||
|
||||

|
||||
|
||||
#### 正则优化(参考Demo)
|
||||
有些正则在实战应用场景中并不理想
|
||||
|
||||
在正则匹配手机号、身份证号码的时候(纯数字类)会存在一些误报(这里匹配身份证号码无法进行校验,误报率很高),但手机号处理这一块可以解决:
|
||||
|
||||
原正则:
|
||||
|
||||
```
|
||||
(1[3-9]\d{9})
|
||||
1[3-9]\d{9}
|
||||
```
|
||||
|
||||
误报场景:`12315188888888123`,这时候会匹配到`15188888888`,而实际上这一段并不是手机号,所以完全可以修改正则为:
|
||||
误报场景:`12315188888888123`,这时候会匹配到`15188888888`,而实际上这一段并不是手机号,所以修改正则为:
|
||||
|
||||
```
|
||||
[^0-9]+(1[3-9]\d{9})[^0-9]+
|
||||
@@ -126,34 +74,14 @@ HaE - Config查看是否进行配置,点击Reload按钮:
|
||||
|
||||
## 实战用法
|
||||
|
||||
### CMS指纹识别
|
||||
1. CMS指纹识别,Discuz正则:`(Powered by Discuz!)`
|
||||
2. OSS对象存储信息泄露,正则:`([A|a]ccess[K|k]ey[I|i]d|[A|a]ccess[K|k]ey[S|s]ecret)`
|
||||
3. 内网地址信息提取,正则:`(?:10\.\d{1,3}\.\d{1,3}\.\d{1,3})|(?:172\.(?:(?:1[6-9])|(?:2\d)|(?:3[01]))\.\d{1,3}\.\d{1,3})|(?:192\.168\.\d{1,3}\.\d{1,3})`
|
||||
4. 实战插件关联搭配,漏洞挖掘案例:https://mp.weixin.qq.com/s/5vNn7dMRZBtv0ojPBAHV7Q
|
||||
...还有诸多使用方法等待大家去发掘。
|
||||
|
||||
例如:识别Discuz,名字:CMS-Discuz,正则:`Powered by Discuz!`,高亮颜色:blue
|
||||
## 文末
|
||||
|
||||

|
||||
随笔:正义感是一个不可丢失的东西。
|
||||
|
||||
Add保存到配置文件中:
|
||||
|
||||

|
||||
|
||||
请求识别:
|
||||
|
||||

|
||||
|
||||
### OSS对象存储信息泄露
|
||||
|
||||
名字:INFO-OSS
|
||||
|
||||
正则:`[A|a]ccess[K|k]ey[I|i]d|[A|a]ccess[K|k]ey[S|s]ecret`
|
||||
|
||||
高亮颜色:cyan
|
||||
|
||||

|
||||
|
||||
Add保存到配置文件中:
|
||||
|
||||

|
||||
|
||||
请求中识别并提取:
|
||||
|
||||

|
||||
Github项目地址(BUG、需求、正则欢迎提交):https://github.com/gh0stkey/HaE
|
||||
|
Before Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 147 KiB |
|
Before Width: | Height: | Size: 549 KiB |
|
Before Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 389 KiB |
|
Before Width: | Height: | Size: 337 KiB |
|
Before Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 215 KiB |
|
Before Width: | Height: | Size: 177 KiB |
|
Before Width: | Height: | Size: 210 KiB |
|
Before Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 407 KiB |
|
Before Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 198 KiB |
BIN
images/16000706401522.jpg
Normal file
|
After Width: | Height: | Size: 214 KiB |
BIN
images/16000708493657.jpg
Normal file
|
After Width: | Height: | Size: 131 KiB |
BIN
images/16000710069404.jpg
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
images/16000719723284.jpg
Normal file
|
After Width: | Height: | Size: 120 KiB |
BIN
images/16000720732854.jpg
Normal file
|
After Width: | Height: | Size: 223 KiB |
BIN
lib/json.jar
Normal file
556
src/burp/BurpExtender.java
Normal file
@@ -0,0 +1,556 @@
|
||||
package burp;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.json.*;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.DefaultCellEditor;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.table.DefaultTableModel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JButton;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.awt.event.ActionEvent;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.border.EtchedBorder;
|
||||
import javax.swing.border.TitledBorder;
|
||||
import javax.swing.event.TableModelEvent;
|
||||
import javax.swing.event.TableModelListener;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.JLabel;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEditorTabFactory, ITab {
|
||||
|
||||
private JFrame frame;
|
||||
private JPanel panel;
|
||||
private JTable table;
|
||||
private JTextField textField;
|
||||
private IBurpExtenderCallbacks callbacks;
|
||||
private static String configFilePath = "config.json";
|
||||
private static String initFilePath = "init.hae";
|
||||
private static String initConfigContent = "{\"Email\":{\"loaded\":true,\"highlight\":true,\"regex\":\"([\\\\w-]+(?:\\\\.[\\\\w-]+)*@(?:[\\\\w](?:[\\\\w-]*[\\\\w])?\\\\.)+[\\\\w](?:[\\\\w-]*[\\\\w])?)\",\"extract\":true,\"color\":\"yellow\"}}";
|
||||
private String[] colorArray = new String[] {"red", "orange", "yellow", "green", "cyan", "blue", "pink", "magenta", "gray"};
|
||||
private static IMessageEditorTab HaETab;
|
||||
private static PrintWriter stdout;
|
||||
|
||||
@Override
|
||||
public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks)
|
||||
{
|
||||
this.callbacks = callbacks;
|
||||
// 设置插件名字
|
||||
callbacks.setExtensionName("HaE - Highlighter and Extractor");
|
||||
|
||||
// 定义输出
|
||||
stdout = new PrintWriter(callbacks.getStdout(), true);
|
||||
stdout.println("@Author: EvilChen");
|
||||
|
||||
// UI
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
initialize();
|
||||
|
||||
// 判断"config.json"文件是否具备内容,如若不具备则进行初始化
|
||||
if (configFilePath.equals("config.json")) {
|
||||
if (readFileContent(configFilePath).equals("")) {
|
||||
writeFileContent(configFilePath, initConfigContent);
|
||||
writeFileContent(initFilePath, configFilePath);
|
||||
}
|
||||
}
|
||||
// 判断配置文件是否存在
|
||||
if (fileExists(configFilePath)) {
|
||||
configFilePath = readFileContent(initFilePath);
|
||||
fillTable();
|
||||
} else {
|
||||
JOptionPane.showMessageDialog(null, "Config File Not Found!", "Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
});
|
||||
callbacks.registerHttpListener(BurpExtender.this);
|
||||
callbacks.registerMessageEditorTabFactory(BurpExtender.this);
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
frame = new JFrame();
|
||||
frame.setBounds(100, 100, 526, 403);
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
|
||||
panel = new JPanel();
|
||||
frame.getContentPane().add(panel, BorderLayout.CENTER);
|
||||
panel.setLayout(new BorderLayout(0, 0));
|
||||
|
||||
JPanel panel_3 = new JPanel();
|
||||
panel.add(panel_3, BorderLayout.NORTH);
|
||||
|
||||
JLabel lblNewLabel_1 = new JLabel("Config File:");
|
||||
panel_3.add(lblNewLabel_1);
|
||||
|
||||
textField = new JTextField();
|
||||
textField.setEditable(false);
|
||||
panel_3.add(textField);
|
||||
textField.setColumns(20);
|
||||
|
||||
textField.setText(configFilePath);
|
||||
|
||||
JButton btnNewButton = new JButton("Select File ...");
|
||||
btnNewButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
JFileChooser jfc = new JFileChooser();
|
||||
jfc.setFileSelectionMode(JFileChooser.FILES_ONLY);
|
||||
jfc.showDialog(new JLabel(), "Choose");
|
||||
File file = jfc.getSelectedFile();
|
||||
textField.setText(file.getAbsolutePath());
|
||||
configFilePath = textField.getText();
|
||||
writeFileContent(initFilePath, configFilePath);
|
||||
fillTable();
|
||||
}
|
||||
});
|
||||
panel_3.add(btnNewButton);
|
||||
|
||||
JPanel panel_2 = new JPanel();
|
||||
panel.add(panel_2, BorderLayout.CENTER);
|
||||
panel_2.setLayout(new BorderLayout(0, 0));
|
||||
|
||||
JPanel panel_1 = new JPanel();
|
||||
panel_2.add(panel_1, BorderLayout.NORTH);
|
||||
panel_1.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null), "Actions", TitledBorder.LEADING, TitledBorder.TOP, null, new Color(0, 0, 0)));
|
||||
|
||||
JButton btnReloadRule = new JButton("Reload Rule");
|
||||
btnReloadRule.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
fillTable();
|
||||
}
|
||||
});
|
||||
panel_1.add(btnReloadRule);
|
||||
|
||||
JButton btnNewRule = new JButton("New Rule");
|
||||
btnNewRule.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
DefaultTableModel dtm = (DefaultTableModel) table.getModel();
|
||||
Vector rules = new Vector();
|
||||
rules.add(true);
|
||||
rules.add("New Rule");
|
||||
rules.add("New Regex");
|
||||
rules.add("red");
|
||||
rules.add(true);
|
||||
rules.add(true);
|
||||
dtm.addRow(rules);
|
||||
}
|
||||
});
|
||||
panel_1.add(btnNewRule);
|
||||
|
||||
JButton btnDeleteRule = new JButton("Delete Rule");
|
||||
btnDeleteRule.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
int selectRows = table.getSelectedRows().length;
|
||||
DefaultTableModel dtm = (DefaultTableModel) table.getModel();
|
||||
if (selectRows == 1) {
|
||||
int selectedRowIndex = table.getSelectedRow();
|
||||
// 在配置文件中删除数据
|
||||
String cellValue = (String) dtm.getValueAt(selectedRowIndex, 1);
|
||||
// System.out.println(cellValue);
|
||||
removeConfig(cellValue);
|
||||
// 在表格中删除数据
|
||||
dtm.removeRow(selectedRowIndex);
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
panel_1.add(btnDeleteRule);
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane();
|
||||
panel_2.add(scrollPane, BorderLayout.CENTER);
|
||||
|
||||
table = new JTable();
|
||||
table.setModel(new DefaultTableModel(
|
||||
new Object[][] {
|
||||
},
|
||||
new String[] {
|
||||
"Loaded", "Name", "Regex", "Color", "isExtract", "isHighlight"
|
||||
}
|
||||
));
|
||||
scrollPane.setViewportView(table);
|
||||
|
||||
table.getColumnModel().getColumn(2).setPreferredWidth(172);
|
||||
table.getColumnModel().getColumn(3).setCellEditor(new DefaultCellEditor(new JComboBox(colorArray)));
|
||||
table.getColumnModel().getColumn(0).setCellEditor(new DefaultCellEditor(new JCheckBox()));
|
||||
table.getColumnModel().getColumn(4).setCellEditor(new DefaultCellEditor(new JCheckBox()));
|
||||
table.getColumnModel().getColumn(5).setCellEditor(new DefaultCellEditor(new JCheckBox()));
|
||||
|
||||
JLabel lblNewLabel = new JLabel("@EvilChen Love YuChen.");
|
||||
lblNewLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
panel.add(lblNewLabel, BorderLayout.SOUTH);
|
||||
|
||||
table.getModel().addTableModelListener(
|
||||
new TableModelListener() {
|
||||
@Override
|
||||
public void tableChanged(TableModelEvent e) {
|
||||
if (e.getType() == TableModelEvent.INSERT || e.getType() == TableModelEvent.UPDATE) {
|
||||
DefaultTableModel dtm = (DefaultTableModel) table.getModel();
|
||||
int rows = dtm.getRowCount();
|
||||
JSONObject jsonObj = new JSONObject();
|
||||
|
||||
for (int i = 0; i < rows; i++) {
|
||||
JSONObject jsonObj1 = new JSONObject();
|
||||
jsonObj1.put("loaded", (boolean) dtm.getValueAt(i, 0));
|
||||
jsonObj1.put("regex", (String) dtm.getValueAt(i, 2));
|
||||
jsonObj1.put("color", (String) dtm.getValueAt(i, 3));
|
||||
jsonObj1.put("extract", (boolean) dtm.getValueAt(i, 4));
|
||||
jsonObj1.put("highlight", (boolean) dtm.getValueAt(i, 5));
|
||||
// 添加数据
|
||||
jsonObj.put((String) dtm.getValueAt(i, 1), jsonObj1);
|
||||
}
|
||||
|
||||
writeFileContent(configFilePath, jsonObj.toString());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
);
|
||||
callbacks.customizeUiComponent(panel);
|
||||
callbacks.customizeUiComponent(panel_1);
|
||||
callbacks.customizeUiComponent(panel_2);
|
||||
callbacks.customizeUiComponent(panel_3);
|
||||
callbacks.customizeUiComponent(scrollPane);
|
||||
callbacks.addSuiteTab(BurpExtender.this);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public IMessageEditorTab createNewInstance(IMessageEditorController controller, boolean editable) {
|
||||
HaETab = new MarkInfoTab(controller, editable);
|
||||
return HaETab;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTabCaption() {
|
||||
return "HaE";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getUiComponent() {
|
||||
return panel;
|
||||
}
|
||||
|
||||
/*
|
||||
* 使用processHttpMessage用来做Highlighter
|
||||
*/
|
||||
@Override
|
||||
public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo) {
|
||||
if (!messageIsRequest) {
|
||||
byte[] content = messageInfo.getResponse();
|
||||
JSONObject jsonObj = matchRegex(content);
|
||||
if (jsonObj.length() > 0) {
|
||||
List<String> colorList = new ArrayList<String>();
|
||||
Iterator<String> k = jsonObj.keys();
|
||||
while (k.hasNext()) {
|
||||
String name = k.next();
|
||||
JSONObject jsonObj2 = new JSONObject(jsonObj.get(name).toString());
|
||||
boolean isHighlight = jsonObj2.getBoolean("highlight");
|
||||
boolean isLoaded = jsonObj2.getBoolean("loaded");
|
||||
if (isHighlight && isLoaded) {
|
||||
colorList.add(jsonObj2.getString("color"));
|
||||
}
|
||||
}
|
||||
if (colorList.size() != 0) {
|
||||
String color = colorUpgrade(getColorKeys(colorList));
|
||||
messageInfo.setHighlight(color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MarkInfoTab implements IMessageEditorTab {
|
||||
private ITextEditor markInfoText;
|
||||
private byte[] currentMessage;
|
||||
|
||||
public MarkInfoTab(IMessageEditorController controller, boolean editable) {
|
||||
markInfoText = callbacks.createTextEditor();
|
||||
markInfoText.setEditable(editable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTabCaption() {
|
||||
return "MarkInfo";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getUiComponent() {
|
||||
return markInfoText.getComponent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(byte[] content, boolean isRequest) {
|
||||
// 这里需要过一次正则匹配决定是否开启Tab
|
||||
if (!isRequest && matchRegex(content).length() != 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getMessage() {
|
||||
return currentMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isModified() {
|
||||
return markInfoText.isTextModified();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getSelectedData() {
|
||||
return markInfoText.getSelectedText();
|
||||
}
|
||||
|
||||
/*
|
||||
* 使用setMessage用来做Extractor
|
||||
*/
|
||||
@Override
|
||||
public void setMessage(byte[] content, boolean isRequest) {
|
||||
if (content.length > 0 && !isRequest) {
|
||||
String result = "";
|
||||
JSONObject jsonObj = matchRegex(content);
|
||||
if (jsonObj.length() != 0) {
|
||||
Iterator<String> k = jsonObj.keys();
|
||||
while (k.hasNext()) {
|
||||
String name = k.next();
|
||||
JSONObject jsonObj1 = new JSONObject(jsonObj.get(name).toString());
|
||||
boolean isExtract = jsonObj1.getBoolean("extract");
|
||||
boolean isLoaded = jsonObj1.getBoolean("loaded");
|
||||
if (isExtract && isLoaded) {
|
||||
String tmpStr = String.format("[%s] %s \n", name, jsonObj1.getString("data"));
|
||||
String tmpStr1 = new String(tmpStr).intern();
|
||||
result += tmpStr;
|
||||
}
|
||||
}
|
||||
}
|
||||
markInfoText.setText(result.getBytes());
|
||||
}
|
||||
currentMessage = content;
|
||||
}
|
||||
}
|
||||
|
||||
private JSONObject matchRegex(byte[] content) {
|
||||
JSONObject tabContent = new JSONObject();
|
||||
// 正则匹配提取内容
|
||||
try {
|
||||
String jsonStr = readFileContent(configFilePath);
|
||||
JSONObject jsonObj = new JSONObject(jsonStr);
|
||||
Iterator<String> k = jsonObj.keys();
|
||||
// 遍历json数组
|
||||
while (k.hasNext()) {
|
||||
String contentString = new String(content, "UTF-8").intern();
|
||||
String name = k.next();
|
||||
JSONObject jsonObj1 = new JSONObject(jsonObj.get(name).toString());
|
||||
JSONObject jsonData = new JSONObject();
|
||||
String regex = jsonObj1.getString("regex");
|
||||
boolean isHighligth = jsonObj1.getBoolean("highlight");
|
||||
boolean isExtract = jsonObj1.getBoolean("extract");
|
||||
boolean isLoaded = jsonObj1.getBoolean("loaded");
|
||||
String color = jsonObj1.getString("color");
|
||||
List<String> result = new ArrayList<String>();
|
||||
|
||||
Pattern pattern = Pattern.compile(regex);
|
||||
Matcher matcher = pattern.matcher(contentString);
|
||||
while (matcher.find()) {
|
||||
// 添加匹配数据至list
|
||||
// 强制用户使用()包裹正则
|
||||
result.add(matcher.group(1));
|
||||
}
|
||||
// 去除重复内容
|
||||
HashSet tmpList = new HashSet(result);
|
||||
result.clear();
|
||||
result.addAll(tmpList);
|
||||
|
||||
if (!result.isEmpty()) {
|
||||
jsonData.put("highlight", isHighligth);
|
||||
jsonData.put("extract", isExtract);
|
||||
jsonData.put("color", color);
|
||||
jsonData.put("data", String.join(",", result));
|
||||
jsonData.put("loaded", isLoaded);
|
||||
// 初始化格式
|
||||
tabContent.put(name, jsonData);
|
||||
}
|
||||
}
|
||||
return tabContent;
|
||||
} catch (Exception e) {
|
||||
return new JSONObject();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* 颜色下标获取
|
||||
*/
|
||||
private List<Integer> getColorKeys(List<String> keys){
|
||||
List<Integer> result = new ArrayList<Integer>();
|
||||
int size = colorArray.length;
|
||||
// 根据颜色获取下标
|
||||
for (int x = 0; x < keys.size(); x++) {
|
||||
for (int v = 0; v < size; v++) {
|
||||
if (colorArray[v].equals(keys.get(x))) {
|
||||
result.add(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* 颜色升级递归算法
|
||||
*/
|
||||
private String colorUpgrade(List<Integer> colorList) {
|
||||
int colorSize = colorList.size();
|
||||
int i = 0;
|
||||
List<Integer> stack = new ArrayList<Integer>();
|
||||
while (i < colorSize) {
|
||||
if (stack.size() > 0) {
|
||||
stack.add(colorList.get(i));
|
||||
i++;
|
||||
} else if (colorList.get(i) != stack.stream().reduce((first, second) -> second).orElse(999999)) {
|
||||
stack.add(colorList.get(i));
|
||||
i++;
|
||||
} else {
|
||||
stack.set(stack.size() - 1, stack.get(stack.size() - 1) - 1);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
int stackSize = stack.size();
|
||||
// 利用HashSet删除重复元素
|
||||
HashSet tmpList = new HashSet(stack);
|
||||
stack.clear();
|
||||
stack.addAll(tmpList);
|
||||
if (stackSize == stack.size()) {
|
||||
List<String> endColorList = new ArrayList<String>();
|
||||
for (int j = 0; j < stack.size(); j++) {
|
||||
int num = stack.get(j);
|
||||
endColorList.add(colorArray[num]);
|
||||
}
|
||||
|
||||
return endColorList.get(0);
|
||||
} else {
|
||||
colorUpgrade(stack);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/*
|
||||
* 判断文件是否存在
|
||||
*/
|
||||
private Boolean fileExists(String fileName) {
|
||||
File file = new File(fileName);
|
||||
if(file.exists()){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
* 获取文件内容
|
||||
*/
|
||||
private String readFileContent(String fileName) {
|
||||
File file = new File(fileName);
|
||||
BufferedReader reader = null;
|
||||
StringBuffer sbf = new StringBuffer();
|
||||
try {
|
||||
reader = new BufferedReader(new FileReader(file));
|
||||
String tempStr;
|
||||
while ((tempStr = reader.readLine()) != null) {
|
||||
sbf.append(tempStr);
|
||||
}
|
||||
reader.close();
|
||||
return sbf.toString();
|
||||
} catch (IOException e) {
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (IOException err) {
|
||||
err.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return sbf.toString();
|
||||
}
|
||||
|
||||
/*
|
||||
* 写入文件内容
|
||||
*/
|
||||
private boolean writeFileContent(String fileName, String fileContent) {
|
||||
try {
|
||||
BufferedWriter out = new BufferedWriter(new FileWriter(fileName));
|
||||
out.write(fileContent);
|
||||
out.close();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
stdout.println(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 删除单条配置内容
|
||||
*/
|
||||
private void removeConfig(String key) {
|
||||
String jsonStr = readFileContent(configFilePath);
|
||||
JSONObject jsonObj = new JSONObject(jsonStr);
|
||||
jsonObj.remove(key);
|
||||
if (writeFileContent(configFilePath, jsonObj.toString())) {
|
||||
JOptionPane.showMessageDialog(null, "Delete Successfully!", "Info", JOptionPane.INFORMATION_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 初始化表格内容
|
||||
*/
|
||||
private void fillTable() {
|
||||
DefaultTableModel dtm=(DefaultTableModel) table.getModel();
|
||||
dtm.setRowCount(0);
|
||||
String jsonStr = readFileContent(configFilePath);
|
||||
JSONObject jsonObj = new JSONObject(jsonStr);
|
||||
Iterator<String> k = jsonObj.keys();
|
||||
// 遍历json数组
|
||||
while (k.hasNext()) {
|
||||
String name = k.next();
|
||||
JSONObject jsonObj1 = new JSONObject(jsonObj.get(name).toString());
|
||||
boolean loaded = jsonObj1.getBoolean("loaded");
|
||||
String regex = jsonObj1.getString("regex");
|
||||
String color = jsonObj1.getString("color");
|
||||
boolean isExtract = jsonObj1.getBoolean("extract");
|
||||
boolean isHighlight = jsonObj1.getBoolean("highlight");
|
||||
// 填充数据
|
||||
Vector rules = new Vector();
|
||||
rules.add(loaded);
|
||||
rules.add(name);
|
||||
rules.add(regex);
|
||||
rules.add(color);
|
||||
rules.add(isExtract);
|
||||
rules.add(isHighlight);
|
||||
dtm.addRow(rules);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
}
|
||||
}
|
||||
97
src/burp/IBurpCollaboratorClientContext.java
Normal file
@@ -0,0 +1,97 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IBurpCollaboratorClientContext.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This interface represents an instance of a Burp Collaborator client context,
|
||||
* which can be used to generate Burp Collaborator payloads and poll the
|
||||
* Collaborator server for any network interactions that result from using those
|
||||
* payloads. Extensions can obtain new instances of this class by calling
|
||||
* <code>IBurpExtenderCallbacks.createBurpCollaboratorClientContext()</code>.
|
||||
* Note that each Burp Collaborator client context is tied to the Collaborator
|
||||
* server configuration that was in place at the time the context was created.
|
||||
*/
|
||||
public interface IBurpCollaboratorClientContext
|
||||
{
|
||||
|
||||
/**
|
||||
* This method is used to generate new Burp Collaborator payloads.
|
||||
*
|
||||
* @param includeCollaboratorServerLocation Specifies whether to include the
|
||||
* Collaborator server location in the generated payload.
|
||||
* @return The payload that was generated.
|
||||
*
|
||||
* @throws IllegalStateException if Burp Collaborator is disabled
|
||||
*/
|
||||
String generatePayload(boolean includeCollaboratorServerLocation);
|
||||
|
||||
/**
|
||||
* This method is used to retrieve all interactions received by the
|
||||
* Collaborator server resulting from payloads that were generated for this
|
||||
* context.
|
||||
*
|
||||
* @return The Collaborator interactions that have occurred resulting from
|
||||
* payloads that were generated for this context.
|
||||
*
|
||||
* @throws IllegalStateException if Burp Collaborator is disabled
|
||||
*/
|
||||
List<IBurpCollaboratorInteraction> fetchAllCollaboratorInteractions();
|
||||
|
||||
/**
|
||||
* This method is used to retrieve interactions received by the Collaborator
|
||||
* server resulting from a single payload that was generated for this
|
||||
* context.
|
||||
*
|
||||
* @param payload The payload for which interactions will be retrieved.
|
||||
* @return The Collaborator interactions that have occurred resulting from
|
||||
* the given payload.
|
||||
*
|
||||
* @throws IllegalStateException if Burp Collaborator is disabled
|
||||
*/
|
||||
List<IBurpCollaboratorInteraction> fetchCollaboratorInteractionsFor(String payload);
|
||||
|
||||
/**
|
||||
* This method is used to retrieve all interactions made by Burp Infiltrator
|
||||
* instrumentation resulting from payloads that were generated for this
|
||||
* context.
|
||||
*
|
||||
* @return The interactions triggered by the Burp Infiltrator
|
||||
* instrumentation that have occurred resulting from payloads that were
|
||||
* generated for this context.
|
||||
*
|
||||
* @throws IllegalStateException if Burp Collaborator is disabled
|
||||
*/
|
||||
List<IBurpCollaboratorInteraction> fetchAllInfiltratorInteractions();
|
||||
|
||||
/**
|
||||
* This method is used to retrieve interactions made by Burp Infiltrator
|
||||
* instrumentation resulting from a single payload that was generated for
|
||||
* this context.
|
||||
*
|
||||
* @param payload The payload for which interactions will be retrieved.
|
||||
* @return The interactions triggered by the Burp Infiltrator
|
||||
* instrumentation that have occurred resulting from the given payload.
|
||||
*
|
||||
* @throws IllegalStateException if Burp Collaborator is disabled
|
||||
*/
|
||||
List<IBurpCollaboratorInteraction> fetchInfiltratorInteractionsFor(String payload);
|
||||
|
||||
/**
|
||||
* This method is used to retrieve the network location of the Collaborator
|
||||
* server.
|
||||
*
|
||||
* @return The hostname or IP address of the Collaborator server.
|
||||
*
|
||||
* @throws IllegalStateException if Burp Collaborator is disabled
|
||||
*/
|
||||
String getCollaboratorServerLocation();
|
||||
}
|
||||
41
src/burp/IBurpCollaboratorInteraction.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IBurpCollaboratorInteraction.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This interface represents a network interaction that occurred with the Burp
|
||||
* Collaborator server.
|
||||
*/
|
||||
public interface IBurpCollaboratorInteraction
|
||||
{
|
||||
|
||||
/**
|
||||
* This method is used to retrieve a property of the interaction. Properties
|
||||
* of all interactions are: interaction_id, type, client_ip, and time_stamp.
|
||||
* Properties of DNS interactions are: query_type and raw_query. The
|
||||
* raw_query value is Base64-encoded. Properties of HTTP interactions are:
|
||||
* protocol, request, and response. The request and response values are
|
||||
* Base64-encoded.
|
||||
*
|
||||
* @param name The name of the property to retrieve.
|
||||
* @return A string representing the property value, or null if not present.
|
||||
*/
|
||||
String getProperty(String name);
|
||||
|
||||
/**
|
||||
* This method is used to retrieve a map containing all properties of the
|
||||
* interaction.
|
||||
*
|
||||
* @return A map containing all properties of the interaction.
|
||||
*/
|
||||
Map<String, String> getProperties();
|
||||
}
|
||||
31
src/burp/IBurpExtender.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IBurpExtender.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
/**
|
||||
* All extensions must implement this interface.
|
||||
*
|
||||
* Implementations must be called BurpExtender, in the package burp, must be
|
||||
* declared public, and must provide a default (public, no-argument)
|
||||
* constructor.
|
||||
*/
|
||||
public interface IBurpExtender
|
||||
{
|
||||
/**
|
||||
* This method is invoked when the extension is loaded. It registers an
|
||||
* instance of the
|
||||
* <code>IBurpExtenderCallbacks</code> interface, providing methods that may
|
||||
* be invoked by the extension to perform various actions.
|
||||
*
|
||||
* @param callbacks An
|
||||
* <code>IBurpExtenderCallbacks</code> object.
|
||||
*/
|
||||
void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks);
|
||||
}
|
||||
1088
src/burp/IBurpExtenderCallbacks.java
Normal file
39
src/burp/IContextMenuFactory.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IContextMenuFactory.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
|
||||
import javax.swing.JMenuItem;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Extensions can implement this interface and then call
|
||||
* <code>IBurpExtenderCallbacks.registerContextMenuFactory()</code> to register
|
||||
* a factory for custom context menu items.
|
||||
*/
|
||||
public interface IContextMenuFactory
|
||||
{
|
||||
/**
|
||||
* This method will be called by Burp when the user invokes a context menu
|
||||
* anywhere within Burp. The factory can then provide any custom context
|
||||
* menu items that should be displayed in the context menu, based on the
|
||||
* details of the menu invocation.
|
||||
*
|
||||
* @param invocation An object that implements the
|
||||
* <code>IContextMenuInvocation</code> interface, which the extension can
|
||||
* query to obtain details of the context menu invocation.
|
||||
* @return A list of custom menu items (which may include sub-menus,
|
||||
* checkbox menu items, etc.) that should be displayed. Extensions may
|
||||
* return
|
||||
* <code>null</code> from this method, to indicate that no menu items are
|
||||
* required.
|
||||
*/
|
||||
List<JMenuItem> createMenuItems(IContextMenuInvocation invocation);
|
||||
}
|
||||
156
src/burp/IContextMenuInvocation.java
Normal file
@@ -0,0 +1,156 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IContextMenuInvocation.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
import java.awt.event.InputEvent;
|
||||
|
||||
/**
|
||||
* This interface is used when Burp calls into an extension-provided
|
||||
* <code>IContextMenuFactory</code> with details of a context menu invocation.
|
||||
* The custom context menu factory can query this interface to obtain details of
|
||||
* the invocation event, in order to determine what menu items should be
|
||||
* displayed.
|
||||
*/
|
||||
public interface IContextMenuInvocation
|
||||
{
|
||||
/**
|
||||
* Used to indicate that the context menu is being invoked in a request
|
||||
* editor.
|
||||
*/
|
||||
static final byte CONTEXT_MESSAGE_EDITOR_REQUEST = 0;
|
||||
/**
|
||||
* Used to indicate that the context menu is being invoked in a response
|
||||
* editor.
|
||||
*/
|
||||
static final byte CONTEXT_MESSAGE_EDITOR_RESPONSE = 1;
|
||||
/**
|
||||
* Used to indicate that the context menu is being invoked in a non-editable
|
||||
* request viewer.
|
||||
*/
|
||||
static final byte CONTEXT_MESSAGE_VIEWER_REQUEST = 2;
|
||||
/**
|
||||
* Used to indicate that the context menu is being invoked in a non-editable
|
||||
* response viewer.
|
||||
*/
|
||||
static final byte CONTEXT_MESSAGE_VIEWER_RESPONSE = 3;
|
||||
/**
|
||||
* Used to indicate that the context menu is being invoked in the Target
|
||||
* site map tree.
|
||||
*/
|
||||
static final byte CONTEXT_TARGET_SITE_MAP_TREE = 4;
|
||||
/**
|
||||
* Used to indicate that the context menu is being invoked in the Target
|
||||
* site map table.
|
||||
*/
|
||||
static final byte CONTEXT_TARGET_SITE_MAP_TABLE = 5;
|
||||
/**
|
||||
* Used to indicate that the context menu is being invoked in the Proxy
|
||||
* history.
|
||||
*/
|
||||
static final byte CONTEXT_PROXY_HISTORY = 6;
|
||||
/**
|
||||
* Used to indicate that the context menu is being invoked in the Scanner
|
||||
* results.
|
||||
*/
|
||||
static final byte CONTEXT_SCANNER_RESULTS = 7;
|
||||
/**
|
||||
* Used to indicate that the context menu is being invoked in the Intruder
|
||||
* payload positions editor.
|
||||
*/
|
||||
static final byte CONTEXT_INTRUDER_PAYLOAD_POSITIONS = 8;
|
||||
/**
|
||||
* Used to indicate that the context menu is being invoked in an Intruder
|
||||
* attack results.
|
||||
*/
|
||||
static final byte CONTEXT_INTRUDER_ATTACK_RESULTS = 9;
|
||||
/**
|
||||
* Used to indicate that the context menu is being invoked in a search
|
||||
* results window.
|
||||
*/
|
||||
static final byte CONTEXT_SEARCH_RESULTS = 10;
|
||||
|
||||
/**
|
||||
* This method can be used to retrieve the native Java input event that was
|
||||
* the trigger for the context menu invocation.
|
||||
*
|
||||
* @return The <code>InputEvent</code> that was the trigger for the context
|
||||
* menu invocation.
|
||||
*/
|
||||
InputEvent getInputEvent();
|
||||
|
||||
/**
|
||||
* This method can be used to retrieve the Burp tool within which the
|
||||
* context menu was invoked.
|
||||
*
|
||||
* @return A flag indicating the Burp tool within which the context menu was
|
||||
* invoked. Burp tool flags are defined in the
|
||||
* <code>IBurpExtenderCallbacks</code> interface.
|
||||
*/
|
||||
int getToolFlag();
|
||||
|
||||
/**
|
||||
* This method can be used to retrieve the context within which the menu was
|
||||
* invoked.
|
||||
*
|
||||
* @return An index indicating the context within which the menu was
|
||||
* invoked. The indices used are defined within this interface.
|
||||
*/
|
||||
byte getInvocationContext();
|
||||
|
||||
/**
|
||||
* This method can be used to retrieve the bounds of the user's selection
|
||||
* into the current message, if applicable.
|
||||
*
|
||||
* @return An int[2] array containing the start and end offsets of the
|
||||
* user's selection in the current message. If the user has not made any
|
||||
* selection in the current message, both offsets indicate the position of
|
||||
* the caret within the editor. If the menu is not being invoked from a
|
||||
* message editor, the method returns <code>null</code>.
|
||||
*/
|
||||
int[] getSelectionBounds();
|
||||
|
||||
/**
|
||||
* This method can be used to retrieve details of the HTTP requests /
|
||||
* responses that were shown or selected by the user when the context menu
|
||||
* was invoked.
|
||||
*
|
||||
* <b>Note:</b> For performance reasons, the objects returned from this
|
||||
* method are tied to the originating context of the messages within the
|
||||
* Burp UI. For example, if a context menu is invoked on the Proxy intercept
|
||||
* panel, then the
|
||||
* <code>IHttpRequestResponse</code> returned by this method will reflect
|
||||
* the current contents of the interception panel, and this will change when
|
||||
* the current message has been forwarded or dropped. If your extension
|
||||
* needs to store details of the message for which the context menu has been
|
||||
* invoked, then you should query those details from the
|
||||
* <code>IHttpRequestResponse</code> at the time of invocation, or you
|
||||
* should use
|
||||
* <code>IBurpExtenderCallbacks.saveBuffersToTempFiles()</code> to create a
|
||||
* persistent read-only copy of the
|
||||
* <code>IHttpRequestResponse</code>.
|
||||
*
|
||||
* @return An array of <code>IHttpRequestResponse</code> objects
|
||||
* representing the items that were shown or selected by the user when the
|
||||
* context menu was invoked. This method returns <code>null</code> if no
|
||||
* messages are applicable to the invocation.
|
||||
*/
|
||||
IHttpRequestResponse[] getSelectedMessages();
|
||||
|
||||
/**
|
||||
* This method can be used to retrieve details of the Scanner issues that
|
||||
* were selected by the user when the context menu was invoked.
|
||||
*
|
||||
* @return An array of <code>IScanIssue</code> objects representing the
|
||||
* issues that were selected by the user when the context menu was invoked.
|
||||
* This method returns <code>null</code> if no Scanner issues are applicable
|
||||
* to the invocation.
|
||||
*/
|
||||
IScanIssue[] getSelectedIssues();
|
||||
}
|
||||
61
src/burp/ICookie.java
Normal file
@@ -0,0 +1,61 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)ICookie.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* This interface is used to hold details about an HTTP cookie.
|
||||
*/
|
||||
public interface ICookie
|
||||
{
|
||||
/**
|
||||
* This method is used to retrieve the domain for which the cookie is in
|
||||
* scope.
|
||||
*
|
||||
* @return The domain for which the cookie is in scope. <b>Note:</b> For
|
||||
* cookies that have been analyzed from responses (by calling
|
||||
* <code>IExtensionHelpers.analyzeResponse()</code> and then
|
||||
* <code>IResponseInfo.getCookies()</code>, the domain will be
|
||||
* <code>null</code> if the response did not explicitly set a domain
|
||||
* attribute for the cookie.
|
||||
*/
|
||||
String getDomain();
|
||||
|
||||
/**
|
||||
* This method is used to retrieve the path for which the cookie is in
|
||||
* scope.
|
||||
*
|
||||
* @return The path for which the cookie is in scope or null if none is set.
|
||||
*/
|
||||
String getPath();
|
||||
|
||||
/**
|
||||
* This method is used to retrieve the expiration time for the cookie.
|
||||
*
|
||||
* @return The expiration time for the cookie, or
|
||||
* <code>null</code> if none is set (i.e., for non-persistent session
|
||||
* cookies).
|
||||
*/
|
||||
Date getExpiration();
|
||||
|
||||
/**
|
||||
* This method is used to retrieve the name of the cookie.
|
||||
*
|
||||
* @return The name of the cookie.
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* This method is used to retrieve the value of the cookie.
|
||||
* @return The value of the cookie.
|
||||
*/
|
||||
String getValue();
|
||||
}
|
||||
356
src/burp/IExtensionHelpers.java
Normal file
@@ -0,0 +1,356 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IExtensionHelpers.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This interface contains a number of helper methods, which extensions can use
|
||||
* to assist with various common tasks that arise for Burp extensions.
|
||||
*
|
||||
* Extensions can call <code>IBurpExtenderCallbacks.getHelpers</code> to obtain
|
||||
* an instance of this interface.
|
||||
*/
|
||||
public interface IExtensionHelpers
|
||||
{
|
||||
|
||||
/**
|
||||
* This method can be used to analyze an HTTP request, and obtain various
|
||||
* key details about it.
|
||||
*
|
||||
* @param request An <code>IHttpRequestResponse</code> object containing the
|
||||
* request to be analyzed.
|
||||
* @return An <code>IRequestInfo</code> object that can be queried to obtain
|
||||
* details about the request.
|
||||
*/
|
||||
IRequestInfo analyzeRequest(IHttpRequestResponse request);
|
||||
|
||||
/**
|
||||
* This method can be used to analyze an HTTP request, and obtain various
|
||||
* key details about it.
|
||||
*
|
||||
* @param httpService The HTTP service associated with the request. This is
|
||||
* optional and may be <code>null</code>, in which case the resulting
|
||||
* <code>IRequestInfo</code> object will not include the full request URL.
|
||||
* @param request The request to be analyzed.
|
||||
* @return An <code>IRequestInfo</code> object that can be queried to obtain
|
||||
* details about the request.
|
||||
*/
|
||||
IRequestInfo analyzeRequest(IHttpService httpService, byte[] request);
|
||||
|
||||
/**
|
||||
* This method can be used to analyze an HTTP request, and obtain various
|
||||
* key details about it. The resulting <code>IRequestInfo</code> object will
|
||||
* not include the full request URL. To obtain the full URL, use one of the
|
||||
* other overloaded <code>analyzeRequest()</code> methods.
|
||||
*
|
||||
* @param request The request to be analyzed.
|
||||
* @return An <code>IRequestInfo</code> object that can be queried to obtain
|
||||
* details about the request.
|
||||
*/
|
||||
IRequestInfo analyzeRequest(byte[] request);
|
||||
|
||||
/**
|
||||
* This method can be used to analyze an HTTP response, and obtain various
|
||||
* key details about it.
|
||||
*
|
||||
* @param response The response to be analyzed.
|
||||
* @return An <code>IResponseInfo</code> object that can be queried to
|
||||
* obtain details about the response.
|
||||
*/
|
||||
IResponseInfo analyzeResponse(byte[] response);
|
||||
|
||||
/**
|
||||
* This method can be used to retrieve details of a specified parameter
|
||||
* within an HTTP request. <b>Note:</b> Use <code>analyzeRequest()</code> to
|
||||
* obtain details of all parameters within the request.
|
||||
*
|
||||
* @param request The request to be inspected for the specified parameter.
|
||||
* @param parameterName The name of the parameter to retrieve.
|
||||
* @return An <code>IParameter</code> object that can be queried to obtain
|
||||
* details about the parameter, or <code>null</code> if the parameter was
|
||||
* not found.
|
||||
*/
|
||||
IParameter getRequestParameter(byte[] request, String parameterName);
|
||||
|
||||
/**
|
||||
* This method can be used to URL-decode the specified data.
|
||||
*
|
||||
* @param data The data to be decoded.
|
||||
* @return The decoded data.
|
||||
*/
|
||||
String urlDecode(String data);
|
||||
|
||||
/**
|
||||
* This method can be used to URL-encode the specified data. Any characters
|
||||
* that do not need to be encoded within HTTP requests are not encoded.
|
||||
*
|
||||
* @param data The data to be encoded.
|
||||
* @return The encoded data.
|
||||
*/
|
||||
String urlEncode(String data);
|
||||
|
||||
/**
|
||||
* This method can be used to URL-decode the specified data.
|
||||
*
|
||||
* @param data The data to be decoded.
|
||||
* @return The decoded data.
|
||||
*/
|
||||
byte[] urlDecode(byte[] data);
|
||||
|
||||
/**
|
||||
* This method can be used to URL-encode the specified data. Any characters
|
||||
* that do not need to be encoded within HTTP requests are not encoded.
|
||||
*
|
||||
* @param data The data to be encoded.
|
||||
* @return The encoded data.
|
||||
*/
|
||||
byte[] urlEncode(byte[] data);
|
||||
|
||||
/**
|
||||
* This method can be used to Base64-decode the specified data.
|
||||
*
|
||||
* @param data The data to be decoded.
|
||||
* @return The decoded data.
|
||||
*/
|
||||
byte[] base64Decode(String data);
|
||||
|
||||
/**
|
||||
* This method can be used to Base64-decode the specified data.
|
||||
*
|
||||
* @param data The data to be decoded.
|
||||
* @return The decoded data.
|
||||
*/
|
||||
byte[] base64Decode(byte[] data);
|
||||
|
||||
/**
|
||||
* This method can be used to Base64-encode the specified data.
|
||||
*
|
||||
* @param data The data to be encoded.
|
||||
* @return The encoded data.
|
||||
*/
|
||||
String base64Encode(String data);
|
||||
|
||||
/**
|
||||
* This method can be used to Base64-encode the specified data.
|
||||
*
|
||||
* @param data The data to be encoded.
|
||||
* @return The encoded data.
|
||||
*/
|
||||
String base64Encode(byte[] data);
|
||||
|
||||
/**
|
||||
* This method can be used to convert data from String form into an array of
|
||||
* bytes. The conversion does not reflect any particular character set, and
|
||||
* a character with the hex representation 0xWXYZ will always be converted
|
||||
* into a byte with the representation 0xYZ. It performs the opposite
|
||||
* conversion to the method <code>bytesToString()</code>, and byte-based
|
||||
* data that is converted to a String and back again using these two methods
|
||||
* is guaranteed to retain its integrity (which may not be the case with
|
||||
* conversions that reflect a given character set).
|
||||
*
|
||||
* @param data The data to be converted.
|
||||
* @return The converted data.
|
||||
*/
|
||||
byte[] stringToBytes(String data);
|
||||
|
||||
/**
|
||||
* This method can be used to convert data from an array of bytes into
|
||||
* String form. The conversion does not reflect any particular character
|
||||
* set, and a byte with the representation 0xYZ will always be converted
|
||||
* into a character with the hex representation 0x00YZ. It performs the
|
||||
* opposite conversion to the method <code>stringToBytes()</code>, and
|
||||
* byte-based data that is converted to a String and back again using these
|
||||
* two methods is guaranteed to retain its integrity (which may not be the
|
||||
* case with conversions that reflect a given character set).
|
||||
*
|
||||
* @param data The data to be converted.
|
||||
* @return The converted data.
|
||||
*/
|
||||
String bytesToString(byte[] data);
|
||||
|
||||
/**
|
||||
* This method searches a piece of data for the first occurrence of a
|
||||
* specified pattern. It works on byte-based data in a way that is similar
|
||||
* to the way the native Java method <code>String.indexOf()</code> works on
|
||||
* String-based data.
|
||||
*
|
||||
* @param data The data to be searched.
|
||||
* @param pattern The pattern to be searched for.
|
||||
* @param caseSensitive Flags whether or not the search is case-sensitive.
|
||||
* @param from The offset within <code>data</code> where the search should
|
||||
* begin.
|
||||
* @param to The offset within <code>data</code> where the search should
|
||||
* end.
|
||||
* @return The offset of the first occurrence of the pattern within the
|
||||
* specified bounds, or -1 if no match is found.
|
||||
*/
|
||||
int indexOf(byte[] data,
|
||||
byte[] pattern,
|
||||
boolean caseSensitive,
|
||||
int from,
|
||||
int to);
|
||||
|
||||
/**
|
||||
* This method builds an HTTP message containing the specified headers and
|
||||
* message body. If applicable, the Content-Length header will be added or
|
||||
* updated, based on the length of the body.
|
||||
*
|
||||
* @param headers A list of headers to include in the message.
|
||||
* @param body The body of the message, of <code>null</code> if the message
|
||||
* has an empty body.
|
||||
* @return The resulting full HTTP message.
|
||||
*/
|
||||
byte[] buildHttpMessage(List<String> headers, byte[] body);
|
||||
|
||||
/**
|
||||
* This method creates a GET request to the specified URL. The headers used
|
||||
* in the request are determined by the Request headers settings as
|
||||
* configured in Burp Spider's options.
|
||||
*
|
||||
* @param url The URL to which the request should be made.
|
||||
* @return A request to the specified URL.
|
||||
*/
|
||||
byte[] buildHttpRequest(URL url);
|
||||
|
||||
/**
|
||||
* This method adds a new parameter to an HTTP request, and if appropriate
|
||||
* updates the Content-Length header.
|
||||
*
|
||||
* @param request The request to which the parameter should be added.
|
||||
* @param parameter An <code>IParameter</code> object containing details of
|
||||
* the parameter to be added. Supported parameter types are:
|
||||
* <code>PARAM_URL</code>, <code>PARAM_BODY</code> and
|
||||
* <code>PARAM_COOKIE</code>.
|
||||
* @return A new HTTP request with the new parameter added.
|
||||
*/
|
||||
byte[] addParameter(byte[] request, IParameter parameter);
|
||||
|
||||
/**
|
||||
* This method removes a parameter from an HTTP request, and if appropriate
|
||||
* updates the Content-Length header.
|
||||
*
|
||||
* @param request The request from which the parameter should be removed.
|
||||
* @param parameter An <code>IParameter</code> object containing details of
|
||||
* the parameter to be removed. Supported parameter types are:
|
||||
* <code>PARAM_URL</code>, <code>PARAM_BODY</code> and
|
||||
* <code>PARAM_COOKIE</code>.
|
||||
* @return A new HTTP request with the parameter removed.
|
||||
*/
|
||||
byte[] removeParameter(byte[] request, IParameter parameter);
|
||||
|
||||
/**
|
||||
* This method updates the value of a parameter within an HTTP request, and
|
||||
* if appropriate updates the Content-Length header. <b>Note:</b> This
|
||||
* method can only be used to update the value of an existing parameter of a
|
||||
* specified type. If you need to change the type of an existing parameter,
|
||||
* you should first call <code>removeParameter()</code> to remove the
|
||||
* parameter with the old type, and then call <code>addParameter()</code> to
|
||||
* add a parameter with the new type.
|
||||
*
|
||||
* @param request The request containing the parameter to be updated.
|
||||
* @param parameter An <code>IParameter</code> object containing details of
|
||||
* the parameter to be updated. Supported parameter types are:
|
||||
* <code>PARAM_URL</code>, <code>PARAM_BODY</code> and
|
||||
* <code>PARAM_COOKIE</code>.
|
||||
* @return A new HTTP request with the parameter updated.
|
||||
*/
|
||||
byte[] updateParameter(byte[] request, IParameter parameter);
|
||||
|
||||
/**
|
||||
* This method can be used to toggle a request's method between GET and
|
||||
* POST. Parameters are relocated between the URL query string and message
|
||||
* body as required, and the Content-Length header is created or removed as
|
||||
* applicable.
|
||||
*
|
||||
* @param request The HTTP request whose method should be toggled.
|
||||
* @return A new HTTP request using the toggled method.
|
||||
*/
|
||||
byte[] toggleRequestMethod(byte[] request);
|
||||
|
||||
/**
|
||||
* This method constructs an <code>IHttpService</code> object based on the
|
||||
* details provided.
|
||||
*
|
||||
* @param host The HTTP service host.
|
||||
* @param port The HTTP service port.
|
||||
* @param protocol The HTTP service protocol.
|
||||
* @return An <code>IHttpService</code> object based on the details
|
||||
* provided.
|
||||
*/
|
||||
IHttpService buildHttpService(String host, int port, String protocol);
|
||||
|
||||
/**
|
||||
* This method constructs an <code>IHttpService</code> object based on the
|
||||
* details provided.
|
||||
*
|
||||
* @param host The HTTP service host.
|
||||
* @param port The HTTP service port.
|
||||
* @param useHttps Flags whether the HTTP service protocol is HTTPS or HTTP.
|
||||
* @return An <code>IHttpService</code> object based on the details
|
||||
* provided.
|
||||
*/
|
||||
IHttpService buildHttpService(String host, int port, boolean useHttps);
|
||||
|
||||
/**
|
||||
* This method constructs an <code>IParameter</code> object based on the
|
||||
* details provided.
|
||||
*
|
||||
* @param name The parameter name.
|
||||
* @param value The parameter value.
|
||||
* @param type The parameter type, as defined in the <code>IParameter</code>
|
||||
* interface.
|
||||
* @return An <code>IParameter</code> object based on the details provided.
|
||||
*/
|
||||
IParameter buildParameter(String name, String value, byte type);
|
||||
|
||||
/**
|
||||
* This method constructs an <code>IScannerInsertionPoint</code> object
|
||||
* based on the details provided. It can be used to quickly create a simple
|
||||
* insertion point based on a fixed payload location within a base request.
|
||||
*
|
||||
* @param insertionPointName The name of the insertion point.
|
||||
* @param baseRequest The request from which to build scan requests.
|
||||
* @param from The offset of the start of the payload location.
|
||||
* @param to The offset of the end of the payload location.
|
||||
* @return An <code>IScannerInsertionPoint</code> object based on the
|
||||
* details provided.
|
||||
*/
|
||||
IScannerInsertionPoint makeScannerInsertionPoint(
|
||||
String insertionPointName,
|
||||
byte[] baseRequest,
|
||||
int from,
|
||||
int to);
|
||||
|
||||
/**
|
||||
* This method analyzes one or more responses to identify variations in a
|
||||
* number of attributes and returns an <code>IResponseVariations</code>
|
||||
* object that can be queried to obtain details of the variations.
|
||||
*
|
||||
* @param responses The responses to analyze.
|
||||
* @return An <code>IResponseVariations</code> object representing the
|
||||
* variations in the responses.
|
||||
*/
|
||||
IResponseVariations analyzeResponseVariations(byte[]... responses);
|
||||
|
||||
/**
|
||||
* This method analyzes one or more responses to identify the number of
|
||||
* occurrences of the specified keywords and returns an
|
||||
* <code>IResponseKeywords</code> object that can be queried to obtain
|
||||
* details of the number of occurrences of each keyword.
|
||||
*
|
||||
* @param keywords The keywords to look for.
|
||||
* @param responses The responses to analyze.
|
||||
* @return An <code>IResponseKeywords</code> object representing the counts
|
||||
* of the keywords appearing in the responses.
|
||||
*/
|
||||
IResponseKeywords analyzeResponseKeywords(List<String> keywords, byte[]... responses);
|
||||
}
|
||||
27
src/burp/IExtensionStateListener.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IExtensionStateListener.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
/**
|
||||
* Extensions can implement this interface and then call
|
||||
* <code>IBurpExtenderCallbacks.registerExtensionStateListener()</code> to
|
||||
* register an extension state listener. The listener will be notified of
|
||||
* changes to the extension's state. <b>Note:</b> Any extensions that start
|
||||
* background threads or open system resources (such as files or database
|
||||
* connections) should register a listener and terminate threads / close
|
||||
* resources when the extension is unloaded.
|
||||
*/
|
||||
public interface IExtensionStateListener
|
||||
{
|
||||
/**
|
||||
* This method is called when the extension is unloaded.
|
||||
*/
|
||||
void extensionUnloaded();
|
||||
}
|
||||
37
src/burp/IHttpListener.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IHttpListener.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
/**
|
||||
* Extensions can implement this interface and then call
|
||||
* <code>IBurpExtenderCallbacks.registerHttpListener()</code> to register an
|
||||
* HTTP listener. The listener will be notified of requests and responses made
|
||||
* by any Burp tool. Extensions can perform custom analysis or modification of
|
||||
* these messages by registering an HTTP listener.
|
||||
*/
|
||||
public interface IHttpListener
|
||||
{
|
||||
/**
|
||||
* This method is invoked when an HTTP request is about to be issued, and
|
||||
* when an HTTP response has been received.
|
||||
*
|
||||
* @param toolFlag A flag indicating the Burp tool that issued the request.
|
||||
* Burp tool flags are defined in the
|
||||
* <code>IBurpExtenderCallbacks</code> interface.
|
||||
* @param messageIsRequest Flags whether the method is being invoked for a
|
||||
* request or response.
|
||||
* @param messageInfo Details of the request / response to be processed.
|
||||
* Extensions can call the setter methods on this object to update the
|
||||
* current message and so modify Burp's behavior.
|
||||
*/
|
||||
void processHttpMessage(int toolFlag,
|
||||
boolean messageIsRequest,
|
||||
IHttpRequestResponse messageInfo);
|
||||
}
|
||||
102
src/burp/IHttpRequestResponse.java
Normal file
@@ -0,0 +1,102 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IHttpRequestResponse.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
/**
|
||||
* This interface is used to retrieve and update details about HTTP messages.
|
||||
*
|
||||
* <b>Note:</b> The setter methods generally can only be used before the message
|
||||
* has been processed, and not in read-only contexts. The getter methods
|
||||
* relating to response details can only be used after the request has been
|
||||
* issued.
|
||||
*/
|
||||
public interface IHttpRequestResponse
|
||||
{
|
||||
/**
|
||||
* This method is used to retrieve the request message.
|
||||
*
|
||||
* @return The request message.
|
||||
*/
|
||||
byte[] getRequest();
|
||||
|
||||
/**
|
||||
* This method is used to update the request message.
|
||||
*
|
||||
* @param message The new request message.
|
||||
*/
|
||||
void setRequest(byte[] message);
|
||||
|
||||
/**
|
||||
* This method is used to retrieve the response message.
|
||||
*
|
||||
* @return The response message.
|
||||
*/
|
||||
byte[] getResponse();
|
||||
|
||||
/**
|
||||
* This method is used to update the response message.
|
||||
*
|
||||
* @param message The new response message.
|
||||
*/
|
||||
void setResponse(byte[] message);
|
||||
|
||||
/**
|
||||
* This method is used to retrieve the user-annotated comment for this item,
|
||||
* if applicable.
|
||||
*
|
||||
* @return The user-annotated comment for this item, or null if none is set.
|
||||
*/
|
||||
String getComment();
|
||||
|
||||
/**
|
||||
* This method is used to update the user-annotated comment for this item.
|
||||
*
|
||||
* @param comment The comment to be assigned to this item.
|
||||
*/
|
||||
void setComment(String comment);
|
||||
|
||||
/**
|
||||
* This method is used to retrieve the user-annotated highlight for this
|
||||
* item, if applicable.
|
||||
*
|
||||
* @return The user-annotated highlight for this item, or null if none is
|
||||
* set.
|
||||
*/
|
||||
String getHighlight();
|
||||
|
||||
/**
|
||||
* This method is used to update the user-annotated highlight for this item.
|
||||
*
|
||||
* @param color The highlight color to be assigned to this item. Accepted
|
||||
* values are: red, orange, yellow, green, cyan, blue, pink, magenta, gray,
|
||||
* or a null String to clear any existing highlight.
|
||||
*/
|
||||
void setHighlight(String color);
|
||||
|
||||
/**
|
||||
* This method is used to retrieve the HTTP service for this request /
|
||||
* response.
|
||||
*
|
||||
* @return An
|
||||
* <code>IHttpService</code> object containing details of the HTTP service.
|
||||
*/
|
||||
IHttpService getHttpService();
|
||||
|
||||
/**
|
||||
* This method is used to update the HTTP service for this request /
|
||||
* response.
|
||||
*
|
||||
* @param httpService An
|
||||
* <code>IHttpService</code> object containing details of the new HTTP
|
||||
* service.
|
||||
*/
|
||||
void setHttpService(IHttpService httpService);
|
||||
|
||||
}
|
||||
25
src/burp/IHttpRequestResponsePersisted.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IHttpRequestResponsePersisted.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
/**
|
||||
* This interface is used for an
|
||||
* <code>IHttpRequestResponse</code> object whose request and response messages
|
||||
* have been saved to temporary files using
|
||||
* <code>IBurpExtenderCallbacks.saveBuffersToTempFiles()</code>.
|
||||
*/
|
||||
public interface IHttpRequestResponsePersisted extends IHttpRequestResponse
|
||||
{
|
||||
/**
|
||||
* This method is deprecated and no longer performs any action.
|
||||
*/
|
||||
@Deprecated
|
||||
void deleteTempFiles();
|
||||
}
|
||||
44
src/burp/IHttpRequestResponseWithMarkers.java
Normal file
@@ -0,0 +1,44 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IHttpRequestResponseWithMarkers.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This interface is used for an
|
||||
* <code>IHttpRequestResponse</code> object that has had markers applied.
|
||||
* Extensions can create instances of this interface using
|
||||
* <code>IBurpExtenderCallbacks.applyMarkers()</code>, or provide their own
|
||||
* implementation. Markers are used in various situations, such as specifying
|
||||
* Intruder payload positions, Scanner insertion points, and highlights in
|
||||
* Scanner issues.
|
||||
*/
|
||||
public interface IHttpRequestResponseWithMarkers extends IHttpRequestResponse
|
||||
{
|
||||
/**
|
||||
* This method returns the details of the request markers.
|
||||
*
|
||||
* @return A list of index pairs representing the offsets of markers for the
|
||||
* request message. Each item in the list is an int[2] array containing the
|
||||
* start and end offsets for the marker. The method may return
|
||||
* <code>null</code> if no request markers are defined.
|
||||
*/
|
||||
List<int[]> getRequestMarkers();
|
||||
|
||||
/**
|
||||
* This method returns the details of the response markers.
|
||||
*
|
||||
* @return A list of index pairs representing the offsets of markers for the
|
||||
* response message. Each item in the list is an int[2] array containing the
|
||||
* start and end offsets for the marker. The method may return
|
||||
* <code>null</code> if no response markers are defined.
|
||||
*/
|
||||
List<int[]> getResponseMarkers();
|
||||
}
|
||||
39
src/burp/IHttpService.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IHttpService.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
/**
|
||||
* This interface is used to provide details about an HTTP service, to which
|
||||
* HTTP requests can be sent.
|
||||
*/
|
||||
public interface IHttpService
|
||||
{
|
||||
/**
|
||||
* This method returns the hostname or IP address for the service.
|
||||
*
|
||||
* @return The hostname or IP address for the service.
|
||||
*/
|
||||
String getHost();
|
||||
|
||||
/**
|
||||
* This method returns the port number for the service.
|
||||
*
|
||||
* @return The port number for the service.
|
||||
*/
|
||||
int getPort();
|
||||
|
||||
/**
|
||||
* This method returns the protocol for the service.
|
||||
*
|
||||
* @return The protocol for the service. Expected values are "http" or
|
||||
* "https".
|
||||
*/
|
||||
String getProtocol();
|
||||
}
|
||||
116
src/burp/IInterceptedProxyMessage.java
Normal file
@@ -0,0 +1,116 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IInterceptedProxyMessage.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
import java.net.InetAddress;
|
||||
|
||||
/**
|
||||
* This interface is used to represent an HTTP message that has been intercepted
|
||||
* by Burp Proxy. Extensions can register an
|
||||
* <code>IProxyListener</code> to receive details of proxy messages using this
|
||||
* interface. *
|
||||
*/
|
||||
public interface IInterceptedProxyMessage
|
||||
{
|
||||
/**
|
||||
* This action causes Burp Proxy to follow the current interception rules to
|
||||
* determine the appropriate action to take for the message.
|
||||
*/
|
||||
static final int ACTION_FOLLOW_RULES = 0;
|
||||
/**
|
||||
* This action causes Burp Proxy to present the message to the user for
|
||||
* manual review or modification.
|
||||
*/
|
||||
static final int ACTION_DO_INTERCEPT = 1;
|
||||
/**
|
||||
* This action causes Burp Proxy to forward the message to the remote server
|
||||
* or client, without presenting it to the user.
|
||||
*/
|
||||
static final int ACTION_DONT_INTERCEPT = 2;
|
||||
/**
|
||||
* This action causes Burp Proxy to drop the message.
|
||||
*/
|
||||
static final int ACTION_DROP = 3;
|
||||
/**
|
||||
* This action causes Burp Proxy to follow the current interception rules to
|
||||
* determine the appropriate action to take for the message, and then make a
|
||||
* second call to processProxyMessage.
|
||||
*/
|
||||
static final int ACTION_FOLLOW_RULES_AND_REHOOK = 0x10;
|
||||
/**
|
||||
* This action causes Burp Proxy to present the message to the user for
|
||||
* manual review or modification, and then make a second call to
|
||||
* processProxyMessage.
|
||||
*/
|
||||
static final int ACTION_DO_INTERCEPT_AND_REHOOK = 0x11;
|
||||
/**
|
||||
* This action causes Burp Proxy to skip user interception, and then make a
|
||||
* second call to processProxyMessage.
|
||||
*/
|
||||
static final int ACTION_DONT_INTERCEPT_AND_REHOOK = 0x12;
|
||||
|
||||
/**
|
||||
* This method retrieves a unique reference number for this
|
||||
* request/response.
|
||||
*
|
||||
* @return An identifier that is unique to a single request/response pair.
|
||||
* Extensions can use this to correlate details of requests and responses
|
||||
* and perform processing on the response message accordingly.
|
||||
*/
|
||||
int getMessageReference();
|
||||
|
||||
/**
|
||||
* This method retrieves details of the intercepted message.
|
||||
*
|
||||
* @return An <code>IHttpRequestResponse</code> object containing details of
|
||||
* the intercepted message.
|
||||
*/
|
||||
IHttpRequestResponse getMessageInfo();
|
||||
|
||||
/**
|
||||
* This method retrieves the currently defined interception action. The
|
||||
* default action is
|
||||
* <code>ACTION_FOLLOW_RULES</code>. If multiple proxy listeners are
|
||||
* registered, then other listeners may already have modified the
|
||||
* interception action before it reaches the current listener. This method
|
||||
* can be used to determine whether this has occurred.
|
||||
*
|
||||
* @return The currently defined interception action. Possible values are
|
||||
* defined within this interface.
|
||||
*/
|
||||
int getInterceptAction();
|
||||
|
||||
/**
|
||||
* This method is used to update the interception action.
|
||||
*
|
||||
* @param interceptAction The new interception action. Possible values are
|
||||
* defined within this interface.
|
||||
*/
|
||||
void setInterceptAction(int interceptAction);
|
||||
|
||||
/**
|
||||
* This method retrieves the name of the Burp Proxy listener that is
|
||||
* processing the intercepted message.
|
||||
*
|
||||
* @return The name of the Burp Proxy listener that is processing the
|
||||
* intercepted message. The format is the same as that shown in the Proxy
|
||||
* Listeners UI - for example, "127.0.0.1:8080".
|
||||
*/
|
||||
String getListenerInterface();
|
||||
|
||||
/**
|
||||
* This method retrieves the client IP address from which the request for
|
||||
* the intercepted message was received.
|
||||
*
|
||||
* @return The client IP address from which the request for the intercepted
|
||||
* message was received.
|
||||
*/
|
||||
InetAddress getClientIpAddress();
|
||||
}
|
||||
31
src/burp/IIntruderAttack.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IIntruderAttack.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
/**
|
||||
* This interface is used to hold details about an Intruder attack.
|
||||
*/
|
||||
public interface IIntruderAttack
|
||||
{
|
||||
/**
|
||||
* This method is used to retrieve the HTTP service for the attack.
|
||||
*
|
||||
* @return The HTTP service for the attack.
|
||||
*/
|
||||
IHttpService getHttpService();
|
||||
|
||||
/**
|
||||
* This method is used to retrieve the request template for the attack.
|
||||
*
|
||||
* @return The request template for the attack.
|
||||
*/
|
||||
byte[] getRequestTemplate();
|
||||
|
||||
}
|
||||
50
src/burp/IIntruderPayloadGenerator.java
Normal file
@@ -0,0 +1,50 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IIntruderPayloadGenerator.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
/**
|
||||
* This interface is used for custom Intruder payload generators. Extensions
|
||||
* that have registered an
|
||||
* <code>IIntruderPayloadGeneratorFactory</code> must return a new instance of
|
||||
* this interface when required as part of a new Intruder attack.
|
||||
*/
|
||||
public interface IIntruderPayloadGenerator
|
||||
{
|
||||
/**
|
||||
* This method is used by Burp to determine whether the payload generator is
|
||||
* able to provide any further payloads.
|
||||
*
|
||||
* @return Extensions should return
|
||||
* <code>false</code> when all the available payloads have been used up,
|
||||
* otherwise
|
||||
* <code>true</code>.
|
||||
*/
|
||||
boolean hasMorePayloads();
|
||||
|
||||
/**
|
||||
* This method is used by Burp to obtain the value of the next payload.
|
||||
*
|
||||
* @param baseValue The base value of the current payload position. This
|
||||
* value may be
|
||||
* <code>null</code> if the concept of a base value is not applicable (e.g.
|
||||
* in a battering ram attack).
|
||||
* @return The next payload to use in the attack.
|
||||
*/
|
||||
byte[] getNextPayload(byte[] baseValue);
|
||||
|
||||
/**
|
||||
* This method is used by Burp to reset the state of the payload generator
|
||||
* so that the next call to
|
||||
* <code>getNextPayload()</code> returns the first payload again. This
|
||||
* method will be invoked when an attack uses the same payload generator for
|
||||
* more than one payload position, for example in a sniper attack.
|
||||
*/
|
||||
void reset();
|
||||
}
|
||||
40
src/burp/IIntruderPayloadGeneratorFactory.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IIntruderPayloadGeneratorFactory.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
/**
|
||||
* Extensions can implement this interface and then call
|
||||
* <code>IBurpExtenderCallbacks.registerIntruderPayloadGeneratorFactory()</code>
|
||||
* to register a factory for custom Intruder payloads.
|
||||
*/
|
||||
public interface IIntruderPayloadGeneratorFactory
|
||||
{
|
||||
/**
|
||||
* This method is used by Burp to obtain the name of the payload generator.
|
||||
* This will be displayed as an option within the Intruder UI when the user
|
||||
* selects to use extension-generated payloads.
|
||||
*
|
||||
* @return The name of the payload generator.
|
||||
*/
|
||||
String getGeneratorName();
|
||||
|
||||
/**
|
||||
* This method is used by Burp when the user starts an Intruder attack that
|
||||
* uses this payload generator.
|
||||
*
|
||||
* @param attack An
|
||||
* <code>IIntruderAttack</code> object that can be queried to obtain details
|
||||
* about the attack in which the payload generator will be used.
|
||||
* @return A new instance of
|
||||
* <code>IIntruderPayloadGenerator</code> that will be used to generate
|
||||
* payloads for the attack.
|
||||
*/
|
||||
IIntruderPayloadGenerator createNewInstance(IIntruderAttack attack);
|
||||
}
|
||||
45
src/burp/IIntruderPayloadProcessor.java
Normal file
@@ -0,0 +1,45 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IIntruderPayloadProcessor.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
/**
|
||||
* Extensions can implement this interface and then call
|
||||
* <code>IBurpExtenderCallbacks.registerIntruderPayloadProcessor()</code> to
|
||||
* register a custom Intruder payload processor.
|
||||
*/
|
||||
public interface IIntruderPayloadProcessor
|
||||
{
|
||||
/**
|
||||
* This method is used by Burp to obtain the name of the payload processor.
|
||||
* This will be displayed as an option within the Intruder UI when the user
|
||||
* selects to use an extension-provided payload processor.
|
||||
*
|
||||
* @return The name of the payload processor.
|
||||
*/
|
||||
String getProcessorName();
|
||||
|
||||
/**
|
||||
* This method is invoked by Burp each time the processor should be applied
|
||||
* to an Intruder payload.
|
||||
*
|
||||
* @param currentPayload The value of the payload to be processed.
|
||||
* @param originalPayload The value of the original payload prior to
|
||||
* processing by any already-applied processing rules.
|
||||
* @param baseValue The base value of the payload position, which will be
|
||||
* replaced with the current payload.
|
||||
* @return The value of the processed payload. This may be
|
||||
* <code>null</code> to indicate that the current payload should be skipped,
|
||||
* and the attack will move directly to the next payload.
|
||||
*/
|
||||
byte[] processPayload(
|
||||
byte[] currentPayload,
|
||||
byte[] originalPayload,
|
||||
byte[] baseValue);
|
||||
}
|
||||
36
src/burp/IMenuItemHandler.java
Normal file
@@ -0,0 +1,36 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IMenuItemHandler.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
/**
|
||||
* Extensions can implement this interface and then call
|
||||
* <code>IBurpExtenderCallbacks.registerMenuItem()</code> to register a custom
|
||||
* context menu item.
|
||||
*
|
||||
* @deprecated Use
|
||||
* <code>IContextMenuFactory</code> instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface IMenuItemHandler
|
||||
{
|
||||
/**
|
||||
* This method is invoked by Burp Suite when the user clicks on a custom
|
||||
* menu item which the extension has registered with Burp.
|
||||
*
|
||||
* @param menuItemCaption The caption of the menu item which was clicked.
|
||||
* This parameter enables extensions to provide a single implementation
|
||||
* which handles multiple different menu items.
|
||||
* @param messageInfo Details of the HTTP message(s) for which the context
|
||||
* menu was displayed.
|
||||
*/
|
||||
void menuItemClicked(
|
||||
String menuItemCaption,
|
||||
IHttpRequestResponse[] messageInfo);
|
||||
}
|
||||
77
src/burp/IMessageEditor.java
Normal file
@@ -0,0 +1,77 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IMessageEditor.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
import java.awt.Component;
|
||||
|
||||
/**
|
||||
* This interface is used to provide extensions with an instance of Burp's HTTP
|
||||
* message editor, for the extension to use in its own UI. Extensions should
|
||||
* call <code>IBurpExtenderCallbacks.createMessageEditor()</code> to obtain an
|
||||
* instance of this interface.
|
||||
*/
|
||||
public interface IMessageEditor
|
||||
{
|
||||
|
||||
/**
|
||||
* This method returns the UI component of the editor, for extensions to add
|
||||
* to their own UI.
|
||||
*
|
||||
* @return The UI component of the editor.
|
||||
*/
|
||||
Component getComponent();
|
||||
|
||||
/**
|
||||
* This method is used to display an HTTP message in the editor.
|
||||
*
|
||||
* @param message The HTTP message to be displayed.
|
||||
* @param isRequest Flags whether the message is an HTTP request or
|
||||
* response.
|
||||
*/
|
||||
void setMessage(byte[] message, boolean isRequest);
|
||||
|
||||
/**
|
||||
* This method is used to retrieve the currently displayed message, which
|
||||
* may have been modified by the user.
|
||||
*
|
||||
* @return The currently displayed HTTP message.
|
||||
*/
|
||||
byte[] getMessage();
|
||||
|
||||
/**
|
||||
* This method is used to determine whether the current message has been
|
||||
* modified by the user.
|
||||
*
|
||||
* @return An indication of whether the current message has been modified by
|
||||
* the user since it was first displayed.
|
||||
*/
|
||||
boolean isMessageModified();
|
||||
|
||||
/**
|
||||
* This method returns the data that is currently selected by the user.
|
||||
*
|
||||
* @return The data that is currently selected by the user, or
|
||||
* <code>null</code> if no selection is made.
|
||||
*/
|
||||
byte[] getSelectedData();
|
||||
|
||||
/**
|
||||
* This method can be used to retrieve the bounds of the user's selection
|
||||
* into the displayed message, if applicable.
|
||||
*
|
||||
* @return An int[2] array containing the start and end offsets of the
|
||||
* user's selection within the displayed message. If the user has not made
|
||||
* any selection in the current message, both offsets indicate the position
|
||||
* of the caret within the editor. For some editor views, the concept of
|
||||
* selection within the message does not apply, in which case this method
|
||||
* returns null.
|
||||
*/
|
||||
int[] getSelectionBounds();
|
||||
}
|
||||
49
src/burp/IMessageEditorController.java
Normal file
@@ -0,0 +1,49 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IMessageEditorController.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
/**
|
||||
* This interface is used by an
|
||||
* <code>IMessageEditor</code> to obtain details about the currently displayed
|
||||
* message. Extensions that create instances of Burp's HTTP message editor can
|
||||
* optionally provide an implementation of
|
||||
* <code>IMessageEditorController</code>, which the editor will invoke when it
|
||||
* requires further information about the current message (for example, to send
|
||||
* it to another Burp tool). Extensions that provide custom editor tabs via an
|
||||
* <code>IMessageEditorTabFactory</code> will receive a reference to an
|
||||
* <code>IMessageEditorController</code> object for each tab instance they
|
||||
* generate, which the tab can invoke if it requires further information about
|
||||
* the current message.
|
||||
*/
|
||||
public interface IMessageEditorController
|
||||
{
|
||||
/**
|
||||
* This method is used to retrieve the HTTP service for the current message.
|
||||
*
|
||||
* @return The HTTP service for the current message.
|
||||
*/
|
||||
IHttpService getHttpService();
|
||||
|
||||
/**
|
||||
* This method is used to retrieve the HTTP request associated with the
|
||||
* current message (which may itself be a response).
|
||||
*
|
||||
* @return The HTTP request associated with the current message.
|
||||
*/
|
||||
byte[] getRequest();
|
||||
|
||||
/**
|
||||
* This method is used to retrieve the HTTP response associated with the
|
||||
* current message (which may itself be a request).
|
||||
*
|
||||
* @return The HTTP response associated with the current message.
|
||||
*/
|
||||
byte[] getResponse();
|
||||
}
|
||||
103
src/burp/IMessageEditorTab.java
Normal file
@@ -0,0 +1,103 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IMessageEditorTab.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
import java.awt.Component;
|
||||
|
||||
/**
|
||||
* Extensions that register an
|
||||
* <code>IMessageEditorTabFactory</code> must return instances of this
|
||||
* interface, which Burp will use to create custom tabs within its HTTP message
|
||||
* editors.
|
||||
*/
|
||||
public interface IMessageEditorTab
|
||||
{
|
||||
/**
|
||||
* This method returns the caption that should appear on the custom tab when
|
||||
* it is displayed. <b>Note:</b> Burp invokes this method once when the tab
|
||||
* is first generated, and the same caption will be used every time the tab
|
||||
* is displayed.
|
||||
*
|
||||
* @return The caption that should appear on the custom tab when it is
|
||||
* displayed.
|
||||
*/
|
||||
String getTabCaption();
|
||||
|
||||
/**
|
||||
* This method returns the component that should be used as the contents of
|
||||
* the custom tab when it is displayed. <b>Note:</b> Burp invokes this
|
||||
* method once when the tab is first generated, and the same component will
|
||||
* be used every time the tab is displayed.
|
||||
*
|
||||
* @return The component that should be used as the contents of the custom
|
||||
* tab when it is displayed.
|
||||
*/
|
||||
Component getUiComponent();
|
||||
|
||||
/**
|
||||
* The hosting editor will invoke this method before it displays a new HTTP
|
||||
* message, so that the custom tab can indicate whether it should be enabled
|
||||
* for that message.
|
||||
*
|
||||
* @param content The message that is about to be displayed, or a zero-length
|
||||
* array if the existing message is to be cleared.
|
||||
* @param isRequest Indicates whether the message is a request or a
|
||||
* response.
|
||||
* @return The method should return
|
||||
* <code>true</code> if the custom tab is able to handle the specified
|
||||
* message, and so will be displayed within the editor. Otherwise, the tab
|
||||
* will be hidden while this message is displayed.
|
||||
*/
|
||||
boolean isEnabled(byte[] content, boolean isRequest);
|
||||
|
||||
/**
|
||||
* The hosting editor will invoke this method to display a new message or to
|
||||
* clear the existing message. This method will only be called with a new
|
||||
* message if the tab has already returned
|
||||
* <code>true</code> to a call to
|
||||
* <code>isEnabled()</code> with the same message details.
|
||||
*
|
||||
* @param content The message that is to be displayed, or
|
||||
* <code>null</code> if the tab should clear its contents and disable any
|
||||
* editable controls.
|
||||
* @param isRequest Indicates whether the message is a request or a
|
||||
* response.
|
||||
*/
|
||||
void setMessage(byte[] content, boolean isRequest);
|
||||
|
||||
/**
|
||||
* This method returns the currently displayed message.
|
||||
*
|
||||
* @return The currently displayed message.
|
||||
*/
|
||||
byte[] getMessage();
|
||||
|
||||
/**
|
||||
* This method is used to determine whether the currently displayed message
|
||||
* has been modified by the user. The hosting editor will always call
|
||||
* <code>getMessage()</code> before calling this method, so any pending
|
||||
* edits should be completed within
|
||||
* <code>getMessage()</code>.
|
||||
*
|
||||
* @return The method should return
|
||||
* <code>true</code> if the user has modified the current message since it
|
||||
* was first displayed.
|
||||
*/
|
||||
boolean isModified();
|
||||
|
||||
/**
|
||||
* This method is used to retrieve the data that is currently selected by
|
||||
* the user.
|
||||
*
|
||||
* @return The data that is currently selected by the user. This may be
|
||||
* <code>null</code> if no selection is currently made.
|
||||
*/
|
||||
byte[] getSelectedData();
|
||||
}
|
||||
38
src/burp/IMessageEditorTabFactory.java
Normal file
@@ -0,0 +1,38 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IMessageEditorTabFactory.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
/**
|
||||
* Extensions can implement this interface and then call
|
||||
* <code>IBurpExtenderCallbacks.registerMessageEditorTabFactory()</code> to
|
||||
* register a factory for custom message editor tabs. This allows extensions to
|
||||
* provide custom rendering or editing of HTTP messages, within Burp's own HTTP
|
||||
* editor.
|
||||
*/
|
||||
public interface IMessageEditorTabFactory
|
||||
{
|
||||
/**
|
||||
* Burp will call this method once for each HTTP message editor, and the
|
||||
* factory should provide a new instance of an
|
||||
* <code>IMessageEditorTab</code> object.
|
||||
*
|
||||
* @param controller An
|
||||
* <code>IMessageEditorController</code> object, which the new tab can query
|
||||
* to retrieve details about the currently displayed message. This may be
|
||||
* <code>null</code> for extension-invoked message editors where the
|
||||
* extension has not provided an editor controller.
|
||||
* @param editable Indicates whether the hosting editor is editable or
|
||||
* read-only.
|
||||
* @return A new
|
||||
* <code>IMessageEditorTab</code> object for use within the message editor.
|
||||
*/
|
||||
IMessageEditorTab createNewInstance(IMessageEditorController controller,
|
||||
boolean editable);
|
||||
}
|
||||
104
src/burp/IParameter.java
Normal file
@@ -0,0 +1,104 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IParameter.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
/**
|
||||
* This interface is used to hold details about an HTTP request parameter.
|
||||
*/
|
||||
public interface IParameter
|
||||
{
|
||||
/**
|
||||
* Used to indicate a parameter within the URL query string.
|
||||
*/
|
||||
static final byte PARAM_URL = 0;
|
||||
/**
|
||||
* Used to indicate a parameter within the message body.
|
||||
*/
|
||||
static final byte PARAM_BODY = 1;
|
||||
/**
|
||||
* Used to indicate an HTTP cookie.
|
||||
*/
|
||||
static final byte PARAM_COOKIE = 2;
|
||||
/**
|
||||
* Used to indicate an item of data within an XML structure.
|
||||
*/
|
||||
static final byte PARAM_XML = 3;
|
||||
/**
|
||||
* Used to indicate the value of a tag attribute within an XML structure.
|
||||
*/
|
||||
static final byte PARAM_XML_ATTR = 4;
|
||||
/**
|
||||
* Used to indicate the value of a parameter attribute within a multi-part
|
||||
* message body (such as the name of an uploaded file).
|
||||
*/
|
||||
static final byte PARAM_MULTIPART_ATTR = 5;
|
||||
/**
|
||||
* Used to indicate an item of data within a JSON structure.
|
||||
*/
|
||||
static final byte PARAM_JSON = 6;
|
||||
|
||||
/**
|
||||
* This method is used to retrieve the parameter type.
|
||||
*
|
||||
* @return The parameter type. The available types are defined within this
|
||||
* interface.
|
||||
*/
|
||||
byte getType();
|
||||
|
||||
/**
|
||||
* This method is used to retrieve the parameter name.
|
||||
*
|
||||
* @return The parameter name.
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* This method is used to retrieve the parameter value.
|
||||
*
|
||||
* @return The parameter value.
|
||||
*/
|
||||
String getValue();
|
||||
|
||||
/**
|
||||
* This method is used to retrieve the start offset of the parameter name
|
||||
* within the HTTP request.
|
||||
*
|
||||
* @return The start offset of the parameter name within the HTTP request,
|
||||
* or -1 if the parameter is not associated with a specific request.
|
||||
*/
|
||||
int getNameStart();
|
||||
|
||||
/**
|
||||
* This method is used to retrieve the end offset of the parameter name
|
||||
* within the HTTP request.
|
||||
*
|
||||
* @return The end offset of the parameter name within the HTTP request, or
|
||||
* -1 if the parameter is not associated with a specific request.
|
||||
*/
|
||||
int getNameEnd();
|
||||
|
||||
/**
|
||||
* This method is used to retrieve the start offset of the parameter value
|
||||
* within the HTTP request.
|
||||
*
|
||||
* @return The start offset of the parameter value within the HTTP request,
|
||||
* or -1 if the parameter is not associated with a specific request.
|
||||
*/
|
||||
int getValueStart();
|
||||
|
||||
/**
|
||||
* This method is used to retrieve the end offset of the parameter value
|
||||
* within the HTTP request.
|
||||
*
|
||||
* @return The end offset of the parameter value within the HTTP request, or
|
||||
* -1 if the parameter is not associated with a specific request.
|
||||
*/
|
||||
int getValueEnd();
|
||||
}
|
||||
37
src/burp/IProxyListener.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IProxyListener.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
/**
|
||||
* Extensions can implement this interface and then call
|
||||
* <code>IBurpExtenderCallbacks.registerProxyListener()</code> to register a
|
||||
* Proxy listener. The listener will be notified of requests and responses being
|
||||
* processed by the Proxy tool. Extensions can perform custom analysis or
|
||||
* modification of these messages, and control in-UI message interception, by
|
||||
* registering a proxy listener.
|
||||
*/
|
||||
public interface IProxyListener
|
||||
{
|
||||
/**
|
||||
* This method is invoked when an HTTP message is being processed by the
|
||||
* Proxy.
|
||||
*
|
||||
* @param messageIsRequest Indicates whether the HTTP message is a request
|
||||
* or a response.
|
||||
* @param message An
|
||||
* <code>IInterceptedProxyMessage</code> object that extensions can use to
|
||||
* query and update details of the message, and control whether the message
|
||||
* should be intercepted and displayed to the user for manual review or
|
||||
* modification.
|
||||
*/
|
||||
void processProxyMessage(
|
||||
boolean messageIsRequest,
|
||||
IInterceptedProxyMessage message);
|
||||
}
|
||||
95
src/burp/IRequestInfo.java
Normal file
@@ -0,0 +1,95 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IRequestInfo.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This interface is used to retrieve key details about an HTTP request.
|
||||
* Extensions can obtain an
|
||||
* <code>IRequestInfo</code> object for a given request by calling
|
||||
* <code>IExtensionHelpers.analyzeRequest()</code>.
|
||||
*/
|
||||
public interface IRequestInfo
|
||||
{
|
||||
/**
|
||||
* Used to indicate that there is no content.
|
||||
*/
|
||||
static final byte CONTENT_TYPE_NONE = 0;
|
||||
/**
|
||||
* Used to indicate URL-encoded content.
|
||||
*/
|
||||
static final byte CONTENT_TYPE_URL_ENCODED = 1;
|
||||
/**
|
||||
* Used to indicate multi-part content.
|
||||
*/
|
||||
static final byte CONTENT_TYPE_MULTIPART = 2;
|
||||
/**
|
||||
* Used to indicate XML content.
|
||||
*/
|
||||
static final byte CONTENT_TYPE_XML = 3;
|
||||
/**
|
||||
* Used to indicate JSON content.
|
||||
*/
|
||||
static final byte CONTENT_TYPE_JSON = 4;
|
||||
/**
|
||||
* Used to indicate AMF content.
|
||||
*/
|
||||
static final byte CONTENT_TYPE_AMF = 5;
|
||||
/**
|
||||
* Used to indicate unknown content.
|
||||
*/
|
||||
static final byte CONTENT_TYPE_UNKNOWN = -1;
|
||||
|
||||
/**
|
||||
* This method is used to obtain the HTTP method used in the request.
|
||||
*
|
||||
* @return The HTTP method used in the request.
|
||||
*/
|
||||
String getMethod();
|
||||
|
||||
/**
|
||||
* This method is used to obtain the URL in the request.
|
||||
*
|
||||
* @return The URL in the request.
|
||||
*/
|
||||
URL getUrl();
|
||||
|
||||
/**
|
||||
* This method is used to obtain the HTTP headers contained in the request.
|
||||
*
|
||||
* @return The HTTP headers contained in the request.
|
||||
*/
|
||||
List<String> getHeaders();
|
||||
|
||||
/**
|
||||
* This method is used to obtain the parameters contained in the request.
|
||||
*
|
||||
* @return The parameters contained in the request.
|
||||
*/
|
||||
List<IParameter> getParameters();
|
||||
|
||||
/**
|
||||
* This method is used to obtain the offset within the request where the
|
||||
* message body begins.
|
||||
*
|
||||
* @return The offset within the request where the message body begins.
|
||||
*/
|
||||
int getBodyOffset();
|
||||
|
||||
/**
|
||||
* This method is used to obtain the content type of the message body.
|
||||
*
|
||||
* @return An indication of the content type of the message body. Available
|
||||
* types are defined within this interface.
|
||||
*/
|
||||
byte getContentType();
|
||||
}
|
||||
73
src/burp/IResponseInfo.java
Normal file
@@ -0,0 +1,73 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IResponseInfo.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This interface is used to retrieve key details about an HTTP response.
|
||||
* Extensions can obtain an
|
||||
* <code>IResponseInfo</code> object for a given response by calling
|
||||
* <code>IExtensionHelpers.analyzeResponse()</code>.
|
||||
*/
|
||||
public interface IResponseInfo
|
||||
{
|
||||
/**
|
||||
* This method is used to obtain the HTTP headers contained in the response.
|
||||
*
|
||||
* @return The HTTP headers contained in the response.
|
||||
*/
|
||||
List<String> getHeaders();
|
||||
|
||||
/**
|
||||
* This method is used to obtain the offset within the response where the
|
||||
* message body begins.
|
||||
*
|
||||
* @return The offset within the response where the message body begins.
|
||||
*/
|
||||
int getBodyOffset();
|
||||
|
||||
/**
|
||||
* This method is used to obtain the HTTP status code contained in the
|
||||
* response.
|
||||
*
|
||||
* @return The HTTP status code contained in the response.
|
||||
*/
|
||||
short getStatusCode();
|
||||
|
||||
/**
|
||||
* This method is used to obtain details of the HTTP cookies set in the
|
||||
* response.
|
||||
*
|
||||
* @return A list of <code>ICookie</code> objects representing the cookies
|
||||
* set in the response, if any.
|
||||
*/
|
||||
List<ICookie> getCookies();
|
||||
|
||||
/**
|
||||
* This method is used to obtain the MIME type of the response, as stated in
|
||||
* the HTTP headers.
|
||||
*
|
||||
* @return A textual label for the stated MIME type, or an empty String if
|
||||
* this is not known or recognized. The possible labels are the same as
|
||||
* those used in the main Burp UI.
|
||||
*/
|
||||
String getStatedMimeType();
|
||||
|
||||
/**
|
||||
* This method is used to obtain the MIME type of the response, as inferred
|
||||
* from the contents of the HTTP message body.
|
||||
*
|
||||
* @return A textual label for the inferred MIME type, or an empty String if
|
||||
* this is not known or recognized. The possible labels are the same as
|
||||
* those used in the main Burp UI.
|
||||
*/
|
||||
String getInferredMimeType();
|
||||
}
|
||||
58
src/burp/IResponseKeywords.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IResponseKeywords.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This interface is used to represent the counts of keywords appearing in a
|
||||
* number of HTTP responses.
|
||||
*/
|
||||
public interface IResponseKeywords
|
||||
{
|
||||
|
||||
/**
|
||||
* This method is used to obtain the list of keywords whose counts vary
|
||||
* between the analyzed responses.
|
||||
*
|
||||
* @return The keywords whose counts vary between the analyzed responses.
|
||||
*/
|
||||
List<String> getVariantKeywords();
|
||||
|
||||
/**
|
||||
* This method is used to obtain the list of keywords whose counts do not
|
||||
* vary between the analyzed responses.
|
||||
*
|
||||
* @return The keywords whose counts do not vary between the analyzed
|
||||
* responses.
|
||||
*/
|
||||
List<String> getInvariantKeywords();
|
||||
|
||||
/**
|
||||
* This method is used to obtain the number of occurrences of an individual
|
||||
* keyword in a response.
|
||||
*
|
||||
* @param keyword The keyword whose count will be retrieved.
|
||||
* @param responseIndex The index of the response. Note responses are
|
||||
* indexed from zero in the order they were originally supplied to the
|
||||
* <code>IExtensionHelpers.analyzeResponseKeywords()</code> and
|
||||
* <code>IResponseKeywords.updateWith()</code> methods.
|
||||
* @return The number of occurrences of the specified keyword for the
|
||||
* specified response.
|
||||
*/
|
||||
int getKeywordCount(String keyword, int responseIndex);
|
||||
|
||||
/**
|
||||
* This method is used to update the analysis based on additional responses.
|
||||
*
|
||||
* @param responses The new responses to include in the analysis.
|
||||
*/
|
||||
void updateWith(byte[]... responses);
|
||||
}
|
||||
62
src/burp/IResponseVariations.java
Normal file
@@ -0,0 +1,62 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IResponseVariations.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This interface is used to represent variations between a number HTTP
|
||||
* responses, according to various attributes.
|
||||
*/
|
||||
public interface IResponseVariations
|
||||
{
|
||||
|
||||
/**
|
||||
* This method is used to obtain the list of attributes that vary between
|
||||
* the analyzed responses.
|
||||
*
|
||||
* @return The attributes that vary between the analyzed responses.
|
||||
*/
|
||||
List<String> getVariantAttributes();
|
||||
|
||||
/**
|
||||
* This method is used to obtain the list of attributes that do not vary
|
||||
* between the analyzed responses.
|
||||
*
|
||||
* @return The attributes that do not vary between the analyzed responses.
|
||||
*/
|
||||
List<String> getInvariantAttributes();
|
||||
|
||||
/**
|
||||
* This method is used to obtain the value of an individual attribute in a
|
||||
* response. Note that the values of some attributes are intrinsically
|
||||
* meaningful (e.g. a word count) while the values of others are less so
|
||||
* (e.g. a checksum of the HTML tag names).
|
||||
*
|
||||
* @param attributeName The name of the attribute whose value will be
|
||||
* retrieved. Extension authors can obtain the list of supported attributes
|
||||
* by generating an <code>IResponseVariations</code> object for a single
|
||||
* response and calling
|
||||
* <code>IResponseVariations.getInvariantAttributes()</code>.
|
||||
* @param responseIndex The index of the response. Note that responses are
|
||||
* indexed from zero in the order they were originally supplied to the
|
||||
* <code>IExtensionHelpers.analyzeResponseVariations()</code> and
|
||||
* <code>IResponseVariations.updateWith()</code> methods.
|
||||
* @return The value of the specified attribute for the specified response.
|
||||
*/
|
||||
int getAttributeValue(String attributeName, int responseIndex);
|
||||
|
||||
/**
|
||||
* This method is used to update the analysis based on additional responses.
|
||||
*
|
||||
* @param responses The new responses to include in the analysis.
|
||||
*/
|
||||
void updateWith(byte[]... responses);
|
||||
}
|
||||
123
src/burp/IScanIssue.java
Normal file
@@ -0,0 +1,123 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IScanIssue.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
/**
|
||||
* This interface is used to retrieve details of Scanner issues. Extensions can
|
||||
* obtain details of issues by registering an <code>IScannerListener</code> or
|
||||
* by calling <code>IBurpExtenderCallbacks.getScanIssues()</code>. Extensions
|
||||
* can also add custom Scanner issues by registering an
|
||||
* <code>IScannerCheck</code> or calling
|
||||
* <code>IBurpExtenderCallbacks.addScanIssue()</code>, and providing their own
|
||||
* implementations of this interface. Note that issue descriptions and other
|
||||
* text generated by extensions are subject to an HTML whitelist that allows
|
||||
* only formatting tags and simple hyperlinks.
|
||||
*/
|
||||
public interface IScanIssue
|
||||
{
|
||||
|
||||
/**
|
||||
* This method returns the URL for which the issue was generated.
|
||||
*
|
||||
* @return The URL for which the issue was generated.
|
||||
*/
|
||||
java.net.URL getUrl();
|
||||
|
||||
/**
|
||||
* This method returns the name of the issue type.
|
||||
*
|
||||
* @return The name of the issue type (e.g. "SQL injection").
|
||||
*/
|
||||
String getIssueName();
|
||||
|
||||
/**
|
||||
* This method returns a numeric identifier of the issue type. See the Burp
|
||||
* Scanner documentation for a listing of all the issue types.
|
||||
*
|
||||
* @return A numeric identifier of the issue type.
|
||||
*/
|
||||
int getIssueType();
|
||||
|
||||
/**
|
||||
* This method returns the issue severity level.
|
||||
*
|
||||
* @return The issue severity level. Expected values are "High", "Medium",
|
||||
* "Low", "Information" or "False positive".
|
||||
*
|
||||
*/
|
||||
String getSeverity();
|
||||
|
||||
/**
|
||||
* This method returns the issue confidence level.
|
||||
*
|
||||
* @return The issue confidence level. Expected values are "Certain", "Firm"
|
||||
* or "Tentative".
|
||||
*/
|
||||
String getConfidence();
|
||||
|
||||
/**
|
||||
* This method returns a background description for this type of issue.
|
||||
*
|
||||
* @return A background description for this type of issue, or
|
||||
* <code>null</code> if none applies. A limited set of HTML tags may be
|
||||
* used.
|
||||
*/
|
||||
String getIssueBackground();
|
||||
|
||||
/**
|
||||
* This method returns a background description of the remediation for this
|
||||
* type of issue.
|
||||
*
|
||||
* @return A background description of the remediation for this type of
|
||||
* issue, or <code>null</code> if none applies. A limited set of HTML tags
|
||||
* may be used.
|
||||
*/
|
||||
String getRemediationBackground();
|
||||
|
||||
/**
|
||||
* This method returns detailed information about this specific instance of
|
||||
* the issue.
|
||||
*
|
||||
* @return Detailed information about this specific instance of the issue,
|
||||
* or <code>null</code> if none applies. A limited set of HTML tags may be
|
||||
* used.
|
||||
*/
|
||||
String getIssueDetail();
|
||||
|
||||
/**
|
||||
* This method returns detailed information about the remediation for this
|
||||
* specific instance of the issue.
|
||||
*
|
||||
* @return Detailed information about the remediation for this specific
|
||||
* instance of the issue, or <code>null</code> if none applies. A limited
|
||||
* set of HTML tags may be used.
|
||||
*/
|
||||
String getRemediationDetail();
|
||||
|
||||
/**
|
||||
* This method returns the HTTP messages on the basis of which the issue was
|
||||
* generated.
|
||||
*
|
||||
* @return The HTTP messages on the basis of which the issue was generated.
|
||||
* <b>Note:</b> The items in this array should be instances of
|
||||
* <code>IHttpRequestResponseWithMarkers</code> if applicable, so that
|
||||
* details of the relevant portions of the request and response messages are
|
||||
* available.
|
||||
*/
|
||||
IHttpRequestResponse[] getHttpMessages();
|
||||
|
||||
/**
|
||||
* This method returns the HTTP service for which the issue was generated.
|
||||
*
|
||||
* @return The HTTP service for which the issue was generated.
|
||||
*/
|
||||
IHttpService getHttpService();
|
||||
|
||||
}
|
||||
81
src/burp/IScanQueueItem.java
Normal file
@@ -0,0 +1,81 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IScanQueueItem.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
/**
|
||||
* This interface is used to retrieve details of items in the Burp Scanner
|
||||
* active scan queue. Extensions can obtain references to scan queue items by
|
||||
* calling
|
||||
* <code>IBurpExtenderCallbacks.doActiveScan()</code>.
|
||||
*/
|
||||
public interface IScanQueueItem
|
||||
{
|
||||
/**
|
||||
* This method returns a description of the status of the scan queue item.
|
||||
*
|
||||
* @return A description of the status of the scan queue item.
|
||||
*/
|
||||
String getStatus();
|
||||
|
||||
/**
|
||||
* This method returns an indication of the percentage completed for the
|
||||
* scan queue item.
|
||||
*
|
||||
* @return An indication of the percentage completed for the scan queue
|
||||
* item.
|
||||
*/
|
||||
@Deprecated
|
||||
byte getPercentageComplete();
|
||||
|
||||
/**
|
||||
* This method returns the number of requests that have been made for the
|
||||
* scan queue item.
|
||||
*
|
||||
* @return The number of requests that have been made for the scan queue
|
||||
* item.
|
||||
*/
|
||||
int getNumRequests();
|
||||
|
||||
/**
|
||||
* This method returns the number of network errors that have occurred for
|
||||
* the scan queue item.
|
||||
*
|
||||
* @return The number of network errors that have occurred for the scan
|
||||
* queue item.
|
||||
*/
|
||||
int getNumErrors();
|
||||
|
||||
/**
|
||||
* This method returns the number of attack insertion points being used for
|
||||
* the scan queue item.
|
||||
*
|
||||
* @return The number of attack insertion points being used for the scan
|
||||
* queue item.
|
||||
*/
|
||||
int getNumInsertionPoints();
|
||||
|
||||
/**
|
||||
* This method allows the scan queue item to be canceled.
|
||||
*/
|
||||
void cancel();
|
||||
|
||||
/**
|
||||
* This method returns details of the issues generated for the scan queue
|
||||
* item. <b>Note:</b> different items within the scan queue may contain
|
||||
* duplicated versions of the same issues - for example, if the same request
|
||||
* has been scanned multiple times. Duplicated issues are consolidated in
|
||||
* the main view of scan results. Extensions can register an
|
||||
* <code>IScannerListener</code> to get details only of unique, newly
|
||||
* discovered Scanner issues post-consolidation.
|
||||
*
|
||||
* @return Details of the issues generated for the scan queue item.
|
||||
*/
|
||||
IScanIssue[] getIssues();
|
||||
}
|
||||
83
src/burp/IScannerCheck.java
Normal file
@@ -0,0 +1,83 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IScannerCheck.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Extensions can implement this interface and then call
|
||||
* <code>IBurpExtenderCallbacks.registerScannerCheck()</code> to register a
|
||||
* custom Scanner check. When performing scanning, Burp will ask the check to
|
||||
* perform active or passive scanning on the base request, and report any
|
||||
* Scanner issues that are identified.
|
||||
*/
|
||||
public interface IScannerCheck
|
||||
{
|
||||
|
||||
/**
|
||||
* The Scanner invokes this method for each base request / response that is
|
||||
* passively scanned. <b>Note:</b> Extensions should only analyze the
|
||||
* HTTP messages provided during passive scanning, and should not make any
|
||||
* new HTTP requests of their own.
|
||||
*
|
||||
* @param baseRequestResponse The base HTTP request / response that should
|
||||
* be passively scanned.
|
||||
* @return A list of <code>IScanIssue</code> objects, or <code>null</code>
|
||||
* if no issues are identified.
|
||||
*/
|
||||
List<IScanIssue> doPassiveScan(IHttpRequestResponse baseRequestResponse);
|
||||
|
||||
/**
|
||||
* The Scanner invokes this method for each insertion point that is actively
|
||||
* scanned. Extensions may issue HTTP requests as required to carry out
|
||||
* active scanning, and should use the
|
||||
* <code>IScannerInsertionPoint</code> object provided to build scan
|
||||
* requests for particular payloads.
|
||||
* <b>Note:</b>
|
||||
* Scan checks should submit raw non-encoded payloads to insertion points,
|
||||
* and the insertion point has responsibility for performing any data
|
||||
* encoding that is necessary given the nature and location of the insertion
|
||||
* point.
|
||||
*
|
||||
* @param baseRequestResponse The base HTTP request / response that should
|
||||
* be actively scanned.
|
||||
* @param insertionPoint An <code>IScannerInsertionPoint</code> object that
|
||||
* can be queried to obtain details of the insertion point being tested, and
|
||||
* can be used to build scan requests for particular payloads.
|
||||
* @return A list of <code>IScanIssue</code> objects, or <code>null</code>
|
||||
* if no issues are identified.
|
||||
*/
|
||||
List<IScanIssue> doActiveScan(
|
||||
IHttpRequestResponse baseRequestResponse,
|
||||
IScannerInsertionPoint insertionPoint);
|
||||
|
||||
/**
|
||||
* The Scanner invokes this method when the custom Scanner check has
|
||||
* reported multiple issues for the same URL path. This can arise either
|
||||
* because there are multiple distinct vulnerabilities, or because the same
|
||||
* (or a similar) request has been scanned more than once. The custom check
|
||||
* should determine whether the issues are duplicates. In most cases, where
|
||||
* a check uses distinct issue names or descriptions for distinct issues,
|
||||
* the consolidation process will simply be a matter of comparing these
|
||||
* features for the two issues.
|
||||
*
|
||||
* @param existingIssue An issue that was previously reported by this
|
||||
* Scanner check.
|
||||
* @param newIssue An issue at the same URL path that has been newly
|
||||
* reported by this Scanner check.
|
||||
* @return An indication of which issue(s) should be reported in the main
|
||||
* Scanner results. The method should return <code>-1</code> to report the
|
||||
* existing issue only, <code>0</code> to report both issues, and
|
||||
* <code>1</code> to report the new issue only.
|
||||
*/
|
||||
int consolidateDuplicateIssues(
|
||||
IScanIssue existingIssue,
|
||||
IScanIssue newIssue);
|
||||
}
|
||||
174
src/burp/IScannerInsertionPoint.java
Normal file
@@ -0,0 +1,174 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IScannerInsertionPoint.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
/**
|
||||
* This interface is used to define an insertion point for use by active Scanner
|
||||
* checks. Extensions can obtain instances of this interface by registering an
|
||||
* <code>IScannerCheck</code>, or can create instances for use by Burp's own
|
||||
* scan checks by registering an
|
||||
* <code>IScannerInsertionPointProvider</code>.
|
||||
*/
|
||||
public interface IScannerInsertionPoint
|
||||
{
|
||||
|
||||
/**
|
||||
* Used to indicate where the payload is inserted into the value of a URL
|
||||
* parameter.
|
||||
*/
|
||||
static final byte INS_PARAM_URL = 0x00;
|
||||
/**
|
||||
* Used to indicate where the payload is inserted into the value of a body
|
||||
* parameter.
|
||||
*/
|
||||
static final byte INS_PARAM_BODY = 0x01;
|
||||
/**
|
||||
* Used to indicate where the payload is inserted into the value of an HTTP
|
||||
* cookie.
|
||||
*/
|
||||
static final byte INS_PARAM_COOKIE = 0x02;
|
||||
/**
|
||||
* Used to indicate where the payload is inserted into the value of an item
|
||||
* of data within an XML data structure.
|
||||
*/
|
||||
static final byte INS_PARAM_XML = 0x03;
|
||||
/**
|
||||
* Used to indicate where the payload is inserted into the value of a tag
|
||||
* attribute within an XML structure.
|
||||
*/
|
||||
static final byte INS_PARAM_XML_ATTR = 0x04;
|
||||
/**
|
||||
* Used to indicate where the payload is inserted into the value of a
|
||||
* parameter attribute within a multi-part message body (such as the name of
|
||||
* an uploaded file).
|
||||
*/
|
||||
static final byte INS_PARAM_MULTIPART_ATTR = 0x05;
|
||||
/**
|
||||
* Used to indicate where the payload is inserted into the value of an item
|
||||
* of data within a JSON structure.
|
||||
*/
|
||||
static final byte INS_PARAM_JSON = 0x06;
|
||||
/**
|
||||
* Used to indicate where the payload is inserted into the value of an AMF
|
||||
* parameter.
|
||||
*/
|
||||
static final byte INS_PARAM_AMF = 0x07;
|
||||
/**
|
||||
* Used to indicate where the payload is inserted into the value of an HTTP
|
||||
* request header.
|
||||
*/
|
||||
static final byte INS_HEADER = 0x20;
|
||||
/**
|
||||
* Used to indicate where the payload is inserted into a URL path folder.
|
||||
*/
|
||||
static final byte INS_URL_PATH_FOLDER = 0x21;
|
||||
/**
|
||||
* Used to indicate where the payload is inserted into a URL path folder.
|
||||
* This is now deprecated; use <code>INS_URL_PATH_FOLDER</code> instead.
|
||||
*/
|
||||
@Deprecated
|
||||
static final byte INS_URL_PATH_REST = INS_URL_PATH_FOLDER;
|
||||
/**
|
||||
* Used to indicate where the payload is inserted into the name of an added
|
||||
* URL parameter.
|
||||
*/
|
||||
static final byte INS_PARAM_NAME_URL = 0x22;
|
||||
/**
|
||||
* Used to indicate where the payload is inserted into the name of an added
|
||||
* body parameter.
|
||||
*/
|
||||
static final byte INS_PARAM_NAME_BODY = 0x23;
|
||||
/**
|
||||
* Used to indicate where the payload is inserted into the body of the HTTP
|
||||
* request.
|
||||
*/
|
||||
static final byte INS_ENTIRE_BODY = 0x24;
|
||||
/**
|
||||
* Used to indicate where the payload is inserted into the URL path
|
||||
* filename.
|
||||
*/
|
||||
static final byte INS_URL_PATH_FILENAME = 0x25;
|
||||
/**
|
||||
* Used to indicate where the payload is inserted at a location manually
|
||||
* configured by the user.
|
||||
*/
|
||||
static final byte INS_USER_PROVIDED = 0x40;
|
||||
/**
|
||||
* Used to indicate where the insertion point is provided by an
|
||||
* extension-registered
|
||||
* <code>IScannerInsertionPointProvider</code>.
|
||||
*/
|
||||
static final byte INS_EXTENSION_PROVIDED = 0x41;
|
||||
/**
|
||||
* Used to indicate where the payload is inserted at an unknown location
|
||||
* within the request.
|
||||
*/
|
||||
static final byte INS_UNKNOWN = 0x7f;
|
||||
|
||||
/**
|
||||
* This method returns the name of the insertion point.
|
||||
*
|
||||
* @return The name of the insertion point (for example, a description of a
|
||||
* particular request parameter).
|
||||
*/
|
||||
String getInsertionPointName();
|
||||
|
||||
/**
|
||||
* This method returns the base value for this insertion point.
|
||||
*
|
||||
* @return the base value that appears in this insertion point in the base
|
||||
* request being scanned, or <code>null</code> if there is no value in the
|
||||
* base request that corresponds to this insertion point.
|
||||
*/
|
||||
String getBaseValue();
|
||||
|
||||
/**
|
||||
* This method is used to build a request with the specified payload placed
|
||||
* into the insertion point. There is no requirement for extension-provided
|
||||
* insertion points to adjust the Content-Length header in requests if the
|
||||
* body length has changed, although Burp-provided insertion points will
|
||||
* always do this and will return a request with a valid Content-Length
|
||||
* header.
|
||||
* <b>Note:</b>
|
||||
* Scan checks should submit raw non-encoded payloads to insertion points,
|
||||
* and the insertion point has responsibility for performing any data
|
||||
* encoding that is necessary given the nature and location of the insertion
|
||||
* point.
|
||||
*
|
||||
* @param payload The payload that should be placed into the insertion
|
||||
* point.
|
||||
* @return The resulting request.
|
||||
*/
|
||||
byte[] buildRequest(byte[] payload);
|
||||
|
||||
/**
|
||||
* This method is used to determine the offsets of the payload value within
|
||||
* the request, when it is placed into the insertion point. Scan checks may
|
||||
* invoke this method when reporting issues, so as to highlight the relevant
|
||||
* part of the request within the UI.
|
||||
*
|
||||
* @param payload The payload that should be placed into the insertion
|
||||
* point.
|
||||
* @return An int[2] array containing the start and end offsets of the
|
||||
* payload within the request, or null if this is not applicable (for
|
||||
* example, where the insertion point places a payload into a serialized
|
||||
* data structure, the raw payload may not literally appear anywhere within
|
||||
* the resulting request).
|
||||
*/
|
||||
int[] getPayloadOffsets(byte[] payload);
|
||||
|
||||
/**
|
||||
* This method returns the type of the insertion point.
|
||||
*
|
||||
* @return The type of the insertion point. Available types are defined in
|
||||
* this interface.
|
||||
*/
|
||||
byte getInsertionPointType();
|
||||
}
|
||||
38
src/burp/IScannerInsertionPointProvider.java
Normal file
@@ -0,0 +1,38 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IScannerInsertionPointProvider.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Extensions can implement this interface and then call
|
||||
* <code>IBurpExtenderCallbacks.registerScannerInsertionPointProvider()</code>
|
||||
* to register a factory for custom Scanner insertion points.
|
||||
*/
|
||||
public interface IScannerInsertionPointProvider
|
||||
{
|
||||
/**
|
||||
* When a request is actively scanned, the Scanner will invoke this method,
|
||||
* and the provider should provide a list of custom insertion points that
|
||||
* will be used in the scan. <b>Note:</b> these insertion points are used in
|
||||
* addition to those that are derived from Burp Scanner's configuration, and
|
||||
* those provided by any other Burp extensions.
|
||||
*
|
||||
* @param baseRequestResponse The base request that will be actively
|
||||
* scanned.
|
||||
* @return A list of
|
||||
* <code>IScannerInsertionPoint</code> objects that should be used in the
|
||||
* scanning, or
|
||||
* <code>null</code> if no custom insertion points are applicable for this
|
||||
* request.
|
||||
*/
|
||||
List<IScannerInsertionPoint> getInsertionPoints(
|
||||
IHttpRequestResponse baseRequestResponse);
|
||||
}
|
||||
30
src/burp/IScannerListener.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IScannerListener.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
/**
|
||||
* Extensions can implement this interface and then call
|
||||
* <code>IBurpExtenderCallbacks.registerScannerListener()</code> to register a
|
||||
* Scanner listener. The listener will be notified of new issues that are
|
||||
* reported by the Scanner tool. Extensions can perform custom analysis or
|
||||
* logging of Scanner issues by registering a Scanner listener.
|
||||
*/
|
||||
public interface IScannerListener
|
||||
{
|
||||
/**
|
||||
* This method is invoked when a new issue is added to Burp Scanner's
|
||||
* results.
|
||||
*
|
||||
* @param issue An
|
||||
* <code>IScanIssue</code> object that the extension can query to obtain
|
||||
* details about the new issue.
|
||||
*/
|
||||
void newScanIssue(IScanIssue issue);
|
||||
}
|
||||
25
src/burp/IScopeChangeListener.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package burp;
|
||||
|
||||
/*
|
||||
* @(#)IScopeChangeListener.java
|
||||
*
|
||||
* Copyright PortSwigger Ltd. All rights reserved.
|
||||
*
|
||||
* This code may be used to extend the functionality of Burp Suite Community Edition
|
||||
* and Burp Suite Professional, provided that this usage does not violate the
|
||||
* license terms for those products.
|
||||
*/
|
||||
/**
|
||||
* Extensions can implement this interface and then call
|
||||
* <code>IBurpExtenderCallbacks.registerScopeChangeListener()</code> to register
|
||||
* a scope change listener. The listener will be notified whenever a change
|
||||
* occurs to Burp's suite-wide target scope.
|
||||
*/
|
||||
public interface IScopeChangeListener
|
||||
{
|
||||
/**
|
||||
* This method is invoked whenever a change occurs to Burp's suite-wide
|
||||
* target scope.
|
||||
*/
|
||||
void scopeChanged();
|
||||
}
|
||||