diff --git a/lib/OpenStack/MetaAPI/API/Images.pm b/lib/OpenStack/MetaAPI/API/Images.pm index 8c9ad31..693d336 100644 --- a/lib/OpenStack/MetaAPI/API/Images.pm +++ b/lib/OpenStack/MetaAPI/API/Images.pm @@ -59,6 +59,8 @@ sub image_from_uid { my ($self, $uid) = @_; die "image_from_uid: uid is required" unless defined $uid; + die "Invalid UUID format '$uid' - expected 8-4-4-4-12 hex format" + unless $uid =~ m{^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$}i; my $uri = $self->root_uri('/images/' . $uid); diff --git a/lib/OpenStack/MetaAPI/Roles/GetFromId.pm b/lib/OpenStack/MetaAPI/Roles/GetFromId.pm index 9a1a1ca..905afd2 100644 --- a/lib/OpenStack/MetaAPI/Roles/GetFromId.pm +++ b/lib/OpenStack/MetaAPI/Roles/GetFromId.pm @@ -5,12 +5,17 @@ use warnings; use Moo::Role; +# RFC 4122 UUID: 8-4-4-4-12 hex groups +my $VALID_UUID = qr{^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$}i; + sub _get_from_id_spec { my ($self, $route, $id) = @_; die "route must be defined when using get_from_id" unless defined $route; die "invalid route '$route' - must starts with /" unless $route =~ m{^/}; die "Undefined 'id' for route '$route'" unless defined $id; + die "Invalid UUID format '$id' for route '$route' - expected 8-4-4-4-12 hex format" + unless $id =~ $VALID_UUID; $route .= '/' unless $route =~ m{/$}; diff --git a/t/create-vm-error-status.t b/t/create-vm-error-status.t index 0ba6816..24d9323 100644 --- a/t/create-vm-error-status.t +++ b/t/create-vm-error-status.t @@ -25,7 +25,7 @@ my $api = get_api_object(use_env => 0); ok $api, "got one api object" or die; my $IMAGE_UID = '170fafa5-1329-44a3-9c27-9bb77b77206d'; -my $SERVER_UID = 'aaaaa-bbbb-ccccc-dddd'; +my $SERVER_UID = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'; mock_get_request( 'http://127.0.0.1:8774/v2.1/flavors', @@ -144,7 +144,7 @@ sub json_create_server { return <<'JSON'; { "server" : { - "id": "aaaaa-bbbb-ccccc-dddd", + "id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", "name" : "vm-error-test" } } diff --git a/t/create-vm-no-floating-ip.t b/t/create-vm-no-floating-ip.t index 2820cda..382b110 100644 --- a/t/create-vm-no-floating-ip.t +++ b/t/create-vm-no-floating-ip.t @@ -28,7 +28,7 @@ my $api = get_api_object(use_env => 0); ok $api, "got one api object" or die; my $IMAGE_UID = '170fafa5-1329-44a3-9c27-9bb77b77206d'; -my $SERVER_UID = 'aaaaa-bbbb-ccccc-dddd'; +my $SERVER_UID = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'; mock_get_request( 'http://127.0.0.1:8774/v2.1/flavors', @@ -101,7 +101,7 @@ sub json_create_server { return <<'JSON'; { "server" : { - "id": "aaaaa-bbbb-ccccc-dddd", + "id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", "name" : "vm-no-floating-ip" } } diff --git a/t/roles.t b/t/roles.t index d911832..92af631 100644 --- a/t/roles.t +++ b/t/roles.t @@ -162,10 +162,10 @@ mock_lwp_useragent(); my $api = get_api_object(use_env => 0); ok $api, "got api object for getfromid test" or die; - my $server_uid = 'abc-def-123'; + my $server_uid = 'abcdef01-2345-6789-abcd-ef0123456789'; mock_get_request( "http://127.0.0.1:8774/v2.1/servers/$server_uid", - application_json('{"server": {"id": "abc-def-123", "name": "my-server", "status": "ACTIVE"}}'), + application_json('{"server": {"id": "abcdef01-2345-6789-abcd-ef0123456789", "name": "my-server", "status": "ACTIVE"}}'), ); my $server = $api->server_from_uid($server_uid); diff --git a/t/service-compute.t b/t/service-compute.t index 105f8cd..87a0bfd 100644 --- a/t/service-compute.t +++ b/t/service-compute.t @@ -108,6 +108,30 @@ ok $api, "got one api object" or die; } +{ + note "Testing server_from_uid rejects malformed UUIDs"; + + like dies { $api->server_from_uid('not-a-uuid') }, + qr/Invalid UUID format/, + "server_from_uid rejects non-UUID string"; + + like dies { $api->server_from_uid('aaa-bbb-ccc') }, + qr/Invalid UUID format/, + "server_from_uid rejects too-short hex-dash string"; + + like dies { $api->server_from_uid('33748c2338dd4f70b774522fc69e7b67') }, + qr/Invalid UUID format/, + "server_from_uid rejects UUID without dashes"; + + like dies { $api->server_from_uid('33748c23-38dd-4f70-b774') }, + qr/Invalid UUID format/, + "server_from_uid rejects truncated UUID"; + + like dies { $api->server_from_uid('ZZZZZZZZ-ZZZZ-ZZZZ-ZZZZ-ZZZZZZZZZZZZ') }, + qr/Invalid UUID format/, + "server_from_uid rejects non-hex characters"; +} + done_testing; sub json_for_server { diff --git a/t/service-image.t b/t/service-image.t index 20ef66a..8ce8970 100644 --- a/t/service-image.t +++ b/t/service-image.t @@ -46,7 +46,7 @@ ok $api, "got one api object" or die; 'created_at' => '2019-04-10T20:23:09Z', 'disk_format' => 'raw', 'file' => '/v2/images/6056cbf415fd5f8c223c8a69341e44ee/file', - 'id' => 'b763530-fe64d-2116b5-c627a7-7f0cf71b', + 'id' => 'b7635300-fe6d-2116-c627-7f0cf71b0000', 'min_disk' => 0, 'min_ram' => 0, 'name' => 'MyImage', @@ -104,6 +104,26 @@ ok $api, "got one api object" or die; } +{ + note "Testing image_from_uid rejects malformed UUIDs"; + + like dies { $api->image_from_uid('not-a-uuid') }, + qr/Invalid UUID format/, + "image_from_uid rejects non-UUID string"; + + like dies { $api->image_from_uid('aaa-bbb-ccc') }, + qr/Invalid UUID format/, + "image_from_uid rejects too-short hex-dash string"; + + like dies { $api->image_from_uid('170fafa513294a3c9c279bb77b77206d') }, + qr/Invalid UUID format/, + "image_from_uid rejects UUID without dashes"; + + like dies { $api->image_from_uid('170fafa5-1329-44a3-9c27') }, + qr/Invalid UUID format/, + "image_from_uid rejects truncated UUID"; +} + done_testing; sub json_for_image { @@ -112,7 +132,7 @@ sub json_for_image { return <<'JSON'; { "min_ram" : 0, - "id" : "b763530-fe64d-2116b5-c627a7-7f0cf71b", + "id" : "b7635300-fe6d-2116-c627-7f0cf71b0000", "os_version" : "7", "created_at" : "2019-04-10T20:23:09Z", "os_arch" : "x86_64", diff --git a/t/xtra-create-vm.t b/t/xtra-create-vm.t index 721dc0e..25991d3 100644 --- a/t/xtra-create-vm.t +++ b/t/xtra-create-vm.t @@ -80,7 +80,7 @@ ok $api, "got one api object" or die; ); mock_get_request( - 'http://127.0.0.1:8774/v2.1/servers/aaaaa-bbbb-ccccc-dddd', + 'http://127.0.0.1:8774/v2.1/servers/aaaaabbb-bccc-cddd-d000-000000000000', application_json(json_for_server()), ); @@ -99,7 +99,7 @@ ok $api, "got one api object" or die; my $iteration = 0; mock_get_request( - 'http://127.0.0.1:8774/v2.1/servers/aaaaa-bbbb-ccccc-dddd', + 'http://127.0.0.1:8774/v2.1/servers/aaaaabbb-bccc-cddd-d000-000000000000', sub { my ($request) = @_; note "checking server state ", ++$iteration; @@ -115,7 +115,7 @@ ok $api, "got one api object" or die; # then the server is active - die "Too many calls to servers/aaaaa-bbbb-ccccc-dddd" + die "Too many calls to servers/aaaaabbb-bccc-cddd-d000-000000000000" if $iteration >= 10; return { @@ -139,12 +139,12 @@ ok $api, "got one api object" or die; ); mock_get_request( - 'http://127.0.0.1:9696/v2.0/ports?device_id=aaaaa-bbbb-ccccc-dddd', + 'http://127.0.0.1:9696/v2.0/ports?device_id=aaaaabbb-bccc-cddd-d000-000000000000', application_json(json_for_ports_device_id()), ); mock_put_request( - 'http://127.0.0.1:9696/v2.0/floatingips/ffffff-000000-fffff-111111-7777777', + 'http://127.0.0.1:9696/v2.0/floatingips/ffffff00-0000-ffff-1111-117777777777', application_json(json_for_put_floatingips()), ); @@ -153,7 +153,7 @@ ok $api, "got one api object" or die; is $vm => hash { field id => '33748c23-38dd-4f70-b774-522fc69e7b67'; field floating_ip_address => '10.1.2.3'; - field floating_ip_id => 'ffffff-000000-fffff-111111-7777777'; + field floating_ip_id => 'ffffff00-0000-ffff-1111-117777777777'; field status => 'ACTIVE'; field user_id => 'fake'; @@ -233,7 +233,7 @@ sub json_for_ports_device_id { "created_at": "2016-03-08T20:19:41", "data_plane_status": null, "description": "", - "device_id": "aaaaa-bbbb-ccccc-dddd", + "device_id": "aaaaabbb-bccc-cddd-d000-000000000000", "device_owner": "network:router_gateway", "dns_assignment": { "hostname": "myport", @@ -284,7 +284,7 @@ sub json_for_put_floatingips { "fixed_ip_address": "172.24.4.228", "floating_ip_address": "10.1.2.3", "floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57", - "id": "ffffff-000000-fffff-111111-7777777", + "id": "ffffff00-0000-ffff-1111-117777777777", "description": "floating ip for testing", "dns_domain": "my-domain.org.", "dns_name": "myfip", @@ -318,7 +318,7 @@ sub json_for_post_floatingips { "fixed_ip_address": "172.24.4.228", "floating_ip_address": "10.1.2.3", "floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57", - "id": "ffffff-000000-fffff-111111-7777777", + "id": "ffffff00-0000-ffff-1111-117777777777", "port_id": "ce705c24-c1ef-408a-bda3-7bbd946164ab", "router_id": "d23abc8d-2991-4a55-ba98-2aaea84cc72f", "status": "ACTIVE", @@ -444,7 +444,7 @@ sub json_create_server { return <<'JSON'; { "server" : { - "id": "aaaaa-bbbb-ccccc-dddd", + "id": "aaaaabbb-bccc-cddd-d000-000000000000", "accessIPv4": "1.2.3.4", "accessIPv6": "80fe::", "name" : "new-server-test", diff --git a/t/xtra-delete-vm.t b/t/xtra-delete-vm.t index a010435..82a9476 100644 --- a/t/xtra-delete-vm.t +++ b/t/xtra-delete-vm.t @@ -36,17 +36,17 @@ my $SERVER_NAME = 'testsuite OpenStack::MetaAPI'; ); mock_get_request( - 'http://127.0.0.1:8774/v2.1/servers/000000-1111-22222-33333-444444', + 'http://127.0.0.1:8774/v2.1/servers/00000000-1111-2222-3333-444444444444', application_json(json_servers_id()), ); mock_get_request( - 'http://127.0.0.1:9696/v2.0/ports?device_id=000000-1111-22222-33333-444444', + 'http://127.0.0.1:9696/v2.0/ports?device_id=00000000-1111-2222-3333-444444444444', application_json(json_for_ports_device_id_unused()), ); mock_delete_request( - 'http://127.0.0.1:8774/v2.1/servers/000000-1111-22222-33333-444444', + 'http://127.0.0.1:8774/v2.1/servers/00000000-1111-2222-3333-444444444444', txt_plain("ok delete server"), ); @@ -57,7 +57,7 @@ my $SERVER_NAME = 'testsuite OpenStack::MetaAPI'; } mock_get_request( - 'http://127.0.0.1:9696/v2.0/ports?device_id=000000-1111-22222-33333-444444', + 'http://127.0.0.1:9696/v2.0/ports?device_id=00000000-1111-2222-3333-444444444444', application_json(json_for_ports_device_id_used()), ); @@ -67,7 +67,7 @@ my $SERVER_NAME = 'testsuite OpenStack::MetaAPI'; ); mock_delete_request( - 'http://127.0.0.1:9696/v2.0/floatingips/ffff-1111-00000-aaaaaaa-777777', + 'http://127.0.0.1:9696/v2.0/floatingips/ffff1111-0000-0aaa-aaaa-777777777777', txt_plain("ok delete floating ip ok"), ); @@ -148,7 +148,7 @@ sub json_for_floatingips { "fixed_ip_address": "10.0.0.3", "floating_ip_address": "172.24.4.228", "port_id": "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b", - "id": "ffff-1111-00000-aaaaaaa-777777", + "id": "ffff1111-0000-0aaa-aaaa-777777777777", "status": "ACTIVE", "port_details": { "status": "ACTIVE", @@ -177,7 +177,7 @@ sub json_for_ports_device_id_used { "created_at": "2016-03-08T20:19:41", "data_plane_status": null, "description": "", - "device_id": "000000-1111-22222-33333-444444", + "device_id": "00000000-1111-2222-3333-444444444444", "device_owner": "network:router_gateway", "dns_assignment": { "hostname": "myport", @@ -230,7 +230,7 @@ sub json_for_ports_device_id_unused { "created_at": "2016-03-08T20:19:41", "data_plane_status": null, "description": "", - "device_id": "0000-00000-0000-0000-0000", + "device_id": "00000000-0000-0000-0000-000000000000", "device_owner": "network:router_gateway", "dns_assignment": { "hostname": "myport", @@ -359,7 +359,7 @@ sub json_servers_id { "swap": 0, "vcpus": 1 }, - "id": "000000-1111-22222-33333-444444", + "id": "00000000-1111-2222-3333-444444444444", "image": { "id": "70a599e0-31e7-49b7-b260-868f441e862b", "links": [ @@ -393,7 +393,7 @@ sub json_servers { { "servers": [ { - "id": "000000-1111-22222-33333-444444", + "id": "00000000-1111-2222-3333-444444444444", "links": [ { "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/22c91117-08de-4894-9aa9-6ef382400985", diff --git a/xt/create_vm.t b/xt/create_vm.t index aedabbc..2d726ac 100644 --- a/xt/create_vm.t +++ b/xt/create_vm.t @@ -16,7 +16,7 @@ use lib "$FindBin::Bin/../t/lib"; use Test::OpenStack::MetaAPI qw{:all}; -my $VALID_ID = match qr{^[a-f0-9\-]+$}; +my $VALID_ID = match qr{^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$}i; my $IMAGE_UID = '170fafa5-1329-44a3-9c27-9bb77b77206d'; my $IMAGE_NAME = 'myimage';