-
-
Notifications
You must be signed in to change notification settings - Fork 7
Add security middlewares: Secure, CORS, CSRF, CleanPath, StripSlashes #43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
configurable middleware adding security headers: - X-Frame-Options, X-Content-Type-Options, Referrer-Policy - X-XSS-Protection, Strict-Transport-Security (HTTPS only) - optional Content-Security-Policy and Permissions-Policy
cross-origin resource sharing middleware with: - configurable allowed origins, methods, headers - automatic preflight handling - credentials support with origin reflection - preflight result caching
cross-site request forgery protection using Fetch metadata: - checks Sec-Fetch-Site and Origin headers - trusted origin allowlist support - bypass patterns for webhooks/OAuth - wraps stdlib http.CrossOriginProtection on Go 1.25+
CleanPath: removes double slashes from URL path - /users//1 becomes /users/1 - preserves trailing slashes and dot segments - preserves percent-encoding in RawPath StripSlashes: removes trailing slashes - /users/ becomes /users - preserves root path /
document Secure, CORS, CSRF, CleanPath, and StripSlashes middlewares with usage examples and configuration options
Pull Request Test Coverage Report for Build 20811297094Details
💛 - Coveralls |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds five new security and URL normalization middlewares to enhance the REST package's security capabilities and request handling:
- Secure: Adds security headers (X-Frame-Options, CSP, HSTS, etc.) with configurable options
- CORS: Cross-Origin Resource Sharing with support for credentials and preflight handling
- CSRF: Cross-Site Request Forgery protection using Fetch metadata headers, with dual implementation (Go 1.25+ stdlib wrapper and custom for earlier versions)
- CleanPath: Removes double slashes from URL paths while preserving encoding
- StripSlashes: Removes trailing slashes from URL paths
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| secure.go | Implements Secure middleware with security headers and HTTPS detection via TLS/proxy headers |
| secure_test.go | Comprehensive tests for Secure middleware covering defaults, custom options, and HTTPS detection |
| cors.go | Implements CORS middleware with origin validation, preflight handling, and credentials support |
| cors_test.go | Tests for CORS including preflight, origin matching, credentials, and integration scenarios |
| csrf.go | Custom CSRF implementation for Go <1.25 using Sec-Fetch-Site and Origin header validation |
| csrf_go125.go | Wrapper for stdlib http.CrossOriginProtection in Go 1.25+ |
| csrf_test.go | Tests for CSRF protection covering safe methods, origin validation, bypass patterns, and trusted origins |
| rewrite.go | Adds CleanPath and StripSlashes middlewares for URL path normalization |
| rewrite_test.go | Tests for CleanPath and StripSlashes including edge cases and RawPath handling |
| README.md | Documentation for all five new middlewares with usage examples and configuration options |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // default: empty | ||
| ExposedHeaders []string | ||
| // AllowCredentials indicates whether the request can include credentials. | ||
| // when true, AllowedOrigins cannot be "*" (browser security restriction). |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment starts with lowercase "when" but should start with an uppercase letter to follow Go documentation conventions.
| // when true, AllowedOrigins cannot be "*" (browser security restriction). | |
| // When true, AllowedOrigins cannot be "*" (browser security restriction). |
| // default: ["*"] | ||
| AllowedOrigins []string | ||
| // AllowedMethods is a list of methods the client is allowed to use. | ||
| // default: GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD | ||
| AllowedMethods []string | ||
| // AllowedHeaders is a list of headers the client is allowed to send. | ||
| // default: Accept, Content-Type, Authorization, X-Requested-With | ||
| AllowedHeaders []string | ||
| // ExposedHeaders is a list of headers that are safe to expose to the client. | ||
| // default: empty | ||
| ExposedHeaders []string | ||
| // AllowCredentials indicates whether the request can include credentials. | ||
| // when true, AllowedOrigins cannot be "*" (browser security restriction). | ||
| // default: false | ||
| AllowCredentials bool | ||
| // MaxAge indicates how long (in seconds) the results of a preflight can be cached. | ||
| // default: 0 (no caching) | ||
| MaxAge int | ||
| } | ||
|
|
||
| // CorsOpt is a functional option for CORSConfig | ||
| type CorsOpt func(*CORSConfig) | ||
|
|
||
| // defaultCORSConfig returns config with sensible defaults |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment starts with lowercase "default" but should start with an uppercase letter to follow Go documentation conventions.
| // default: ["*"] | |
| AllowedOrigins []string | |
| // AllowedMethods is a list of methods the client is allowed to use. | |
| // default: GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD | |
| AllowedMethods []string | |
| // AllowedHeaders is a list of headers the client is allowed to send. | |
| // default: Accept, Content-Type, Authorization, X-Requested-With | |
| AllowedHeaders []string | |
| // ExposedHeaders is a list of headers that are safe to expose to the client. | |
| // default: empty | |
| ExposedHeaders []string | |
| // AllowCredentials indicates whether the request can include credentials. | |
| // when true, AllowedOrigins cannot be "*" (browser security restriction). | |
| // default: false | |
| AllowCredentials bool | |
| // MaxAge indicates how long (in seconds) the results of a preflight can be cached. | |
| // default: 0 (no caching) | |
| MaxAge int | |
| } | |
| // CorsOpt is a functional option for CORSConfig | |
| type CorsOpt func(*CORSConfig) | |
| // defaultCORSConfig returns config with sensible defaults | |
| // Default: ["*"] | |
| AllowedOrigins []string | |
| // AllowedMethods is a list of methods the client is allowed to use. | |
| // Default: GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD | |
| AllowedMethods []string | |
| // AllowedHeaders is a list of headers the client is allowed to send. | |
| // Default: Accept, Content-Type, Authorization, X-Requested-With | |
| AllowedHeaders []string | |
| // ExposedHeaders is a list of headers that are safe to expose to the client. | |
| // Default: empty | |
| ExposedHeaders []string | |
| // AllowCredentials indicates whether the request can include credentials. | |
| // when true, AllowedOrigins cannot be "*" (browser security restriction). | |
| // Default: false | |
| AllowCredentials bool | |
| // MaxAge indicates how long (in seconds) the results of a preflight can be cached. | |
| // Default: 0 (no caching) | |
| MaxAge int | |
| } | |
| // CorsOpt is a functional option for CORSConfig | |
| type CorsOpt func(*CORSConfig) | |
| // DefaultCORSConfig returns config with sensible defaults. |
| // sTSSeconds sets max-age for Strict-Transport-Security. 0 disables. | ||
| // only sent when request uses HTTPS. Default: 31536000 (1 year) | ||
| STSSeconds int | ||
| // sTSIncludeSubdomains adds includeSubDomains to HSTS. Default: true | ||
| STSIncludeSubdomains bool | ||
| // sTSPreload adds preload flag to HSTS. Default: false | ||
| STSPreload bool | ||
| // xSSProtection sets X-XSS-Protection header. Default: 1; mode=block |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment has lowercase field name reference instead of the actual field name. The comment says "sTSSeconds" but the actual field name is "STSSeconds" (capitalized). This same issue appears for "sTSIncludeSubdomains" (line 25) and "sTSPreload" (line 27).
| // sTSSeconds sets max-age for Strict-Transport-Security. 0 disables. | |
| // only sent when request uses HTTPS. Default: 31536000 (1 year) | |
| STSSeconds int | |
| // sTSIncludeSubdomains adds includeSubDomains to HSTS. Default: true | |
| STSIncludeSubdomains bool | |
| // sTSPreload adds preload flag to HSTS. Default: false | |
| STSPreload bool | |
| // xSSProtection sets X-XSS-Protection header. Default: 1; mode=block | |
| // STSSeconds sets max-age for Strict-Transport-Security. 0 disables. | |
| // only sent when request uses HTTPS. Default: 31536000 (1 year) | |
| STSSeconds int | |
| // STSIncludeSubdomains adds includeSubDomains to HSTS. Default: true | |
| STSIncludeSubdomains bool | |
| // STSPreload adds preload flag to HSTS. Default: false | |
| STSPreload bool | |
| // XSSProtection sets X-XSS-Protection header. Default: 1; mode=block |
| // default: ["*"] | ||
| AllowedOrigins []string | ||
| // AllowedMethods is a list of methods the client is allowed to use. | ||
| // default: GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD | ||
| AllowedMethods []string | ||
| // AllowedHeaders is a list of headers the client is allowed to send. | ||
| // default: Accept, Content-Type, Authorization, X-Requested-With | ||
| AllowedHeaders []string | ||
| // ExposedHeaders is a list of headers that are safe to expose to the client. | ||
| // default: empty | ||
| ExposedHeaders []string | ||
| // AllowCredentials indicates whether the request can include credentials. | ||
| // when true, AllowedOrigins cannot be "*" (browser security restriction). | ||
| // default: false | ||
| AllowCredentials bool | ||
| // MaxAge indicates how long (in seconds) the results of a preflight can be cached. | ||
| // default: 0 (no caching) |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment starts with lowercase "default" but should start with an uppercase letter to follow Go documentation conventions.
| // default: ["*"] | |
| AllowedOrigins []string | |
| // AllowedMethods is a list of methods the client is allowed to use. | |
| // default: GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD | |
| AllowedMethods []string | |
| // AllowedHeaders is a list of headers the client is allowed to send. | |
| // default: Accept, Content-Type, Authorization, X-Requested-With | |
| AllowedHeaders []string | |
| // ExposedHeaders is a list of headers that are safe to expose to the client. | |
| // default: empty | |
| ExposedHeaders []string | |
| // AllowCredentials indicates whether the request can include credentials. | |
| // when true, AllowedOrigins cannot be "*" (browser security restriction). | |
| // default: false | |
| AllowCredentials bool | |
| // MaxAge indicates how long (in seconds) the results of a preflight can be cached. | |
| // default: 0 (no caching) | |
| // Default: ["*"] | |
| AllowedOrigins []string | |
| // AllowedMethods is a list of methods the client is allowed to use. | |
| // Default: GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD | |
| AllowedMethods []string | |
| // AllowedHeaders is a list of headers the client is allowed to send. | |
| // Default: Accept, Content-Type, Authorization, X-Requested-With | |
| AllowedHeaders []string | |
| // ExposedHeaders is a list of headers that are safe to expose to the client. | |
| // Default: empty | |
| ExposedHeaders []string | |
| // AllowCredentials indicates whether the request can include credentials. | |
| // when true, AllowedOrigins cannot be "*" (browser security restriction). | |
| // Default: false | |
| AllowCredentials bool | |
| // MaxAge indicates how long (in seconds) the results of a preflight can be cached. | |
| // Default: 0 (no caching) |
| // default: ["*"] | ||
| AllowedOrigins []string | ||
| // AllowedMethods is a list of methods the client is allowed to use. | ||
| // default: GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD | ||
| AllowedMethods []string | ||
| // AllowedHeaders is a list of headers the client is allowed to send. | ||
| // default: Accept, Content-Type, Authorization, X-Requested-With | ||
| AllowedHeaders []string | ||
| // ExposedHeaders is a list of headers that are safe to expose to the client. | ||
| // default: empty | ||
| ExposedHeaders []string | ||
| // AllowCredentials indicates whether the request can include credentials. | ||
| // when true, AllowedOrigins cannot be "*" (browser security restriction). | ||
| // default: false | ||
| AllowCredentials bool | ||
| // MaxAge indicates how long (in seconds) the results of a preflight can be cached. | ||
| // default: 0 (no caching) |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment starts with lowercase "default" but should start with an uppercase letter to follow Go documentation conventions.
| // default: ["*"] | |
| AllowedOrigins []string | |
| // AllowedMethods is a list of methods the client is allowed to use. | |
| // default: GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD | |
| AllowedMethods []string | |
| // AllowedHeaders is a list of headers the client is allowed to send. | |
| // default: Accept, Content-Type, Authorization, X-Requested-With | |
| AllowedHeaders []string | |
| // ExposedHeaders is a list of headers that are safe to expose to the client. | |
| // default: empty | |
| ExposedHeaders []string | |
| // AllowCredentials indicates whether the request can include credentials. | |
| // when true, AllowedOrigins cannot be "*" (browser security restriction). | |
| // default: false | |
| AllowCredentials bool | |
| // MaxAge indicates how long (in seconds) the results of a preflight can be cached. | |
| // default: 0 (no caching) | |
| // Default: ["*"] | |
| AllowedOrigins []string | |
| // AllowedMethods is a list of methods the client is allowed to use. | |
| // Default: GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD | |
| AllowedMethods []string | |
| // AllowedHeaders is a list of headers the client is allowed to send. | |
| // Default: Accept, Content-Type, Authorization, X-Requested-With | |
| AllowedHeaders []string | |
| // ExposedHeaders is a list of headers that are safe to expose to the client. | |
| // Default: empty | |
| ExposedHeaders []string | |
| // AllowCredentials indicates whether the request can include credentials. | |
| // when true, AllowedOrigins cannot be "*" (browser security restriction). | |
| // Default: false | |
| AllowCredentials bool | |
| // MaxAge indicates how long (in seconds) the results of a preflight can be cached. | |
| // Default: 0 (no caching) |
| // SecureConfig defines security headers configuration. | ||
| // Use SecOpt functions to customize. | ||
| type SecureConfig struct { | ||
| // xFrameOptions sets X-Frame-Options header. Default: DENY |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment has lowercase field name references instead of the actual field names. The comment says "xFrameOptions" but the actual field name is "XFrameOptions" (capitalized). This inconsistency appears in multiple comment lines.
| type SecureConfig struct { | ||
| // xFrameOptions sets X-Frame-Options header. Default: DENY | ||
| XFrameOptions string | ||
| // xContentTypeOptions sets X-Content-Type-Options. Default: nosniff |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment has lowercase field name references instead of the actual field names. The comment says "xContentTypeOptions" but the actual field name is "XContentTypeOptions" (capitalized).
| // Use CorsOpt functions to customize. | ||
| type CORSConfig struct { | ||
| // AllowedOrigins is a list of origins that may access the resource. | ||
| // use "*" to allow all origins (not recommended with credentials). |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment starts with lowercase "use" but should start with an uppercase letter to follow Go documentation conventions.
| // use "*" to allow all origins (not recommended with credentials). | |
| // Use "*" to allow all origins (not recommended with credentials). |
| // use "*" to allow all origins (not recommended with credentials). | ||
| // default: ["*"] | ||
| AllowedOrigins []string | ||
| // AllowedMethods is a list of methods the client is allowed to use. | ||
| // default: GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD | ||
| AllowedMethods []string | ||
| // AllowedHeaders is a list of headers the client is allowed to send. | ||
| // default: Accept, Content-Type, Authorization, X-Requested-With | ||
| AllowedHeaders []string | ||
| // ExposedHeaders is a list of headers that are safe to expose to the client. | ||
| // default: empty | ||
| ExposedHeaders []string | ||
| // AllowCredentials indicates whether the request can include credentials. | ||
| // when true, AllowedOrigins cannot be "*" (browser security restriction). | ||
| // default: false | ||
| AllowCredentials bool | ||
| // MaxAge indicates how long (in seconds) the results of a preflight can be cached. | ||
| // default: 0 (no caching) |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment starts with lowercase "default" but should start with an uppercase letter to follow Go documentation conventions.
| // use "*" to allow all origins (not recommended with credentials). | |
| // default: ["*"] | |
| AllowedOrigins []string | |
| // AllowedMethods is a list of methods the client is allowed to use. | |
| // default: GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD | |
| AllowedMethods []string | |
| // AllowedHeaders is a list of headers the client is allowed to send. | |
| // default: Accept, Content-Type, Authorization, X-Requested-With | |
| AllowedHeaders []string | |
| // ExposedHeaders is a list of headers that are safe to expose to the client. | |
| // default: empty | |
| ExposedHeaders []string | |
| // AllowCredentials indicates whether the request can include credentials. | |
| // when true, AllowedOrigins cannot be "*" (browser security restriction). | |
| // default: false | |
| AllowCredentials bool | |
| // MaxAge indicates how long (in seconds) the results of a preflight can be cached. | |
| // default: 0 (no caching) | |
| // Use "*" to allow all origins (not recommended with credentials). | |
| // Default: ["*"] | |
| AllowedOrigins []string | |
| // AllowedMethods is a list of methods the client is allowed to use. | |
| // Default: GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD | |
| AllowedMethods []string | |
| // AllowedHeaders is a list of headers the client is allowed to send. | |
| // Default: Accept, Content-Type, Authorization, X-Requested-With | |
| AllowedHeaders []string | |
| // ExposedHeaders is a list of headers that are safe to expose to the client. | |
| // Default: empty | |
| ExposedHeaders []string | |
| // AllowCredentials indicates whether the request can include credentials. | |
| // When true, AllowedOrigins cannot be "*" (browser security restriction). | |
| // Default: false | |
| AllowCredentials bool | |
| // MaxAge indicates how long (in seconds) the results of a preflight can be cached. | |
| // Default: 0 (no caching) |
| // use "*" to allow all origins (not recommended with credentials). | ||
| // default: ["*"] | ||
| AllowedOrigins []string | ||
| // AllowedMethods is a list of methods the client is allowed to use. | ||
| // default: GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD | ||
| AllowedMethods []string | ||
| // AllowedHeaders is a list of headers the client is allowed to send. | ||
| // default: Accept, Content-Type, Authorization, X-Requested-With | ||
| AllowedHeaders []string | ||
| // ExposedHeaders is a list of headers that are safe to expose to the client. | ||
| // default: empty | ||
| ExposedHeaders []string | ||
| // AllowCredentials indicates whether the request can include credentials. | ||
| // when true, AllowedOrigins cannot be "*" (browser security restriction). | ||
| // default: false | ||
| AllowCredentials bool | ||
| // MaxAge indicates how long (in seconds) the results of a preflight can be cached. | ||
| // default: 0 (no caching) |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment starts with lowercase "default" but should start with an uppercase letter to follow Go documentation conventions.
| // use "*" to allow all origins (not recommended with credentials). | |
| // default: ["*"] | |
| AllowedOrigins []string | |
| // AllowedMethods is a list of methods the client is allowed to use. | |
| // default: GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD | |
| AllowedMethods []string | |
| // AllowedHeaders is a list of headers the client is allowed to send. | |
| // default: Accept, Content-Type, Authorization, X-Requested-With | |
| AllowedHeaders []string | |
| // ExposedHeaders is a list of headers that are safe to expose to the client. | |
| // default: empty | |
| ExposedHeaders []string | |
| // AllowCredentials indicates whether the request can include credentials. | |
| // when true, AllowedOrigins cannot be "*" (browser security restriction). | |
| // default: false | |
| AllowCredentials bool | |
| // MaxAge indicates how long (in seconds) the results of a preflight can be cached. | |
| // default: 0 (no caching) | |
| // Use "*" to allow all origins (not recommended with credentials). | |
| // Default: ["*"] | |
| AllowedOrigins []string | |
| // AllowedMethods is a list of methods the client is allowed to use. | |
| // Default: GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD | |
| AllowedMethods []string | |
| // AllowedHeaders is a list of headers the client is allowed to send. | |
| // Default: Accept, Content-Type, Authorization, X-Requested-With | |
| AllowedHeaders []string | |
| // ExposedHeaders is a list of headers that are safe to expose to the client. | |
| // Default: empty | |
| ExposedHeaders []string | |
| // AllowCredentials indicates whether the request can include credentials. | |
| // When true, AllowedOrigins cannot be "*" (browser security restriction). | |
| // Default: false | |
| AllowCredentials bool | |
| // MaxAge indicates how long (in seconds) the results of a preflight can be cached. | |
| // Default: 0 (no caching) |
New middlewares for common security and URL normalization needs:
All middlewares follow functional options pattern for configuration. Full test coverage (96.9%) and documentation included.