1package com.peachpasswords.autofill2345678910111213data class PasswordPolicy(14 val minLength: Int? = null,15 val maxLength: Int? = null,16) {17 1819202122 fun generationLength(default: Int = DEFAULT_LENGTH, hardMax: Int = HARD_MAX): Int {23 val target = (maxLength ?: default).coerceAtMost(hardMax)24 val raised = minLength?.let { target.coerceAtLeast(it) } ?: target25 val capped = maxLength?.let { raised.coerceAtMost(it) } ?: raised26 return capped.coerceAtLeast(MIN_GENERATION_LENGTH)27 }2829 30 val isWeaklyConstrained: Boolean31 get() = maxLength != null && maxLength < MIN_GENERATION_LENGTH3233 34 fun constraintNote(length: Int): String = when {35 isWeaklyConstrained -> "This site limits passwords to $maxLength characters (weak)"36 maxLength != null && length >= maxLength -> "This site allows max $maxLength characters"37 minLength != null && length <= minLength -> "This site requires at least $minLength characters"38 else -> ""39 }404142 companion object {43 const val DEFAULT_LENGTH = 2044 const val MIN_GENERATION_LENGTH = 1245 const val HARD_MAX = 2564647 484950515253 fun fromHtmlAttributes(attrs: Map<String, String>): PasswordPolicy {54 var min = attrs["minlength"]?.toIntOrNull()?.takeIf { it > 0 }55 var max = attrs["maxlength"]?.toIntOrNull()?.takeIf { it > 0 }56 val pattern = attrs["pattern"]57 if (pattern != null && (min == null || max == null)) {58 val range = Regex("\\{(\\d+),(\\d+)\\}").find(pattern)59 val minOnly = Regex("\\{(\\d+),\\}").find(pattern)60 val exact = Regex("\\{(\\d+)\\}").find(pattern)61 when {62 range != null -> {63 if (min == null) min = range.groupValues[1].toIntOrNull()64 if (max == null) max = range.groupValues[2].toIntOrNull()65 }66 minOnly != null -> if (min == null) min = minOnly.groupValues[1].toIntOrNull()67 exact != null -> {68 val n = exact.groupValues[1].toIntOrNull()69 if (min == null) min = n70 if (max == null) max = n71 }72 }73 }74 return PasswordPolicy(minLength = min, maxLength = max)75 }76 }77}