Skip to content

Commit 45c243e

Browse files
committed
Some bug fixes in censor logic
1 parent 3047e0b commit 45c243e

File tree

10 files changed

+223
-369
lines changed

10 files changed

+223
-369
lines changed

acra-censor/acra-censor_implementation.go

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -64,35 +64,40 @@ func (acraCensor *AcraCensor) ReleaseAll() {
6464
}
6565

6666
// HandleQuery processes every query through each handler.
67-
func (acraCensor *AcraCensor) HandleQuery(query string) error {
68-
if query == "" {
69-
return nil
70-
}
67+
func (acraCensor *AcraCensor) HandleQuery(rawQuery string) error {
7168
if len(acraCensor.handlers) == 0 {
7269
// no handlers, AcraCensor won't work
7370
return nil
7471
}
75-
normalizedQuery, queryWithHiddenValues, err := common.NormalizeAndRedactSQLQuery(query)
76-
if err == common.ErrQuerySyntaxError && acraCensor.ignoreParseError {
77-
acraCensor.logger.WithError(err).Infof("Parsing error on query (first %v symbols): %s", common.LogQueryLength, common.TrimStringToN(queryWithHiddenValues, common.LogQueryLength))
78-
79-
}
80-
if err != nil {
81-
// ignore parsing errors to forward it as is to acra-censor to allow filter it via QueryIgnore handler
82-
normalizedQuery = query
72+
normalizedQuery, queryWithHiddenValues, parsedQuery, err := common.HandleRawSQLQuery(rawQuery)
73+
if err == common.ErrQuerySyntaxError {
74+
acraCensor.logger.WithError(err).Warning("Failed to parse input query")
75+
if acraCensor.ignoreParseError {
76+
acraCensor.logger.Infof("Unparsed query has been allowed")
77+
return nil
78+
}
79+
acraCensor.logger.Errorf("Unparsed query has been forbidden")
80+
return err
8381
}
82+
8483
for _, handler := range acraCensor.handlers {
85-
// in QueryCapture Handler use only redacted queries
84+
// in QueryCapture Handler we use only redacted queries
8685
if queryCaptureHandler, ok := handler.(*handlers.QueryCaptureHandler); ok {
87-
queryCaptureHandler.CheckQuery(queryWithHiddenValues)
86+
queryCaptureHandler.CheckQuery(queryWithHiddenValues, parsedQuery)
8887
continue
8988
}
90-
continueHandling, err := handler.CheckQuery(normalizedQuery)
91-
if err != nil {
92-
// continue to next handler
93-
if err == common.ErrQuerySyntaxError && acraCensor.ignoreParseError {
89+
// in QueryIgnore Handler we use only raw queries
90+
if queryIgnoreHandler, ok := handler.(*handlers.QueryIgnoreHandler); ok {
91+
continueHandling, _ := queryIgnoreHandler.CheckQuery(rawQuery, parsedQuery)
92+
if continueHandling {
9493
continue
94+
} else {
95+
break
9596
}
97+
}
98+
// remained handlers operate
99+
continueHandling, err := handler.CheckQuery(normalizedQuery, parsedQuery)
100+
if err != nil {
96101
acraCensor.logger.Errorf("Forbidden query: '%s'", queryWithHiddenValues)
97102
return err
98103
}

acra-censor/acra-censor_interfaces.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@ limitations under the License.
2222
// https://github.com/cossacklabs/acra/wiki/AcraCensor
2323
package acracensor
2424

25+
import "github.com/xwb1989/sqlparser"
26+
2527
// QueryHandlerInterface describes what actions are available for queries.
2628
type QueryHandlerInterface interface {
27-
CheckQuery(sqlQuery string) (bool, error) //1st return arg specifies whether continue verification or not, 2nd specifies whether query is forbidden
29+
CheckQuery(sqlQuery string, parsedQuery sqlparser.Statement) (bool, error) //1st return arg specifies whether continue verification or not, 2nd specifies whether query is forbidden
2830
Release()
2931
}
3032

acra-censor/acra-censor_test.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ func TestWhitelistQueries(t *testing.T) {
105105
}
106106
}
107107
//acracensor should block this query because it is not in whitelist
108-
err = acraCensor.HandleQuery("SELECT * FROM Schema.views;")
108+
err = acraCensor.HandleQuery("SELECT * FROM testDB.testTbl;")
109109
if err != common.ErrQueryNotInWhitelist {
110110
t.Fatal(err)
111111
}
@@ -774,7 +774,6 @@ func TestBlacklistQueries(t *testing.T) {
774774
"SELECT Name, Age FROM Patients WHERE Age > 40 GROUP BY Age ORDER BY Name;",
775775
"SELECT COUNT(CustomerID), Country FROM Customers GROUP BY Country;",
776776
"SELECT SUM(Salary) FROM Employee WHERE Emp_Age < 30;",
777-
"SELECT * FROM Schema.views;",
778777
}
779778
sqlInsertQueries := []string{
780779
"INSERT SalesStaff1 VALUES (2, 'Michael', 'Blythe'), (3, 'Linda', 'Mitchell'),(4, 'Jillian', 'Carson'), (5, 'Garrett', 'Vargas');",
@@ -1828,23 +1827,35 @@ func TestDifferentTablesParsing(t *testing.T) {
18281827

18291828
blacklist := handlers.NewBlacklistHandler()
18301829
blacklist.AddTables([]string{"x", "y"})
1831-
_, err := blacklist.CheckQuery(testQuery)
1830+
1831+
acraCensor := NewAcraCensor()
1832+
defer acraCensor.ReleaseAll()
1833+
//set our acracensor to use blacklist for query evaluating
1834+
acraCensor.AddHandler(blacklist)
1835+
1836+
err := acraCensor.HandleQuery(testQuery)
18321837
if err != nil {
18331838
t.Fatal(err)
18341839
}
18351840
blacklist.AddTables([]string{"z", "Shippers"})
1836-
_, err = blacklist.CheckQuery(testQuery)
1841+
err = acraCensor.HandleQuery(testQuery)
18371842
if err != common.ErrAccessToForbiddenTableBlacklist {
18381843
t.Fatal(err)
18391844
}
1845+
1846+
acraCensor.RemoveHandler(blacklist)
1847+
18401848
whitelist := handlers.NewWhitelistHandler()
18411849
whitelist.AddTables([]string{"Orders", "Customers", "NotShippers"})
1842-
_, err = whitelist.CheckQuery(testQuery)
1850+
1851+
//set our acracensor to use whitelist for query evaluating
1852+
acraCensor.AddHandler(whitelist)
1853+
err = acraCensor.HandleQuery(testQuery)
18431854
if err != common.ErrAccessToForbiddenTableWhitelist {
18441855
t.Fatal(err)
18451856
}
18461857
whitelist.AddTables([]string{"Shippers"})
1847-
_, err = whitelist.CheckQuery(testQuery)
1858+
err = acraCensor.HandleQuery(testQuery)
18481859
if err != nil {
18491860
t.Fatal(err)
18501861
}

acra-censor/common/common.go

Lines changed: 114 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,19 +185,20 @@ func TrimStringToN(query string, n int) string {
185185
return query[:n]
186186
}
187187

188-
// NormalizeAndRedactSQLQuery returns a normalized (lowercases SQL commands) SQL string,
188+
// HandleRawSQLQuery returns a normalized (lowercases SQL commands) SQL string,
189189
// and redacted SQL string with the params stripped out for display.
190190
// Taken from sqlparser package
191-
func NormalizeAndRedactSQLQuery(sql string) (normalizedQuery string, redactedQuery string, error error) {
191+
func HandleRawSQLQuery(sql string) (normalizedQuery, redactedQuery string, parsedQuery sqlparser.Statement, err error) {
192192
bv := map[string]*querypb.BindVariable{}
193193
sqlStripped, _ := sqlparser.SplitMarginComments(sql)
194194

195195
// sometimes queries might have ; at the end, that should be stripped
196196
sqlStripped = strings.TrimSuffix(sqlStripped, ";")
197197

198198
stmt, err := sqlparser.Parse(sqlStripped)
199+
outputStmt, _ := sqlparser.Parse(sqlStripped)
199200
if err != nil {
200-
return "", "", err
201+
return "", "", nil, ErrQuerySyntaxError
201202
}
202203

203204
normalizedQ := sqlparser.String(stmt)
@@ -206,5 +207,114 @@ func NormalizeAndRedactSQLQuery(sql string) (normalizedQuery string, redactedQue
206207
sqlparser.Normalize(stmt, bv, ValueMask)
207208
redactedQ := sqlparser.String(stmt)
208209

209-
return normalizedQ, redactedQ, nil
210+
return normalizedQ, redactedQ, outputStmt, nil
211+
}
212+
213+
// CheckPatternsMatching evaluates if parsed query matches specified set of patterns
214+
func CheckPatternsMatching(patterns []sqlparser.Statement, parsedQuery sqlparser.Statement) bool {
215+
for _, pattern := range patterns {
216+
if checkSinglePatternMatch(parsedQuery, pattern) {
217+
return true
218+
}
219+
}
220+
return false
221+
}
222+
223+
// CheckExactQueriesMatch evaluates if query presents in set of queries
224+
func CheckExactQueriesMatch(normalizedQuery string, setOfQueries map[string]bool) bool {
225+
if !setOfQueries[normalizedQuery] {
226+
return false
227+
}
228+
return true
229+
}
230+
231+
// CheckTableNamesMatch evaluates if query contains table presented in specified set of tables
232+
func CheckTableNamesMatch(parsedQuery sqlparser.Statement, setOfTables map[string]bool) (bool, bool) {
233+
atLeastOneTableNameMatch := false
234+
allTableNamesMatch := false
235+
236+
switch parsedQuery.(type) {
237+
case *sqlparser.Select:
238+
selectQuery := parsedQuery.(*sqlparser.Select)
239+
atLeastOneTableNameMatch, allTableNamesMatch = checkTableExprsMatch(selectQuery.From, setOfTables)
240+
break
241+
case *sqlparser.Insert:
242+
insertQuery := parsedQuery.(*sqlparser.Insert)
243+
if setOfTables[insertQuery.Table.Name.String()] {
244+
atLeastOneTableNameMatch = true
245+
allTableNamesMatch = true
246+
} else {
247+
atLeastOneTableNameMatch = false
248+
allTableNamesMatch = false
249+
}
250+
break
251+
default:
252+
//TODO other query types
253+
return false, false
254+
}
255+
256+
return atLeastOneTableNameMatch, allTableNamesMatch
257+
}
258+
259+
// Tables matchers
260+
func checkTableExprsMatch(tables sqlparser.TableExprs, setOfTables map[string]bool) (bool, bool) {
261+
oneTableMatch := false
262+
allTablesMatch := false
263+
counter := 0
264+
for _, tableExpr := range tables {
265+
oneTableMatchInternal, allTablesMatchInternal := checkTableExprMatch(tableExpr, setOfTables)
266+
if oneTableMatchInternal {
267+
oneTableMatch = true
268+
if allTablesMatchInternal {
269+
counter++
270+
}
271+
}
272+
}
273+
if counter == len(tables) {
274+
allTablesMatch = true
275+
}
276+
return oneTableMatch, allTablesMatch
277+
}
278+
279+
func checkTableExprMatch(table sqlparser.TableExpr, setOfTables map[string]bool) (bool, bool) {
280+
oneTableMatch := false
281+
allTablesMatch := false
282+
283+
switch table.(type) {
284+
case *sqlparser.AliasedTableExpr:
285+
oneTableMatch, allTablesMatch = checkAliasedTable(table.(*sqlparser.AliasedTableExpr), setOfTables)
286+
case *sqlparser.JoinTableExpr:
287+
oneTableMatch, allTablesMatch = checkJoinedTable(table.(*sqlparser.JoinTableExpr), setOfTables)
288+
case *sqlparser.ParenTableExpr:
289+
oneTableMatch, allTablesMatch = checkParenTable(table.(*sqlparser.ParenTableExpr), setOfTables)
290+
}
291+
return oneTableMatch, allTablesMatch
292+
}
293+
294+
func checkAliasedTable(table *sqlparser.AliasedTableExpr, setOfTables map[string]bool) (bool, bool) {
295+
if setOfTables[sqlparser.String(table.Expr)] {
296+
return true, true
297+
}
298+
return false, false
299+
}
300+
301+
func checkJoinedTable(table *sqlparser.JoinTableExpr, setOfTables map[string]bool) (bool, bool) {
302+
oneTableMatch := false
303+
allTablesMatch := false
304+
305+
oneLeftTableMatchInternal, allLeftTablesMatchInternal := checkTableExprMatch(table.LeftExpr, setOfTables)
306+
oneRightTableMatchInternal, allRightTablesMatchInternal := checkTableExprMatch(table.RightExpr, setOfTables)
307+
308+
if oneLeftTableMatchInternal || oneRightTableMatchInternal {
309+
oneTableMatch = true
310+
}
311+
if allLeftTablesMatchInternal && allRightTablesMatchInternal {
312+
allTablesMatch = true
313+
}
314+
return oneTableMatch, allTablesMatch
315+
}
316+
317+
func checkParenTable(table *sqlparser.ParenTableExpr, setOfTables map[string]bool) (bool, bool) {
318+
singleTableMatch, allTablesMatch := checkTableExprsMatch(table.Exprs, setOfTables)
319+
return singleTableMatch, allTablesMatch
210320
}

acra-censor/common/matching_logic.go

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
// Package handlers contains all query handlers for AcraCensor:
17+
// Package common contains all query handlers for AcraCensor:
1818
// blacklist handler, which allows everything and forbids specific query/pattern/table;
1919
// whitelist handler, which allows query/pattern/table and restricts/forbids everything else;
2020
// ignore handler, which allows to ignore any query;
@@ -25,27 +25,11 @@ package common
2525

2626
import (
2727
"bytes"
28-
log "github.com/sirupsen/logrus"
2928
"github.com/xwb1989/sqlparser"
3029
"reflect"
3130
"strings"
3231
)
3332

34-
func CheckPatternsMatching(patterns []sqlparser.Statement, query string) (bool, error) {
35-
parsedQuery, err := sqlparser.Parse(query)
36-
if err != nil {
37-
log.WithError(err).Errorln("Can't parse query")
38-
return false, ErrQuerySyntaxError
39-
}
40-
41-
for _, pattern := range patterns {
42-
if checkSinglePatternMatch(parsedQuery, pattern) {
43-
return true, nil
44-
}
45-
}
46-
return false, nil
47-
}
48-
4933
func checkSinglePatternMatch(query, pattern sqlparser.Statement) bool {
5034
switch pattern.(type) {
5135
case *sqlparser.Union:

acra-censor/common/matching_logic_test.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,13 @@ func testSingleTopLevelPlaceholder(t *testing.T, pattern string, indexOfMatchedQ
7171
}
7272
match := false
7373
for index := range testQueries {
74-
match, err = CheckPatternsMatching(parsedPatterns, testQueries[index])
74+
75+
stmt, err := sqlparser.Parse(testQueries[index])
76+
if err != nil {
77+
t.Fatal(err)
78+
}
79+
80+
match, err = CheckPatternsMatching(parsedPatterns, stmt)
7581
if err != nil {
7682
t.Fatal(err)
7783
}

0 commit comments

Comments
 (0)