Only treat comment markers outside literals as comments

removeComments() used regexes that also matched "--" and "/*" inside
string literals. A literal like '--' or '/*' then removed the code after
it from the checks while PostgreSQL still ran it, e.g.
COPY (SELECT '--') TO PROGRAM 'sh' passed the forbidden-construct check,
and a DELETE in a CTE between '/*' and '*/' literals skipped the
confirmation.

Comments and literals are now found by one scanner that walks the SQL
the way the database does ('...', E'...', "...", $tag$...$tag$, -- and
block comments), shared by removeComments(), removeLiterals() and
splitStatements().
This commit is contained in:
2026-09-30 03:46:34 +02:00
parent f4b7484e2f
commit 10055c9358
2 changed files with 102 additions and 47 deletions
+9
View File
@@ -24,6 +24,11 @@ class SqlGuardTest extends TestCase
["SELECT lo_import('/etc/passwd')"],
["SELECT dblink_connect('host=x')"],
["SELECT * FROM t INTO OUTFILE '/tmp/x'"],
["COPY (SELECT '/*') TO PROGRAM 'sh' --*/"],
["COPY (SELECT '--') TO PROGRAM 'sh'"],
["COPY (SELECT \$\$/*\$\$) TO PROGRAM 'sh' --*/"],
["COPY (SELECT \"/*\") TO PROGRAM 'sh' --*/"],
["COPY (SELECT E'\\'/*') TO PROGRAM 'sh' --*/"],
];
}
@@ -69,6 +74,7 @@ class SqlGuardTest extends TestCase
["SELECT \$\$DELETE FROM x\$\$"],
["SELECT E'it\\'s delete' FROM t"],
['VALUES (1)'],
["SELECT '/* not a comment */' AS a, '-- nor this' AS b"],
];
}
@@ -106,6 +112,9 @@ class SqlGuardTest extends TestCase
["SELECT setval('seq', 1)"],
['MERGE INTO t USING s ON true WHEN MATCHED THEN DELETE'],
['VACUUM'],
["WITH a AS (SELECT '/*'), b AS (DELETE FROM t RETURNING 1) SELECT '*/'"],
["SELECT '--' AS x FROM t WHERE a IN (SELECT 1) FOR UPDATE"],
["WITH a AS (SELECT \$\$/*\$\$), b AS (DELETE FROM t RETURNING 1) SELECT '*/'"],
['', ],
];
}