Constants.java

1
package com.renomad.minum.state;
2
3
import com.renomad.minum.logging.LoggingLevel;
4
import com.renomad.minum.utils.TimeUtils;
5
6
import java.io.FileInputStream;
7
import java.io.IOException;
8
import java.util.*;
9
10
/**
11
 * Very important system design decisions are made here.  All
12
 * developers on this project should look through each of these.
13
 */
14
public final class Constants {
15
16
    private final Properties properties;
17
18
    public Constants() {
19
        this(null);
20
    }
21
22
    public Constants(Properties props) {
23
        properties = Objects.requireNonNullElseGet(props, Constants::getConfiguredProperties);
24
25
        serverPort = getProp("SERVER_PORT",  8080);
26
        secureServerPort = getProp("SSL_SERVER_PORT",  8443);
27
        hostName = properties.getProperty("HOST_NAME",  "localhost");
28
        dbDirectory = properties.getProperty("DB_DIRECTORY",  "db");
29
        staticFilesDirectory = properties.getProperty("STATIC_FILES_DIRECTORY",  "static");
30
        logLevels = convertLoggingStringsToEnums(getProp("LOG_LEVELS", "DEBUG,TRACE,ASYNC_ERROR,AUDIT"));
31
        keystorePath = properties.getProperty("KEYSTORE_PATH",  "");
32
        keystorePassword = properties.getProperty("KEYSTORE_PASSWORD",  "");
33
        maxReadSizeBytes = getProp("MAX_READ_SIZE_BYTES",  10 * 1024 * 1024);
34
        maxReadLineSizeBytes = getProp("MAX_READ_LINE_SIZE_BYTES",  1024);
35
        socketTimeoutMillis = getProp("SOCKET_TIMEOUT_MILLIS", 7 * 1000);
36
        keepAliveTimeoutSeconds = getProp("KEEP_ALIVE_TIMEOUT_SECONDS", 3);
37
        vulnSeekingJailDuration = getProp("VULN_SEEKING_JAIL_DURATION", 10 * 1000);
38
        isTheBrigEnabled = getProp("IS_THE_BRIG_ENABLED", true);
39
        suspiciousErrors = new HashSet<>(getProp("SUSPICIOUS_ERRORS", ""));
40
        suspiciousPaths = new HashSet<>(getProp("SUSPICIOUS_PATHS", ""));
41
        startTime = System.currentTimeMillis();
42
        extraMimeMappings = getProp("EXTRA_MIME_MAPPINGS", "");
43
        staticFileCacheTime = getProp("STATIC_FILE_CACHE_TIME", 60 * 5);
44
        useCacheForStaticFiles = getProp("USE_CACHE_FOR_STATIC_FILES", true);
45
        maxElementsLruCacheStaticFiles = getProp("MAX_ELEMENTS_LRU_CACHE_STATIC_FILES", 1000);
46
        maxAppendCount = getProp("MAX_DATABASE_APPEND_COUNT", 100_000);
47
        maxLinesPerConsolidatedDatabaseFile = getProp("MAX_DATABASE_CONSOLIDATED_FILE_LINES", 100_000);
48
        enableSystemRunningMarker = getProp("ENABLE_SYSTEM_RUNNING_MARKER", true);
49
    }
50
51
    /**
52
     * The port for our regular, non-encrypted server
53
     */
54
    public final int serverPort;
55
56
    /**
57
     * The port for our encrypted server
58
     */
59
    public final int secureServerPort;
60
61
    /**
62
     * This is returned as the "host:" attribute in an HTTP request
63
     */
64
    public final String hostName;
65
66
    /**
67
     * This is the root directory of our database
68
     */
69
    public final String dbDirectory;
70
71
    /**
72
     * Root directory of static files
73
     */
74
    public final String staticFilesDirectory;
75
76
    /**
77
     * The default logging levels
78
     */
79
    public final List<LoggingLevel> logLevels;
80
81
    /**
82
     * The path to the keystore, required for encrypted TLS communication
83
     */
84
    public final String keystorePath;
85
86
    /**
87
     * The password of the keystore, used for TLS
88
     */
89
    public final String keystorePassword;
90
91
    /**
92
     * this is the most bytes we'll read while parsing the Request body
93
     */
94
    public final int maxReadSizeBytes;
95
96
    /**
97
     * this is the most bytes we'll read on a single line, when reading by
98
     * line.  This is especially relevant when reading headers and request lines, which
99
     * can bulk up with jwt's or query strings, respectively.
100
     */
101
    public final int maxReadLineSizeBytes;
102
103
    /**
104
     * How long will we let a socket live before we crash it closed?
105
     * See {@link java.net.Socket#setSoTimeout(int)}
106
     */
107
    public final int socketTimeoutMillis;
108
109
    /**
110
     * We include this value in the keep-alive header. It lets the
111
     * browser know how long to hold the socket open, in seconds,
112
     * before it decides we aren't sending anything else and closes it.
113
     */
114
    public final int keepAliveTimeoutSeconds;
115
116
    /**
117
     * If a client does something that we consider an indicator for attacking, put them in
118
     * jail for a longer duration.
119
     */
120
    public final int vulnSeekingJailDuration;
121
122
    /**
123
     * TheBrig is what puts client ip's in jail, if we feel they are attacking us.
124
     * If this is disabled, that functionality is removed.
125
     */
126
    public final boolean isTheBrigEnabled;
127
128
    /**
129
     * These are a list of error messages that often indicate unusual behavior, maybe an attacker
130
     */
131
    public final Set<String> suspiciousErrors;
132
133
    /**
134
     * These are a list of paths that often indicate unusual behavior, maybe an attacker
135
     */
136
    public final Set<String> suspiciousPaths;
137
138
    /**
139
     * This value is the result of running System.currentTimeMillis() when this
140
     * class gets instantiated, and that is done at the very beginning.
141
     */
142
    public final long startTime;
143
144
    /**
145
     * These are key-value pairs for mappings between a file
146
     * suffix and a mime type.
147
     * <p>
148
     *     These are read by our system in the StaticFilesCache
149
     *     as key-1,value-1,key-2,value-2,... and so on.
150
     * </p>
151
     * @see <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types">Basics of HTTP</a>
152
     *
153
     */
154
    public final List<String> extraMimeMappings;
155
156
    /**
157
     * Length of time, in seconds, for static files to be cached,
158
     * per the provisions of the Cache-Control header, e.g.
159
     * <pre>
160
     *     {@code Cache-Control: max-age=300}
161
     * </pre>
162
     */
163
    public final long staticFileCacheTime;
164
165
    /**
166
     * Whether we will use caching for the static files.
167
     * <p>
168
     *     When a user requests a path we don't recognize, we
169
     *     go looking for it.
170
     *     If we have already found it for someone else, it will
171
     *     be in a cache.
172
     * </p>
173
     * <p>
174
     *     However, if we are doing development, it helps to
175
     *     not have caching enabled - it can confuse.
176
     * </p>
177
     */
178
    public final boolean useCacheForStaticFiles;
179
180
    /**
181
     * This is the maximum count of database entries that will
182
     * be appended to a file before switching to a new file.
183
     * Only applies when using DbEngine2.
184
     */
185
    public final int maxAppendCount;
186
187
    /**
188
     * This number is the maximum count of lines we will allow in a consolidated
189
     * database file, to support the needs of the database.
190
     */
191
    public final int maxLinesPerConsolidatedDatabaseFile;
192
193
    /**
194
     * This constant controls the maximum number of elements for the {@link com.renomad.minum.utils.LRUCache}
195
     * we create for use by {@link com.renomad.minum.utils.FileUtils}. As files are read
196
     * by FileUtil's methods, they will be stored in this cache, to avoid reading from
197
     * disk.  However, caching can certainly complicate things, so if you would prefer
198
     * not to store these values in a cache, set {@link #useCacheForStaticFiles} to false.
199
     * <p>
200
     *     The unit here is the number of elements to store in the cache.  Be aware: elements
201
     *     can be of any size, so two caches each having a max size of 1000 elements could be
202
     *     drastically different sizes.
203
     * </p>
204
     */
205
    public final int maxElementsLruCacheStaticFiles;
206
207
    /**
208
     * This flag controls whether the system will write a file to disk
209
     * indicating that the program is running, and delete that file
210
     * when the program ends.  Default is true.
211
     */
212
    public final boolean enableSystemRunningMarker;
213
214
215
    /* ************************ **
216
            HELPER METHODS
217
    ** ************************ */
218
219
220
    /**
221
     * A helper method to remove some redundant boilerplate code for grabbing
222
     * configuration values from minum.config
223
     */
224
    private int getProp(String propName, int propDefault) {
225 1 1. getProp : replaced int return with 0 for com/renomad/minum/state/Constants::getProp → KILLED
        return Integer.parseInt(properties.getProperty(propName, String.valueOf(propDefault)));
226
    }
227
228
    /**
229
     * A helper method to remove some redundant boilerplate code for grabbing
230
     * configuration values from minum.config
231
     */
232
    private boolean getProp(String propName, boolean propDefault) {
233 2 1. getProp : replaced boolean return with false for com/renomad/minum/state/Constants::getProp → TIMED_OUT
2. getProp : replaced boolean return with true for com/renomad/minum/state/Constants::getProp → TIMED_OUT
        return Boolean.parseBoolean(properties.getProperty(propName, String.valueOf(propDefault)));
234
    }
235
236
    /**
237
     * A helper method to remove some redundant boilerplate code for grabbing
238
     * configuration values from minum.config
239
     */
240
    private List<String> getProp(String propName, String propDefault) {
241
        String propValue = properties.getProperty(propName);
242 1 1. getProp : replaced return value with Collections.emptyList for com/renomad/minum/state/Constants::getProp → KILLED
        return extractList(propValue, propDefault);
243
    }
244
245
    /**
246
     * Extract a list out of a comma-delimited string.
247
     * <br>
248
     * Example: a,b, c, d -> List.of("a","b","c","d")
249
     * @param propValue the value of a property
250
     * @param propDefault the default value to use, if the propValue is null
251
     */
252
    static List<String> extractList(String propValue, String propDefault) {
253 1 1. extractList : negated conditional → KILLED
        if (propValue == null) {
254 1 1. extractList : negated conditional → KILLED
            if (propDefault.isBlank()) {
255
                return List.of();
256
            } else {
257 1 1. extractList : replaced return value with Collections.emptyList for com/renomad/minum/state/Constants::extractList → TIMED_OUT
                return Arrays.asList(propDefault.trim().split("\\s*,\\s*"));
258
            }
259
        } else {
260 1 1. extractList : replaced return value with Collections.emptyList for com/renomad/minum/state/Constants::extractList → KILLED
            return Arrays.asList(propValue.trim().split("\\s*,\\s*"));
261
        }
262
    }
263
264
    public static Properties getConfiguredProperties() {
265 1 1. getConfiguredProperties : replaced return value with null for com/renomad/minum/state/Constants::getConfiguredProperties → KILLED
        return getConfiguredProperties("minum.config");
266
    }
267
268
    /**
269
     * Reads properties from minum.config
270
     */
271
    static Properties getConfiguredProperties(String fileName) {
272
        var props = new Properties();
273
        try (FileInputStream fis = new FileInputStream(fileName)) {
274
            System.out.println(TimeUtils.getTimestampIsoInstant() +
275
                    " found properties file at ./minum.config.  Loading properties");
276 1 1. getConfiguredProperties : removed call to java/util/Properties::load → KILLED
            props.load(fis);
277
        } catch (IOException ex) {
278
            System.out.println(CONFIG_ERROR_MESSAGE);
279
        }
280 1 1. getConfiguredProperties : replaced return value with null for com/renomad/minum/state/Constants::getConfiguredProperties → KILLED
        return props;
281
    }
282
283
    /**
284
     * Given a list of strings representing logging levels,
285
     * convert it to a list of enums.  Log levels are enumerated
286
     * in {@link LoggingLevel}.
287
     */
288
    static List<LoggingLevel> convertLoggingStringsToEnums(List<String> logLevels) {
289
        List<String> logLevelStrings = logLevels.stream().map(String::toUpperCase).toList();
290
        List<LoggingLevel> enabledLoggingLevels = new ArrayList<>();
291
        for (LoggingLevel t : LoggingLevel.values()) {
292
            if (logLevelStrings.contains(t.name())) {
293
                enabledLoggingLevels.add(t);
294
            }
295
        }
296 1 1. convertLoggingStringsToEnums : replaced return value with Collections.emptyList for com/renomad/minum/state/Constants::convertLoggingStringsToEnums → KILLED
        return enabledLoggingLevels;
297
    }
298
299
    private static final String CONFIG_ERROR_MESSAGE = """
300
                
301
                
302
                
303
                ----------------------------------------------------------------
304
                ----------------- System Configuration Missing -----------------
305
                ----------------------------------------------------------------
306
                
307
                No properties file found at ./minum.config
308
                
309
                Continuing, using defaults.  See source code for Minum for an
310
                example minum.config, which will allow you to customize behavior.
311
                
312
                ----------------------------------------------------------------
313
                ----------------------------------------------------------------
314
                """;
315
316
    @Override
317
    public boolean equals(Object o) {
318 3 1. equals : negated conditional → KILLED
2. equals : negated conditional → KILLED
3. equals : replaced boolean return with true for com/renomad/minum/state/Constants::equals → KILLED
        if (o == null || getClass() != o.getClass()) return false;
319
        Constants constants = (Constants) o;
320 26 1. equals : negated conditional → KILLED
2. equals : negated conditional → KILLED
3. equals : negated conditional → KILLED
4. equals : negated conditional → KILLED
5. equals : negated conditional → KILLED
6. equals : negated conditional → KILLED
7. equals : negated conditional → KILLED
8. equals : negated conditional → KILLED
9. equals : replaced boolean return with true for com/renomad/minum/state/Constants::equals → KILLED
10. equals : negated conditional → KILLED
11. equals : negated conditional → KILLED
12. equals : negated conditional → KILLED
13. equals : negated conditional → KILLED
14. equals : negated conditional → KILLED
15. equals : negated conditional → KILLED
16. equals : negated conditional → KILLED
17. equals : negated conditional → KILLED
18. equals : negated conditional → KILLED
19. equals : negated conditional → KILLED
20. equals : negated conditional → KILLED
21. equals : negated conditional → KILLED
22. equals : negated conditional → KILLED
23. equals : negated conditional → KILLED
24. equals : negated conditional → KILLED
25. equals : negated conditional → KILLED
26. equals : negated conditional → KILLED
        return serverPort == constants.serverPort && secureServerPort == constants.secureServerPort && maxReadSizeBytes == constants.maxReadSizeBytes && maxReadLineSizeBytes == constants.maxReadLineSizeBytes && socketTimeoutMillis == constants.socketTimeoutMillis && keepAliveTimeoutSeconds == constants.keepAliveTimeoutSeconds && vulnSeekingJailDuration == constants.vulnSeekingJailDuration && isTheBrigEnabled == constants.isTheBrigEnabled && startTime == constants.startTime && staticFileCacheTime == constants.staticFileCacheTime && useCacheForStaticFiles == constants.useCacheForStaticFiles && maxAppendCount == constants.maxAppendCount && maxLinesPerConsolidatedDatabaseFile == constants.maxLinesPerConsolidatedDatabaseFile && maxElementsLruCacheStaticFiles == constants.maxElementsLruCacheStaticFiles && enableSystemRunningMarker == constants.enableSystemRunningMarker && Objects.equals(properties, constants.properties) && Objects.equals(hostName, constants.hostName) && Objects.equals(dbDirectory, constants.dbDirectory) && Objects.equals(staticFilesDirectory, constants.staticFilesDirectory) && Objects.equals(logLevels, constants.logLevels) && Objects.equals(keystorePath, constants.keystorePath) && Objects.equals(keystorePassword, constants.keystorePassword) && Objects.equals(suspiciousErrors, constants.suspiciousErrors) && Objects.equals(suspiciousPaths, constants.suspiciousPaths) && Objects.equals(extraMimeMappings, constants.extraMimeMappings);
321
    }
322
323
    @Override
324
    public int hashCode() {
325 1 1. hashCode : replaced int return with 0 for com/renomad/minum/state/Constants::hashCode → KILLED
        return Objects.hash(properties, serverPort, secureServerPort, hostName, dbDirectory, staticFilesDirectory, logLevels, keystorePath, keystorePassword, maxReadSizeBytes, maxReadLineSizeBytes, socketTimeoutMillis, keepAliveTimeoutSeconds, vulnSeekingJailDuration, isTheBrigEnabled, suspiciousErrors, suspiciousPaths, startTime, extraMimeMappings, staticFileCacheTime, useCacheForStaticFiles, maxAppendCount, maxLinesPerConsolidatedDatabaseFile, maxElementsLruCacheStaticFiles, enableSystemRunningMarker);
326
    }
327
}
328
329
330

Mutations

225

1.1
Location : getProp
Killed by : com.renomad.minum.state.ConstantsTests.testCustomProps(com.renomad.minum.state.ConstantsTests)
replaced int return with 0 for com/renomad/minum/state/Constants::getProp → KILLED

233

1.1
Location : getProp
Killed by : none
replaced boolean return with false for com/renomad/minum/state/Constants::getProp → TIMED_OUT

2.2
Location : getProp
Killed by : none
replaced boolean return with true for com/renomad/minum/state/Constants::getProp → TIMED_OUT

242

1.1
Location : getProp
Killed by : com.renomad.minum.web.SocketWrapperTests.testSendingSingleByte(com.renomad.minum.web.SocketWrapperTests)
replaced return value with Collections.emptyList for com/renomad/minum/state/Constants::getProp → KILLED

253

1.1
Location : extractList
Killed by : com.renomad.minum.state.ConstantsTests.testGetProps_Array(com.renomad.minum.state.ConstantsTests)
negated conditional → KILLED

254

1.1
Location : extractList
Killed by : com.renomad.minum.web.WebEngineTests
negated conditional → KILLED

257

1.1
Location : extractList
Killed by : none
replaced return value with Collections.emptyList for com/renomad/minum/state/Constants::extractList → TIMED_OUT

260

1.1
Location : extractList
Killed by : com.renomad.minum.state.ConstantsTests.testGetProps_Array(com.renomad.minum.state.ConstantsTests)
replaced return value with Collections.emptyList for com/renomad/minum/state/Constants::extractList → KILLED

265

1.1
Location : getConfiguredProperties
Killed by : com.renomad.minum.logging.TestLoggerTests.test_testLoggerQueue_Basic(com.renomad.minum.logging.TestLoggerTests)
replaced return value with null for com/renomad/minum/state/Constants::getConfiguredProperties → KILLED

276

1.1
Location : getConfiguredProperties
Killed by : com.renomad.minum.web.WebFrameworkTests.test_readStaticFile_IOException(com.renomad.minum.web.WebFrameworkTests)
removed call to java/util/Properties::load → KILLED

280

1.1
Location : getConfiguredProperties
Killed by : com.renomad.minum.state.ConstantsTests.testGettingConfiguredPropertiesFromFile_NothingFound(com.renomad.minum.state.ConstantsTests)
replaced return value with null for com/renomad/minum/state/Constants::getConfiguredProperties → KILLED

296

1.1
Location : convertLoggingStringsToEnums
Killed by : com.renomad.minum.state.ConstantsTests.test_convertLoggingStringsToEnums(com.renomad.minum.state.ConstantsTests)
replaced return value with Collections.emptyList for com/renomad/minum/state/Constants::convertLoggingStringsToEnums → KILLED

318

1.1
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
negated conditional → KILLED

2.2
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
negated conditional → KILLED

3.3
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
replaced boolean return with true for com/renomad/minum/state/Constants::equals → KILLED

320

1.1
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
negated conditional → KILLED

2.2
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
negated conditional → KILLED

3.3
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
negated conditional → KILLED

4.4
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
negated conditional → KILLED

5.5
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
negated conditional → KILLED

6.6
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
negated conditional → KILLED

7.7
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
negated conditional → KILLED

8.8
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
negated conditional → KILLED

9.9
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
replaced boolean return with true for com/renomad/minum/state/Constants::equals → KILLED

10.10
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
negated conditional → KILLED

11.11
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
negated conditional → KILLED

12.12
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
negated conditional → KILLED

13.13
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
negated conditional → KILLED

14.14
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
negated conditional → KILLED

15.15
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
negated conditional → KILLED

16.16
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
negated conditional → KILLED

17.17
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
negated conditional → KILLED

18.18
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
negated conditional → KILLED

19.19
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
negated conditional → KILLED

20.20
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
negated conditional → KILLED

21.21
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
negated conditional → KILLED

22.22
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
negated conditional → KILLED

23.23
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
negated conditional → KILLED

24.24
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
negated conditional → KILLED

25.25
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
negated conditional → KILLED

26.26
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
negated conditional → KILLED

325

1.1
Location : hashCode
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
replaced int return with 0 for com/renomad/minum/state/Constants::hashCode → KILLED

Active mutators

Tests examined


Report generated by PIT 1.17.0