1   /*
2    * Copyright 2023 LINE Corporation
3    *
4    * LINE Corporation licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package com.linecorp.centraldogma.server.internal;
17  
18  import java.io.File;
19  import java.io.IOException;
20  import java.nio.file.Path;
21  import java.util.ArrayList;
22  import java.util.Collections;
23  import java.util.List;
24  import java.util.Set;
25  import java.util.concurrent.TimeUnit;
26  import java.util.regex.Pattern;
27  
28  import javax.annotation.Nullable;
29  
30  import org.eclipse.jgit.lib.Config;
31  import org.eclipse.jgit.storage.file.FileBasedConfig;
32  import org.eclipse.jgit.transport.RefSpec;
33  import org.eclipse.jgit.util.FS;
34  import org.eclipse.jgit.util.SystemReader;
35  
36  import com.google.common.annotations.VisibleForTesting;
37  
38  import com.linecorp.armeria.common.util.SystemInfo;
39  
40  /**
41   * A JGit {@link SystemReader} that prevents from reading system-wide or user-specific configurations that might
42   * interfere with Central Dogma.
43   */
44  public final class IsolatedSystemReader extends SystemReader {
45  
46      private static final Pattern allowedSystemPropertyNamePattern = Pattern.compile(
47              "^(?:java|os|file|path|line|user|native|jdk)\\.");
48  
49      @VisibleForTesting
50      static final FileBasedConfig EMPTY_CONFIG = new EmptyConfig();
51      private static final SystemReader INSTANCE = new IsolatedSystemReader();
52      private static final String[] EMPTY_STRING_ARRAY = {};
53  
54      public static void install() {
55          SystemReader.setInstance(INSTANCE);
56      }
57  
58      private IsolatedSystemReader() {
59      }
60  
61      @Override
62      public String getHostname() {
63          return SystemInfo.hostname();
64      }
65  
66      @Nullable
67      @Override
68      public String getenv(String variable) {
69          return null;
70      }
71  
72      @Nullable
73      @Override
74      public String getProperty(String key) {
75          if (allowedSystemPropertyNamePattern.matcher(key).find()) {
76              return System.getProperty(key);
77          }
78  
79          // Don't read the system properties those specified in System.getProperties().
80          return null;
81      }
82  
83      @Override
84      public FileBasedConfig openUserConfig(Config parent, FS fs) {
85          return EMPTY_CONFIG;
86      }
87  
88      @Override
89      public FileBasedConfig openSystemConfig(Config parent, FS fs) {
90          return EMPTY_CONFIG;
91      }
92  
93      @Override
94      public FileBasedConfig openJGitConfig(Config parent, FS fs) {
95          return EMPTY_CONFIG;
96      }
97  
98      @Override
99      public long getCurrentTime() {
100         return System.currentTimeMillis();
101     }
102 
103     @Override
104     public int getTimezone(long when) {
105         return getTimeZone().getOffset(when) / (60 * 1000);
106     }
107 
108     private static final class EmptyConfig extends FileBasedConfig {
109         EmptyConfig() {
110             super(null, null, null);
111         }
112 
113         @Override
114         public void load() {
115             // Do nothing because we don't want to load anything from external sources.
116         }
117 
118         @Override
119         public void save() throws IOException {
120             // Do nothing.
121         }
122 
123         @Override
124         public boolean isOutdated() {
125             return false;
126         }
127 
128         @Override
129         public int getInt(String section, String name, int defaultValue) {
130             return defaultValue;
131         }
132 
133         @Override
134         public int getInt(String section, String subsection, String name, int defaultValue) {
135             return defaultValue;
136         }
137 
138         @Override
139         public long getLong(String section, String name, long defaultValue) {
140             return defaultValue;
141         }
142 
143         @Override
144         public long getLong(String section, String subsection, String name, long defaultValue) {
145             return defaultValue;
146         }
147 
148         @Override
149         public boolean getBoolean(String section, String name, boolean defaultValue) {
150             return defaultValue;
151         }
152 
153         @Override
154         public boolean getBoolean(String section, String subsection, String name, boolean defaultValue) {
155             return defaultValue;
156         }
157 
158         @Override
159         public <T extends Enum<?>> T getEnum(String section, String subsection, String name, T defaultValue) {
160             return defaultValue;
161         }
162 
163         @Override
164         public <T extends Enum<?>> T getEnum(T[] all, String section, String subsection, String name,
165                                              T defaultValue) {
166             return defaultValue;
167         }
168 
169         @Override
170         @Nullable
171         public String getString(String section, String subsection, String name) {
172             return null;
173         }
174 
175         @Override
176         public String[] getStringList(String section, String subsection, String name) {
177             return EMPTY_STRING_ARRAY;
178         }
179 
180         @Override
181         public long getTimeUnit(String section, String subsection, String name, long defaultValue,
182                                 TimeUnit wantUnit) {
183             return defaultValue;
184         }
185 
186         @Override
187         public Path getPath(String section, String subsection, String name, FS fs, File resolveAgainst,
188                             Path defaultValue) {
189             return defaultValue;
190         }
191 
192         @Override
193         public List<RefSpec> getRefSpecs(String section, String subsection, String name) {
194             // We return a mutable list to match the original behavior.
195             return new ArrayList<>();
196         }
197 
198         @Override
199         public Set<String> getSubsections(String section) {
200             return Collections.emptySet();
201         }
202 
203         @Override
204         public Set<String> getSections() {
205             return Collections.emptySet();
206         }
207 
208         @Override
209         public Set<String> getNames(String section) {
210             return Collections.emptySet();
211         }
212 
213         @Override
214         public Set<String> getNames(String section, String subsection) {
215             return Collections.emptySet();
216         }
217 
218         @Override
219         public Set<String> getNames(String section, boolean recursive) {
220             return Collections.emptySet();
221         }
222 
223         @Override
224         public Set<String> getNames(String section, String subsection, boolean recursive) {
225             return Collections.emptySet();
226         }
227 
228         @Override
229         public <T> T get(SectionParser<T> parser) {
230             return parser.parse(this);
231         }
232 
233         @Override
234         public String toText() {
235             return "";
236         }
237 
238         @Override
239         public String toString() {
240             // super.toString() triggers a NullPointerException because it assumes getFile() returns non-null,
241             // which is not the case for us.
242             return getClass().getSimpleName();
243         }
244     }
245 }