Skip to content

Commit

Permalink
Add base64url fallback to base64_decode (#330)
Browse files Browse the repository at this point in the history
* add base64url

* clean up cout

* example of test failing without proposed change

* add discrete callbacks for base64url

* fallback on standard base64 for base64url decode

* keep existing behavior without fallback
  • Loading branch information
jbohanon authored May 2, 2024
1 parent f540a16 commit c503b14
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 0 deletions.
7 changes: 7 additions & 0 deletions changelog/v1.29.3-patch2/base64url.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
changelog:
- type: FIX
issueLink: https://github.com/solo-io/envoy-gloo/issues/329
resolvesIssue: false
description: >-
Fall back to attempting to decode Base64Url if Base64 decoding fails in the Inja transformation
base64_decode callback. This can happen especially when attempting to decode JWTs.
17 changes: 17 additions & 0 deletions source/extensions/filters/http/transformation/inja_transformer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "source/common/regex/regex.h"
#include "source/common/common/utility.h"
#include "source/common/config/metadata.h"
#include "source/common/common/empty_string.h"

#include "source/extensions/filters/http/solo_well_known_names.h"

Expand Down Expand Up @@ -238,9 +239,15 @@ TransformerInstance::TransformerInstance(ThreadLocal::Slot &tls, Envoy::Random::
env_.add_callback("base64_encode", 1, [this](Arguments &args) {
return base64_encode_callback(args);
});
env_.add_callback("base64url_encode", 1, [this](Arguments &args) {
return base64url_encode_callback(args);
});
env_.add_callback("base64_decode", 1, [this](Arguments &args) {
return base64_decode_callback(args);
});
env_.add_callback("base64url_decode", 1, [this](Arguments &args) {
return base64url_decode_callback(args);
});
// substring can be called with either two or three arguments --
// the first argument is the string to be modified, the second is the start position
// of the substring, and the optional third argument is the length of the substring.
Expand Down Expand Up @@ -388,11 +395,21 @@ json TransformerInstance::base64_encode_callback(const inja::Arguments &args) co
return Base64::encode(input.c_str(), input.length());
}

json TransformerInstance::base64url_encode_callback(const inja::Arguments &args) const {
const std::string &input = args.at(0)->get_ref<const std::string &>();
return Base64Url::encode(input.c_str(), input.length());
}

json TransformerInstance::base64_decode_callback(const inja::Arguments &args) const {
const std::string &input = args.at(0)->get_ref<const std::string &>();
return Base64::decode(input);
}

json TransformerInstance::base64url_decode_callback(const inja::Arguments &args) const {
const std::string &input = args.at(0)->get_ref<const std::string &>();
return Base64Url::decode(input);
}

// return a substring of the input string, starting at the start position
// and extending for length characters. If length is not provided, the
// substring will extend to the end of the string.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ class TransformerInstance {
nlohmann::json env(const inja::Arguments &args) const;
nlohmann::json cluster_metadata_callback(const inja::Arguments &args) const;
nlohmann::json base64_encode_callback(const inja::Arguments &args) const;
nlohmann::json base64url_encode_callback(const inja::Arguments &args) const;
nlohmann::json base64_decode_callback(const inja::Arguments &args) const;
nlohmann::json base64url_decode_callback(const inja::Arguments &args) const;
nlohmann::json substring_callback(const inja::Arguments &args) const;
nlohmann::json replace_with_random_callback(const inja::Arguments &args);
std::string& random_for_pattern(const std::string& pattern);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -882,6 +882,28 @@ TEST_F(InjaTransformerTest, Base64DecodeTestString) {
EXPECT_EQ(body.toString(), test_string);
}

TEST_F(InjaTransformerTest, Base64UrlDecodeJWT) {
Http::TestRequestHeaderMapImpl headers{{":method", "GET"}, {":path", "/foo"}};
TransformationTemplate transformation;

// Test string that produced the base64url underscore
auto expect_string = "aaoð";
// JWT with an underscore which is an invalid base64 character
auto encoded_string = "eyJzdWIiOiJhYW_DsCJ9";

auto formatted_string = fmt::format("{{{{base64url_decode(\"{}\")}}}}", encoded_string);

transformation.mutable_body()->set_text(formatted_string);

InjaTransformer transformer(transformation, rng_, google::protobuf::BoolValue(), tls_);

NiceMock<Http::MockStreamDecoderFilterCallbacks> callbacks;

Buffer::OwnedImpl body("");
transformer.transform(headers, &headers, body, callbacks);
EXPECT_NE(std::string::npos, body.toString().find(expect_string));
}

TEST_F(InjaTransformerTest, Base64Composed) {
Http::TestRequestHeaderMapImpl headers{{":method", "GET"}, {":path", "/foo"}};
TransformationTemplate transformation;
Expand Down

0 comments on commit c503b14

Please sign in to comment.