@@ -642,6 +642,120 @@ void instantiateEscapeUnsafeChar() {
642642 .isEqualTo ("buckets/f%2Fo%2Fo/objects/b%2Fa%2Fr" );
643643 }
644644
645+ @ Test
646+ void instantiateRejectsDotInSingleWildcard () {
647+ PathTemplate template =
648+ PathTemplate .create ("/compute/v1/projects/{project}/regions/{region}/addresses" );
649+ ValidationException dotException =
650+ Assertions .assertThrows (
651+ ValidationException .class ,
652+ () -> template .instantiate ("project" , "my-project" , "region" , "." ));
653+ Truth .assertThat (dotException .getMessage ())
654+ .isEqualTo ("Value for region must not contain segments that are exactly . or .." );
655+ }
656+
657+ @ Test
658+ void instantiateRejectsDoubleDotInSingleWildcard () {
659+ PathTemplate template =
660+ PathTemplate .create ("/compute/v1/projects/{project}/regions/{region}/addresses" );
661+ ValidationException dotDotException =
662+ Assertions .assertThrows (
663+ ValidationException .class ,
664+ () -> template .instantiate ("project" , "my-project" , "region" , ".." ));
665+ Truth .assertThat (dotDotException .getMessage ())
666+ .isEqualTo ("Value for region must not contain segments that are exactly . or .." );
667+ }
668+
669+ @ Test
670+ void instantiateRejectsDotInMultiSegmentWildcard () {
671+ PathTemplate template =
672+ PathTemplate .create ("/v2/{parent=projects/*/locations/*}/content:inspect" );
673+ ValidationException dotException =
674+ Assertions .assertThrows (
675+ ValidationException .class ,
676+ () -> template .instantiate ("parent" , "projects/my-project/locations/." ));
677+ Truth .assertThat (dotException .getMessage ())
678+ .isEqualTo ("Value for parent must not contain segments that are exactly . or .." );
679+ }
680+
681+ @ Test
682+ void instantiateRejectsDoubleDotInMultiSegmentWildcard () {
683+ PathTemplate template =
684+ PathTemplate .create ("/v2/{parent=projects/*/locations/*}/content:inspect" );
685+ ValidationException dotDotException =
686+ Assertions .assertThrows (
687+ ValidationException .class ,
688+ () -> template .instantiate ("parent" , "projects/my-project/locations/.." ));
689+ Truth .assertThat (dotDotException .getMessage ())
690+ .isEqualTo ("Value for parent must not contain segments that are exactly . or .." );
691+ }
692+
693+ @ Test
694+ void instantiateRejectsDotInPathWildcard () {
695+ PathTemplate template =
696+ PathTemplate .create ("/v3/{name=projects/*/monitoredResourceDescriptors/**}" );
697+ for (String invalidName :
698+ new String [] {
699+ "projects/my-project/monitoredResourceDescriptors/instance/my-instance/." ,
700+ "projects/my-project/monitoredResourceDescriptors/." ,
701+ "projects/my-project/monitoredResourceDescriptors/instance/./my-instance" ,
702+ "projects/my-project/monitoredResourceDescriptors/ . "
703+ }) {
704+ ValidationException exception =
705+ Assertions .assertThrows (
706+ ValidationException .class , () -> template .instantiate ("name" , invalidName ));
707+ Truth .assertThat (exception .getMessage ())
708+ .isEqualTo ("Value for name must not contain segments that are exactly . or .." );
709+ }
710+ }
711+
712+ @ Test
713+ void instantiateRejectsDoubleDotInPathWildcard () {
714+ PathTemplate template =
715+ PathTemplate .create ("/v3/{name=projects/*/monitoredResourceDescriptors/**}" );
716+ for (String invalidName :
717+ new String [] {
718+ "projects/my-project/monitoredResourceDescriptors/instance/my-instance/.." ,
719+ "projects/my-project/monitoredResourceDescriptors/.." ,
720+ "projects/my-project/monitoredResourceDescriptors/instance/./my-instance/.." ,
721+ "projects/my-project/monitoredResourceDescriptors/instance/../.." ,
722+ "projects/my-project/monitoredResourceDescriptors/ .. "
723+ }) {
724+ ValidationException exception =
725+ Assertions .assertThrows (
726+ ValidationException .class , () -> template .instantiate ("name" , invalidName ));
727+ Truth .assertThat (exception .getMessage ())
728+ .isEqualTo ("Value for name must not contain segments that are exactly . or .." );
729+ }
730+ }
731+
732+ @ Test
733+ void instantiateAllowsSafeDotsAndPercentEncodesReservedChars () {
734+ PathTemplate template = PathTemplate .create ("/v1/{x=projects/*/databases/*/documents/**}" );
735+ Truth .assertThat (
736+ template .instantiate ("x" , "projects/sys-prod-123/databases/default/documents/my.txt" ))
737+ .isEqualTo ("v1/projects/sys-prod-123/databases/default/documents/my.txt" );
738+ Truth .assertThat (
739+ template .instantiate (
740+ "x" , "projects/sys-prod-123/databases/default/documents/my..file.txt" ))
741+ .isEqualTo ("v1/projects/sys-prod-123/databases/default/documents/my..file.txt" );
742+ Truth .assertThat (
743+ template .instantiate (
744+ "x" , "projects/sys-prod-123/databases/default/documents/doc?key=val" ))
745+ .isEqualTo ("v1/projects/sys-prod-123/databases/default/documents/doc%3Fkey%3Dval" );
746+ Truth .assertThat (
747+ template .instantiate ("x" , "projects/sys-prod-123/databases/default/documents/doc#frag" ))
748+ .isEqualTo ("v1/projects/sys-prod-123/databases/default/documents/doc%23frag" );
749+ Truth .assertThat (
750+ template .instantiate (
751+ "x" , "projects/sys-prod-123/databases/default/documents/doc with space" ))
752+ .isEqualTo ("v1/projects/sys-prod-123/databases/default/documents/doc%20with%20space" );
753+ Truth .assertThat (
754+ template .instantiate (
755+ "x" , "projects/sys-prod-123/databases/default/documents/%2e%2e/escape-db" ))
756+ .isEqualTo ("v1/projects/sys-prod-123/databases/default/documents/%252e%252e/escape-db" );
757+ }
758+
645759 @ Test
646760 void instantiateNotEscapeForUnboundedWildcard () {
647761 PathTemplate template = PathTemplate .create ("buckets/*/objects/**" );
0 commit comments