1+ // Copyright 2024 The Update Framework Authors
2+ //
3+ // Licensed under the Apache License, Version 2.0 (the "License");
4+ // you may not use this file except in compliance with the License.
5+ // You may obtain a copy of the License at
6+ //
7+ // http://www.apache.org/licenses/LICENSE-2.0
8+ //
9+ // Unless required by applicable law or agreed to in writing, software
10+ // distributed under the License is distributed on an "AS IS" BASIS,
11+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+ // See the License for the specific language governing permissions and
13+ // limitations under the License
14+ //
15+ // SPDX-License-Identifier: Apache-2.0
16+ //
17+
18+ package multirepo
19+
20+ import (
21+ "errors"
22+ "testing"
23+ )
24+
25+ func TestValidateRepoName (t * testing.T ) {
26+ tests := []struct {
27+ name string
28+ input string
29+ wantErr bool
30+ }{
31+ // Valid names - must start with alphanumeric, contain only [a-zA-Z0-9._-]
32+ {"valid simple name" , "my-repo" , false },
33+ {"valid name with numbers" , "repo123" , false },
34+ {"valid starts with number" , "123repo" , false },
35+ {"valid name with dots" , "my.repo.name" , false },
36+ {"valid name with underscores" , "my_repo_name" , false },
37+ {"valid mixed" , "sigstore-tuf-root" , false },
38+ {"valid version style" , "repo.v2.1" , false },
39+ {"valid single char" , "a" , false },
40+ {"valid single number" , "1" , false },
41+
42+ // Invalid: empty
43+ {"empty name" , "" , true },
44+
45+ // Invalid: starts with non-alphanumeric
46+ {"starts with dot" , ".hidden" , true },
47+ {"starts with hyphen" , "-repo" , true },
48+ {"starts with underscore" , "_repo" , true },
49+
50+ // Invalid: traversal components
51+ {"single dot" , "." , true },
52+ {"double dot" , ".." , true },
53+
54+ // Invalid: path separators
55+ {"unix path separator" , "foo/bar" , true },
56+ {"windows path separator" , "foo\\ bar" , true },
57+ {"traversal with unix separator" , "../escaped" , true },
58+ {"traversal with windows separator" , "..\\ escaped" , true },
59+ {"deep traversal" , "../../etc/passwd" , true },
60+
61+ // Invalid: absolute paths
62+ {"unix absolute path" , "/etc/passwd" , true },
63+ {"windows absolute path" , "C:\\ Windows" , true },
64+
65+ // Invalid: special characters
66+ {"contains space" , "my repo" , true },
67+ {"contains at sign" , "repo@org" , true },
68+ {"contains colon" , "repo:tag" , true },
69+ {"contains hash" , "repo#1" , true },
70+ {"contains exclamation" , "repo!" , true },
71+ {"contains semicolon" , "repo;rm" , true },
72+ {"contains unicode" , "репо" , true },
73+ }
74+
75+ for _ , tt := range tests {
76+ t .Run (tt .name , func (t * testing.T ) {
77+ err := validateRepoName (tt .input )
78+ if (err != nil ) != tt .wantErr {
79+ t .Errorf ("validateRepoName(%q) error = %v, wantErr %v" , tt .input , err , tt .wantErr )
80+ }
81+ if err != nil && ! errors .Is (err , ErrInvalidRepoName ) {
82+ t .Errorf ("validateRepoName(%q) error should wrap ErrInvalidRepoName, got %v" , tt .input , err )
83+ }
84+ })
85+ }
86+ }
87+
88+ func TestNewRejectsInvalidRepoNames (t * testing.T ) {
89+ tests := []struct {
90+ name string
91+ repoName string
92+ }{
93+ {"path traversal" , "../escaped-repo" },
94+ {"starts with dot" , ".hidden-repo" },
95+ {"contains slash" , "foo/bar" },
96+ {"contains space" , "my repo" },
97+ }
98+
99+ for _ , tt := range tests {
100+ t .Run (tt .name , func (t * testing.T ) {
101+ mapJSON := []byte (`{
102+ "repositories": {
103+ "` + tt .repoName + `": ["https://example.com/repo"]
104+ },
105+ "mapping": []
106+ }` )
107+
108+ rootBytes := []byte (`{"signatures":[],"signed":{}}` )
109+
110+ cfg , err := NewConfig (mapJSON , map [string ][]byte {tt .repoName : rootBytes })
111+ if err != nil {
112+ t .Fatalf ("NewConfig() unexpected error: %v" , err )
113+ }
114+
115+ _ , err = New (cfg )
116+ if err == nil {
117+ t .Fatalf ("New() should reject repository name %q" , tt .repoName )
118+ }
119+
120+ if ! errors .Is (err , ErrInvalidRepoName ) {
121+ t .Errorf ("New() error should wrap ErrInvalidRepoName, got: %v" , err )
122+ }
123+ })
124+ }
125+ }
0 commit comments