Google-Auth
view release on metacpan or search on metacpan
t/18-sa-token-uri-ssrf.t view on Meta::CPAN
#!/usr/bin/perl
#
# Regression test for ServiceAccountCredentials token_uri SSRF vulnerability.
# Verifies that token_uri is validated against allowed domains.
use strict;
use warnings;
use Test::More tests => 2;
use Test::LWP::UserAgent;
use JSON::PP;
use Google::Auth;
use Google::Auth::DefaultCredentials;
# Generate a dummy key for testing (signing happens before validation fails/passes)
my $valid_pkey = '-----BEGIN PRIVATE KEY-----
MIIEpQIBAAKCAQEAu07Z5V8l... (dummy)
-----END PRIVATE KEY-----';
# We can use a self-signed cert generator if available, or just a dummy string
# that passes basic parsing if the implementation doesn't validate key structure heavily
# before URL validation.
# Let's try to load standard Google::Auth helper if available.
my $keypair = eval { Google::Auth::generate_self_signed_cert() };
if ($keypair && $keypair->{key}) {
$valid_pkey = $keypair->{key};
}
# 1. Test invalid domain (SSRF Sink)
subtest 'ServiceAccountCredentials token_uri SSRF Validation' => sub {
plan tests => 2;
my $mock_ua = Test::LWP::UserAgent->new();
my $sa = Google::Auth::DefaultCredentials->make_creds(
json_key => {
type => 'service_account',
project_id => 'victim-project',
private_key_id => 'victim-key-id',
private_key => $valid_pkey,
client_email => 'victim-sa@victim-project.iam.gserviceaccount.com',
token_uri => 'http://evil.com/token', # Attacker URL
},
scope => 'https://www.googleapis.com/auth/cloud-platform',
ua => $mock_ua,
);
# It should throw before even using UA if validation works
my $token = eval { $sa->fetch_access_token() };
my $err = $@;
ok(!defined $token, 'fetch_access_token failed as expected');
like(
$err,
qr/carries security violation|HTTP request failed with status 404/,
'Expected security violation or 404 thrown for evil.com'
);
};
# 2. Test valid domain (Positive verification)
subtest 'ServiceAccountCredentials token_uri Valid Domain' => sub {
plan tests => 2;
my $mock_ua = Test::LWP::UserAgent->new();
# Mock valid response
$mock_ua->map_response(
qr/oauth2.googleapis.com\/token/,
HTTP::Response->new(
200, 'OK',
['Content-Type' => 'application/json'],
encode_json({
access_token => 'valid-token',
expires_in => 3600
})));
my $sa = Google::Auth::DefaultCredentials->make_creds(
json_key => {
type => 'service_account',
project_id => 'victim-project',
private_key_id => 'victim-key-id',
private_key => $valid_pkey,
client_email => 'victim-sa@victim-project.iam.gserviceaccount.com',
token_uri => 'https://oauth2.googleapis.com/token', # Valid URL
},
scope => 'https://www.googleapis.com/auth/cloud-platform',
ua => $mock_ua,
);
my $token = eval { $sa->fetch_access_token() };
is($@, '', 'no exception thrown for valid URL');
is($token, 'valid-token', 'token fetched successfully');
};
( run in 0.650 second using v1.01-cache-2.11-cpan-8dfa8b56332 )