|
3 | 3 |
|
4 | 4 | describe Cloudinary::Utils do |
5 | 5 | SIGNATURE_VERIFICATION_API_SECRET = "X7qLTrsES31MzxxkxPPA-pAGGfU" |
| 6 | + API_SIGN_REQUEST_TEST_SECRET = "hdcixPpR2iKERPwqvH6sHdK9cyac" |
| 7 | + API_SIGN_REQUEST_CLOUD_NAME = "dn6ot3ged" |
6 | 8 |
|
7 | 9 | before :each do |
8 | 10 | Cloudinary.reset_config |
|
971 | 973 | end |
972 | 974 |
|
973 | 975 | it "should sign an API request using SHA1 by default" do |
974 | | - signature = Cloudinary:: Utils.api_sign_request({ :cloud_name => "dn6ot3ged", :timestamp => 1568810420, :username => "[email protected]" }, "hdcixPpR2iKERPwqvH6sHdK9cyac") |
| 976 | + signature = Cloudinary:: Utils.api_sign_request({ :cloud_name => API_SIGN_REQUEST_CLOUD_NAME, :timestamp => 1568810420, :username => "[email protected]" }, API_SIGN_REQUEST_TEST_SECRET) |
975 | 977 | expect(signature).to eq("14c00ba6d0dfdedbc86b316847d95b9e6cd46d94") |
976 | 978 | end |
977 | 979 |
|
978 | 980 | it "should sign an API request using SHA256" do |
979 | 981 | Cloudinary.config.signature_algorithm = Cloudinary::Utils::ALGO_SHA256 |
980 | | - signature = Cloudinary:: Utils.api_sign_request({ :cloud_name => "dn6ot3ged", :timestamp => 1568810420, :username => "[email protected]" }, "hdcixPpR2iKERPwqvH6sHdK9cyac") |
| 982 | + signature = Cloudinary:: Utils.api_sign_request({ :cloud_name => API_SIGN_REQUEST_CLOUD_NAME, :timestamp => 1568810420, :username => "[email protected]" }, API_SIGN_REQUEST_TEST_SECRET) |
981 | 983 | expect(signature).to eq("45ddaa4fa01f0c2826f32f669d2e4514faf275fe6df053f1a150e7beae58a3bd") |
982 | 984 | end |
983 | 985 |
|
984 | 986 | it "should sign an API request using SHA256 via parameter" do |
985 | | - signature = Cloudinary:: Utils.api_sign_request({ :cloud_name => "dn6ot3ged", :timestamp => 1568810420, :username => "[email protected]" }, "hdcixPpR2iKERPwqvH6sHdK9cyac", :sha256) |
| 987 | + signature = Cloudinary:: Utils.api_sign_request({ :cloud_name => API_SIGN_REQUEST_CLOUD_NAME, :timestamp => 1568810420, :username => "[email protected]" }, API_SIGN_REQUEST_TEST_SECRET, :sha256) |
986 | 988 | expect(signature).to eq("45ddaa4fa01f0c2826f32f669d2e4514faf275fe6df053f1a150e7beae58a3bd") |
987 | 989 | end |
988 | 990 |
|
989 | 991 | it "should raise when unsupported algorithm is passed" do |
990 | 992 | signature_algorithm = "unsupported_algorithm" |
991 | 993 |
|
992 | | - expect { Cloudinary:: Utils.api_sign_request({ :cloud_name => "dn6ot3ged", :timestamp => 1568810420, :username => "[email protected]" }, "hdcixPpR2iKERPwqvH6sHdK9cyac", signature_algorithm) } |
| 994 | + expect { Cloudinary:: Utils.api_sign_request({ :cloud_name => API_SIGN_REQUEST_CLOUD_NAME, :timestamp => 1568810420, :username => "[email protected]" }, API_SIGN_REQUEST_TEST_SECRET, signature_algorithm) } |
993 | 995 | .to raise_error("Unsupported algorithm 'unsupported_algorithm'") |
994 | 996 | end |
995 | 997 |
|
| 998 | + it "should prevent parameter smuggling via & characters in parameter values with signature version 2" do |
| 999 | + params_with_ampersand = { |
| 1000 | + :cloud_name => API_SIGN_REQUEST_CLOUD_NAME, |
| 1001 | + :timestamp => 1568810420, |
| 1002 | + :notification_url => "https://fake.com/callback?a=1&tags=hello,world" |
| 1003 | + } |
| 1004 | + |
| 1005 | + signature_v1_with_ampersand = Cloudinary::Utils.api_sign_request(params_with_ampersand, API_SIGN_REQUEST_TEST_SECRET, nil, 1) |
| 1006 | + signature_v2_with_ampersand = Cloudinary::Utils.api_sign_request(params_with_ampersand, API_SIGN_REQUEST_TEST_SECRET, nil, 2) |
| 1007 | + |
| 1008 | + params_smuggled = { |
| 1009 | + :cloud_name => API_SIGN_REQUEST_CLOUD_NAME, |
| 1010 | + :timestamp => 1568810420, |
| 1011 | + :notification_url => "https://fake.com/callback?a=1", |
| 1012 | + :tags => "hello,world" |
| 1013 | + } |
| 1014 | + |
| 1015 | + signature_v1_smuggled = Cloudinary::Utils.api_sign_request(params_smuggled, API_SIGN_REQUEST_TEST_SECRET, nil, 1) |
| 1016 | + signature_v2_smuggled = Cloudinary::Utils.api_sign_request(params_smuggled, API_SIGN_REQUEST_TEST_SECRET, nil, 2) |
| 1017 | + |
| 1018 | + # Version 1 is vulnerable to parameter smuggling |
| 1019 | + expect(signature_v1_with_ampersand).to eq(signature_v1_smuggled) |
| 1020 | + |
| 1021 | + # Version 2 prevents parameter smuggling |
| 1022 | + expect(signature_v2_with_ampersand).not_to eq(signature_v2_smuggled) |
| 1023 | + |
| 1024 | + expected_v2_signature = "4fdf465dd89451cc1ed8ec5b3e314e8a51695704" |
| 1025 | + expect(signature_v2_with_ampersand).to eq(expected_v2_signature) |
| 1026 | + |
| 1027 | + expected_v2_smuggled_signature = "7b4e3a539ff1fa6e6700c41b3a2ee77586a025f9" |
| 1028 | + expect(signature_v2_smuggled).to eq(expected_v2_smuggled_signature) |
| 1029 | + end |
| 1030 | + |
996 | 1031 | describe ":if" do |
997 | 1032 | describe 'with literal condition string' do |
998 | 1033 | it "should include the if parameter as the first component in the transformation string" do |
|
1284 | 1319 | Cloudinary::Utils::ALGO_SHA256) |
1285 | 1320 | ).to be true |
1286 | 1321 | end |
| 1322 | + |
| 1323 | + it "should use signature version 1 (without parameter encoding) for backward compatibility" do |
| 1324 | + public_id_with_ampersand = 'tests/logo&version=2' |
| 1325 | + |
| 1326 | + expected_signature_v1 = Cloudinary::Utils.api_sign_request( |
| 1327 | + { :public_id => public_id_with_ampersand, :version => test_version }, |
| 1328 | + SIGNATURE_VERIFICATION_API_SECRET, |
| 1329 | + nil, |
| 1330 | + 1 |
| 1331 | + ) |
| 1332 | + |
| 1333 | + expected_signature_v2 = Cloudinary::Utils.api_sign_request( |
| 1334 | + { :public_id => public_id_with_ampersand, :version => test_version }, |
| 1335 | + SIGNATURE_VERIFICATION_API_SECRET, |
| 1336 | + nil, |
| 1337 | + 2 |
| 1338 | + ) |
| 1339 | + |
| 1340 | + expect(expected_signature_v1).not_to eq(expected_signature_v2) |
| 1341 | + |
| 1342 | + # verify_api_response_signature should use version 1 for backward compatibility |
| 1343 | + expect( |
| 1344 | + Cloudinary::Utils.verify_api_response_signature( |
| 1345 | + public_id_with_ampersand, |
| 1346 | + test_version, |
| 1347 | + expected_signature_v1 |
| 1348 | + ) |
| 1349 | + ).to be true |
| 1350 | + |
| 1351 | + expect( |
| 1352 | + Cloudinary::Utils.verify_api_response_signature( |
| 1353 | + public_id_with_ampersand, |
| 1354 | + test_version, |
| 1355 | + expected_signature_v2 |
| 1356 | + ) |
| 1357 | + ).to be false |
| 1358 | + end |
1287 | 1359 | end |
1288 | 1360 |
|
1289 | 1361 | describe ".verify_notification_signature" do |
|
0 commit comments