From 7fcdbf91538a2661adbf14fd1b1bf91c9c04ef05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eric=20Br=C3=A9chemier?= Date: Thu, 20 Jul 2017 16:21:31 +0200 Subject: [PATCH] strip newline before Base64 decoding (#916) --- CHANGELOG.md | 1 + circle.yml | 1 + main/Main.hs | 4 +- test/io-tests.sh | 111 ++++++++++++++++++ .../configs/base64-secret-from-file.config | 10 ++ test/io-tests/configs/secret-from-file.config | 10 ++ test/io-tests/secrets/ascii.b64 | 1 + test/io-tests/secrets/ascii.jwt | 1 + test/io-tests/secrets/ascii.noeol | 2 + test/io-tests/secrets/ascii.txt | 2 + test/io-tests/secrets/binary.b64 | 1 + test/io-tests/secrets/binary.eol | 1 + test/io-tests/secrets/binary.jwt | 1 + test/io-tests/secrets/binary.noeol | 1 + test/io-tests/secrets/utf8.b64 | 1 + test/io-tests/secrets/utf8.jwt | 1 + test/io-tests/secrets/utf8.noeol | 1 + test/io-tests/secrets/utf8.txt | 1 + test/io-tests/secrets/word.b64 | 1 + test/io-tests/secrets/word.jwt | 1 + test/io-tests/secrets/word.noeol | 1 + test/io-tests/secrets/word.txt | 1 + 22 files changed, 153 insertions(+), 2 deletions(-) create mode 100755 test/io-tests.sh create mode 100644 test/io-tests/configs/base64-secret-from-file.config create mode 100644 test/io-tests/configs/secret-from-file.config create mode 100644 test/io-tests/secrets/ascii.b64 create mode 100644 test/io-tests/secrets/ascii.jwt create mode 100644 test/io-tests/secrets/ascii.noeol create mode 100644 test/io-tests/secrets/ascii.txt create mode 100644 test/io-tests/secrets/binary.b64 create mode 100644 test/io-tests/secrets/binary.eol create mode 100644 test/io-tests/secrets/binary.jwt create mode 100644 test/io-tests/secrets/binary.noeol create mode 100644 test/io-tests/secrets/utf8.b64 create mode 100644 test/io-tests/secrets/utf8.jwt create mode 100644 test/io-tests/secrets/utf8.noeol create mode 100644 test/io-tests/secrets/utf8.txt create mode 100644 test/io-tests/secrets/word.b64 create mode 100644 test/io-tests/secrets/word.jwt create mode 100644 test/io-tests/secrets/word.noeol create mode 100644 test/io-tests/secrets/word.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 041bceb9a..773174b3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Fixed +- #877, Base64 secret read from a file ending with a newline - @eric-brechemier - #896, Boolean env var interpolation in config file - @begriffs ## [0.4.2.0] - 2017-06-11 diff --git a/circle.yml b/circle.yml index a97ce0893..58dbda86c 100644 --- a/circle.yml +++ b/circle.yml @@ -16,6 +16,7 @@ dependencies: test: override: - POSTGREST_TEST_CONNECTION=$(test/create_test_db "postgres://ubuntu@localhost" postgrest_test) stack test + - test/io-tests.sh - git ls-files | grep '\.l\?hs$' | xargs stack exec -- hlint -X QuasiQuotes -X NoPatternSynonyms "$@" - stack exec -- cabal update - stack exec --no-ghc-package-path -- cabal install --only-d --dry-run diff --git a/main/Main.hs b/main/Main.hs index 7cd881c03..4493b40a8 100644 --- a/main/Main.hs +++ b/main/Main.hs @@ -22,7 +22,7 @@ import Data.ByteString.Base64 (decode) import Data.IORef (IORef, atomicWriteIORef, newIORef, readIORef) import Data.String (IsString (..)) -import Data.Text (pack, replace, stripPrefix) +import Data.Text (pack, replace, stripPrefix, strip) import Data.Text.Encoding (decodeUtf8, encodeUtf8) import Data.Text.IO (hPutStrLn, readFile) import Data.Time.Clock.POSIX (getPOSIXTime) @@ -278,7 +278,7 @@ loadSecretFile conf = extractAndTransform mSecret transformString :: Bool -> Text -> IO ByteString transformString False t = return . encodeUtf8 $ t transformString True t = - case decode (encodeUtf8 $ replaceUrlChars t) of + case decode (encodeUtf8 $ strip $ replaceUrlChars t) of Left errMsg -> panic $ pack errMsg Right bs -> return bs setSecret bs = conf {configJwtSecret = Just bs} diff --git a/test/io-tests.sh b/test/io-tests.sh new file mode 100755 index 000000000..06d802933 --- /dev/null +++ b/test/io-tests.sh @@ -0,0 +1,111 @@ +#!/bin/sh +# Run unit tests for Input/Ouput of PostgREST seen as a black box +# with test output in Test Anything Protocol format. +# +# References: +# [1] Test Anything Protocol +# https://testanything.org/ +# +# [2] TAP Specification +# https://testanything.org/tap-specification.html +# +# [3] List of TCP and UDP port numbers +# https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers +# +cd "$(dirname "$0")" +cd io-tests + +# Port for Test PostgREST Server (must match config) +pgrPort=49421 # in range 49152–65535: for private or temporary use + +# TAP utilities +currentTest=1 +failedTests=0 +bailOut(){ echo "Bail out! $1"; exit 1; } +result(){ echo "$1 $currentTest $2"; currentTest=$(( $currentTest + 1 )); } +todo(){ result 'ok' "# TODO: $*"; } +skip(){ result 'ok' "# SKIP: $*"; } +ok(){ result 'ok' "- $1"; } +ko(){ result 'not ok' "- $1"; failedTests=$(( $failedTests + 1 )); } +comment(){ echo "# $1"; } + +# Utilities to start/stop test PostgREST server running in the background +pgrStart(){ stack exec -- postgrest "$1" >/dev/null & pgrPID="$!"; } +pgrStartRead(){ stack exec -- postgrest "$1" >/dev/null < "$2" & pgrPID="$!"; } +pgrStarted(){ kill -0 "$pgrPID" 2>/dev/null; } +pgrStop(){ kill "$pgrPID" 2>/dev/null; } +pgrStopAll(){ pkill -f "$(stack path --local-install-root)/bin/postgrest"; } + +# Utilities to send HTTP requests to the PostgREST server +rootStatus(){ + curl -s -o /dev/null -w '%{http_code}' "http://localhost:$pgrPort/" +} +authorsStatus(){ + curl -s -o /dev/null -w '%{http_code}' \ + -H "Authorization: Bearer $( cat "$1" )" \ + "http://localhost:$pgrPort/authors_only" +} + +# Start and End of Unit Tests +setUp(){ pgrStopAll; } +cleanUp(){ pgrStopAll; } + +# Unit Test Templates +readSecretFromFile(){ + case "$1" in + *.b64) + pgrConfig="base64-secret-from-file.config";; + *) + pgrConfig="secret-from-file.config";; + esac + pgrStartRead "./configs/$pgrConfig" "./secrets/$1" + while pgrStarted && test "$( rootStatus )" -ne 200 + do + # wait for the server to start + sleep 0.1 \ + || sleep 1 # fallback: subsecond sleep is not standard and may fail + done + if pgrStarted + then + authorsJwt="./secrets/${1%.*}.jwt" + httpStatus="$( authorsStatus "$authorsJwt" )" + if test "$httpStatus" -eq 200 + then + ok "authentication with $2 secret read from a file" + else + ko "failed to authenticate using JWT for $2 secret: $httpStatus" + fi + else + ko "failed to read $2 secret from a file" + fi + pgrStop +} + +# PRE: curl must be available +test -n "$(command -v curl)" || bailOut 'curl is not available' + +# PRE: postgres must be running +psql -l 1>/dev/null 2>/dev/null || bailOut 'postgres is not running' + +setUp + +totalTests=12 +echo "1..$totalTests" + +readSecretFromFile word.noeol 'simple (no EOL)' +skip readSecretFromFile word.txt 'simple' +readSecretFromFile ascii.noeol 'ASCII (no EOL)' +skip readSecretFromFile ascii.txt 'ASCII' +readSecretFromFile utf8.noeol 'UTF-8 (no EOL)' +skip readSecretFromFile utf8.txt 'UTF-8' +skip readSecretFromFile binary.noeol 'binary' +skip readSecretFromFile binary.eol 'binary (+EOL)' + +readSecretFromFile word.b64 'Base64 (simple)' +readSecretFromFile ascii.b64 'Base64 (ASCII)' +readSecretFromFile utf8.b64 'Base64 (UTF-8)' +readSecretFromFile binary.b64 'Base64 (binary)' + +cleanUp + +exit $failedTests diff --git a/test/io-tests/configs/base64-secret-from-file.config b/test/io-tests/configs/base64-secret-from-file.config new file mode 100644 index 000000000..98e116b76 --- /dev/null +++ b/test/io-tests/configs/base64-secret-from-file.config @@ -0,0 +1,10 @@ +db-uri = "postgres:///postgrest_test" +db-schema = "test" +db-anon-role = "postgrest_test_anonymous" +db-pool = 1 +server-host = "*4" +server-port = 49421 + +# Read secret from a file: /dev/stdin (alias for standard input) +jwt-secret = "@/dev/stdin" +secret-is-base64 = true diff --git a/test/io-tests/configs/secret-from-file.config b/test/io-tests/configs/secret-from-file.config new file mode 100644 index 000000000..26a71112f --- /dev/null +++ b/test/io-tests/configs/secret-from-file.config @@ -0,0 +1,10 @@ +db-uri = "postgres:///postgrest_test" +db-schema = "test" +db-anon-role = "postgrest_test_anonymous" +db-pool = 1 +server-host = "*4" +server-port = 49421 + +# Read secret from a file: /dev/stdin (alias for standard input) +jwt-secret = "@/dev/stdin" +secret-is-base64 = false diff --git a/test/io-tests/secrets/ascii.b64 b/test/io-tests/secrets/ascii.b64 new file mode 100644 index 000000000..3cae5a981 --- /dev/null +++ b/test/io-tests/secrets/ascii.b64 @@ -0,0 +1 @@ +QSBCIEMKSXQncyBlYXN5IGFzLCAxIDIgMw== diff --git a/test/io-tests/secrets/ascii.jwt b/test/io-tests/secrets/ascii.jwt new file mode 100644 index 000000000..6def4713b --- /dev/null +++ b/test/io-tests/secrets/ascii.jwt @@ -0,0 +1 @@ +eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIn0.MsR2A5HkhQdBsuQhXH8TvUdlvezBm5JEu4SOmHj34KI \ No newline at end of file diff --git a/test/io-tests/secrets/ascii.noeol b/test/io-tests/secrets/ascii.noeol new file mode 100644 index 000000000..ecdc0b6f3 --- /dev/null +++ b/test/io-tests/secrets/ascii.noeol @@ -0,0 +1,2 @@ +A B C +It's easy as, 1 2 3 \ No newline at end of file diff --git a/test/io-tests/secrets/ascii.txt b/test/io-tests/secrets/ascii.txt new file mode 100644 index 000000000..cb84d70a2 --- /dev/null +++ b/test/io-tests/secrets/ascii.txt @@ -0,0 +1,2 @@ +A B C +It's easy as, 1 2 3 diff --git a/test/io-tests/secrets/binary.b64 b/test/io-tests/secrets/binary.b64 new file mode 100644 index 000000000..c8f7d998e --- /dev/null +++ b/test/io-tests/secrets/binary.b64 @@ -0,0 +1 @@ +i6aWVaZ4Zt8= diff --git a/test/io-tests/secrets/binary.eol b/test/io-tests/secrets/binary.eol new file mode 100644 index 000000000..4996d03e9 --- /dev/null +++ b/test/io-tests/secrets/binary.eol @@ -0,0 +1 @@ +Uxf diff --git a/test/io-tests/secrets/binary.jwt b/test/io-tests/secrets/binary.jwt new file mode 100644 index 000000000..3c85cd7a7 --- /dev/null +++ b/test/io-tests/secrets/binary.jwt @@ -0,0 +1 @@ +eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIn0.ySt_r_BN596NjZGcUAnlMAflDARDjrsR2c-fkOWlbFs \ No newline at end of file diff --git a/test/io-tests/secrets/binary.noeol b/test/io-tests/secrets/binary.noeol new file mode 100644 index 000000000..b967aa167 --- /dev/null +++ b/test/io-tests/secrets/binary.noeol @@ -0,0 +1 @@ +Uxf \ No newline at end of file diff --git a/test/io-tests/secrets/utf8.b64 b/test/io-tests/secrets/utf8.b64 new file mode 100644 index 000000000..dc727e0e8 --- /dev/null +++ b/test/io-tests/secrets/utf8.b64 @@ -0,0 +1 @@ +4pqg77iPIOKaoO+4jiBVbmljb2RlIOKYoO+4jyDimKA= diff --git a/test/io-tests/secrets/utf8.jwt b/test/io-tests/secrets/utf8.jwt new file mode 100644 index 000000000..edda5fb2d --- /dev/null +++ b/test/io-tests/secrets/utf8.jwt @@ -0,0 +1 @@ +eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIn0.VFfaoJohnDeJMA-awM6Y7blbOjXDNwmtx48Z-0bIKrE \ No newline at end of file diff --git a/test/io-tests/secrets/utf8.noeol b/test/io-tests/secrets/utf8.noeol new file mode 100644 index 000000000..fa85f89d9 --- /dev/null +++ b/test/io-tests/secrets/utf8.noeol @@ -0,0 +1 @@ +⚠️ ⚠︎ Unicode ☠️ ☠ \ No newline at end of file diff --git a/test/io-tests/secrets/utf8.txt b/test/io-tests/secrets/utf8.txt new file mode 100644 index 000000000..0661df314 --- /dev/null +++ b/test/io-tests/secrets/utf8.txt @@ -0,0 +1 @@ +⚠️ ⚠︎ Unicode ☠️ ☠ diff --git a/test/io-tests/secrets/word.b64 b/test/io-tests/secrets/word.b64 new file mode 100644 index 000000000..49b69ed61 --- /dev/null +++ b/test/io-tests/secrets/word.b64 @@ -0,0 +1 @@ +QUJDMTIz diff --git a/test/io-tests/secrets/word.jwt b/test/io-tests/secrets/word.jwt new file mode 100644 index 000000000..3dfcac028 --- /dev/null +++ b/test/io-tests/secrets/word.jwt @@ -0,0 +1 @@ +eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIn0.yERzinJhzJ5XYKZuxVroqsAwGXMtCxntfm8HVxc1amI \ No newline at end of file diff --git a/test/io-tests/secrets/word.noeol b/test/io-tests/secrets/word.noeol new file mode 100644 index 000000000..8edce441f --- /dev/null +++ b/test/io-tests/secrets/word.noeol @@ -0,0 +1 @@ +ABC123 \ No newline at end of file diff --git a/test/io-tests/secrets/word.txt b/test/io-tests/secrets/word.txt new file mode 100644 index 000000000..92dd64739 --- /dev/null +++ b/test/io-tests/secrets/word.txt @@ -0,0 +1 @@ +ABC123