Skip to content

Commit 5abacba

Browse files
taimoorzaeemsteve-chavez
authored andcommitted
fix(error): leaking table and function names when calculating hint
Increase similarity score to 0.75 from 0.33 for table and functions error hint. Signed-off-by: Taimoor Zaeem <taimoorzaeem@gmail.com>
1 parent 2861b35 commit 5abacba

9 files changed

Lines changed: 66 additions & 21 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ All notable changes to this project will be documented in this file. From versio
1414
+ Removed unnecessary double count when building the `Content-Range`.
1515
- Add config `client_error_verbosity` to customize error verbosity by @taimoorzaeem in #4088, #3980, #3824
1616

17+
### Fixed
18+
19+
- Fix leaking table and function names when calculating error hint by @taimoorzaeem in #4675
20+
1721
### Changed
1822

1923
- Log error when `db-schemas` config contains schema `pg_catalog` or `information_schema` by @taimoorzaeem in #4359

src/PostgREST/Error.hs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ noRelBetweenHint parent child schema allRels = ("Perhaps you meant '" <>) <$>
338338
-- Just "Perhaps you meant to call the function api.test"
339339
--
340340
-- >>> noRpcHint "api" "other" [] procs []
341-
-- Just "Perhaps you meant to call the function api.another"
341+
-- Nothing
342342
--
343343
-- >>> noRpcHint "api" "noclosealternative" [] procs []
344344
-- Nothing
@@ -375,16 +375,30 @@ noRpcHint schema procName params allProcs overloadedProcs =
375375
-- E.g. ["val", "param", "name"] into "(name, param, val)"
376376
listToText = ("(" <>) . (<> ")") . T.intercalate ", " . sort
377377
possibleProcs
378-
| null overloadedProcs = Fuzzy.getOne fuzzySetOfProcs procName
379-
| otherwise = (procName <>) <$> Fuzzy.getOne fuzzySetOfParams (listToText params)
378+
| null overloadedProcs = getFuzzyHint HintProcedure fuzzySetOfProcs procName
379+
| otherwise = (procName <>) <$> getFuzzyHint HintParams fuzzySetOfParams (listToText params)
380380

381381
-- |
382382
-- Do a fuzzy search in all tables in the same schema and return closest result
383383
tableNotFoundHint :: Text -> Text -> SchemaCache -> Maybe Text
384384
tableNotFoundHint schema tblName SchemaCache{dbTablesFuzzyIndex}
385385
= fmap (\tbl -> "Perhaps you meant the table '" <> schema <> "." <> tbl <> "'") perhapsTable
386386
where
387-
perhapsTable = (`Fuzzy.getOne` tblName) =<< HM.lookup schema dbTablesFuzzyIndex
387+
perhapsTable = (\fuzzySet -> getFuzzyHint HintTable fuzzySet tblName) =<< HM.lookup schema dbTablesFuzzyIndex
388+
389+
data HintType
390+
= HintTable
391+
| HintProcedure
392+
| HintParams
393+
394+
-- | Get hint using Fuzzy Search with at least 0.75 similarity score
395+
getFuzzyHint :: HintType -> Fuzzy.FuzzySet -> Text -> Maybe Text
396+
getFuzzyHint hintType =
397+
let minScore = 0.75 :: Double -- used for table and procedure name hints
398+
in case hintType of
399+
HintTable -> Fuzzy.getOneWithMinScore minScore
400+
HintProcedure -> Fuzzy.getOneWithMinScore minScore
401+
HintParams -> Fuzzy.getOne -- For params, we stick to `getOne` which defaults to 0.33 min score, not a security risk to reveal params
388402

389403
compressedRel :: Relationship -> JSON.Value
390404
-- An ambiguousness error cannot happen for computed relationships TODO refactor so this mempty is not needed

test/io/test_io.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1768,11 +1768,11 @@ def test_client_error_verbosity_config(defaultenv):
17681768
}
17691769

17701770
with run(env=env) as postgrest:
1771-
response = postgrest.session.get("/itemsxx")
1771+
response = postgrest.session.get("/itemsx")
17721772
assert response.status_code == 404
17731773
assert response.json() == {
17741774
"code": "PGRST205",
1775-
"message": "Could not find the table 'public.itemsxx' in the schema cache",
1775+
"message": "Could not find the table 'public.itemsx' in the schema cache",
17761776
}
17771777

17781778
env = {
@@ -1781,11 +1781,11 @@ def test_client_error_verbosity_config(defaultenv):
17811781
}
17821782

17831783
with run(env=env) as postgrest:
1784-
response = postgrest.session.get("/itemsxx")
1784+
response = postgrest.session.get("/itemsx")
17851785
assert response.status_code == 404
17861786
assert response.json() == {
17871787
"code": "PGRST205",
1788-
"message": "Could not find the table 'public.itemsxx' in the schema cache",
1788+
"message": "Could not find the table 'public.itemsx' in the schema cache",
17891789
"details": None,
17901790
"hint": "Perhaps you meant the table 'public.items'",
17911791
}

test/spec/Feature/ConcurrentSpec.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ spec =
2525
raceTest 10 $
2626
get "/fakefake"
2727
`shouldRespondWith`
28-
[json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.factories'","message":"Could not find the table 'test.fakefake' in the schema cache"} |]
28+
[json| {"code":"PGRST205","details":null,"hint":null,"message":"Could not find the table 'test.fakefake' in the schema cache"} |]
2929
{ matchStatus = 404
3030
, matchHeaders = []
3131
}

test/spec/Feature/Query/DeleteSpec.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ spec =
116116
it "fails with 404" $
117117
request methodDelete "/foozle?id=eq.101" [] ""
118118
`shouldRespondWith`
119-
[json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.foo'","message":"Could not find the table 'test.foozle' in the schema cache"} |]
119+
[json| {"code":"PGRST205","details":null,"hint":null,"message":"Could not find the table 'test.foozle' in the schema cache"} |]
120120
{ matchStatus = 404
121121
, matchHeaders = []
122122
}

test/spec/Feature/Query/ErrorSpec.hs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ pgErrorCodeMapping = do
4242
it "works with SchemaCache error" $
4343
get "/non_existent_table"
4444
`shouldRespondWith`
45-
[json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.collision_test_table'","message":"Could not find the table 'test.non_existent_table' in the schema cache"} |]
45+
[json| {"code":"PGRST205","details":null,"hint":null,"message":"Could not find the table 'test.non_existent_table' in the schema cache"} |]
4646
{ matchStatus = 404
4747
, matchHeaders = [ "Proxy-Status" <:> "PostgREST; error=PGRST205"
48-
, "Content-Length" <:> "182" ]
48+
, "Content-Length" <:> "129" ]
4949
}
5050

5151
it "works with Jwt error" $ do
@@ -75,3 +75,30 @@ pgErrorCodeMapping = do
7575
, matchHeaders = [ "Proxy-Status" <:> "PostgREST; error=123"
7676
, "Content-Length" <:> "59" ]
7777
}
78+
79+
context "show hint on PGRST205 table not found error" $ do
80+
it "show hint when similarity score is at least 75%" $ do
81+
get "/projectx" -- at least 75% similar to "projects"
82+
`shouldRespondWith`
83+
[json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.projects'","message":"Could not find the table 'test.projectx' in the schema cache"} |]
84+
{ matchStatus = 404
85+
, matchHeaders = [ "Proxy-Status" <:> "PostgREST; error=PGRST205"
86+
, "Content-Length" <:> "160" ]
87+
}
88+
89+
get "/projecxx" -- at least 75% similar to "projects"
90+
`shouldRespondWith`
91+
[json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.projects'","message":"Could not find the table 'test.projecxx' in the schema cache"} |]
92+
{ matchStatus = 404
93+
, matchHeaders = [ "Proxy-Status" <:> "PostgREST; error=PGRST205"
94+
, "Content-Length" <:> "160" ]
95+
}
96+
97+
it "don't show hint when similarity score is less than 75%" $
98+
get "/projxxxx" -- less than 75% similar to "projects"
99+
`shouldRespondWith`
100+
[json| {"code":"PGRST205","details":null,"hint":null,"message":"Could not find the table 'test.projxxxx' in the schema cache"} |]
101+
{ matchStatus = 404
102+
, matchHeaders = [ "Proxy-Status" <:> "PostgREST; error=PGRST205"
103+
, "Content-Length" <:> "119" ]
104+
}

test/spec/Feature/Query/InsertSpec.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,7 @@ spec actualPgVersion = do
483483
{"id": 204, "body": "yyy"},
484484
{"id": 205, "body": "zzz"}]|]
485485
`shouldRespondWith`
486-
[json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.articles'","message":"Could not find the table 'test.garlic' in the schema cache"} |]
486+
[json| {"code":"PGRST205","details":null,"hint":null,"message":"Could not find the table 'test.garlic' in the schema cache"} |]
487487
{ matchStatus = 404
488488
, matchHeaders = []
489489
}

test/spec/Feature/Query/QuerySpec.hs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ spec = do
2626
it "causes a 404" $
2727
get "/faketable"
2828
`shouldRespondWith`
29-
[json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.private_table'","message":"Could not find the table 'test.faketable' in the schema cache"} |]
29+
[json| {"code":"PGRST205","details":null,"hint":null,"message":"Could not find the table 'test.faketable' in the schema cache"} |]
3030
{ matchStatus = 404
31-
, matchHeaders = ["Content-Length" <:> "166"]
31+
, matchHeaders = ["Content-Length" <:> "120"]
3232
}
3333

3434
describe "Filtering response" $ do
@@ -852,7 +852,7 @@ spec = do
852852
-- the existence of first table, #3869
853853
it "table not found error if first table does not exist" $
854854
get "/car_model_sales_202101?select=id,name,car_models(id,name)&order=id.asc" `shouldRespondWith`
855-
[json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.car_model_sales'","message":"Could not find the table 'test.car_model_sales_202101' in the schema cache"} |]
855+
[json| {"code":"PGRST205","details":null,"hint":null,"message":"Could not find the table 'test.car_model_sales_202101' in the schema cache"} |]
856856
{ matchStatus = 404
857857
, matchHeaders = [matchContentTypeJson]
858858
}
@@ -870,7 +870,7 @@ spec = do
870870

871871
it "table not found error if first table does not exist" $
872872
get "/car_models_default?select=id,name,car_model_sales(id,name)&order=id.asc" `shouldRespondWith`
873-
[json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.car_model_sales'","message":"Could not find the table 'test.car_models_default' in the schema cache"} |]
873+
[json| {"code":"PGRST205","details":null,"hint":null,"message":"Could not find the table 'test.car_models_default' in the schema cache"} |]
874874
{ matchStatus = 404
875875
, matchHeaders = [matchContentTypeJson]
876876
}

test/spec/Feature/Query/UpdateSpec.hs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ spec = do
1818
request methodPatch "/fake" []
1919
[json| { "real": false } |]
2020
`shouldRespondWith`
21-
[json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.factories'","message":"Could not find the table 'test.fake' in the schema cache"} |]
21+
[json| {"code":"PGRST205","details":null,"hint":null,"message":"Could not find the table 'test.fake' in the schema cache"} |]
2222
{ matchStatus = 404
23-
, matchHeaders = ["Content-Length" <:> "157"]
23+
, matchHeaders = ["Content-Length" <:> "115"]
2424
}
2525

2626

@@ -363,9 +363,9 @@ spec = do
363363
{"id": 204, "body": "yyy"},
364364
{"id": 205, "body": "zzz"}]|]
365365
`shouldRespondWith`
366-
[json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.articles'","message":"Could not find the table 'test.garlic' in the schema cache"} |]
366+
[json| {"code":"PGRST205","details":null,"hint":null,"message":"Could not find the table 'test.garlic' in the schema cache"} |]
367367
{ matchStatus = 404
368-
, matchHeaders = ["Content-Length" <:> "158"]
368+
, matchHeaders = ["Content-Length" <:> "117"]
369369
}
370370

371371
context "apply defaults on missing values" $ do

0 commit comments

Comments
 (0)