mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2025-06-30 09:53:05 +03:00
Adding ability to select SORTABLE_HTML as dump format to have sortable tables in HTML dumps.
This commit is contained in:
parent
cc245a0d05
commit
11b8da9431
|
@ -174,8 +174,8 @@ b60c96780cad4a257f91a0611b08cfcc52f242908c5d5ab2bf9034ef07869602 lib/core/conve
|
||||||
55e7d63aae317763afcbdbea1c7731497c93bad14f6d032a0ccfffe72ffc121f lib/core/decorators.py
|
55e7d63aae317763afcbdbea1c7731497c93bad14f6d032a0ccfffe72ffc121f lib/core/decorators.py
|
||||||
595c7dfde7c67cdb674fb019a24b07a501a9cdb6321e4f8ce3d3354cd9526eae lib/core/defaults.py
|
595c7dfde7c67cdb674fb019a24b07a501a9cdb6321e4f8ce3d3354cd9526eae lib/core/defaults.py
|
||||||
e8f6f1df8814b7b03c3eba22901837555083f66c99ee93b943911de785736bfa lib/core/dicts.py
|
e8f6f1df8814b7b03c3eba22901837555083f66c99ee93b943911de785736bfa lib/core/dicts.py
|
||||||
5fb6ef1772580a701b1b109858163a1c16446928f8c29170d67ad4d0171c0950 lib/core/dump.py
|
2c611c19a3c38b755a6e070a2c96ce6ac9a84123640b66bdb0a1c4a2d4ac6abf lib/core/dump.py
|
||||||
874c8eb7391ef0f82b6e870499daa336a79a6d014a23e7452205f5ef0b6a9744 lib/core/enums.py
|
3f438ad1ddfb3e7767837590bd0cedbf2af8a6f21c28603f62236b5623e42d04 lib/core/enums.py
|
||||||
67ab7a8f756b63e75e8b564d647e72362d7245d6b32b2881be02321ceaaca876 lib/core/exception.py
|
67ab7a8f756b63e75e8b564d647e72362d7245d6b32b2881be02321ceaaca876 lib/core/exception.py
|
||||||
0379d59be9e2400e39abbb99fbceeb22d4c3b69540504a0cb59bf3aaf53d05a9 lib/core/gui.py
|
0379d59be9e2400e39abbb99fbceeb22d4c3b69540504a0cb59bf3aaf53d05a9 lib/core/gui.py
|
||||||
99d0e94dd5fe60137abf48bfa051129fb251f5c40f0f7a270c89fbcb07323730 lib/core/__init__.py
|
99d0e94dd5fe60137abf48bfa051129fb251f5c40f0f7a270c89fbcb07323730 lib/core/__init__.py
|
||||||
|
@ -188,7 +188,7 @@ bf77f9fc4296f239687297aee1fd6113b34f855965a6f690b52e26bd348cb353 lib/core/profi
|
||||||
4eff81c639a72b261c8ba1c876a01246e718e6626e8e77ae9cc6298b20a39355 lib/core/replication.py
|
4eff81c639a72b261c8ba1c876a01246e718e6626e8e77ae9cc6298b20a39355 lib/core/replication.py
|
||||||
bbd1dcda835934728efc6d68686e9b0da72b09b3ee38f3c0ab78e8c18b0ba726 lib/core/revision.py
|
bbd1dcda835934728efc6d68686e9b0da72b09b3ee38f3c0ab78e8c18b0ba726 lib/core/revision.py
|
||||||
eed6b0a21b3e69c5583133346b0639dc89937bd588887968ee85f8389d7c3c96 lib/core/session.py
|
eed6b0a21b3e69c5583133346b0639dc89937bd588887968ee85f8389d7c3c96 lib/core/session.py
|
||||||
5072218a58696b0bd425421022e557da29b32a54ea181686c83b4130b6edf1ee lib/core/settings.py
|
fbe9bfd62846770aa87133b7eddb278da5959411fc47f5261d227f381492f609 lib/core/settings.py
|
||||||
2bec97d8a950f7b884e31dfe9410467f00d24f21b35672b95f8d68ed59685fd4 lib/core/shell.py
|
2bec97d8a950f7b884e31dfe9410467f00d24f21b35672b95f8d68ed59685fd4 lib/core/shell.py
|
||||||
e90a359b37a55c446c60e70ccd533f87276714d0b09e34f69b0740fd729ddbf8 lib/core/subprocessng.py
|
e90a359b37a55c446c60e70ccd533f87276714d0b09e34f69b0740fd729ddbf8 lib/core/subprocessng.py
|
||||||
54f7c70b4c7a9931f7ff3c1c12030180bde38e35a306d5e343ad6052919974cd lib/core/target.py
|
54f7c70b4c7a9931f7ff3c1c12030180bde38e35a306d5e343ad6052919974cd lib/core/target.py
|
||||||
|
@ -199,7 +199,7 @@ ff39235aee7e33498c66132d17e6e86e7b8a29754e3fdecd880ca8356b17f791 lib/core/unesc
|
||||||
ce65f9e8e1c726de3cec6abf31a2ffdbc16c251f772adcc14f67dee32d0f6b57 lib/core/wordlist.py
|
ce65f9e8e1c726de3cec6abf31a2ffdbc16c251f772adcc14f67dee32d0f6b57 lib/core/wordlist.py
|
||||||
99d0e94dd5fe60137abf48bfa051129fb251f5c40f0f7a270c89fbcb07323730 lib/__init__.py
|
99d0e94dd5fe60137abf48bfa051129fb251f5c40f0f7a270c89fbcb07323730 lib/__init__.py
|
||||||
ba16fdd71fba31990dc92ff5a7388fb0ebac21ca905c314be6c8c2b868f94ab7 lib/parse/banner.py
|
ba16fdd71fba31990dc92ff5a7388fb0ebac21ca905c314be6c8c2b868f94ab7 lib/parse/banner.py
|
||||||
d757343f241b14e23aefb2177b6c2598f1bc06253fd93b0d8a28d4a55c267100 lib/parse/cmdline.py
|
8347cdcc053b3a08518283b6cf41a1abe72cfe0b34c1bc6de45ce7ca43ec0de5 lib/parse/cmdline.py
|
||||||
d1fa3b9457f0e934600519309cbd3d84f9e6158a620866e7b352078c7c136f01 lib/parse/configfile.py
|
d1fa3b9457f0e934600519309cbd3d84f9e6158a620866e7b352078c7c136f01 lib/parse/configfile.py
|
||||||
9af4c86e41e50bd6055573a7b76e380a6658b355320c72dd6d2d5ddab14dc082 lib/parse/handler.py
|
9af4c86e41e50bd6055573a7b76e380a6658b355320c72dd6d2d5ddab14dc082 lib/parse/handler.py
|
||||||
13b3ab678a2c422ce1dea9558668c05e562c0ec226f36053259a0be7280ebf92 lib/parse/headers.py
|
13b3ab678a2c422ce1dea9558668c05e562c0ec226f36053259a0be7280ebf92 lib/parse/headers.py
|
||||||
|
@ -476,8 +476,8 @@ fff84edc86b7d22dc01148fb10bb43d51cb9638dff21436fb94555db2a664766 plugins/generi
|
||||||
5a473c60853f54f1a4b14d79b8237f659278fe8a6b42e935ed573bf22b6d5b2c README.md
|
5a473c60853f54f1a4b14d79b8237f659278fe8a6b42e935ed573bf22b6d5b2c README.md
|
||||||
78aafd53980096364f0c995c6283931bff505aed88fed1e7906fb06ee60e9c5b sqlmapapi.py
|
78aafd53980096364f0c995c6283931bff505aed88fed1e7906fb06ee60e9c5b sqlmapapi.py
|
||||||
168309215af7dd5b0b71070e1770e72f1cbb29a3d8025143fb8aa0b88cd56b62 sqlmapapi.yaml
|
168309215af7dd5b0b71070e1770e72f1cbb29a3d8025143fb8aa0b88cd56b62 sqlmapapi.yaml
|
||||||
5e172e315524845fe091aa0b7b29303c92ac8f67594c6d50f026d627e415b7ed sqlmap.conf
|
c94e1a4832ae6ef786431acb595428736bf13e2933c147d8bc70ba6913d65cbb sqlmap.conf
|
||||||
3a18b78b1aaf7236a35169db20eb21ca7d7fb907cd38dd34650f1da81c010cd6 sqlmap.py
|
56b17e8239000043d375d56f724baa7135f8b106439a7d10024bba746fb26d01 sqlmap.py
|
||||||
adda508966db26c30b11390d6483c1fa25b092942a29730e739e1e50c403a21f tamper/0eunion.py
|
adda508966db26c30b11390d6483c1fa25b092942a29730e739e1e50c403a21f tamper/0eunion.py
|
||||||
d38fe5ab97b401810612eae049325aa990c55143504b25cc9924810917511dee tamper/apostrophemask.py
|
d38fe5ab97b401810612eae049325aa990c55143504b25cc9924810917511dee tamper/apostrophemask.py
|
||||||
8de713d1534d8cda171db4ceeb9f4324bcc030bbef21ffeaf60396c6bece31e4 tamper/apostrophenullencode.py
|
8de713d1534d8cda171db4ceeb9f4324bcc030bbef21ffeaf60396c6bece31e4 tamper/apostrophenullencode.py
|
||||||
|
|
|
@ -47,6 +47,8 @@ from lib.core.exception import SqlmapValueException
|
||||||
from lib.core.replication import Replication
|
from lib.core.replication import Replication
|
||||||
from lib.core.settings import DUMP_FILE_BUFFER_SIZE
|
from lib.core.settings import DUMP_FILE_BUFFER_SIZE
|
||||||
from lib.core.settings import HTML_DUMP_CSS_STYLE
|
from lib.core.settings import HTML_DUMP_CSS_STYLE
|
||||||
|
from lib.core.settings import HTML_DUMP_CSS_SORTABLE_STYLE
|
||||||
|
from lib.core.settings import HTML_DUMP_SORTABLE_JAVASCRIPT
|
||||||
from lib.core.settings import IS_WIN
|
from lib.core.settings import IS_WIN
|
||||||
from lib.core.settings import METADB_SUFFIX
|
from lib.core.settings import METADB_SUFFIX
|
||||||
from lib.core.settings import MIN_BINARY_DISK_DUMP_SIZE
|
from lib.core.settings import MIN_BINARY_DISK_DUMP_SIZE
|
||||||
|
@ -541,6 +543,9 @@ class Dump(object):
|
||||||
dataToDumpFile(dumpFP, "<meta name=\"generator\" content=\"%s\" />\n" % VERSION_STRING)
|
dataToDumpFile(dumpFP, "<meta name=\"generator\" content=\"%s\" />\n" % VERSION_STRING)
|
||||||
dataToDumpFile(dumpFP, "<title>%s</title>\n" % ("%s%s" % ("%s." % db if METADB_SUFFIX not in db else "", table)))
|
dataToDumpFile(dumpFP, "<title>%s</title>\n" % ("%s%s" % ("%s." % db if METADB_SUFFIX not in db else "", table)))
|
||||||
dataToDumpFile(dumpFP, HTML_DUMP_CSS_STYLE)
|
dataToDumpFile(dumpFP, HTML_DUMP_CSS_STYLE)
|
||||||
|
if conf.dumpSortable:
|
||||||
|
dataToDumpFile(dumpFP, HTML_DUMP_CSS_SORTABLE_STYLE)
|
||||||
|
dataToDumpFile(dumpFP, HTML_DUMP_SORTABLE_JAVASCRIPT)
|
||||||
dataToDumpFile(dumpFP, "\n</head>\n<body>\n<table>\n<thead>\n<tr>\n")
|
dataToDumpFile(dumpFP, "\n</head>\n<body>\n<table>\n<thead>\n<tr>\n")
|
||||||
|
|
||||||
if count == 1:
|
if count == 1:
|
||||||
|
|
|
@ -229,6 +229,7 @@ class REGISTRY_OPERATION(object):
|
||||||
class DUMP_FORMAT(object):
|
class DUMP_FORMAT(object):
|
||||||
CSV = "CSV"
|
CSV = "CSV"
|
||||||
HTML = "HTML"
|
HTML = "HTML"
|
||||||
|
SORTABLE_HTML = "SORTABLE_HTML"
|
||||||
SQLITE = "SQLITE"
|
SQLITE = "SQLITE"
|
||||||
|
|
||||||
class HTTP_HEADER(object):
|
class HTTP_HEADER(object):
|
||||||
|
|
|
@ -918,29 +918,163 @@ HTTP_CHUNKED_SPLIT_KEYWORDS = ("SELECT", "UPDATE", "INSERT", "FROM", "LOAD_FILE"
|
||||||
|
|
||||||
# CSS style used in HTML dump format
|
# CSS style used in HTML dump format
|
||||||
HTML_DUMP_CSS_STYLE = """<style>
|
HTML_DUMP_CSS_STYLE = """<style>
|
||||||
table{
|
table {
|
||||||
margin:10;
|
margin: 10px;
|
||||||
background-color:#FFFFFF;
|
background: #fff;
|
||||||
font-family:verdana;
|
font: 12px verdana;
|
||||||
font-size:12px;
|
text-align: center;
|
||||||
align:center;
|
|
||||||
}
|
}
|
||||||
thead{
|
thead{
|
||||||
font-weight:bold;
|
font-weight:bold;
|
||||||
background-color:#4F81BD;
|
background-color:#4F81BD;
|
||||||
color:#FFFFFF;
|
color: #fff;
|
||||||
}
|
}
|
||||||
tr:nth-child(even) {
|
tr:nth-child(even) {
|
||||||
background-color: #D3DFEE
|
background-color: #D3DFEE;
|
||||||
}
|
|
||||||
td{
|
|
||||||
font-size:12px;
|
|
||||||
}
|
|
||||||
th{
|
|
||||||
font-size:12px;
|
|
||||||
}
|
}
|
||||||
</style>"""
|
</style>"""
|
||||||
|
|
||||||
|
HTML_DUMP_CSS_SORTABLE_STYLE = """
|
||||||
|
<style>
|
||||||
|
table thead th {
|
||||||
|
cursor: pointer;
|
||||||
|
white-space: nowrap;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
table thead th::after,
|
||||||
|
table thead th::before {
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
table thead th::after {
|
||||||
|
margin-left: 3px;
|
||||||
|
content: "▸";
|
||||||
|
}
|
||||||
|
|
||||||
|
table thead th:hover::after,
|
||||||
|
table thead th[aria-sort]::after {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
table thead th[aria-sort=descending]::after {
|
||||||
|
content: "▾";
|
||||||
|
}
|
||||||
|
|
||||||
|
table thead th[aria-sort=ascending]::after {
|
||||||
|
content: "▴";
|
||||||
|
}
|
||||||
|
|
||||||
|
table thead th.indicator-left::before {
|
||||||
|
margin-right: 3px;
|
||||||
|
content: "▸";
|
||||||
|
}
|
||||||
|
|
||||||
|
table thead th.indicator-left[aria-sort=descending]::before {
|
||||||
|
color: inherit;
|
||||||
|
content: "▾";
|
||||||
|
}
|
||||||
|
|
||||||
|
table thead th.indicator-left[aria-sort=ascending]::before {
|
||||||
|
color: inherit;
|
||||||
|
content: "▴";
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
"""
|
||||||
|
HTML_DUMP_SORTABLE_JAVASCRIPT = """<script>
|
||||||
|
window.addEventListener('DOMContentLoaded', () => {
|
||||||
|
document.addEventListener('click', event => {
|
||||||
|
try {
|
||||||
|
const isAltSort = event.shiftKey || event.altKey;
|
||||||
|
|
||||||
|
// Find the clicked table header
|
||||||
|
const findParentElement = (element, nodeName) =>
|
||||||
|
element.nodeName === nodeName ? element : findParentElement(element.parentNode, nodeName);
|
||||||
|
|
||||||
|
const headerCell = findParentElement(event.target, 'TH');
|
||||||
|
const headerRow = headerCell.parentNode;
|
||||||
|
const thead = headerRow.parentNode;
|
||||||
|
const table = thead.parentNode;
|
||||||
|
|
||||||
|
if (thead.nodeName !== 'THEAD') return;
|
||||||
|
|
||||||
|
// Reset sort indicators on other headers
|
||||||
|
Array.from(headerRow.cells).forEach(cell => {
|
||||||
|
if (cell !== headerCell) cell.removeAttribute('aria-sort');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Toggle sort direction
|
||||||
|
const currentSort = headerCell.getAttribute('aria-sort');
|
||||||
|
const isAscending = table.classList.contains('asc') && currentSort !== 'ascending';
|
||||||
|
const sortDirection = (currentSort === 'descending' || isAscending) ? 'ascending' : 'descending';
|
||||||
|
headerCell.setAttribute('aria-sort', sortDirection);
|
||||||
|
|
||||||
|
// Debounce sort operation
|
||||||
|
if (table.dataset.timer) clearTimeout(Number(table.dataset.timer));
|
||||||
|
|
||||||
|
table.dataset.timer = setTimeout(() => {
|
||||||
|
sortTable(table, isAltSort);
|
||||||
|
}, 1).toString();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Sorting error:', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function sortTable(table, useAltSort) {
|
||||||
|
table.dispatchEvent(new CustomEvent('sort-start', { bubbles: true }));
|
||||||
|
|
||||||
|
const sortHeader = table.tHead.querySelector('th[aria-sort]');
|
||||||
|
const headerRow = table.tHead.children[0];
|
||||||
|
const isAscending = sortHeader.getAttribute('aria-sort') === 'ascending';
|
||||||
|
const shouldPushEmpty = table.classList.contains('n-last');
|
||||||
|
const sortColumnIndex = Number(sortHeader.dataset.sortCol ?? sortHeader.cellIndex);
|
||||||
|
|
||||||
|
const getCellValue = cell => {
|
||||||
|
if (useAltSort) return cell.dataset.sortAlt;
|
||||||
|
return cell.dataset.sort ?? cell.textContent;
|
||||||
|
};
|
||||||
|
|
||||||
|
const compareRows = (row1, row2) => {
|
||||||
|
const value1 = getCellValue(row1.cells[sortColumnIndex]);
|
||||||
|
const value2 = getCellValue(row2.cells[sortColumnIndex]);
|
||||||
|
|
||||||
|
// Handle empty values
|
||||||
|
if (shouldPushEmpty) {
|
||||||
|
if (value1 === '' && value2 !== '') return -1;
|
||||||
|
if (value2 === '' && value1 !== '') return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare numerically if possible, otherwise use string comparison
|
||||||
|
const numericDiff = Number(value1) - Number(value2);
|
||||||
|
const comparison = isNaN(numericDiff) ?
|
||||||
|
value1.localeCompare(value2, undefined, { numeric: true }) :
|
||||||
|
numericDiff;
|
||||||
|
|
||||||
|
// Handle tiebreaker
|
||||||
|
if (comparison === 0 && headerRow.cells[sortColumnIndex]?.dataset.sortTbr) {
|
||||||
|
const tiebreakIndex = Number(headerRow.cells[sortColumnIndex].dataset.sortTbr);
|
||||||
|
return compareRows(row1, row2, tiebreakIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isAscending ? -comparison : comparison;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sort each tbody
|
||||||
|
Array.from(table.tBodies).forEach(tbody => {
|
||||||
|
const rows = Array.from(tbody.rows);
|
||||||
|
const sortedRows = rows.sort(compareRows);
|
||||||
|
|
||||||
|
const newTbody = tbody.cloneNode();
|
||||||
|
newTbody.append(...sortedRows);
|
||||||
|
tbody.replaceWith(newTbody);
|
||||||
|
});
|
||||||
|
|
||||||
|
table.dispatchEvent(new CustomEvent('sort-end', { bubbles: true }));
|
||||||
|
}
|
||||||
|
</script>"""
|
||||||
# Leaving (dirty) possibility to change values from here (e.g. `export SQLMAP__MAX_NUMBER_OF_THREADS=20`)
|
# Leaving (dirty) possibility to change values from here (e.g. `export SQLMAP__MAX_NUMBER_OF_THREADS=20`)
|
||||||
for key, value in os.environ.items():
|
for key, value in os.environ.items():
|
||||||
if key.upper().startswith("%s_" % SQLMAP_ENVIRONMENT_PREFIX):
|
if key.upper().startswith("%s_" % SQLMAP_ENVIRONMENT_PREFIX):
|
||||||
|
|
|
@ -674,7 +674,7 @@ def cmdLineParser(argv=None):
|
||||||
help="Store dumped data to a custom file")
|
help="Store dumped data to a custom file")
|
||||||
|
|
||||||
general.add_argument("--dump-format", dest="dumpFormat",
|
general.add_argument("--dump-format", dest="dumpFormat",
|
||||||
help="Format of dumped data (CSV (default), HTML or SQLITE)")
|
help="Format of dumped data (CSV (default), HTML, SORTABLE_HTML or SQLITE)")
|
||||||
|
|
||||||
general.add_argument("--encoding", dest="encoding",
|
general.add_argument("--encoding", dest="encoding",
|
||||||
help="Character encoding used for data retrieval (e.g. GBK)")
|
help="Character encoding used for data retrieval (e.g. GBK)")
|
||||||
|
|
|
@ -754,8 +754,10 @@ csvDel = ,
|
||||||
dumpFile =
|
dumpFile =
|
||||||
|
|
||||||
# Format of dumped data
|
# Format of dumped data
|
||||||
# Valid: CSV, HTML or SQLITE
|
# Valid: CSV, HTML, SORTABLE_HTML or SQLITE
|
||||||
dumpFormat = CSV
|
dumpFormat = SORTABLE_HTML
|
||||||
|
|
||||||
|
dumpSortable = False
|
||||||
|
|
||||||
# Force character encoding used for data retrieval.
|
# Force character encoding used for data retrieval.
|
||||||
encoding =
|
encoding =
|
||||||
|
|
|
@ -158,6 +158,11 @@ def main():
|
||||||
if checkPipedInput():
|
if checkPipedInput():
|
||||||
conf.batch = True
|
conf.batch = True
|
||||||
|
|
||||||
|
if conf.get("dumpFormat") == "SORTABLE_HTML":
|
||||||
|
conf.dumpFormat = "HTML"
|
||||||
|
conf.dumpSortable = True
|
||||||
|
else:
|
||||||
|
conf.dumpSortable = False
|
||||||
if conf.get("api"):
|
if conf.get("api"):
|
||||||
# heavy imports
|
# heavy imports
|
||||||
from lib.utils.api import StdDbOut
|
from lib.utils.api import StdDbOut
|
||||||
|
|
Loading…
Reference in New Issue
Block a user