sanitizer

package
v0.0.0-...-6067653 Latest Latest
Warning

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

Go to latest
Published: Aug 11, 2025 License: Apache-2.0 Imports: 9 Imported by: 0

README

Sanitizer Package

The sanitizer package provides functions for cleaning and normalizing input data in Go applications. Unlike validation which checks if data meets criteria, sanitization transforms data into a clean, standardized format.

Overview

This package focuses on data transformation and cleaning rather than validation. All functions are direct transformations that return cleaned data.

Features

  • String Cleaning: Trim whitespace, normalize case, remove unwanted characters
  • Length Management: Truncate strings to maximum lengths
  • Content Filtering: Remove HTML, control characters, or specific character sets
  • Format Normalization: Convert multi-line text to single lines, normalize whitespace
  • Character Set Filtering: Keep only alphanumeric, alphabetic, or numeric characters

Core Functions

Apply Pattern

The sanitizer package provides a generic Apply function for chaining multiple sanitization operations and a Compose function for creating reusable transformation pipelines.

Core Functions
  • Apply[T any](value T, transforms ...func(T) T) T - Applies multiple transformations sequentially with type safety
  • Compose[T any](transforms ...func(T) T) func(T) T - Creates reusable transformation functions from multiple transforms
String Sanitization Functions
Basic String Operations
  • Trim(s string) string - Remove leading and trailing whitespace
  • ToLower(s string) string - Convert to lowercase
  • ToUpper(s string) string - Convert to uppercase
  • ToTitle(s string) string - Convert to title case
  • TrimToLower(s string) string - Trim and convert to lowercase
  • TrimToUpper(s string) string - Trim and convert to uppercase
Length and Content Management
  • MaxLength(s string, maxLen int) string - Truncate to maximum length
  • RemoveExtraWhitespace(s string) string - Normalize whitespace to single spaces
  • RemoveControlChars(s string) string - Remove control characters (except \n, \r, \t)
  • StripHTML(s string) string - Remove HTML tags and unescape entities
  • SingleLine(s string) string - Convert multi-line text to single line
Character Filtering
  • RemoveChars(s, chars string) string - Remove specified characters
  • ReplaceChars(s, old, new string) string - Replace specified characters
  • KeepAlphanumeric(s string) string - Keep only letters, digits, and spaces
  • KeepAlpha(s string) string - Keep only letters and spaces
  • KeepDigits(s string) string - Keep only numeric digits
  • ToKebabCase(s string) string - Convert to kebab-case
  • ToSnakeCase(s string) string - Convert to snake_case
  • ToCamelCase(s string) string - Convert to camelCase

Usage Examples

Apply Pattern Usage
// Apply multiple transformations in sequence
input := "  HELLO    WORLD!@#  "
clean := sanitizer.Apply(input,
    sanitizer.Trim,
    sanitizer.RemoveExtraWhitespace,
    sanitizer.ToLower,
)
// Result: "hello world!@#"

// Create reusable transformation functions
emailRule := sanitizer.Compose(
    sanitizer.Trim,
    sanitizer.ToLower,
)
cleanEmail := emailRule("  [email protected]  ") // "[email protected]"

// Use composed rules in Apply
result := sanitizer.Apply(dirtyEmail, emailRule)
Basic String Cleaning
// Direct function calls
input := "  Hello World!  "
clean := sanitizer.TrimToLower(input) // "hello world!"

// Normalize whitespace
messy := "hello    world\n\ntest"
normalized := sanitizer.RemoveExtraWhitespace(messy) // "hello world test"
Length Management
// Truncate long descriptions
description := "This is a very long description that needs to be shortened"
short := sanitizer.MaxLength(description, 20) // "This is a very long "
Content Filtering
// Clean HTML content
htmlContent := "<p>Hello <strong>world</strong>!</p>"
plainText := sanitizer.StripHTML(htmlContent) // "Hello world!"

// Remove unwanted characters
phone := "123-456-7890"
digits := sanitizer.KeepDigits(phone) // "1234567890"
Form Input Sanitization
type UserInput struct {
    Name        string
    Email       string
    Description string
    Phone       string
}

// Using direct function calls
func (u *UserInput) Sanitize() {
    u.Name = sanitizer.TrimToTitle(u.Name)
    u.Email = sanitizer.TrimToLower(u.Email)
    u.Description = sanitizer.MaxLength(sanitizer.RemoveExtraWhitespace(u.Description), 500)
    u.Phone = sanitizer.KeepDigits(u.Phone)
}

// Using Apply pattern for more complex sanitization
func (u *UserInput) SanitizeWithApply() {
    u.Name = sanitizer.Apply(u.Name,
        sanitizer.Trim,
        sanitizer.RemoveExtraWhitespace,
        sanitizer.ToTitle,
    )

    u.Email = sanitizer.Apply(u.Email,
        sanitizer.TrimToLower,
    )

    u.Description = sanitizer.Apply(u.Description,
        sanitizer.Trim,
        sanitizer.RemoveExtraWhitespace,
        func(s string) string { return sanitizer.MaxLength(s, 500) },
    )

    u.Phone = sanitizer.Apply(u.Phone,
        sanitizer.KeepDigits,
    )
}

// Using Compose for reusable transformation pipelines
func (u *UserInput) SanitizeWithCompose() {
    nameRule := sanitizer.Compose(sanitizer.Trim, sanitizer.RemoveExtraWhitespace, sanitizer.ToTitle)
    emailRule := sanitizer.Compose(sanitizer.TrimToLower)
    descRule := sanitizer.Compose(
        sanitizer.Trim,
        sanitizer.RemoveExtraWhitespace,
        func(s string) string { return sanitizer.MaxLength(s, 500) },
    )

    u.Name = nameRule(u.Name)
    u.Email = emailRule(u.Email)
    u.Description = descRule(u.Description)
    u.Phone = sanitizer.KeepDigits(u.Phone)
}
Character Set Filtering
// Extract only letters for name processing
input := "John123!@#"
name := sanitizer.KeepAlpha(input) // "John"

// Get alphanumeric for usernames
username := "user_name@123"
clean := sanitizer.KeepAlphanumeric(username) // "user name123"
Multi-line Text Processing
// Convert multi-line input to single line
multiLine := `First line
Second line
Third line`
singleLine := sanitizer.SingleLine(multiLine) // "First line Second line Third line"

Best Practices

  1. Sanitize Early: Clean input data as soon as it enters your system
  2. Use Apply for Multiple Transforms: For multiple transformations, use Apply for clearer sequential processing
  3. Create Reusable Pipelines: Use Compose to create reusable transformation functions
  4. Choose the Right Pattern: Use direct functions for simple cases, Apply for sequences, Compose for reusability
  5. Preserve Intent: Choose sanitization that preserves the intended meaning of data
  6. Document Transformations: Be clear about what sanitization is applied to each field
  7. Test Edge Cases: Verify behavior with empty strings, unicode, and edge cases

Design Principles

  • Simple API: Direct function calls, generic Apply pattern, and Compose for reusability
  • Type Safe: Generic functions preserve input types throughout transformations
  • No Side Effects: All functions are pure transformations
  • Unicode Safe: Proper handling of unicode characters and runes
  • Performance Focused: Efficient implementations using standard library
  • Composable: Functions can be chained, combined, and reused through functional composition
  • Clean: No unnecessary complexity like field names or structured errors

Supported Types

Currently supports string sanitization. Future phases will include:

  • Format-specific sanitization (email, phone, URL)
  • Security-focused sanitization (XSS prevention)
  • Numeric sanitization with generics
  • Collection sanitization (slices, maps)

Documentation

Overview

Package sanitizer provides a comprehensive collection of helper functions for cleaning, normalising and securing data of various kinds.

The functions are grouped conceptually into several areas:

  • Strings – trimming, case conversion, whitespace normalisation, masking and conversion between common naming conventions (snake_case, kebab-case, camelCase, …).

  • Collections – utilities for filtering, deduplicating, transforming and limiting slices and maps.

  • Numeric – generic helpers for clamping, rounding and otherwise constraining numeric values.

  • Format – normalisation helpers for e-mail addresses, phone numbers, credit-card numbers, URLs, postal codes and similar user input.

  • Security – defensive routines that escape or strip dangerous content (HTML tags & attributes, SQL/LDAP/shell metacharacters, path traversal, …) and that mask sensitive data before it is logged or rendered.

The package is completely stateless and depends only on the Go standard library (plus the `maps` package from Go 1.21+). All helpers are implemented as small, focused functions that can be freely combined. For convenience the higher-order Apply and Compose helpers allow the creation of sanitisation pipelines:

clean := sanitizer.Compose(
    sanitizer.Trim,
    sanitizer.NormalizeWhitespace,
    sanitizer.ToLower,
)

safe := clean("  Mixed CASE   Input\n") // "mixed case input"

Usage

Import the package using its module-qualified path:

import "github.com/dmitrymomot/saaskit/pkg/sanitizer"

Example – e-mail address normalisation:

raw   := "  [email protected] "
email := sanitizer.NormalizeEmail(raw)
// email == "[email protected]"

Example – securely rendering un-trusted HTML input:

safeHTML := sanitizer.PreventXSS(userInput)

Error handling

None of the helpers returns an error – they always fall back to a safe result (usually the original input or an empty string) if sanitisation fails.

Performance

All operations are implemented with efficiency in mind and allocate only what is necessary. Because there is no global state the helpers are safe for use from multiple goroutines concurrently.

See the package-level examples and individual function documentation for further details.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Abs

func Abs[T Signed](value T) T

Abs returns the absolute value of a signed numeric value.

func Apply

func Apply[T any](value T, transforms ...func(T) T) T

Apply creates functional composition pipeline for sanitization transformations. Useful for building complex sanitization chains while maintaining type safety.

func Clamp

func Clamp[T Numeric](value T, min T, max T) T

Clamp constrains a numeric value to be within the specified range [min, max]. If the value is less than min, it returns min. If greater than max, it returns max.

func ClampMax

func ClampMax[T Numeric](value T, max T) T

ClampMax ensures a numeric value is not greater than the specified maximum.

func ClampMin

func ClampMin[T Numeric](value T, min T) T

ClampMin ensures a numeric value is not less than the specified minimum.

func ClampPrecision

func ClampPrecision[T Float](value T, min T, max T, decimalPlaces int) T

ClampPrecision limits floating-point precision by rounding to specified decimal places and ensuring the result is within the given range.

func ClampToNonNegative

func ClampToNonNegative[T Signed](value T) T

ClampToNonNegative ensures the value is non-negative (>= 0). Returns 0 if value < 0.

func ClampToPositive

func ClampToPositive[T Numeric](value T) T

ClampToPositive ensures the value is positive (> 0). Returns 1 if value <= 0.

func CleanStringMap

func CleanStringMap(m map[string]string) map[string]string

CleanStringMap applies standard form data cleanup: lowercase keys, trim values, remove empties.

func CleanStringSlice

func CleanStringSlice(slice []string) []string

CleanStringSlice applies standard form data cleanup pipeline.

func Compose

func Compose[T any](transforms ...func(T) T) func(T) T

Compose creates reusable sanitization pipelines that can be stored and reused. Preferred over repeated Apply calls when the same transformation chain is used multiple times.

func Deduplicate

func Deduplicate[T comparable](slice []T) []T

Deduplicate preserves first occurrence order to maintain user intent in form submissions.

func DeduplicateStrings

func DeduplicateStrings(slice []string) []string

func DeduplicateStringsIgnoreCase

func DeduplicateStringsIgnoreCase(slice []string) []string

DeduplicateStringsIgnoreCase preserves original casing of first occurrence.

func EscapeHTML

func EscapeHTML(s string) string

EscapeHTML escapes HTML special characters to prevent XSS attacks.

func EscapeSQLString

func EscapeSQLString(s string) string

EscapeSQLString escapes single quotes in SQL strings to prevent injection.

func ExtractDomain

func ExtractDomain(rawURL string) string

ExtractDomain assumes HTTPS protocol for parsing; validates host to prevent invalid domains.

func ExtractEmailDomain

func ExtractEmailDomain(email string) string

func ExtractMapKeys

func ExtractMapKeys[K comparable, V any](m map[K]V) []K

func ExtractMapValues

func ExtractMapValues[K comparable, V any](m map[K]V) []V

func ExtractNumbers

func ExtractNumbers(s string) string

ExtractNumbers concatenates all digit sequences, useful for ID extraction from mixed content.

func ExtractPhoneDigits

func ExtractPhoneDigits(phone string) string

func FilterEmpty

func FilterEmpty(slice []string) []string

FilterEmpty removes whitespace-only entries to prevent empty form fields from polluting data.

func FilterEmptyMapValues

func FilterEmptyMapValues[K comparable](m map[K]string) map[K]string

FilterEmptyMapValues removes whitespace-only values to prevent empty data storage.

func FilterMapByKeys

func FilterMapByKeys[V any](m map[string]V, pattern string) map[string]V

FilterMapByKeys uses case-insensitive substring matching for consistent behavior.

func FilterMapByValues

func FilterMapByValues[K comparable](m map[K]string, pattern string) map[K]string

func FilterSlice

func FilterSlice[T any](slice []T, predicate func(T) bool) []T

func FilterSliceByPattern

func FilterSliceByPattern(slice []string, pattern string) []string

FilterSliceByPattern uses case-insensitive substring matching for user-friendly filtering.

func FormatCreditCard

func FormatCreditCard(cardNumber string) string

FormatCreditCard validates common card lengths (13-19 digits) before formatting for display.

func FormatPhoneUS

func FormatPhoneUS(phone string) string

FormatPhoneUS enforces NANP format; preserves original if not 10 digits to avoid data loss.

func FormatPostalCodeCA

func FormatPostalCodeCA(postalCode string) string

FormatPostalCodeCA enforces standard Canadian format (A1A 1A1); preserves invalid input.

func FormatPostalCodeUS

func FormatPostalCodeUS(postalCode string) string

FormatPostalCodeUS handles both ZIP and ZIP+4 formats; preserves invalid input.

func FormatSSN

func FormatSSN(ssn string) string

FormatSSN enforces standard 9-digit format; preserves original if invalid to avoid data loss.

func KeepAlpha

func KeepAlpha(s string) string

func KeepAlphanumeric

func KeepAlphanumeric(s string) string

KeepAlphanumeric preserves spaces for readability while removing special characters.

func KeepDigits

func KeepDigits(s string) string

func LimitLength

func LimitLength(s string, maxLength int) string

LimitLength truncates input to prevent DoS attacks through large inputs.

func LimitMapSize

func LimitMapSize[K comparable, V any](m map[K]V, maxSize int) map[K]V

LimitMapSize prevents memory exhaustion from malicious input; iteration order is random.

func LimitSliceLength

func LimitSliceLength[T any](slice []T, maxLength int) []T

LimitSliceLength prevents memory exhaustion from malicious input arrays.

func MapToSlice

func MapToSlice[K comparable, V any](m map[K]V) []V

MapToSlice order is non-deterministic due to Go's map iteration randomization.

func MaskCreditCard

func MaskCreditCard(cardNumber string) string

MaskCreditCard follows PCI DSS requirement to show only last 4 digits.

func MaskEmail

func MaskEmail(email string) string

MaskEmail preserves full domain for user recognition while hiding personal info.

func MaskPhone

func MaskPhone(phone string) string

MaskPhone follows PCI compliance pattern of showing last 4 digits for user recognition.

func MaskSSN

func MaskSSN(ssn string) string

MaskSSN follows privacy regulations requiring masking of all but last 4 digits.

func MaskString

func MaskString(s string, visibleChars int) string

MaskString preserves start/end characters for user recognition while hiding sensitive middle. Handles Unicode properly and prevents over-masking short strings.

func MaxLength

func MaxLength(s string, maxLen int) string

MaxLength handles Unicode properly and prevents buffer overflows from malicious input.

func MergeStringMaps

func MergeStringMaps(ms ...map[string]string) map[string]string

MergeStringMaps applies last-writer-wins semantics for duplicate keys.

func NonZero

func NonZero[T Numeric](value T) T

NonZero returns 1 if the value is zero, otherwise returns the value unchanged.

func NormalizeCreditCard

func NormalizeCreditCard(cardNumber string) string

NormalizeCreditCard strips formatting for PCI-compliant storage and validation.

func NormalizeEmail

func NormalizeEmail(email string) string

NormalizeEmail prevents common email input errors but preserves original for invalid formats. Consolidates consecutive dots which can cause delivery issues with some email providers.

func NormalizePath

func NormalizePath(path string) string

NormalizePath normalizes a file path and prevents traversal attacks.

func NormalizePhone

func NormalizePhone(phone string) string

NormalizePhone strips formatting to enable consistent database storage and comparison.

func NormalizePostalCode

func NormalizePostalCode(postalCode string) string

NormalizePostalCode creates consistent format for database storage and comparison.

func NormalizeSSN

func NormalizeSSN(ssn string) string

NormalizeSSN strips formatting for consistent storage and validation of sensitive data.

func NormalizeToRange

func NormalizeToRange[T Float](value T, fromMin T, fromMax T, toMin T, toMax T) T

NormalizeToRange maps a value from one range to another range proportionally. Maps value from [fromMin, fromMax] to [toMin, toMax].

func NormalizeURL

func NormalizeURL(rawURL string) string

NormalizeURL assumes HTTPS for security; preserves original on parse errors to avoid data loss. Removes trailing slash for consistent URL comparison and caching.

func NormalizeWhitespace

func NormalizeWhitespace(s string) string

NormalizeWhitespace prevents layout issues from multiple spaces, tabs, and newlines.

func Percentage

func Percentage[T Numeric](part T, whole T) float64

Percentage calculates what percentage 'part' is of 'whole', clamped between 0 and 100. Returns 0 if whole is zero.

func PreventHeaderInjection

func PreventHeaderInjection(s string) string

PreventHeaderInjection removes characters that could be used for HTTP header injection.

func PreventLDAPInjection

func PreventLDAPInjection(s string) string

PreventLDAPInjection removes LDAP injection characters.

func PreventPathTraversal

func PreventPathTraversal(path string) string

PreventPathTraversal removes path traversal attempts (../ and ..\).

func PreventXSS

func PreventXSS(s string) string

PreventXSS applies comprehensive XSS prevention measures.

func RemoveChars

func RemoveChars(s string, chars string) string

func RemoveControlChars

func RemoveControlChars(s string) string

RemoveControlChars prevents injection attacks while preserving common whitespace.

func RemoveControlSequences

func RemoveControlSequences(s string) string

RemoveControlSequences removes ANSI escape sequences and other control characters.

func RemoveExtraWhitespace

func RemoveExtraWhitespace(s string) string

RemoveExtraWhitespace prevents layout issues and normalizes user input formatting.

func RemoveFragment

func RemoveFragment(rawURL string) string

func RemoveJavaScriptEvents

func RemoveJavaScriptEvents(s string) string

RemoveJavaScriptEvents removes JavaScript event handlers from HTML attributes.

func RemoveNonAlphanumeric

func RemoveNonAlphanumeric(s string) string

func RemoveNullBytes

func RemoveNullBytes(s string) string

RemoveNullBytes removes null bytes that could cause issues in C-based systems.

func RemoveQueryParams

func RemoveQueryParams(rawURL string) string

RemoveQueryParams useful for URL comparison and preventing tracking parameter leakage.

func RemoveSQLKeywords

func RemoveSQLKeywords(s string) string

RemoveSQLKeywords removes common SQL keywords that could be used for injection.

func RemoveShellMetacharacters

func RemoveShellMetacharacters(s string) string

RemoveShellMetacharacters removes shell metacharacters that could be used for injection.

func ReplaceChars

func ReplaceChars(s string, old string, new string) string

func ReverseSlice

func ReverseSlice[T any](slice []T) []T

func Round

func Round[T Float](value T) T

Round rounds a floating-point number to the nearest integer.

func RoundDown

func RoundDown[T Float](value T) T

RoundDown rounds a floating-point number down to the nearest integer.

func RoundToDecimalPlaces

func RoundToDecimalPlaces[T Float](value T, places int) T

RoundToDecimalPlaces rounds a floating-point number to the specified number of decimal places.

func RoundUp

func RoundUp[T Float](value T) T

RoundUp rounds a floating-point number up to the nearest integer.

func SafeDivide

func SafeDivide[T Numeric](numerator T, denominator T, fallback T) T

SafeDivide performs division with protection against division by zero. Returns the result of numerator/denominator, or fallback if denominator is zero.

func SanitizeEmail

func SanitizeEmail(email string) string

SanitizeEmail removes dangerous characters from email addresses while preserving valid format.

func SanitizeFilename

func SanitizeFilename(filename string) string

SanitizeFilename prevents filesystem vulnerabilities and ensures cross-platform compatibility. Enforces 255-byte limit and provides fallback for completely invalid names.

func SanitizeHTMLAttributes

func SanitizeHTMLAttributes(s string) string

SanitizeHTMLAttributes removes potentially dangerous HTML attributes.

func SanitizeMapKeys

func SanitizeMapKeys[V any](m map[string]V, sanitizer func(string) string) map[string]V

SanitizeMapKeys drops entries with empty keys after sanitization to prevent key collisions.

func SanitizeMapValues

func SanitizeMapValues[K comparable](m map[K]string, sanitizer func(string) string) map[K]string

func SanitizePath

func SanitizePath(path string) string

SanitizePath cleans and normalizes file paths to prevent directory traversal.

func SanitizeSQLIdentifier

func SanitizeSQLIdentifier(s string) string

SanitizeSQLIdentifier ensures SQL identifiers (table names, column names) are safe.

func SanitizeSecureFilename

func SanitizeSecureFilename(filename string) string

SanitizeSecureFilename makes a filename safe by removing dangerous characters.

func SanitizeShellArgument

func SanitizeShellArgument(arg string) string

SanitizeShellArgument makes a string safe for use as a shell argument.

func SanitizeURL

func SanitizeURL(url string) string

SanitizeURL removes dangerous elements from URLs while preserving valid structure.

func SanitizeUserInput

func SanitizeUserInput(s string) string

SanitizeUserInput applies comprehensive sanitization for user input.

func SingleLine

func SingleLine(s string) string

SingleLine useful for form fields and log messages that need to be on one line.

func SliceToMap

func SliceToMap[T any](slice []T) map[int]T

func SortStrings

func SortStrings(slice []string) []string

SortStrings creates sorted copy to avoid mutating input slice.

func SortStringsIgnoreCase

func SortStringsIgnoreCase(slice []string) []string

SortStringsIgnoreCase preserves original casing while sorting by lowercase comparison.

func StripHTML

func StripHTML(s string) string

StripHTML prevents XSS by removing tags and decoding entities for safe text extraction.

func StripScriptTags

func StripScriptTags(s string) string

StripScriptTags removes all <script> tags and their content.

func ToCamelCase

func ToCamelCase(s string) string

ToCamelCase follows JavaScript convention: first word lowercase, subsequent words capitalized.

func ToKebabCase

func ToKebabCase(s string) string

ToKebabCase prevents consecutive dashes and ensures clean URL-safe identifiers.

func ToLower

func ToLower(s string) string

func ToLowerStringSlice

func ToLowerStringSlice(slice []string) []string

func ToSnakeCase

func ToSnakeCase(s string) string

ToSnakeCase prevents consecutive underscores for clean database column names.

func ToTitle

func ToTitle(s string) string

func ToUpper

func ToUpper(s string) string

func TransformSlice

func TransformSlice[T any, R any](slice []T, transform func(T) R) []R

func Trim

func Trim(s string) string

func TrimStringSlice

func TrimStringSlice(slice []string) []string

func TrimToLower

func TrimToLower(s string) string

func TrimToUpper

func TrimToUpper(s string) string

func TruncateToInt

func TruncateToInt[T Float](value T) T

TruncateToInt truncates a floating-point number to an integer, removing the decimal part.

func UnescapeHTML

func UnescapeHTML(s string) string

UnescapeHTML unescapes HTML entities.

func ZeroIfNegative

func ZeroIfNegative[T Signed](value T) T

ZeroIfNegative returns zero if the value is negative, otherwise returns the value.

func ZeroIfPositive

func ZeroIfPositive[T Signed](value T) T

ZeroIfPositive returns zero if the value is positive, otherwise returns the value.

Types

type Float

type Float interface {
	~float32 | ~float64
}

Float represents floating-point numeric types.

type Numeric

type Numeric interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64 |
		~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 |
		~float32 | ~float64
}

Numeric represents numeric types that support basic arithmetic operations.

type Signed

type Signed interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64 | ~float32 | ~float64
}

Signed represents signed numeric types.

Jump to

Keyboard shortcuts

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