on dev help sign in
insert & inject

improve insert a bit and test for sql injection (pending for now)

commitdb25fbf81fbc16b23247eb03e84a0182601c7157 authorEitan Chatav <eitan.chatav@gmail.com> dateSun 23 Apr 2017, 13:34 (10 years ago) parent6253103
3 files changed+45−30
squeel-postgresql/src/Squeel/PostgreSQL/PQ.hs+8−3 squeel-postgresql/src/Squeel/PostgreSQL/Query.hs+27−23 squeel-postgresql/test/Squeel/PostgreSQL/QuerySpec.hs+10−4
squeel-postgresql/src/Squeel/PostgreSQL/PQ.hs+8−3
@@ -24,6 +24,7 @@ import Data.Proxy
2424import Data.Text (Text)
2525import Data.Vinyl
2626import Data.Vinyl.Functor
27+import GHC.TypeLits
2728
2829import qualified Database.PostgreSQL.LibPQ as LibPQ
2930
@@ -33,9 +34,13 @@ import Squeel.PostgreSQL.Type
3334
3435newtype Connection db = Connection { unConnection :: LibPQ.Connection }
3536
36−newtype PQ db0 db1 m x = PQ
37− { runPQ :: Connection db0 -> m (x, Connection db1) }
38− deriving Functor
37+newtype PQ
38+ (db0 :: [(Symbol,[(Symbol,PGType)])])
39+ (db1 :: [(Symbol,[(Symbol,PGType)])])
40+ (m :: * -> *)
41+ (x :: *) =
42+ PQ { runPQ :: Connection db0 -> m (x, Connection db1) }
43+ deriving Functor
3944
4045evalPQ :: Functor m => PQ db0 db1 m x -> Connection db0 -> m x
4146evalPQ (PQ pq) = fmap fst . pq
squeel-postgresql/src/Squeel/PostgreSQL/Query.hs+27−23
@@ -96,8 +96,11 @@ instance (KnownSymbol label, AllAliased (x' ': xs))
9696 (x :& xs :: Rec (Aliased obj) ('(label, x) ': x' : xs)) =
9797 renderAliased render x <> ", " <> renderAllAliased render xs
9898
99−newtype Expression ps (xs :: [(Symbol,PGType)]) y =
100− UnsafeExpression { renderExpression :: ByteString }
99+newtype Expression
100+ (ps :: [PGType])
101+ (xs :: [(Symbol,PGType)])
102+ (y :: PGType) =
103+ UnsafeExpression { renderExpression :: ByteString }
101104
102105param1 :: Expression (p1:ps) cols p1
103106param1 = UnsafeExpression "$1"
@@ -170,8 +173,11 @@ instance OrdB (Expression ps xs y) where
170173 x <* y = UnsafeExpression $ renderExpression x <> "<" <> renderExpression y
171174 x <=* y = UnsafeExpression $ renderExpression x <> "<=" <> renderExpression y
172175
173−newtype Projection ps xs ys = UnsafeProjection
174− { renderProjection :: ByteString }
176+newtype Projection
177+ (ps :: [PGType])
178+ (xs :: [(Symbol,PGType)])
179+ (ys :: [(Symbol,PGType)]) =
180+ UnsafeProjection { renderProjection :: ByteString }
175181
176182star :: Projection ps xs xs
177183star = UnsafeProjection "*"
@@ -254,33 +260,31 @@ subselect selection = Relation
254260 , offsetting = Nothing
255261 }
256262
257−newtype Insertion ps xss xs = UnsafeInsertion
258− {renderInsertion :: ByteString }
263+newtype Query
264+ (ps :: [PGType])
265+ (xss :: [(Symbol,[(Symbol,PGType)])])
266+ (yss :: [(Symbol,[(Symbol,PGType)])])
267+ (zs :: [(Symbol,PGType)]) =
268+ UnsafeQuery { renderQuery :: ByteString }
269+newtype PreparedQuery ps xss yss zs =
270+ UnsafePreparedQuery { renderPreparedQuery :: ByteString }
259271
260−into
261− :: forall label xss xs ps
272+select :: Selection ps xss ys -> Query ps xss xss ys
273+select selection = UnsafeQuery $
274+ "SELECT " <> renderSelection selection <> ";"
275+
276+insertInto
277+ :: forall xss xs label ps
262278 . (HasField label xss xs, UnzipAliased xs)
263279 => Proxy label
264280 -> Rec (Aliased (Expression ps '[])) xs
265− -> Insertion ps xss xs
266−into table values = UnsafeInsertion $ "INTO "
281+ -> Query ps xss xss '[]
282+insertInto table values = UnsafeQuery $ "INSERT INTO "
267283 <> fieldName table (Proxy @xss) (Proxy @xs)
268284 <> " (" <> ByteString.intercalate ", " names
269285 <> ") VALUES ("
270286 <> ByteString.intercalate ", "
271287 (recordToList (rmap (Const . renderExpression) exprs))
272− <> ")"
288+ <> ");"
273289 where
274290 (names,exprs) = unzipAliased values
275−
276−newtype Query ps xss yss zs = UnsafeQuery { renderQuery :: ByteString }
277−newtype PreparedQuery ps xss yss zs =
278− UnsafePreparedQuery { renderPreparedQuery :: ByteString }
279−
280−select :: Selection ps xss ys -> Query ps xss xss ys
281−select selection = UnsafeQuery $
282− "SELECT " <> renderSelection selection <> ";"
283−
284−insert :: Insertion ps xss xs -> Query ps xss xss '[]
285−insert insertion = UnsafeQuery $
286− "INSERT " <> renderInsertion insertion <> ";"
squeel-postgresql/test/Squeel/PostgreSQL/QuerySpec.hs+10−4
@@ -50,9 +50,15 @@ spec = do
5050 `shouldBe`
5151 "SELECT * FROM table1 OFFSET 1+2;"
5252 it "correctly render simple INSERTs" $ do
53− renderQuery (insert insertion)
53+ renderQuery (insertInto @Tables @Columns (Proxy @"table1")
54+ ((2 `As` #col1 :& 4 `As` #col2 :& RNil)))
5455 `shouldBe`
5556 "INSERT INTO table1 (col1, col2) VALUES (2, 4);"
57+ it "should be safe against SQL injection in literal text" $ do
58+ pendingWith "gotta find an escape function"
59+ -- renderQuery (select (literalText `from` table1))
60+ -- `shouldBe`
61+ -- "SELECT '\\'DROP TABLE table1;' AS literal FROM table1;"
5662
5763type Columns = '[ '("col1", 'PGInt4), '("col2", 'PGInt4)]
5864type Tables = '[ '("table1", Columns)]
@@ -60,11 +66,11 @@ type Tables = '[ '("table1", Columns)]
6066sumAndCol1 :: Projection '[] Columns '[ '("sum", 'PGInt4), '("col1", 'PGInt4)]
6167sumAndCol1 = project ((#col1 + #col2) `As` #sum :& #col1 :& RNil)
6268
69+literalText :: Projection '[] Columns '[ '("literal", 'PGText)]
70+literalText = project ("\';DROP TABLE table1;" `As` #literal :& RNil)
71+
6372table1 :: Relation '[] Tables Columns
6473table1 = #table1
6574
6675parameterizedTable1 :: Relation '[ 'PGInt8] Tables Columns
6776parameterizedTable1 = #table1
68−
69−insertion :: Insertion '[] Tables Columns
70−insertion = into (Proxy @"table1") (2 `As` #col1 :& 4 `As` #col2 :& RNil)