xss

package module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 13, 2026 License: MIT Imports: 11 Imported by: 0

README

XSS Middleware

Go Version License Build Status Go Report Card Go Reference

A Gin middleware that automatically sanitizes XSS payloads from incoming HTTP requests before they reach your route handlers. Supports GET query parameters, JSON, form-encoded, and multipart bodies.

XSS filtering is performed by bluemonday. The default policy is StrictPolicy (strips all HTML).

Demo

Input:  {"name": "<script>alert(1)</script>", "bio": "hello"}
Output: {"bio": "hello", "name": ""}

Installation

go get github.com/codeskine/xss-middleware
go mod tidy

Getting Started

package main

import (
    "github.com/gin-gonic/gin"
    xss "github.com/codeskine/xss-middleware"
)

func main() {
    r := gin.Default()
    r.Use(xss.New()) // StrictPolicy · "password" field skipped · 1 MB body cap

    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{"message": "pong"})
    })
    r.Run()
}

Features

What gets sanitized
Request type Content-Type
GET query string keys and values
POST / PUT / PATCH application/json — string values and object keys
POST / PUT / PATCH application/x-www-form-urlencoded — keys and values
POST / PUT / PATCH multipart/form-data — text field keys and values; binary parts (images, video, audio, archives) pass through unchanged

Content-Type matching is case-insensitive. All other methods and content types pass through without modification.

Configuration options
Option Description
WithStrictPolicy() Use bluemonday.StrictPolicy — strips all HTML (default)
WithUGCPolicy() Use bluemonday.UGCPolicy — keeps safe tags like <b>, <i>, <a>
WithPolicy(p) Use a custom *bluemonday.Policy
SkipFields(fields...) Skip value sanitization for named fields ("password" is always skipped)
WithMaxBodySize(n) Cap JSON/form body; requests over the limit return 413 (default: 1 MB)
WithMaxMultipartSize(n) Cap multipart body; requests over the limit return 413 (default: 32 MB)
Custom options
r.Use(xss.New(
    xss.WithUGCPolicy(),
    xss.SkipFields("token", "create_date"),
    xss.WithMaxBodySize(512 * 1024),    // 512 KB for JSON / form
    xss.WithMaxMultipartSize(10 << 20), // 10 MB for multipart
))
Bring your own bluemonday policy
p := bluemonday.NewPolicy()
p.AllowElements("b", "i", "em")

r.Use(xss.New(xss.WithPolicy(p)))

Contributing

See CONTRIBUTING.md.

Acknowledgements

Forked from dvwright/xss-mw.
XSS filtering by microcosm-cc/bluemonday.

License

MIT

Documentation

Overview

Package xss provides a Gin middleware that sanitizes XSS payloads from incoming HTTP requests before they reach route handlers.

Quick start

r := gin.Default()
r.Use(xss.New()) // StrictPolicy, "password" skipped, 1 MB body cap

Supported content types

  • GET: query string keys and values
  • POST/PUT/PATCH with application/json: all string values and object keys
  • POST/PUT/PATCH with application/x-www-form-urlencoded: all keys and values
  • POST/PUT/PATCH with multipart/form-data: text field keys and values; binary parts (images, video, audio, archives) pass through unchanged

Content-Type matching is case-insensitive. All other methods and content types pass through without modification.

Sanitization

String values and object keys are sanitized using bluemonday. The default policy (bluemonday.StrictPolicy) strips all HTML tags and attributes, leaving plain text only. Use WithUGCPolicy or WithPolicy to configure a different policy.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func New

func New(opts ...Option) gin.HandlerFunc

New returns a Gin middleware handler that sanitizes XSS payloads from incoming HTTP requests before they reach route handlers. It is safe for concurrent use; all configuration is captured at construction time and each request operates on its own local state.

Requests that exceed the configured body size limit are rejected with 413. Malformed JSON, form data, or multipart boundaries are rejected with 400. All other errors (e.g. read failures) are also rejected with 400.

Defaults: bluemonday.StrictPolicy, "password" field always skipped, 1 MB cap for JSON/form bodies, 32 MB cap for multipart bodies.

Example

ExampleNew shows basic JSON sanitization using the default StrictPolicy. All HTML tags and their content are stripped from string values and object keys.

package main

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"

	"github.com/gin-gonic/gin"

	xss "github.com/codeskine/xss-middleware"
)

func main() {
	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.Use(xss.New())
	r.POST("/", func(c *gin.Context) {
		b, _ := io.ReadAll(c.Request.Body)
		fmt.Println(string(b))
	})

	req := httptest.NewRequest(http.MethodPost, "/",
		strings.NewReader(`{"name":"<script>alert(1)</script>"}`))
	req.Header.Set("Content-Type", "application/json")
	r.ServeHTTP(httptest.NewRecorder(), req)
}
Output:
{"name":""}
Example (UgcPolicy)

ExampleNew_ugcPolicy demonstrates UGCPolicy, which keeps safe formatting tags (b, i, em, a, img, …) while still stripping scripts and dangerous attributes.

package main

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"

	"github.com/gin-gonic/gin"

	xss "github.com/codeskine/xss-middleware"
)

func main() {
	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.Use(xss.New(xss.WithUGCPolicy()))
	r.POST("/", func(c *gin.Context) {
		b, _ := io.ReadAll(c.Request.Body)
		fmt.Println(string(b))
	})

	req := httptest.NewRequest(http.MethodPost, "/",
		strings.NewReader(`{"name":"hello <script>evil()</script>"}`))
	req.Header.Set("Content-Type", "application/json")
	r.ServeHTTP(httptest.NewRecorder(), req)
}
Output:
{"name":"hello "}

Types

type Option

type Option func(*config)

Option is a functional option that configures the middleware returned by New. Options are applied in the order they are passed; later options override earlier ones.

func SkipFields

func SkipFields(fields ...string) Option

SkipFields registers field names whose values bypass sanitization. "password" is always skipped regardless of this option.

Skipping applies to JSON object values, form-encoded field values, and multipart text field values. Keys are still sanitized regardless of whether the corresponding value is skipped.

Field names are matched case-sensitively against the original key.

Example

ExampleSkipFields shows how to exclude sensitive fields from value sanitization. Keys are still sanitized; only values of the named fields are passed through unchanged.

package main

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"

	"github.com/gin-gonic/gin"

	xss "github.com/codeskine/xss-middleware"
)

func main() {
	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.Use(xss.New(xss.SkipFields("token")))
	r.POST("/", func(c *gin.Context) {
		b, _ := io.ReadAll(c.Request.Body)
		fmt.Println(string(b))
	})

	body := `{"token":"eyJhbGciOiJIUzI1NiJ9.payload.sig","name":"<script>xss</script>"}`
	req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	r.ServeHTTP(httptest.NewRecorder(), req)
}
Output:
{"name":"","token":"eyJhbGciOiJIUzI1NiJ9.payload.sig"}

func WithMaxBodySize

func WithMaxBodySize(n int64) Option

WithMaxBodySize caps the request body size for JSON and form-encoded requests. Requests whose body exceeds n bytes are rejected with HTTP 413. The default limit is 1 MB (1 << 20 bytes). Values <= 0 are ignored.

func WithMaxMultipartSize

func WithMaxMultipartSize(n int64) Option

WithMaxMultipartSize caps the request body size for multipart/form-data requests. Requests whose body exceeds n bytes are rejected with HTTP 413. The default limit is 32 MB (32 << 20 bytes). Values <= 0 are ignored.

func WithPolicy

func WithPolicy(p *bluemonday.Policy) Option

WithPolicy configures the middleware to use p for HTML sanitization, overriding any previously set policy option. Use this when neither WithStrictPolicy nor WithUGCPolicy fits — for example, to allow a specific set of elements for a rich-text field.

The caller is responsible for ensuring p is safe for concurrent use.

Example

ExampleWithPolicy demonstrates how to supply a fully custom bluemonday policy.

package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"

	"github.com/gin-gonic/gin"
	"github.com/microcosm-cc/bluemonday"

	xss "github.com/codeskine/xss-middleware"
)

func main() {
	gin.SetMode(gin.TestMode)
	p := bluemonday.NewPolicy()
	p.AllowElements("b", "i", "em")

	r := gin.New()
	r.Use(xss.New(xss.WithPolicy(p)))
	r.GET("/", func(c *gin.Context) {
		fmt.Println(c.Query("q"))
	})

	req := httptest.NewRequest(http.MethodGet, "/?q=<em>ok</em><script>bad</script>", nil)
	r.ServeHTTP(httptest.NewRecorder(), req)
}
Output:
<em>ok</em>

func WithStrictPolicy

func WithStrictPolicy() Option

WithStrictPolicy configures the middleware to use bluemonday.StrictPolicy, which strips all HTML tags and attributes, leaving plain text only. This is the default when no policy option is specified.

func WithUGCPolicy

func WithUGCPolicy() Option

WithUGCPolicy configures the middleware to use bluemonday.UGCPolicy, which allows a curated subset of HTML safe for user-generated content: basic formatting (b, i, em, strong), links (a with rel="nofollow"), images with safe src attributes, and similar. Scripts, style attributes, and other dangerous constructs are always stripped.

Use this policy when the application renders sanitized content as HTML, for example in comment sections or WYSIWYG editors.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL