Skip to content

Commit a4435a8

Browse files
authored
Merge pull request #10 from brokenhandsio/vapor3
Vapor 3
2 parents 8cd8e2e + 466baca commit a4435a8

18 files changed

+287
-328
lines changed

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ language: generic
55
sudo: required
66
dist: trusty
77

8-
osx_image: xcode9.1
8+
osx_image: xcode9.3
99
before_install:
1010
- if [ $TRAVIS_OS_NAME == "osx" ]; then
1111
brew update;
@@ -24,7 +24,7 @@ script:
2424
- swift test
2525

2626
after_success:
27-
- eval "$(curl -sL https://raw.githubusercontent.com/vapor-community/swift/master/codecov)"
27+
- eval "$(curl -sL https://raw.githubusercontent.com/vapor-community/swift/swift-4-codecov/codecov-swift4)"
2828

2929
notifications:
3030
email:

Package.swift

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
1+
// swift-tools-version:4.0
2+
13
import PackageDescription
24

35
let package = Package(
46
name: "VaporSecurityHeaders",
7+
products: [
8+
.library(name: "VaporSecurityHeaders", targets: ["VaporSecurityHeaders"]),
9+
],
510
dependencies: [
6-
.Package(url: "https://github.com/vapor/vapor.git", majorVersion: 2),
11+
.package(url: "https://github.com/vapor/vapor.git", from: "3.0.0"),
12+
],
13+
targets: [
14+
.target(name: "VaporSecurityHeaders", dependencies: ["Vapor"]),
15+
.testTarget(name: "VaporSecurityHeadersTests", dependencies: ["VaporSecurityHeaders"]),
716
]
817
)

[email protected]

Lines changed: 0 additions & 17 deletions
This file was deleted.

README.md

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<br>
44
<br>
55
<a href="https://swift.org">
6-
<img src="http://img.shields.io/badge/Swift-4-brightgreen.svg" alt="Language">
6+
<img src="http://img.shields.io/badge/Swift-4.1-brightgreen.svg" alt="Language">
77
</a>
88
<a href="https://travis-ci.org/brokenhandsio/VaporSecurityHeaders">
99
<img src="https://travis-ci.org/brokenhandsio/VaporSecurityHeaders.svg?branch=master" alt="Build Status">
@@ -35,29 +35,21 @@ These headers will *help* prevent cross-site scripting attacks, SSL downgrade at
3535

3636
# Usage
3737

38-
To use Vapor Security Headers, just add the middleware to your `Config` and then to your `droplet.json`. Vapor Security Headers makes this easy to do with a `builder` function on the factory:
38+
To use Vapor Security Headers, just register the middleware with your services and add it to your `MiddlewareConfig`. Vapor Security Headers makes this easy to do with a `build` function on the factory. In `configure.swift` add:
3939

4040
```swift
41-
let config = Config()
4241
let securityHeadersFactory = SecurityHeadersFactory()
43-
config.addConfigurable(middleware: securityHeadersFactory.builder(), name: "security-headers"))
44-
let drop = Droplet(config)
42+
services.register(securityHeadersFactory.build())
43+
44+
var middlewareConfig = MiddlewareConfig()
45+
// ...
46+
middlewareConfig.use(SecurityHeaders.self)
47+
services.register(middlewareConfig)
4548
```
4649

4750
The default factory will add default values to your site for Content-Security-Policy, X-XSS-Protection, X-Frame-Options and X-Content-Type-Options.
4851

49-
***Note:*** You should ensure you set the security headers as the first middleware in your `droplet.json` to make sure the headers get added to all responses:
50-
51-
```json
52-
{
53-
...
54-
"middleware": [
55-
"security-headers",
56-
...
57-
],
58-
...
59-
}
60-
```
52+
***Note:*** You should ensure you set the security headers as the last middleware in your `MiddlewareConfig` (i.e., the first middleware to be applied to responses) to make sure the headers get added to all responses.
6153

6254
If you want to add your own values, it is easy to do using the factory. For instance, to add a content security policy configuration, just do:
6355

@@ -72,7 +64,7 @@ You will need to add it as a dependency in your `Package.swift` file:
7264
```swift
7365
dependencies: [
7466
...,
75-
.package(url: "https://github.com/brokenhandsio/VaporSecurityHeaders.git", from: "1.1.0")
67+
.package(url: "https://github.com/brokenhandsio/VaporSecurityHeaders.git", from: "2.0.0")
7668
]
7769
```
7870

@@ -140,14 +132,22 @@ Check out [https://report-uri.io/](https://report-uri.io/) for a free tool to se
140132

141133
### Page Specific CSP
142134

143-
Vapor Security Headers also supports setting the CSP on a route or request basis. If the middleware has been added to the Droplet, you can override the CSP for a request. This allows you to have a strict default CSP, but allow content from extra sources when required, such as only allowing the Javascript for blog comments on the blog page. Create a separate `ContentSecurityPolicyConfiguration` and then add it to the request. For example, inside a route handler, you could do:
135+
Vapor Security Headers also supports setting the CSP on a route or request basis. If the middleware has been added to the `MiddlewareConfig`, you can override the CSP for a request. This allows you to have a strict default CSP, but allow content from extra sources when required, such as only allowing the Javascript for blog comments on the blog page. Create a separate `ContentSecurityPolicyConfiguration` and then add it to the request. For example, inside a route handler, you could do:
144136

145137
```swift
146138
let pageSpecificCSPVaue = "default-src 'none'; script-src https://comments.disqus.com;"
147139
let pageSpecificCSP = ContentSecurityPolicyConfiguration(value: pageSpecificCSPValue)
148140
request.contentSecurityPolicy = pageSpecificCSP
149141
```
150142

143+
You must also enable the `CSPRequestConfiguration` service for this to work. In `configure.swift` add:
144+
145+
```swift
146+
services.register { _ in
147+
return CSPRequestConfiguration()
148+
}
149+
```
150+
151151
## Content-Security-Policy-Report-Only
152152

153153
Content-Security-Policy-Report-Only works in exactly the same way as Content-Security-Policy except that any violations will not block content, but they will be reported back to you. This is extremely useful for testing a CSP before rolling it out over your site. You can run both side by side - so for example have a fairly simply policy under Content-Security-Policy but test a more restrictive policy over Content-Security-Policy-Report-Only. The great thing about this is that your users do all your testing for you!
@@ -175,7 +175,7 @@ To just enable the protection:
175175
let xssProtectionConfig = XssProtectionConfiguration(option: .enable)
176176
```
177177

178-
To sanitize the page and report the violation:
178+
To sanitise the page and report the violation:
179179

180180
```swift
181181
let xssProtectionConfig = XssProtectionConfiguration(option: .report("https://report-uri.com"))
Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import HTTP
1+
import Vapor
22

33
public struct ContentSecurityPolicyConfiguration: SecurityHeaderConfiguration {
44

@@ -9,22 +9,32 @@ public struct ContentSecurityPolicyConfiguration: SecurityHeaderConfiguration {
99
}
1010

1111
func setHeader(on response: Response, from request: Request) {
12-
if let requestCsp = request.contentSecurityPolicy {
13-
response.headers[HeaderKey.contentSecurityPolicy] = requestCsp.value
12+
if let requestCSP = request.contentSecurityPolicy {
13+
response.http.headers.replaceOrAdd(name: .contentSecurityPolicy, value: requestCSP.value)
1414
} else {
15-
response.headers[HeaderKey.contentSecurityPolicy] = value
15+
response.http.headers.replaceOrAdd(name: .contentSecurityPolicy, value: value)
1616
}
1717
}
1818
}
1919

20-
extension Request {
20+
public class CSPRequestConfiguration: Service {
21+
var configuration: ContentSecurityPolicyConfiguration?
22+
public init() {}
23+
}
2124

25+
extension Request {
2226
public var contentSecurityPolicy: ContentSecurityPolicyConfiguration? {
2327
get {
24-
return storage["cspConfig"] as? ContentSecurityPolicyConfiguration
28+
if let requestConfig = try? privateContainer.make(CSPRequestConfiguration.self) {
29+
return requestConfig.configuration
30+
} else {
31+
return nil
32+
}
2533
}
2634
set {
27-
storage["cspConfig"] = newValue
35+
if let requestConfig = try? privateContainer.make(CSPRequestConfiguration.self) {
36+
requestConfig.configuration = newValue
37+
}
2838
}
2939
}
3040
}

Sources/VaporSecurityHeaders/Configurations/ContentSecurityPolicyReportOnlyConfiguration.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import HTTP
1+
import Vapor
22

33
public struct ContentSecurityPolicyReportOnlyConfiguration: SecurityHeaderConfiguration {
44

@@ -9,6 +9,6 @@ public struct ContentSecurityPolicyReportOnlyConfiguration: SecurityHeaderConfig
99
}
1010

1111
func setHeader(on response: Response, from request: Request) {
12-
response.headers[HeaderKey.contentSecurityPolicyReportOnly] = value
12+
response.http.headers.replaceOrAdd(name: .contentSecurityPolicyReportOnly, value: value)
1313
}
1414
}

Sources/VaporSecurityHeaders/Configurations/ContentTypeOptionsConfiguration.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import HTTP
1+
import Vapor
22

33
public struct ContentTypeOptionsConfiguration: SecurityHeaderConfiguration {
44

@@ -16,7 +16,7 @@ public struct ContentTypeOptionsConfiguration: SecurityHeaderConfiguration {
1616
func setHeader(on response: Response, from request: Request) {
1717
switch option {
1818
case .nosniff:
19-
response.headers[HeaderKey.xContentTypeOptions] = "nosniff"
19+
response.http.headers.replaceOrAdd(name: .xContentTypeOptions, value: "nosniff")
2020
default:
2121
break
2222
}

Sources/VaporSecurityHeaders/Configurations/FrameOptionsConfiguration.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import HTTP
2+
import Vapor
23

34
public struct FrameOptionsConfiguration: SecurityHeaderConfiguration {
45

@@ -17,11 +18,11 @@ public struct FrameOptionsConfiguration: SecurityHeaderConfiguration {
1718
func setHeader(on response: Response, from request: Request) {
1819
switch option {
1920
case .deny:
20-
response.headers[HeaderKey.xFrameOptions] = "DENY"
21+
response.http.headers.replaceOrAdd(name: .xFrameOptions, value: "DENY")
2122
case .sameOrigin:
22-
response.headers[HeaderKey.xFrameOptions] = "SAMEORIGIN"
23+
response.http.headers.replaceOrAdd(name: .xFrameOptions, value: "SAMEORIGIN")
2324
case .allow(let from):
24-
response.headers[HeaderKey.xFrameOptions] = "ALLOW-FROM \(from)"
25+
response.http.headers.replaceOrAdd(name: .xFrameOptions, value: "ALLOW-FROM \(from)")
2526
}
2627
}
2728
}

Sources/VaporSecurityHeaders/Configurations/ReferrerPolicyConfiguration.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import HTTP
1+
import Vapor
22

33
public struct ReferrerPolicyConfiguration: SecurityHeaderConfiguration {
44

@@ -21,6 +21,6 @@ public struct ReferrerPolicyConfiguration: SecurityHeaderConfiguration {
2121
}
2222

2323
func setHeader(on response: Response, from request: Request) {
24-
response.headers[HeaderKey.referrerPolicy] = option.rawValue
24+
response.http.headers.replaceOrAdd(name: .referrerPolicy, value: option.rawValue)
2525
}
2626
}

Sources/VaporSecurityHeaders/Configurations/ServerConfiguration.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import HTTP
2+
import Vapor
23

34
public struct ServerConfiguration: SecurityHeaderConfiguration {
45
private let value: String
@@ -8,6 +9,6 @@ public struct ServerConfiguration: SecurityHeaderConfiguration {
89
}
910

1011
func setHeader(on response: Response, from request: Request) {
11-
response.headers[HeaderKey.server] = value
12+
response.http.headers.replaceOrAdd(name: .server, value: value)
1213
}
1314
}

0 commit comments

Comments
 (0)