<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #48721 Part 2 of https://github.com/fleetdm/fleet/issues/43488 # Checklist for submitter - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters. ## Testing - [x] Added/updated automated tests - [x] QA'd all new/changed functionality manually ## Database migrations - [x] Ensured the correct collation is explicitly set for character columns (`COLLATE utf8mb4_unicode_ci`). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Windows devices can now create and securely escrow managed local account passwords during enrollment. * Added Windows managed local account status and password availability to host details. * Device-reported setup errors are surfaced with helpful details. * Account creation is automatically requested when supported by the device, plan, and configuration. * **Bug Fixes** * Windows accounts are excluded from password rotation workflows. * Re-enrollment correctly triggers account creation when needed. * Passwords remain available when settings change after enrollment. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
80 lines
2.4 KiB
Go
80 lines
2.4 KiB
Go
package str
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
// MaxErrorResponseBytes is the maximum number of bytes captured from a remote
|
|
// error response body before the string is truncated.
|
|
const MaxErrorResponseBytes = 512 * 1024
|
|
|
|
// TruncateErrorResponse caps s at MaxErrorResponseBytes bytes. When the string
|
|
// is longer it is cut at a valid UTF-8 boundary and " [truncated]" is appended.
|
|
func TruncateErrorResponse(s string) string {
|
|
if len(s) <= MaxErrorResponseBytes {
|
|
return s
|
|
}
|
|
cut := s[:MaxErrorResponseBytes]
|
|
// Step back from the cut point to ensure we end on a valid rune boundary.
|
|
for !utf8.ValidString(cut) {
|
|
cut = cut[:len(cut)-1]
|
|
}
|
|
return cut + " [truncated]"
|
|
}
|
|
|
|
// TruncateRunes returns s shortened to at most maxRunes characters, preserving the start of the string. Use it before
|
|
// storing device- or user-supplied text in a column: utf8mb4 VARCHAR(N) in MySQL counts characters (runes), not bytes,
|
|
// so slicing on runes both matches the column constraint and cannot cut a multi-byte character in half and produce invalid UTF-8.
|
|
func TruncateRunes(s string, maxRunes int) string {
|
|
if len(s) <= maxRunes {
|
|
// Fast path: a string of at most maxRunes bytes cannot exceed maxRunes characters.
|
|
return s
|
|
}
|
|
if utf8.RuneCountInString(s) <= maxRunes {
|
|
return s
|
|
}
|
|
return string([]rune(s)[:maxRunes])
|
|
}
|
|
|
|
func SplitAndTrim(s string, delimiter string, removeEmpty bool) []string {
|
|
parts := strings.Split(s, delimiter)
|
|
cleaned := make([]string, 0, len(parts))
|
|
for _, part := range parts {
|
|
part = strings.TrimSpace(part)
|
|
if !removeEmpty || part != "" {
|
|
cleaned = append(cleaned, part)
|
|
}
|
|
}
|
|
return cleaned
|
|
}
|
|
|
|
// ParseUintList parses a comma-separated string of unsigned integers, trimming
|
|
// whitespace around each value and silently skipping values that cannot be
|
|
// parsed. Returns nil for an empty input.
|
|
func ParseUintList(s string) []uint {
|
|
if s == "" {
|
|
return nil
|
|
}
|
|
parts := strings.Split(s, ",")
|
|
result := make([]uint, 0, len(parts))
|
|
for _, p := range parts {
|
|
p = strings.TrimSpace(p)
|
|
if v, err := strconv.ParseUint(p, 10, 0); err == nil {
|
|
result = append(result, uint(v))
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// ParseStringList parses a comma-separated string into a slice of strings,
|
|
// trimming whitespace and dropping empty values. Returns nil for an empty
|
|
// input.
|
|
func ParseStringList(s string) []string {
|
|
if s == "" {
|
|
return nil
|
|
}
|
|
return SplitAndTrim(s, ",", true)
|
|
}
|