@@ -786,6 +786,83 @@ func TestNewRequest_errorForNoTrailingSlash(t *testing.T) {
786786 }
787787}
788788
789+ func TestCheckURLPathTraversal (t * testing.T ) {
790+ t .Parallel ()
791+ tests := []struct {
792+ input string
793+ wantErr error
794+ }{
795+ {"repos/o/r/contents/file.txt" , nil },
796+ {"repos/o/r/contents/dir/file.txt" , nil },
797+ {"repos/o/r/contents/file..txt" , nil },
798+ {"repos/o/r?q=a..b" , nil },
799+ {"repos/../admin/users" , ErrPathForbidden },
800+ {"repos/x/../../../admin" , ErrPathForbidden },
801+ {"../admin" , ErrPathForbidden },
802+ {"repos/o/r/contents/.." , ErrPathForbidden },
803+ {"repos/o/r/contents/../secrets" , ErrPathForbidden },
804+ // Full URLs with scheme.
805+ {"https://api.github.com/repos/../admin" , ErrPathForbidden },
806+ {"https://api.github.com/repos/o/r/contents/file.txt" , nil },
807+ {"https://api.github.com/repos/o/r/contents/file..txt" , nil },
808+ // URL with fragment.
809+ {"repos/o/r/contents/file.txt#section" , nil },
810+ {"repos/../admin#frag" , ErrPathForbidden },
811+ // URL with userinfo.
812+ {"https://user:pass@api.github.com/repos/../admin" , ErrPathForbidden },
813+ {"https://user:pass@api.github.com/repos/o/r" , nil },
814+ }
815+ for _ , tt := range tests {
816+ err := checkURLPathTraversal (tt .input )
817+ if ! errors .Is (err , tt .wantErr ) {
818+ t .Errorf ("checkURLPathTraversal(%q) = %v, want %v" , tt .input , err , tt .wantErr )
819+ }
820+ }
821+ }
822+
823+ func TestNewRequest_pathTraversal (t * testing.T ) {
824+ t .Parallel ()
825+ c := NewClient (nil )
826+
827+ tests := []struct {
828+ urlStr string
829+ wantError bool
830+ }{
831+ {"repos/o/r/readme" , false },
832+ {"repos/o/r/contents/file..txt" , false },
833+ {"repos/x/../../../admin/users" , true },
834+ {"repos/../admin" , true },
835+ }
836+ for _ , tt := range tests {
837+ _ , err := c .NewRequest ("GET" , tt .urlStr , nil )
838+ if tt .wantError && ! errors .Is (err , ErrPathForbidden ) {
839+ t .Errorf ("NewRequest(%q): want ErrPathForbidden, got %v" , tt .urlStr , err )
840+ } else if ! tt .wantError && err != nil {
841+ t .Errorf ("NewRequest(%q): unexpected error: %v" , tt .urlStr , err )
842+ }
843+ }
844+ }
845+
846+ func TestNewFormRequest_pathTraversal (t * testing.T ) {
847+ t .Parallel ()
848+ c := NewClient (nil )
849+
850+ _ , err := c .NewFormRequest ("repos/x/../../../admin" , nil )
851+ if ! errors .Is (err , ErrPathForbidden ) {
852+ t .Fatalf ("NewFormRequest with path traversal: want ErrPathForbidden, got %v" , err )
853+ }
854+ }
855+
856+ func TestNewUploadRequest_pathTraversal (t * testing.T ) {
857+ t .Parallel ()
858+ c := NewClient (nil )
859+
860+ _ , err := c .NewUploadRequest ("repos/x/../../../admin" , nil , 0 , "" )
861+ if ! errors .Is (err , ErrPathForbidden ) {
862+ t .Fatalf ("NewUploadRequest with path traversal: want ErrPathForbidden, got %v" , err )
863+ }
864+ }
865+
789866func TestNewFormRequest (t * testing.T ) {
790867 t .Parallel ()
791868 c := NewClient (nil )
0 commit comments