From 92774310cf91f0ff4d7ddff629824814e06151e3 Mon Sep 17 00:00:00 2001 From: Adam Pritchard Date: Tue, 14 May 2019 16:05:46 -0400 Subject: [PATCH] Change ModifyLandingPage to base64-encode It also uses #! instead of # (to avoid anchor-jump problems). And puts "dev" and "debug" flags in the params when indicated. --- base64.cpp | 6 ++++++ base64.hpp | 2 ++ base64_test.cpp | 8 ++++++++ psicash.cpp | 20 ++++++++++++++++---- psicash.hpp | 1 + psicash_test.cpp | 32 +++++++++++++++----------------- 6 files changed, 48 insertions(+), 21 deletions(-) diff --git a/base64.cpp b/base64.cpp index 9162bc6..98059de 100644 --- a/base64.cpp +++ b/base64.cpp @@ -130,4 +130,10 @@ std::vector B64Decode(const std::string& b64encoded) { return ret; } +std::string TrimPadding(const std::string& s) { + auto trimmed = s; + trimmed.erase(s.find_last_not_of("=") + 1); + return trimmed; +} + } // namespace base64 \ No newline at end of file diff --git a/base64.hpp b/base64.hpp index 531063a..f456642 100644 --- a/base64.hpp +++ b/base64.hpp @@ -33,6 +33,8 @@ std::string B64Encode(const BYTE* buf, unsigned int bufLen); std::vector B64Decode(const std::string& b64encoded); +std::string TrimPadding(const std::string& s); + } // namespace base64 #endif //PSICASHLIB_BASE64_H diff --git a/base64_test.cpp b/base64_test.cpp index ac56d93..55446d3 100644 --- a/base64_test.cpp +++ b/base64_test.cpp @@ -80,3 +80,11 @@ TEST(TestBase64, Decode) v = B64Decode("Zm9vYg"); ASSERT_EQ(v, want); } + +TEST(TestBase64, TrimPadding) +{ + ASSERT_EQ(TrimPadding("abc"), "abc"); + ASSERT_EQ(TrimPadding("abc="), "abc"); + ASSERT_EQ(TrimPadding("abc=="), "abc"); + ASSERT_EQ(TrimPadding("abc==="), "abc"); +} diff --git a/psicash.cpp b/psicash.cpp index 079fb1a..dface28 100644 --- a/psicash.cpp +++ b/psicash.cpp @@ -88,6 +88,7 @@ PsiCash::~PsiCash() { Error PsiCash::Init(const char* user_agent, const char* file_store_root, MakeHTTPRequestFn make_http_request_fn, bool test) { + test_ = test; if (test) { server_scheme_ = dev::kAPIServerScheme; server_hostname_ = dev::kAPIServerHostname; @@ -288,19 +289,27 @@ Result PsiCash::ModifyLandingPage(const string& url_string) const { psicash_data["tokens"] = auth_tokens[kEarnerTokenType]; } + if (test_) { + psicash_data["dev"] = 1; + psicash_data["debug"] = 1; + } + // Get the metadata (sponsor ID, etc.) psicash_data["metadata"] = user_data_->GetRequestMetadata(); string json_data; try { - // The third argument is "ensure ASCII" - json_data = psicash_data.dump(-1, ' ', true); + json_data = psicash_data.dump(-1, ' ', // disable indent + true); // ensure ASCII } catch (json::exception& e) { return MakeCriticalError( utils::Stringer("json dump failed: ", e.what(), "; id:", e.id).c_str()); } + // Base64-encode the JSON + auto b64 = base64::TrimPadding(base64::B64Encode(json_data)); + // Our preference is to put the our data into the URL's fragment/hash/anchor, // because we'd prefer the data not be sent to the server. // But if there already is a fragment value then we'll put our data into the query parameters. @@ -308,12 +317,15 @@ Result PsiCash::ModifyLandingPage(const string& url_string) const { // for the page than adding a query parameter that will be ignored.) if (url.fragment_.empty()) { - url.fragment_ = kLandingPageParamKey + "=" + URL::Encode(json_data, true); + // When setting in the fragment, we use "#!psicash=etc". The ! prevents the + // fragment from accidentally functioning as a jump-to anchor on a landing page + // (where we don't control element IDs, etc.). + url.fragment_ = "!" + kLandingPageParamKey + "=" + b64; } else { if (!url.query_.empty()) { url.query_ += "&"; } - url.query_ += kLandingPageParamKey + "=" + URL::Encode(json_data, true); + url.query_ += kLandingPageParamKey + "=" + b64; } return url.ToString(); diff --git a/psicash.hpp b/psicash.hpp index 802ac73..83ea62f 100644 --- a/psicash.hpp +++ b/psicash.hpp @@ -368,6 +368,7 @@ class PsiCash { RefreshState(const std::vector& purchase_classes, bool allow_recursion); protected: + bool test_; std::string user_agent_; std::string server_scheme_; std::string server_hostname_; diff --git a/psicash_test.cpp b/psicash_test.cpp index 5c107d9..59d3893 100644 --- a/psicash_test.cpp +++ b/psicash_test.cpp @@ -643,7 +643,8 @@ TEST_F(TestPsiCash, RemovePurchases) { TEST_F(TestPsiCash, ModifyLandingPage) { PsiCashTester pc; - auto err = pc.Init(user_agent_, GetTempDir().c_str(), nullptr, true); + // Pass false for test so that we don't get "dev" and "debug" in all the params + auto err = pc.Init(user_agent_, GetTempDir().c_str(), nullptr, false); ASSERT_FALSE(err); const string key_part = "psicash="; @@ -660,7 +661,7 @@ TEST_F(TestPsiCash, ModifyLandingPage) { ASSERT_EQ(url_out.scheme_host_path_, url_in.scheme_host_path_); ASSERT_EQ(url_out.query_, url_in.query_); ASSERT_EQ(url_out.fragment_, - key_part + URL::Encode("{\"metadata\":{},\"tokens\":null,\"v\":1}", true)); + "!"s + key_part + base64::TrimPadding(base64::B64Encode("{\"metadata\":{},\"tokens\":null,\"v\":1}"))); url_in = {"https://asdf.sadf.gf", "gfaf=asdf", ""}; res = pc.ModifyLandingPage(url_in.ToString()); @@ -669,7 +670,7 @@ TEST_F(TestPsiCash, ModifyLandingPage) { ASSERT_EQ(url_out.scheme_host_path_, url_in.scheme_host_path_); ASSERT_EQ(url_out.query_, url_in.query_); ASSERT_EQ(url_out.fragment_, - key_part + URL::Encode("{\"metadata\":{},\"tokens\":null,\"v\":1}", true)); + "!"s + key_part + base64::TrimPadding(base64::B64Encode("{\"metadata\":{},\"tokens\":null,\"v\":1}"))); url_in = {"https://asdf.sadf.gf/asdfilj/adf", "gfaf=asdf", ""}; res = pc.ModifyLandingPage(url_in.ToString()); @@ -678,7 +679,7 @@ TEST_F(TestPsiCash, ModifyLandingPage) { ASSERT_EQ(url_out.scheme_host_path_, url_in.scheme_host_path_); ASSERT_EQ(url_out.query_, url_in.query_); ASSERT_EQ(url_out.fragment_, - key_part + URL::Encode("{\"metadata\":{},\"tokens\":null,\"v\":1}", true)); + "!"s + key_part + base64::TrimPadding(base64::B64Encode("{\"metadata\":{},\"tokens\":null,\"v\":1}"))); url_in = {"https://asdf.sadf.gf/asdfilj/adf.html", "gfaf=asdf", ""}; res = pc.ModifyLandingPage(url_in.ToString()); @@ -687,7 +688,7 @@ TEST_F(TestPsiCash, ModifyLandingPage) { ASSERT_EQ(url_out.scheme_host_path_, url_in.scheme_host_path_); ASSERT_EQ(url_out.query_, url_in.query_); ASSERT_EQ(url_out.fragment_, - key_part + URL::Encode("{\"metadata\":{},\"tokens\":null,\"v\":1}", true)); + "!"s + key_part + base64::TrimPadding(base64::B64Encode("{\"metadata\":{},\"tokens\":null,\"v\":1}"))); url_in = {"https://asdf.sadf.gf/asdfilj/adf.html", "", "regffd"}; res = pc.ModifyLandingPage(url_in.ToString()); @@ -695,7 +696,7 @@ TEST_F(TestPsiCash, ModifyLandingPage) { url_out.Parse(*res); ASSERT_EQ(url_out.scheme_host_path_, url_in.scheme_host_path_); ASSERT_EQ(url_out.query_, - key_part + URL::Encode("{\"metadata\":{},\"tokens\":null,\"v\":1}", true)); + key_part + base64::TrimPadding(base64::B64Encode("{\"metadata\":{},\"tokens\":null,\"v\":1}"))); ASSERT_EQ(url_out.fragment_, url_in.fragment_); url_in = {"https://asdf.sadf.gf/asdfilj/adf.html", "adfg=asdf&vfjnk=fadjn", "regffd"}; @@ -705,7 +706,7 @@ TEST_F(TestPsiCash, ModifyLandingPage) { ASSERT_EQ(url_out.scheme_host_path_, url_in.scheme_host_path_); ASSERT_EQ(url_out.query_, url_in.query_ + "&" + key_part + - URL::Encode("{\"metadata\":{},\"tokens\":null,\"v\":1}", true)); + base64::TrimPadding(base64::B64Encode("{\"metadata\":{},\"tokens\":null,\"v\":1}"))); ASSERT_EQ(url_out.fragment_, url_in.fragment_); // @@ -720,9 +721,8 @@ TEST_F(TestPsiCash, ModifyLandingPage) { url_out.Parse(*res); ASSERT_EQ(url_out.scheme_host_path_, url_in.scheme_host_path_); ASSERT_EQ(url_out.query_, url_in.query_); - ASSERT_EQ(url_out.fragment_, key_part + URL::Encode("{\"metadata\":{\"k\":\"v\"},\"tokens\":" - "null,\"v\":1}", - true)); + ASSERT_EQ(url_out.fragment_, + "!"s + key_part + base64::TrimPadding(base64::B64Encode("{\"metadata\":{\"k\":\"v\"},\"tokens\":null,\"v\":1}"))); // With tokens @@ -737,9 +737,8 @@ TEST_F(TestPsiCash, ModifyLandingPage) { url_out.Parse(*res); ASSERT_EQ(url_out.scheme_host_path_, url_in.scheme_host_path_); ASSERT_EQ(url_out.query_, url_in.query_); - ASSERT_EQ(url_out.fragment_, key_part + URL::Encode("{\"metadata\":{\"k\":\"v\"},\"tokens\":" - "\"kEarnerTokenType\",\"v\":1}", - true)); + ASSERT_EQ(url_out.fragment_, + "!"s + key_part + base64::TrimPadding(base64::B64Encode("{\"metadata\":{\"k\":\"v\"},\"tokens\":\"kEarnerTokenType\",\"v\":1}"))); // Some tokens, but no earner token (different code path) auth_tokens = {{kSpenderTokenType, "kSpenderTokenType"}, @@ -752,9 +751,8 @@ TEST_F(TestPsiCash, ModifyLandingPage) { url_out.Parse(*res); ASSERT_EQ(url_out.scheme_host_path_, url_in.scheme_host_path_); ASSERT_EQ(url_out.query_, url_in.query_); - ASSERT_EQ(url_out.fragment_, key_part + URL::Encode("{\"metadata\":{\"k\":\"v\"},\"tokens\":" - "null,\"v\":1}", - true)); + ASSERT_EQ(url_out.fragment_, + "!"s + key_part + base64::TrimPadding(base64::B64Encode("{\"metadata\":{\"k\":\"v\"},\"tokens\":null,\"v\":1}"))); // // Errors @@ -1221,8 +1219,8 @@ TEST_F(TestPsiCash, NewExpiringPurchase) { ASSERT_TRUE(purchase_result->purchase->server_time_expiry); ASSERT_TRUE(purchase_result->purchase->local_time_expiry); auto local_now = datetime::DateTime::Now(); - ASSERT_NEAR(purchase_result->purchase->server_time_expiry->MillisSinceEpoch(), local_now.MillisSinceEpoch(), 5000); ASSERT_NEAR(purchase_result->purchase->local_time_expiry->MillisSinceEpoch(), local_now.MillisSinceEpoch(), 5000); + ASSERT_NEAR(purchase_result->purchase->server_time_expiry->MillisSinceEpoch(), local_now.MillisSinceEpoch(), 5000) << "Try resyncing your local clock"; // Check UserData -- purchase should still be valid ASSERT_EQ(pc.user_data().GetLastTransactionID(), purchase_result->purchase->id); auto purchases = pc.GetPurchases();