This PR contains the work done to amend the `.env.local` handling for the Website service, plus other small fixes. Reviewed-on: rock-n-code/loud-amsterdam#19 Co-authored-by: Javier Cicchelli <javier@rock-n-code.com> Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>
135 lines
4.1 KiB
Swift
135 lines
4.1 KiB
Swift
import Hummingbird
|
|
import HummingbirdTesting
|
|
import Testing
|
|
|
|
@testable import WebsiteLibrary
|
|
|
|
@Suite("SecurityHeadersMiddleware middleware", .tags(.middleware))
|
|
struct SecurityHeadersMiddlewareTests {
|
|
|
|
// MARK: Functional tests
|
|
|
|
@Test
|
|
func `applies the default security headers to a response`() async throws {
|
|
try await app().test(.router) { client in
|
|
try await client.execute(
|
|
uri: "/hello",
|
|
method: .get
|
|
) { response in
|
|
#expect(response.status == .ok)
|
|
#expect(response.headers[.contentSecurityPolicy] == String.Security.contentSecurityPolicy)
|
|
#expect(response.headers[.xContentTypeOptions] == String.Security.contentTypeOptions)
|
|
#expect(response.headers[.frameOptions] == String.Security.frameOptions)
|
|
#expect(response.headers[.referrerPolicy] == String.Security.referrerPolicy)
|
|
#expect(response.headers[.permissionsPolicy] == String.Security.permissionsPolicy)
|
|
}
|
|
}
|
|
}
|
|
|
|
@Test
|
|
func `omits strict-transport-security by default`() async throws {
|
|
try await app().test(.router) { client in
|
|
try await client.execute(
|
|
uri: "/hello",
|
|
method: .get
|
|
) { response in
|
|
#expect(response.headers[.strictTransportSecurity] == nil)
|
|
}
|
|
}
|
|
}
|
|
|
|
@Test
|
|
func `applies strict-transport-security when configured`() async throws {
|
|
let value = "max-age=31536000; includeSubDomains"
|
|
|
|
try await app(
|
|
configuration: .init(strictTransportSecurity: value)
|
|
).test(.router) { client in
|
|
try await client.execute(
|
|
uri: "/hello",
|
|
method: .get
|
|
) { response in
|
|
#expect(response.headers[.strictTransportSecurity] == value)
|
|
}
|
|
}
|
|
}
|
|
|
|
@Test
|
|
func `applies a custom header value`() async throws {
|
|
let value = "default-src 'none'"
|
|
|
|
try await app(
|
|
configuration: .init(contentSecurityPolicy: value)
|
|
).test(.router) { client in
|
|
try await client.execute(
|
|
uri: "/hello",
|
|
method: .get
|
|
) { response in
|
|
#expect(response.headers[.contentSecurityPolicy] == value)
|
|
}
|
|
}
|
|
}
|
|
|
|
@Test
|
|
func `omits a header whose configured value is nil`() async throws {
|
|
try await app(
|
|
configuration: .init(contentTypeOptions: nil)
|
|
).test(.router) { client in
|
|
try await client.execute(
|
|
uri: "/hello",
|
|
method: .get
|
|
) { response in
|
|
#expect(response.headers[.xContentTypeOptions] == nil)
|
|
}
|
|
}
|
|
}
|
|
|
|
@Test
|
|
func `replaces an existing header value set downstream`() async throws {
|
|
try await app().test(.router) { client in
|
|
try await client.execute(
|
|
uri: "/weak",
|
|
method: .get
|
|
) { response in
|
|
#expect(response.headers[.xContentTypeOptions] == String.Security.contentTypeOptions)
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// MARK: - Helpers
|
|
|
|
private extension SecurityHeadersMiddlewareTests {
|
|
|
|
// MARK: Methods
|
|
|
|
/// Builds an application whose router applies the security-headers middleware ahead of two
|
|
/// routes: `/hello` returns a plain body, and `/weak` returns a response that already carries a
|
|
/// deliberately weak `X-Content-Type-Options` value for the middleware to override.
|
|
func app(
|
|
configuration: SecurityHeadersMiddleware<BasicRequestContext>.Configuration = .init()
|
|
) -> some ApplicationProtocol {
|
|
let router = Router()
|
|
|
|
router.addMiddleware {
|
|
SecurityHeadersMiddleware(configuration: configuration)
|
|
}
|
|
|
|
router.get("hello") { _, _ in
|
|
"Hello!"
|
|
}
|
|
|
|
router.get("weak") { _, _ -> Response in
|
|
var response = Response(status: .ok)
|
|
|
|
response.headers[.xContentTypeOptions] = "weak"
|
|
|
|
return response
|
|
}
|
|
|
|
return Application(router: router)
|
|
}
|
|
|
|
}
|