Read about our upcoming Code of Conduct on this issue

namespace_validator.rb 3.86 KB
Newer Older
1
2
3
4
# NamespaceValidator
#
# Custom validator for GitLab namespace values.
#
5
6
# Values are checked for formatting and exclusion from a list of reserved path
# names.
7
class NamespaceValidator < ActiveModel::EachValidator
8
9
10
11
12
13
14
15
16
17
  # All routes that appear on the top level must be listed here.
  # This will make sure that groups cannot be created with these names
  # as these routes would be masked by the paths already in place.
  #
  # Example:
  #   /api/api-project
  #
  #  the path `api` shouldn't be allowed because it would be masked by `api/*`
  #
  TOP_LEVEL_ROUTES = Set.new(%w[
18
    .well-known
19
20
21
22
23
24
25
26
27
28
29
    admin
    all
    assets
    ci
    dashboard
    files
    groups
    help
    hooks
    issues
    merge_requests
Robert Schilling's avatar
Robert Schilling committed
30
    new
31
32
33
34
35
    notes
    profile
    projects
    public
    repository
36
    robots.txt
37
38
39
40
41
42
43
44
    s
    search
    services
    snippets
    teams
    u
    unsubscribes
    users
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
    api
    autocomplete
    search
    member
    explore
    uploads
    import
    notification_settings
    abuse_reports
    invites
    help
    koding
    health_check
    jwt
    oauth
    sent_notifications
61
  ]).freeze
62

63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
  # All project routes with wildcard argument must be listed here.
  # Otherwise it can lead to routing issues when route considered as project name.
  #
  # Example:
  #  /group/project/tree/deploy_keys
  #
  #  without tree as reserved name routing can match 'group/project' as group name,
  #  'tree' as project name and 'deploy_keys' as route.
  #
  WILDCARD_ROUTES = Set.new(%w[tree commits wikis new edit create update logs_tree
                               preview blob blame raw files create_dir find_file
                               artifacts graphs refs badges info git-upload-pack
                               git-receive-pack gitlab-lfs autocomplete_sources
                               templates avatar commit pages compare network snippets
                               services mattermost deploy_keys forks import merge_requests
                               branches merged_branches tags protected_branches variables
                               triggers pipelines environments cycle_analytics builds
                               hooks container_registry milestones labels issues
                               project_members group_links notes noteable boards todos
                               uploads runners runner_projects settings repository
                               transfer remove_fork archive unarchive housekeeping
                               toggle_star preview_markdown export remove_export
                               generate_new_export download_export activity
                               new_issue_address registry])
87

88
  STRICT_RESERVED = (TOP_LEVEL_ROUTES | WILDCARD_ROUTES)
89

90
91
92
93
94
95
96
  def self.valid_full_path?(full_path)
    pieces = full_path.split('/')
    first_part = pieces.first
    pieces.all? do |namespace|
      type = first_part == namespace ? :top_level : :wildcard
      valid?(namespace, type: type)
    end
97
98
  end

99
100
101
102
103
104
105
106
107
108
  def self.valid?(value, type: :strict)
    !reserved?(value, type: type) && follow_format?(value)
  end

  def self.reserved?(value, type: :strict)
    case type
    when :wildcard
      WILDCARD_ROUTES.include?(value)
    when :top_level
      TOP_LEVEL_ROUTES.include?(value)
109
    else
110
      STRICT_RESERVED.include?(value)
111
    end
112
113
114
115
116
117
118
119
  end

  def self.follow_format?(value)
    value =~ Gitlab::Regex.namespace_regex
  end

  delegate :reserved?, :follow_format?, to: :class

120
  def validate_each(record, attribute, value)
121
    unless follow_format?(value)
122
123
124
      record.errors.add(attribute, Gitlab::Regex.namespace_regex_message)
    end

125
    if reserved?(value, type: validation_type(record))
126
127
128
      record.errors.add(attribute, "#{value} is a reserved name")
    end
  end
129
130
131
132
133
134
135
136
137
138
139

  def validation_type(record)
    case record
    when Group
      record.parent_id ? :wildcard : :top_level
    when Project
      :wildcard
    else
      :strict
    end
  end
140
end