aws-sns-verify 0.0.0.2 → 0.0.0.3
raw patch · 10 files changed
+250/−209 lines, 10 filesdep +crypton-x509dep +crypton-x509-validationdep −x509dep −x509-validationdep ~aesondep ~aeson-qqdep ~asyncPVP ok
version bump matches the API change (PVP)
Dependencies added: crypton-x509, crypton-x509-validation
Dependencies removed: x509, x509-validation
Dependency ranges changed: aeson, aeson-qq, async, bytestring, errors, hspec, http-conduit, http-types, memory, network-uri, pem, regex-tdfa, text, wai, warp
API changes (from Hackage documentation)
Files
- CHANGELOG.md +8/−3
- aws-sns-verify.cabal +22/−22
- library/Amazon/SNS/Verify.hs +2/−4
- library/Amazon/SNS/Verify/Payload.hs +6/−6
- library/Amazon/SNS/Verify/Prelude.hs +1/−1
- library/Amazon/SNS/Verify/ValidURI.hs +10/−6
- library/Amazon/SNS/Verify/Validate.hs +63/−49
- tests/Amazon/SNS/Verify/TestPrelude.hs +6/−5
- tests/Amazon/SNS/Verify/ValidateSpec.hs +125/−106
- tests/Amazon/SNS/VerifySpec.hs +7/−7
CHANGELOG.md view
@@ -1,10 +1,15 @@-## [_Unreleased_](https://github.com/freckle/aws-sns-verify/compare/v0.0.0.2...main)+## [_Unreleased_](https://github.com/freckle/aws-sns-verify/compare/v0.0.0.3...main) -## [v0.0.0.2](https://github.com/freckle/aws-sns-verify/compare/v0.0.0.2...v0.0.0.1)+## [v0.0.0.3](https://github.com/freckle/aws-sns-verify/compare/v0.0.0.2...v0.0.0.3) +- Migrate to `crypton-x509*`+- Remove CI for GHC's 8.6 and 8.8++## [v0.0.0.2](https://github.com/freckle/aws-sns-verify/compare/v0.0.0.1...v0.0.0.2)+ - Validate PEM has come from AWS before checking signature. -## [v0.0.0.1](https://github.com/freckle/aws-sns-verify/compare/v0.0.0.1...v0.0.0.0)+## [v0.0.0.1](https://github.com/freckle/aws-sns-verify/compare/v0.0.0.0...v0.0.0.1) - Fix typo in subscribe signature
aws-sns-verify.cabal view
@@ -1,6 +1,6 @@ cabal-version: 1.18 name: aws-sns-verify-version: 0.0.0.2+version: 0.0.0.3 license: MIT license-file: LICENSE copyright: 2022 Freckle By Renaissance@@ -59,18 +59,18 @@ -Wno-all-missed-specialisations build-depends:- aeson >=1.3.1.1,+ aeson, base >=4.7 && <5,- bytestring >=0.10.8.2,- errors >=2.3.0,- http-conduit >=2.3.2,- memory >=0.14.18,- network-uri >=2.6.1.0,- pem >=0.2.4,- regex-tdfa >=1.2.3.1,- text >=1.2.3.1,- x509 >=1.7.5,- x509-validation >=1.6.11+ bytestring,+ crypton-x509,+ crypton-x509-validation,+ errors,+ http-conduit,+ memory,+ network-uri,+ pem,+ regex-tdfa,+ text if impl(ghc >=8.10) ghc-options:@@ -110,17 +110,17 @@ -Wno-all-missed-specialisations -threaded -rtsopts -with-rtsopts=-N build-depends:- aeson-qq >=0.8.2,- async >=2.2.1,- aws-sns-verify -any,+ aeson-qq,+ async,+ aws-sns-verify, base >=4.7 && <5,- hspec >=2.5.5,- http-types >=0.12.2,- regex-tdfa >=1.2.3.1,- text >=1.2.3.1,- wai >=3.2.1.2,- warp >=3.2.25,- x509-validation >=1.6.11+ crypton-x509-validation,+ hspec,+ http-types,+ regex-tdfa,+ text,+ wai,+ warp if impl(ghc >=8.10) ghc-options:
library/Amazon/SNS/Verify.hs view
@@ -3,7 +3,7 @@ , verifySNSMessageEither , verifySNSMessageJSON , verifySNSMessageJSONEither- , SNSNotificationValidationError(..)+ , SNSNotificationValidationError (..) ) where import Amazon.SNS.Verify.Prelude@@ -12,7 +12,7 @@ import Amazon.SNS.Verify.Validate import Control.Error (hoistEither, runExceptT) import Data.Aeson (FromJSON, Value, eitherDecode)-import Data.Aeson.Types (Result(Error, Success), fromJSON)+import Data.Aeson.Types (Result (Error, Success), fromJSON) import Data.Bifunctor (first) import Data.ByteString.Lazy (fromStrict) import Data.Text.Encoding (encodeUtf8)@@ -20,7 +20,6 @@ -- | Decode and verify an SNS message as JSON -- -- The same as 'verifySNSMessage', but decodes the message as `JSON`.--- verifySNSMessageJSON :: (FromJSON a, MonadIO m) => Value -> m a verifySNSMessageJSON = unTryIO id <=< verifySNSMessageJSONEither @@ -45,7 +44,6 @@ -- or `UnsubscribeConfirmation`. -- 2. Verified against its signature. -- 3. And in the case of subscription events responded to.--- verifySNSMessage :: MonadIO m => Value -> m Text verifySNSMessage = unTryIO id <=< verifySNSMessageEither
library/Amazon/SNS/Verify/Payload.hs view
@@ -1,8 +1,8 @@ module Amazon.SNS.Verify.Payload- ( SNSPayload(..)- , SNSType(..)- , SNSNotification(..)- , SNSSubscription(..)+ ( SNSPayload (..)+ , SNSType (..)+ , SNSNotification (..)+ , SNSSubscription (..) ) where import Amazon.SNS.Verify.Prelude@@ -69,7 +69,7 @@ deriving stock (Show, Eq, Generic) instance FromJSON SNSNotification where- parseJSON = genericParseJSON $ defaultOptions { fieldLabelModifier = drop 3 }+ parseJSON = genericParseJSON $ defaultOptions {fieldLabelModifier = drop 3} data SNSSubscription = SNSSubscription { snsToken :: Text@@ -78,4 +78,4 @@ deriving stock (Show, Eq, Generic) instance FromJSON SNSSubscription where- parseJSON = genericParseJSON $ defaultOptions { fieldLabelModifier = drop 3 }+ parseJSON = genericParseJSON $ defaultOptions {fieldLabelModifier = drop 3}
library/Amazon/SNS/Verify/Prelude.hs view
@@ -20,7 +20,7 @@ unTryIO :: (MonadIO m, Exception e) => (a -> e) -> Either a b -> m b unTryIO e = either (throwIO . e) pure -unTryE :: (Monad m) => (a -> e) -> Either a b -> ExceptT e m b+unTryE :: Monad m => (a -> e) -> Either a b -> ExceptT e m b unTryE e = either (throwE . e) pure fromMaybeM :: Monad m => m a -> Maybe a -> m a
library/Amazon/SNS/Verify/ValidURI.hs view
@@ -9,6 +9,14 @@ import Amazon.SNS.Verify.Prelude +_devScheme :: String+_devScheme = "http:"++_prodScheme :: String+_prodScheme = "https:"++{- FOURMOLU_DISABLE -}+ validScheme :: String validScheme = #ifdef DEVELOPMENT@@ -17,12 +25,6 @@ _prodScheme #endif -_devScheme :: String-_devScheme = "http:"--_prodScheme :: String-_prodScheme = "https:"- validRegPattern :: String validRegPattern = #ifdef DEVELOPMENT@@ -30,6 +32,8 @@ #else prodRegPattern #endif++{- FOURMOLU_ENABLE -} devRegPattern :: String devRegPattern = "^localhost$"
library/Amazon/SNS/Verify/Validate.hs view
@@ -1,8 +1,8 @@ module Amazon.SNS.Verify.Validate ( validateSnsMessage , handleSubscription- , SNSNotificationValidationError(..)- , ValidSNSMessage(..)+ , SNSNotificationValidationError (..)+ , ValidSNSMessage (..) ) where import Amazon.SNS.Verify.Prelude@@ -11,23 +11,30 @@ import Amazon.SNS.Verify.ValidURI (validRegPattern, validScheme) import Control.Error (ExceptT, catMaybes, headMay, runExceptT, throwE) import Control.Monad (when)-import Data.ByteArray.Encoding (Base(Base64), convertFromBase)+import Data.ByteArray.Encoding (Base (Base64), convertFromBase) import Data.PEM (pemContent, pemParseLBS) import qualified Data.Text as T import Data.Text.Encoding (encodeUtf8) import Data.X509- ( HashALG(..)- , PubKeyALG(..)- , SignatureALG(..)+ ( HashALG (..)+ , PubKeyALG (..)+ , SignatureALG (..) , SignedCertificate , certPubKey , decodeSignedCertificate , getCertificate ) import Data.X509.Validation- (SignatureFailure, SignatureVerification(..), verifySignature)+ ( SignatureFailure+ , SignatureVerification (..)+ , verifySignature+ ) import Network.HTTP.Simple- (getResponseBody, getResponseStatusCode, httpLbs, parseRequest_)+ ( getResponseBody+ , getResponseStatusCode+ , httpLbs+ , parseRequest_+ ) import Network.URI (parseURI, uriAuthority, uriRegName, uriScheme) import Text.Regex.TDFA ((=~)) @@ -43,24 +50,26 @@ -- in the documentation below. -- -- <https://docs.aws.amazon.com/sns/latest/dg/sns-verify-signature-of-message.html>--- validateSnsMessage :: MonadIO m => SNSPayload -> m (Either SNSNotificationValidationError ValidSNSMessage) validateSnsMessage payload@SNSPayload {..} = runExceptT $ do- signature <- unTryE BadSignature $ convertFromBase Base64 $ encodeUtf8- snsSignature+ signature <-+ unTryE BadSignature+ $ convertFromBase Base64+ $ encodeUtf8+ snsSignature signedCert <- retrieveCertificate payload- let- valid = verifySignature- (SignatureALG HashSHA1 PubKeyALG_RSA)- (certPubKey $ getCertificate signedCert)- (unsignedSignature payload)- signature+ let valid =+ verifySignature+ (SignatureALG HashSHA1 PubKeyALG_RSA)+ (certPubKey $ getCertificate signedCert)+ (unsignedSignature payload)+ signature case valid of SignaturePass -> pure $ case snsTypePayload of- Notification{} -> SNSMessage snsMessage+ Notification {} -> SNSMessage snsMessage SubscriptionConfirmation x -> SNSSubscribe x UnsubscribeConfirmation x -> SNSUnsubscribe x SignatureFailed e -> throwE $ InvalidPayload e@@ -81,33 +90,37 @@ validateCertUrl certUrl = do uri <- fromMaybeM (Left $ BadUri certUrlStr) $ parseURI certUrlStr if uriScheme uri- == validScheme- && maybe "" uriRegName (uriAuthority uri)- =~ validRegPattern+ == validScheme+ && maybe "" uriRegName (uriAuthority uri)+ =~ validRegPattern then Right certUrlStr else Left $ BadUri certUrlStr- where certUrlStr = T.unpack certUrl+ where+ certUrlStr = T.unpack certUrl unsignedSignature :: SNSPayload -> ByteString unsignedSignature SNSPayload {..} =- encodeUtf8 $ mconcat $ (<> "\n") <$> catMaybes- [ Just "Message"- , Just snsMessage- , Just "MessageId"- , Just snsMessageId- , "SubscribeURL" <$ mSubscribeUrl- , mSubscribeUrl- , "Subject" <$ mSubject- , mSubject- , Just "Timestamp"- , Just snsTimestamp- , "Token" <$ mToken- , mToken- , Just "TopicArn"- , Just snsTopicArn- , Just "Type"- , Just snsType- ]+ encodeUtf8+ $ mconcat+ $ (<> "\n")+ <$> catMaybes+ [ Just "Message"+ , Just snsMessage+ , Just "MessageId"+ , Just snsMessageId+ , "SubscribeURL" <$ mSubscribeUrl+ , mSubscribeUrl+ , "Subject" <$ mSubject+ , mSubject+ , Just "Timestamp"+ , Just snsTimestamp+ , "Token" <$ mToken+ , mToken+ , Just "TopicArn"+ , Just snsTopicArn+ , Just "Type"+ , Just snsType+ ] where (mSubject, mToken, mSubscribeUrl) = case snsTypePayload of Notification x -> (snsSubject x, Nothing, Nothing)@@ -120,14 +133,15 @@ :: MonadIO m => ValidSNSMessage -> m (Either SNSNotificationValidationError Text)-handleSubscription = runExceptT . \case- SNSMessage t -> pure t- SNSSubscribe SNSSubscription {..} -> do- response <- httpLbs $ parseRequest_ $ T.unpack snsSubscribeURL- when (getResponseStatusCode response >= 300) $ do- throwE BadSubscription- throwE SubscribeMessageResponded- SNSUnsubscribe{} -> throwE UnsubscribeMessage+handleSubscription =+ runExceptT . \case+ SNSMessage t -> pure t+ SNSSubscribe SNSSubscription {..} -> do+ response <- httpLbs $ parseRequest_ $ T.unpack snsSubscribeURL+ when (getResponseStatusCode response >= 300) $ do+ throwE BadSubscription+ throwE SubscribeMessageResponded+ SNSUnsubscribe {} -> throwE UnsubscribeMessage data SNSNotificationValidationError = BadPem String@@ -141,4 +155,4 @@ | UnsubscribeMessage | SubscribeMessageResponded deriving stock (Show, Eq)- deriving anyclass Exception+ deriving anyclass (Exception)
tests/Amazon/SNS/Verify/TestPrelude.hs view
@@ -16,11 +16,12 @@ useCertServer :: IO () -> IO () useCertServer action = do (setReady, whenReady) <- initReadyState- race_ (whenReady action)- $ runSettings (setBeforeMainLoop setReady . setPort 3000 $ defaultSettings)- $ \req send -> if rawPathInfo req == "/404"- then send $ responseLBS notFound404 [] ""- else send $ responseFile ok200 [] "./tests/cert.pem" Nothing+ race_ (whenReady action) $+ runSettings (setBeforeMainLoop setReady . setPort 3000 $ defaultSettings) $+ \req send ->+ if rawPathInfo req == "/404"+ then send $ responseLBS notFound404 [] ""+ else send $ responseFile ok200 [] "./tests/cert.pem" Nothing where initReadyState = do ready <- newEmptyMVar
tests/Amazon/SNS/Verify/ValidateSpec.hs view
@@ -6,137 +6,156 @@ import Amazon.SNS.Verify.Payload import Amazon.SNS.Verify.Validate-import Data.X509.Validation (SignatureFailure(..))+import Data.X509.Validation (SignatureFailure (..)) spec :: Spec spec = around_ useCertServer $ do describe "validateSnsMessage" $ do it "successfully validates an SNS notification" $ do let message = "Some message"- x <- validateSnsMessage $ SNSPayload- { snsMessage = message- , snsMessageId = "78d4d7a0-a3eb-5c4d-834f-8d5fa9813ab6"- , snsTimestamp = "2022-05-18T14:52:26.952Z"- , snsTopicArn = "arn:aws:sns:us-west-2:123456789012:MyTopic"- , snsType = "Notification"- , snsSignatureVersion = "1"- , snsSignature =- "Dg24trcOUiLjclt5JwyJS0JEOnEEbi6P30XS6KBxMCwzZ08a04UwjaFTW9Ae8xurhBS5YESz1fY28vTwvEmxh/20WmB3bWIDOMp9v5RI8XSZOvpMm+hdQJ43VqGhEDyAvRU6iCDLihDlZNc/sBCwl9X0H4kh/8vIElRif9gFBbYI94ZHGgqEV+Zc3gVKo9Udrl/MxNvMVadsO/+/oPVUeWibQr3xfGK95oc/ocuNAgi0MOxZmLVnibHu36KOTSvy2qSLonnRRFcbaauYZJ4js7oTq+1ujXNO72oPLaeG3pVJ2grqMc5z8tKQxFnSTE3es7wQarU/CLrbO8j0isbnWw=="- , snsSigningCertURL = cert- , snsTypePayload = Notification $ SNSNotification- { snsSubject =- Just- "SynthesisTaskNotification { TaskId: 680a1f1b-f3ae-4474-aa8f-3b6dfe52e656, Status: COMPLETED }"- }- }+ x <-+ validateSnsMessage+ $ SNSPayload+ { snsMessage = message+ , snsMessageId = "78d4d7a0-a3eb-5c4d-834f-8d5fa9813ab6"+ , snsTimestamp = "2022-05-18T14:52:26.952Z"+ , snsTopicArn = "arn:aws:sns:us-west-2:123456789012:MyTopic"+ , snsType = "Notification"+ , snsSignatureVersion = "1"+ , snsSignature =+ "Dg24trcOUiLjclt5JwyJS0JEOnEEbi6P30XS6KBxMCwzZ08a04UwjaFTW9Ae8xurhBS5YESz1fY28vTwvEmxh/20WmB3bWIDOMp9v5RI8XSZOvpMm+hdQJ43VqGhEDyAvRU6iCDLihDlZNc/sBCwl9X0H4kh/8vIElRif9gFBbYI94ZHGgqEV+Zc3gVKo9Udrl/MxNvMVadsO/+/oPVUeWibQr3xfGK95oc/ocuNAgi0MOxZmLVnibHu36KOTSvy2qSLonnRRFcbaauYZJ4js7oTq+1ujXNO72oPLaeG3pVJ2grqMc5z8tKQxFnSTE3es7wQarU/CLrbO8j0isbnWw=="+ , snsSigningCertURL = cert+ , snsTypePayload =+ Notification+ $ SNSNotification+ { snsSubject =+ Just+ "SynthesisTaskNotification { TaskId: 680a1f1b-f3ae-4474-aa8f-3b6dfe52e656, Status: COMPLETED }"+ }+ } x `shouldBe` Right (SNSMessage message) it "fails to validate a currupt SNS notification" $ do- let- go = validateSnsMessage $ SNSPayload- { snsMessage = "Some message"- , snsMessageId = "corrupt"- , snsTimestamp = "2022-05-18T14:52:26.952Z"- , snsTopicArn = "arn:aws:sns:us-west-2:123456789012:MyTopic"- , snsType = "Notification"- , snsSignatureVersion = "1"- , snsSignature =- "Dg24trcOUiLjclt5JwyJS0JEOnEEbi6P30XS6KBxMCwzZ08a04UwjaFTW9Ae8xurhBS5YESz1fY28vTwvEmxh/20WmB3bWIDOMp9v5RI8XSZOvpMm+hdQJ43VqGhEDyAvRU6iCDLihDlZNc/sBCwl9X0H4kh/8vIElRif9gFBbYI94ZHGgqEV+Zc3gVKo9Udrl/MxNvMVadsO/+/oPVUeWibQr3xfGK95oc/ocuNAgi0MOxZmLVnibHu36KOTSvy2qSLonnRRFcbaauYZJ4js7oTq+1ujXNO72oPLaeG3pVJ2grqMc5z8tKQxFnSTE3es7wQarU/CLrbO8j0isbnWw=="- , snsSigningCertURL = cert- , snsTypePayload = Notification $ SNSNotification- { snsSubject =- Just- "SynthesisTaskNotification { TaskId: 680a1f1b-f3ae-4474-aa8f-3b6dfe52e656, Status: COMPLETED }"- }- }+ let go =+ validateSnsMessage+ $ SNSPayload+ { snsMessage = "Some message"+ , snsMessageId = "corrupt"+ , snsTimestamp = "2022-05-18T14:52:26.952Z"+ , snsTopicArn = "arn:aws:sns:us-west-2:123456789012:MyTopic"+ , snsType = "Notification"+ , snsSignatureVersion = "1"+ , snsSignature =+ "Dg24trcOUiLjclt5JwyJS0JEOnEEbi6P30XS6KBxMCwzZ08a04UwjaFTW9Ae8xurhBS5YESz1fY28vTwvEmxh/20WmB3bWIDOMp9v5RI8XSZOvpMm+hdQJ43VqGhEDyAvRU6iCDLihDlZNc/sBCwl9X0H4kh/8vIElRif9gFBbYI94ZHGgqEV+Zc3gVKo9Udrl/MxNvMVadsO/+/oPVUeWibQr3xfGK95oc/ocuNAgi0MOxZmLVnibHu36KOTSvy2qSLonnRRFcbaauYZJ4js7oTq+1ujXNO72oPLaeG3pVJ2grqMc5z8tKQxFnSTE3es7wQarU/CLrbO8j0isbnWw=="+ , snsSigningCertURL = cert+ , snsTypePayload =+ Notification+ $ SNSNotification+ { snsSubject =+ Just+ "SynthesisTaskNotification { TaskId: 680a1f1b-f3ae-4474-aa8f-3b6dfe52e656, Status: COMPLETED }"+ }+ } go `shouldReturn` Left (InvalidPayload SignatureInvalid) it "fails to validate a bad PEM" $ do- let- go = validateSnsMessage $ SNSPayload- { snsMessage = "Some message"- , snsMessageId = "corrupt"- , snsTimestamp = "2022-05-18T14:52:26.952Z"- , snsTopicArn = "arn:aws:sns:us-west-2:123456789012:MyTopic"- , snsType = "Notification"- , snsSignatureVersion = "1"- , snsSignature =- "Dg24trcOUiLjclt5JwyJS0JEOnEEbi6P30XS6KBxMCwzZ08a04UwjaFTW9Ae8xurhBS5YESz1fY28vTwvEmxh/20WmB3bWIDOMp9v5RI8XSZOvpMm+hdQJ43VqGhEDyAvRU6iCDLihDlZNc/sBCwl9X0H4kh/8vIElRif9gFBbYI94ZHGgqEV+Zc3gVKo9Udrl/MxNvMVadsO/+/oPVUeWibQr3xfGK95oc/ocuNAgi0MOxZmLVnibHu36KOTSvy2qSLonnRRFcbaauYZJ4js7oTq+1ujXNO72oPLaeG3pVJ2grqMc5z8tKQxFnSTE3es7wQarU/CLrbO8j0isbnWw=="- , snsSigningCertURL = "http://localhost:3000/404"- , snsTypePayload = Notification $ SNSNotification- { snsSubject =- Just- "SynthesisTaskNotification { TaskId: 680a1f1b-f3ae-4474-aa8f-3b6dfe52e656, Status: COMPLETED }"- }- }+ let go =+ validateSnsMessage+ $ SNSPayload+ { snsMessage = "Some message"+ , snsMessageId = "corrupt"+ , snsTimestamp = "2022-05-18T14:52:26.952Z"+ , snsTopicArn = "arn:aws:sns:us-west-2:123456789012:MyTopic"+ , snsType = "Notification"+ , snsSignatureVersion = "1"+ , snsSignature =+ "Dg24trcOUiLjclt5JwyJS0JEOnEEbi6P30XS6KBxMCwzZ08a04UwjaFTW9Ae8xurhBS5YESz1fY28vTwvEmxh/20WmB3bWIDOMp9v5RI8XSZOvpMm+hdQJ43VqGhEDyAvRU6iCDLihDlZNc/sBCwl9X0H4kh/8vIElRif9gFBbYI94ZHGgqEV+Zc3gVKo9Udrl/MxNvMVadsO/+/oPVUeWibQr3xfGK95oc/ocuNAgi0MOxZmLVnibHu36KOTSvy2qSLonnRRFcbaauYZJ4js7oTq+1ujXNO72oPLaeG3pVJ2grqMc5z8tKQxFnSTE3es7wQarU/CLrbO8j0isbnWw=="+ , snsSigningCertURL = "http://localhost:3000/404"+ , snsTypePayload =+ Notification+ $ SNSNotification+ { snsSubject =+ Just+ "SynthesisTaskNotification { TaskId: 680a1f1b-f3ae-4474-aa8f-3b6dfe52e656, Status: COMPLETED }"+ }+ } go `shouldReturn` Left (BadPem "Empty List") it "fails to validate an unexpected url" $ do- let- go = validateSnsMessage $ SNSPayload- { snsMessage = "Some message"- , snsMessageId = "corrupt"- , snsTimestamp = "2022-05-18T14:52:26.952Z"- , snsTopicArn = "arn:aws:sns:us-west-2:123456789012:MyTopic"- , snsType = "Notification"- , snsSignatureVersion = "1"- , snsSignature =- "Dg24trcOUiLjclt5JwyJS0JEOnEEbi6P30XS6KBxMCwzZ08a04UwjaFTW9Ae8xurhBS5YESz1fY28vTwvEmxh/20WmB3bWIDOMp9v5RI8XSZOvpMm+hdQJ43VqGhEDyAvRU6iCDLihDlZNc/sBCwl9X0H4kh/8vIElRif9gFBbYI94ZHGgqEV+Zc3gVKo9Udrl/MxNvMVadsO/+/oPVUeWibQr3xfGK95oc/ocuNAgi0MOxZmLVnibHu36KOTSvy2qSLonnRRFcbaauYZJ4js7oTq+1ujXNO72oPLaeG3pVJ2grqMc5z8tKQxFnSTE3es7wQarU/CLrbO8j0isbnWw=="- , snsSigningCertURL = "http://attacker.com/evil.pem"- , snsTypePayload = Notification $ SNSNotification- { snsSubject =- Just- "SynthesisTaskNotification { TaskId: 680a1f1b-f3ae-4474-aa8f-3b6dfe52e656, Status: COMPLETED }"- }- }+ let go =+ validateSnsMessage+ $ SNSPayload+ { snsMessage = "Some message"+ , snsMessageId = "corrupt"+ , snsTimestamp = "2022-05-18T14:52:26.952Z"+ , snsTopicArn = "arn:aws:sns:us-west-2:123456789012:MyTopic"+ , snsType = "Notification"+ , snsSignatureVersion = "1"+ , snsSignature =+ "Dg24trcOUiLjclt5JwyJS0JEOnEEbi6P30XS6KBxMCwzZ08a04UwjaFTW9Ae8xurhBS5YESz1fY28vTwvEmxh/20WmB3bWIDOMp9v5RI8XSZOvpMm+hdQJ43VqGhEDyAvRU6iCDLihDlZNc/sBCwl9X0H4kh/8vIElRif9gFBbYI94ZHGgqEV+Zc3gVKo9Udrl/MxNvMVadsO/+/oPVUeWibQr3xfGK95oc/ocuNAgi0MOxZmLVnibHu36KOTSvy2qSLonnRRFcbaauYZJ4js7oTq+1ujXNO72oPLaeG3pVJ2grqMc5z8tKQxFnSTE3es7wQarU/CLrbO8j0isbnWw=="+ , snsSigningCertURL = "http://attacker.com/evil.pem"+ , snsTypePayload =+ Notification+ $ SNSNotification+ { snsSubject =+ Just+ "SynthesisTaskNotification { TaskId: 680a1f1b-f3ae-4474-aa8f-3b6dfe52e656, Status: COMPLETED }"+ }+ } go `shouldReturn` Left (BadUri "http://attacker.com/evil.pem") it "successfully validates an SNS subscription" $ do -- pendingWith "need valid subscription payload" let- message- = "You have chosen to subscribe to the topic arn:aws:sns:us-west-2:123456789012:MyTopic.\nTo confirm the subscription, visit the SubscribeURL included in this message."- subscription = SNSSubscription- { snsToken = "2336412f37..."- , snsSubscribeURL =- "https://sns.us-west-2.amazonaws.com/?Action=ConfirmSubscription&TopicArn=arn:aws:sns:us-west-2:123456789012:MyTopic&Token=2336412f37..."- }- x <- validateSnsMessage $ SNSPayload- { snsMessage = message- , snsMessageId = "165545c9-2a5c-472c-8df2-7ff2be2b3b1b"- , snsTimestamp = "2012-04-26T20:45:04.751Z"- , snsTopicArn = "arn:aws:sns:us-west-2:123456789012:MyTopic"- , snsType = "SubscriptionConfirmation"- , snsSignatureVersion = "1"- , snsSignature =- "Jec/VlsopbiA2fCckj/IwTPjDbSuFkl2hNKL898sQuZcMeKeOLthYs7YlF+xLi+Ip6rG/X08GZKtCqpoiSgKW8D9PI6eHVM2JQa76sFJO5ZdPylrDH+URwBf28gT+1l/VYk4p3VK8RZo+3Wkn87HXwxTq1YoN390o5ncT34zaBDtLx2cUA8+JOnYjItmYjVXDhrEBF6xad/vIY8V2o5xyQOfEWLm71/Tcs3radzNoSj2xlLQyJKPOzV661fG6Xz1vVKfDVC03+Q4Pn67SmU1wWRRT1nDwPPzQlcDAiAGRjB1U/C5iHfLQFF3dKo4azylkrM2ReTCMm9KMyIWqjq5eg=="- , snsSigningCertURL = cert- , snsTypePayload = SubscriptionConfirmation subscription- }+ message =+ "You have chosen to subscribe to the topic arn:aws:sns:us-west-2:123456789012:MyTopic.\nTo confirm the subscription, visit the SubscribeURL included in this message."+ subscription =+ SNSSubscription+ { snsToken = "2336412f37..."+ , snsSubscribeURL =+ "https://sns.us-west-2.amazonaws.com/?Action=ConfirmSubscription&TopicArn=arn:aws:sns:us-west-2:123456789012:MyTopic&Token=2336412f37..."+ }+ x <-+ validateSnsMessage+ $ SNSPayload+ { snsMessage = message+ , snsMessageId = "165545c9-2a5c-472c-8df2-7ff2be2b3b1b"+ , snsTimestamp = "2012-04-26T20:45:04.751Z"+ , snsTopicArn = "arn:aws:sns:us-west-2:123456789012:MyTopic"+ , snsType = "SubscriptionConfirmation"+ , snsSignatureVersion = "1"+ , snsSignature =+ "Jec/VlsopbiA2fCckj/IwTPjDbSuFkl2hNKL898sQuZcMeKeOLthYs7YlF+xLi+Ip6rG/X08GZKtCqpoiSgKW8D9PI6eHVM2JQa76sFJO5ZdPylrDH+URwBf28gT+1l/VYk4p3VK8RZo+3Wkn87HXwxTq1YoN390o5ncT34zaBDtLx2cUA8+JOnYjItmYjVXDhrEBF6xad/vIY8V2o5xyQOfEWLm71/Tcs3radzNoSj2xlLQyJKPOzV661fG6Xz1vVKfDVC03+Q4Pn67SmU1wWRRT1nDwPPzQlcDAiAGRjB1U/C5iHfLQFF3dKo4azylkrM2ReTCMm9KMyIWqjq5eg=="+ , snsSigningCertURL = cert+ , snsTypePayload = SubscriptionConfirmation subscription+ } x `shouldBe` Right (SNSSubscribe subscription) it "successfully validates an SNS unsubscribe" $ do -- pendingWith "need valid subscription payload" let- message- = "You have chosen to subscribe to the topic arn:aws:sns:us-west-2:123456789012:MyTopic.\nTo confirm the subscription, visit the SubscribeURL included in this message."- subscription = SNSSubscription- { snsToken = "2336412f37..."- , snsSubscribeURL =- "https://sns.us-west-2.amazonaws.com/?Action=ConfirmSubscription&TopicArn=arn:aws:sns:us-west-2:123456789012:MyTopic&Token=2336412f37..."- }- x <- validateSnsMessage $ SNSPayload- { snsMessage = message- , snsMessageId = "165545c9-2a5c-472c-8df2-7ff2be2b3b1b"- , snsTimestamp = "2012-04-26T20:45:04.751Z"- , snsTopicArn = "arn:aws:sns:us-west-2:123456789012:MyTopic"- , snsType = "UnsubscribeConfirmation"- , snsSignatureVersion = "1"- , snsSignature =- "fKtmZTE6xvGhbcCTchFPLmuhdoXI7hxWrE9qe1RjeLDecMaZGmqsn4rOrFDsteqot4ItLuJqvV7RtImGXrMa/JNnZdP71lG6FdrKTiGqZNrnxZZYbIuZMAsSQM4E8VaRwbxLXuPQY9IYFP4y9GfsdpDYx0tpbXOxGz/JFVQjTFpHY55BmV6Ec73g0X/eLSEdKfHtWg/gVf6W27ewa40jXvaa78VmcVXbPXIKwzGgSSSe9t6xxVe0kLjKXaDyJTl3rbZJZJgBLInbychWNq1vGHGZQhtCyxjKRfKIWNWDbHdM/fUBGUhuv089CblWpq8g/21HiJ9n+S3VSn0hCXB5hg=="- , snsSigningCertURL = cert- , snsTypePayload = UnsubscribeConfirmation subscription- }+ message =+ "You have chosen to subscribe to the topic arn:aws:sns:us-west-2:123456789012:MyTopic.\nTo confirm the subscription, visit the SubscribeURL included in this message."+ subscription =+ SNSSubscription+ { snsToken = "2336412f37..."+ , snsSubscribeURL =+ "https://sns.us-west-2.amazonaws.com/?Action=ConfirmSubscription&TopicArn=arn:aws:sns:us-west-2:123456789012:MyTopic&Token=2336412f37..."+ }+ x <-+ validateSnsMessage+ $ SNSPayload+ { snsMessage = message+ , snsMessageId = "165545c9-2a5c-472c-8df2-7ff2be2b3b1b"+ , snsTimestamp = "2012-04-26T20:45:04.751Z"+ , snsTopicArn = "arn:aws:sns:us-west-2:123456789012:MyTopic"+ , snsType = "UnsubscribeConfirmation"+ , snsSignatureVersion = "1"+ , snsSignature =+ "fKtmZTE6xvGhbcCTchFPLmuhdoXI7hxWrE9qe1RjeLDecMaZGmqsn4rOrFDsteqot4ItLuJqvV7RtImGXrMa/JNnZdP71lG6FdrKTiGqZNrnxZZYbIuZMAsSQM4E8VaRwbxLXuPQY9IYFP4y9GfsdpDYx0tpbXOxGz/JFVQjTFpHY55BmV6Ec73g0X/eLSEdKfHtWg/gVf6W27ewa40jXvaa78VmcVXbPXIKwzGgSSSe9t6xxVe0kLjKXaDyJTl3rbZJZJgBLInbychWNq1vGHGZQhtCyxjKRfKIWNWDbHdM/fUBGUhuv089CblWpq8g/21HiJ9n+S3VSn0hCXB5hg=="+ , snsSigningCertURL = cert+ , snsTypePayload = UnsubscribeConfirmation subscription+ } x `shouldBe` Right (SNSUnsubscribe subscription) cert :: Text
tests/Amazon/SNS/VerifySpec.hs view
@@ -13,8 +13,8 @@ spec = around_ useCertServer $ do describe "verifySNSMessage" $ do it "successfully validates an SNS notification" $ do- let- payload = [aesonQQ|+ let payload =+ [aesonQQ| { Message: "Some message" , MessageId: "78d4d7a0-a3eb-5c4d-834f-8d5fa9813ab6" , Timestamp: "2022-05-18T14:52:26.952Z"@@ -31,8 +31,8 @@ x `shouldBe` "Some message" it "successfully confirms a subscription" $ do- let- payload = [aesonQQ|+ let payload =+ [aesonQQ| { Message: "Some message" , MessageId: "78d4d7a0-a3eb-5c4d-834f-8d5fa9813ab6" , Timestamp: "2022-05-18T14:52:26.952Z"@@ -48,7 +48,7 @@ |] verifySNSMessage payload- `shouldThrow` (\case- SubscribeMessageResponded -> True- _ -> False+ `shouldThrow` ( \case+ SubscribeMessageResponded -> True+ _ -> False )