Move SSD remote config predicates to typed Kotlin
ober
f9166cd134e28c23ad0dfa0782b6cf38ece85513
--- a/templates/ssd-review.ss +++ b/templates/ssd-review.ss @@ -1535,10 +1535,15 @@ TruthIndexEntry-sourceName TruthIndexEntry-canonicalSourceName TruthIndexEntry-page TruthIndexEntry-dpi TruthIndexEntry-groupCount TruthIndexEntry-truthTime betterTruthIndex - oldDestinationBytes urlEffectivePort) + fileExists fileIsRegular destinationNewFileDelta + oldDestinationBytes urlEffectivePort + remoteBearerTokenPresent remoteBearerTokenValid + remotePinPresent remoteRelativePathSafe) (type File) (type URL) (type Int32) + (extern (fileExistsRaw (file : File)) : Bool + (kotlin-member-call exists)) (extern (fileIsFile (file : File)) : Bool (kotlin-member-get isFile)) (extern (fileLength (file : File)) : Int @@ -1572,11 +1577,30 @@ (TruthIndexEntry-groupCount current)) (> (TruthIndexEntry-truthTime candidate) (TruthIndexEntry-truthTime current))))) + (def (fileExists (file : File)) : Bool + (fileExistsRaw file)) + (def (fileIsRegular (file : File)) : Bool + (and (fileExists file) (fileIsFile file))) + (def (destinationNewFileDelta (destination : File)) : Int32 + (if (fileExists destination) (int32 0) (int32 1))) (def (oldDestinationBytes (destination : File)) : Int (if (fileIsFile destination) (fileLength destination) (int 0))) (def (urlEffectivePort (url : URL)) : Int32 (let ((port (urlPort url))) - (if (>= port (int32 0)) port (urlDefaultPort url)))))) + (if (>= port (int32 0)) port (urlDefaultPort url)))) + (def (remoteBearerTokenPresent (token : String)) : Bool + (not (string-blank? token))) + (def (remoteBearerTokenValid (token : String)) : Bool + (or (string-blank? token) + (and (<= (string-length token) 4096) + (and (not (string-contains? token "\n")) + (not (string-contains? token "\r")))))) + (def (remotePinPresent (pinText : String)) : Bool + (not (string-blank? pinText))) + (def (remoteRelativePathSafe (relative : String)) : Bool + (and (not (string-blank? relative)) + (and (not (string-starts-with? relative "/")) + (not (string-contains? relative ".."))))))) (typed-kotlin-file "com/sfb/ssdreview/JsonGeometry.kt" (kotlin-imports (org json JSONArray)) @@ -7790,7 +7814,7 @@ " require(bytes in 0..MAX_ZIP_ENTRY_BYTES) { \"Per-file storage limit exceeded\" }" " val stats = storageStats()" " val oldBytes = oldDestinationBytes(destination)" - " val newFiles = stats.files + if (destination.exists()) 0 else 1" + " val newFiles = stats.files + destinationNewFileDelta(destination)" " val newBytes = stats.bytes - oldBytes + bytes" " require(newFiles <= MAX_STORAGE_FILES && newBytes <= MAX_STORAGE_BYTES) {" " \"Storage quota exceeded\"" @@ -7832,12 +7856,12 @@ " val url = URL(endpoint.trimEnd('/'))" " require(url.protocol == \"https\" && url.host.isNotBlank()) { \"HTTPS endpoint required\" }" " require(url.userInfo == null && url.query == null && url.ref == null) { \"Unsafe endpoint URL\" }" - " if (token.isNotBlank()) {" - " require(token.length <= MAX_AUTH_TOKEN_CHARS && !token.contains('\\n') && !token.contains('\\r')) {" + " if (remoteBearerTokenPresent(token)) {" + " require(remoteBearerTokenValid(token)) {" " \"Invalid remote authentication token\"" " }" " }" - " val pin = if (pinText.isNotBlank()) {" + " val pin = if (remotePinPresent(pinText)) {" " val encodedPin = pinText.removePrefix(\"sha256/\")" " Base64.decode(encodedPin, Base64.NO_WRAP).also {" " require(it.size == 32) { \"SPKI SHA-256 pin must decode to 32 bytes\" }" @@ -7850,7 +7874,7 @@ " }" "" " private fun remoteUrl(relative: String): URL {" - " require(relative.isNotBlank() && !relative.startsWith('/') && !relative.contains(\"..\"))" + " require(remoteRelativePathSafe(relative))" " val config = remoteConfig ?: throw IllegalStateException(\"Remote SSD service is not locally configured\")" " val candidate = URL(config.apiUrl.toString().trimEnd('/') + \"/\" + relative)" " val basePort = urlEffectivePort(config.apiUrl)"