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