HTML sanitization using struct tags. Powered by bluemonday.
Becoming fed up with manually sanitizing every string field in complex nested structures, I created a tag-based approach to handle HTML sanitization declaratively.
A sane input sanitization experience:
- Tag-based - just add the tag
- Recursive - handles nested structs, slices, maps, pointers, generics
- Built-in policies:
strictandugcpowered by bluemonday policies - Extensible with policies or custom functions
go get -u github.com/kraciasty/stzrimport "github.com/kraciasty/stzr"
type Character struct {
Name string `sanitize:"strict"`
Bio string `sanitize:"ugc"`
}
character := &Character{
Name: `<script>alert('morty')</script>Rick <b>Sanchez</b>`,
Bio: `Genius <b>scientist</b> from dimension C-137 <script>alert('wubba lubba dub dub')</script>`,
}
stzr.SanitizeStruct(character)
// Name: "Rick Sanchez"
// Bio: "Genius <b>scientist</b> from dimension C-137"Note
Nested structures work automatically - structs, slices, maps, and pointers are processed recursively:
type Episode struct {
Title string `sanitize:"strict"`
Description string `sanitize:"ugc"`
Comments []Comment // Nested structs handled automatically
SkippedComment Comment `sanitize:"-"` // Skipped
}
type Comment struct {
Text string `sanitize:"ugc"`
Author string `sanitize:"strict"`
Dimension string `sanitize:"strict"`
}clean, err := stzr.SanitizeString("ugc", `<script>bad</script>Wubba lubba <b>dub dub</b>!`)
// Returns: "Wubba lubba <b>dub dub</b>!"Custom Policies
minimal := bluemonday.NewPolicy().AllowElements("b", "i")
links := bluemonday.NewPolicy().
AllowElements("a").
AllowAttrs("href").
OnElements("a")
sanitizer := stzr.New(
stzr.WithPolicy("minimal", minimal),
stzr.WithPolicy("links", links),
)
type Blog struct {
Content string `sanitize:"minimal"`
Footer string `sanitize:"links"`
}Integration with oapi-codegen
Add x-oapi-codegen-extra-tags to your OpenAPI spec:
# openapi.yml
components:
schemas:
CreateCharacterRequest:
type: object
properties:
name:
type: string
x-oapi-codegen-extra-tags: # <---
sanitize: "strict" # strict policy
backstory:
type: string
x-oapi-codegen-extra-tags: # <---
sanitize: "ugc" # ugc policyGenerates:
type CreateCharacterRequest struct {
Name string `json:"name" sanitize:"strict"`
Backstory string `json:"backstory" sanitize:"ugc"`
}Integration with Protocol Buffers
Use something like protoc-go-inject-tag to add sanitization tags to generated protobuf structs:
// character.proto
message CreateCharacterRequest {
string name = 1; // @inject_tag: sanitize:"strict"
string backstory = 2; // @inject_tag: sanitize:"ugc"
}After generation, run:
protoc-go-inject-tag -input="*.pb.go"Results in:
type CreateCharacterRequest struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty" sanitize:"strict"`
Backstory string `protobuf:"bytes,2,opt,name=backstory,proto3" json:"backstory,omitempty" sanitize:"ugc"`
}For the Go code documentation reference - check pkg.go.dev.
Contributions are welcome! If you find any issues or want to enhance the project, please submit a pull request.
This project is licensed under the MIT License. See the LICENSE file for more information.