Skip to content

Commit 0338c13

Browse files
committed
Minor update for SQLlinter
1 parent 5f99b28 commit 0338c13

3 files changed

Lines changed: 45 additions & 1 deletion

File tree

lib/core/settings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from thirdparty import six
2121

2222
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
23-
VERSION = "1.10.7.101"
23+
VERSION = "1.10.7.102"
2424
TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable"
2525
TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34}
2626
VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE)

lib/utils/sqllint.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,16 @@
7575
# "a,limit,b" would false-positive.
7676
_CLAUSE_KEYWORDS = frozenset(("FROM", "WHERE", "HAVING", "INTO"))
7777

78+
# single-occurrence clause keywords (at most one per SELECT scope) with no
79+
# identifier-collision risk - unlike GROUP/ORDER, which double as column names.
80+
# a repeat at the same paren-depth is the 'WHERE x WHERE y' structural bug (e.g.
81+
# a schema filter appended onto a base query that already carries a WHERE).
82+
_SINGLE_CLAUSE_KEYWORDS = frozenset(("WHERE", "HAVING"))
83+
84+
# set operators that begin a fresh SELECT, resetting single-occurrence clauses at
85+
# the current scope ('a WHERE x UNION b WHERE y' is legal; two WHEREs are not).
86+
_SET_OPERATORS = frozenset(("UNION", "EXCEPT", "INTERSECT", "MINUS"))
87+
7888
# sqlmap's own templating markers. If any survives into a *final* outbound payload
7989
# a substitution failed upstream (agent.py / cleanupPayload / queries.xml) - always
8090
# a bug. Matched on the raw payload because a marker can leak anywhere (bare, inside
@@ -309,6 +319,10 @@ def checkSanity(sql, keywords=None):
309319
True
310320
>>> bool(checkSanity("1UNION SELECT NULL"))
311321
True
322+
>>> bool(checkSanity("SELECT a FROM t WHERE x=1 WHERE y=2"))
323+
True
324+
>>> checkSanity("SELECT a FROM t WHERE x=1 UNION SELECT b FROM u WHERE y=2")
325+
[]
312326
"""
313327
if not sql:
314328
return []
@@ -419,4 +433,28 @@ def checkSanity(sql, keywords=None):
419433
if cur.type == T_OTHER:
420434
issues.append("stray character '%s' at offset %d" % (cur.value, cur.start))
421435

436+
# -- duplicated single-occurrence clause at one scope ('WHERE x WHERE y') --
437+
# WHERE/HAVING may appear at most once per SELECT scope; a second one at the
438+
# same paren-depth (no set operator or ';' resetting the SELECT in between)
439+
# is a structural impossibility no surrounding query can undo - subquery
440+
# clauses live at a deeper depth and reset on '(' / ')'.
441+
scopeSeen = [set()]
442+
for token in sig:
443+
if token.type == T_LPAREN:
444+
scopeSeen.append(set())
445+
elif token.type == T_RPAREN:
446+
if len(scopeSeen) > 1:
447+
scopeSeen.pop()
448+
elif token.type == T_SEMI:
449+
scopeSeen = [set()]
450+
elif token.type == T_KEYWORD:
451+
word = token.value.upper()
452+
if word in _SINGLE_CLAUSE_KEYWORDS:
453+
if word in scopeSeen[-1]:
454+
issues.append("duplicate '%s' clause at offset %d" % (word, token.start))
455+
else:
456+
scopeSeen[-1].add(word)
457+
elif word in _SET_OPERATORS:
458+
scopeSeen[-1].clear()
459+
422460
return issues

tests/test_sqllint.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@
6666
"SELECT LIMIT 0 1 DISTINCT(user) FROM INFORMATION_SCHEMA.SYSTEM_USERS", # HSQLDB space-LIMIT
6767
"SELECT TOP 1 name FROM master..sysdatabases", # MSSQL TOP + db..table
6868
"CONCAT('\\',0x71,(SELECT 1))", # backslash-literal string (ANSI)
69+
"SELECT a FROM t WHERE x=1 UNION SELECT b FROM u WHERE y=2", # two WHEREs across UNION (legal)
70+
"SELECT a FROM (SELECT b FROM t WHERE c=1) z WHERE d=2", # subquery WHERE + outer WHERE (different scopes)
71+
"SELECT a FROM t WHERE x IN (SELECT c FROM d WHERE e=1) AND f=2", # WHERE with a WHERE'd subquery
6972
)
7073

7174
# malformed fragments/statements that MUST flag
@@ -92,6 +95,9 @@
9295
"1 UNION ALLSELECT NULL", # glued keyword after UNION
9396
"1 AND ORD(MID((SELECT COUNT(x FROM t),1,1))>64", # unbalanced parentheses (dropped ')')
9497
"1 UNION ALL SELECT ,CONCAT(0x71,a,0x71) FROM t", # comma right after SELECT
98+
"SELECT OWNER,OBJECT_NAME FROM SYS.ALL_OBJECTS WHERE OBJECT_TYPE IN ('TABLE','VIEW') WHERE OWNER IN ('APPU')", # double WHERE (mis-appended schema filter)
99+
"SELECT tabschema,tabname FROM syscat.tables WHERE type IN ('T','V') WHERE tabschema IN ('DB2INST1')", # double WHERE (DB2)
100+
"SELECT a FROM t WHERE x=1 HAVING c>1 HAVING d<2", # duplicate HAVING
95101
)
96102

97103
# cross-dialect valid constructs the near-keyword / comma / digit-glue rules must

0 commit comments

Comments
 (0)