Ctrl K
ComunidadGuías…

internal/htmlsanitize/htmlsanitize.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package htmlsanitize

import (
	"bytes"
	"encoding/json"
	"html"
	"io"
	"mime"
	"strings"
)

// String escapes text before it is returned to browser-facing management clients.
func String(value string) string {
	return html.EscapeString(value)
}

// Strings escapes each string in values while preserving order.
func Strings(values []string) []string {
	out := make([]string, 0, len(values))
	for _, value := range values {
		out = append(out, String(value))
	}
	return out
}

// JSONBody escapes all string values in a JSON document.
func JSONBody(body []byte) ([]byte, bool) {
	trimmed := bytes.TrimSpace(body)
	if len(trimmed) == 0 {
		return body, false
	}

	decoder := json.NewDecoder(bytes.NewReader(trimmed))
	decoder.UseNumber()
	var value any
	if errDecode := decoder.Decode(&value); errDecode != nil {
		return body, false
	}
	var extra any
	if errExtra := decoder.Decode(&extra); errExtra != io.EOF {
		return body, false
	}

	var buffer bytes.Buffer
	encoder := json.NewEncoder(&buffer)
	encoder.SetEscapeHTML(false)
	if errEncode := encoder.Encode(JSONValue(value)); errEncode != nil {
		return body, false
	}
	return bytes.TrimSuffix(buffer.Bytes(), []byte("\n")), true
}

// JSONBodyIfLikely escapes JSON bodies when the content type or body shape indicates JSON.
func JSONBodyIfLikely(body []byte, contentType string) ([]byte, bool) {
	if IsJSONContentType(contentType) || LooksLikeJSON(body) {
		return JSONBody(body)
	}
	return body, false
}

// JSONValue recursively escapes string values in JSON-compatible data.
func JSONValue(value any) any {
	switch typed := value.(type) {
	case string:
		return String(typed)
	case []any:
		out := make([]any, len(typed))
		for index, item := range typed {
			out[index] = JSONValue(item)
		}
		return out
	case map[string]any:
		out := make(map[string]any, len(typed))
		for key, item := range typed {
			out[key] = JSONValue(item)
		}
		return out
	default:
		return value
	}
}

// IsJSONContentType reports whether contentType is application/json or a +json type.
func IsJSONContentType(contentType string) bool {
	mediaType, _, errParse := mime.ParseMediaType(strings.TrimSpace(contentType))
	if errParse != nil {
		mediaType = strings.TrimSpace(contentType)
	}
	mediaType = strings.ToLower(mediaType)
	return mediaType == "application/json" || strings.HasSuffix(mediaType, "+json")
}

// LooksLikeJSON reports whether body starts with an object or array JSON marker.
func LooksLikeJSON(body []byte) bool {
	trimmed := bytes.TrimSpace(body)
	if len(trimmed) == 0 {
		return false
	}
	return trimmed[0] == '{' || trimmed[0] == '['
}