Skip to content
This repository has been archived by the owner on Apr 7, 2022. It is now read-only.

Commit

Permalink
SameSite Attribute support none, default = lax (#376)
Browse files Browse the repository at this point in the history
* add same site attribute -> none

* fix typo

* fix error caused in vapor SessionsConfig.swift:41:27

* add tests for samesite attribute serialization/parsing
  • Loading branch information
pankajsoni19 committed Jul 27, 2020
1 parent fba1329 commit 0464b71
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 15 deletions.
32 changes: 18 additions & 14 deletions Sources/HTTP/Cookies/HTTPCookieValue.swift
Expand Up @@ -37,7 +37,7 @@ public struct HTTPCookieValue: ExpressibleByStringLiteral {
var path: String?
var secure = false
var httpOnly = false
var sameSite: HTTPSameSitePolicy?
var sameSite: HTTPSameSitePolicy = .lax

for (key, val) in header.parameters {
switch key {
Expand All @@ -47,7 +47,7 @@ public struct HTTPCookieValue: ExpressibleByStringLiteral {
case "httponly": httpOnly = true
case "secure": secure = true
case "max-age": maxAge = Int(val) ?? 0
case "samesite": sameSite = HTTPSameSitePolicy(rawValue: val)
case "samesite": sameSite = HTTPSameSitePolicy(rawValue: val) ?? .lax
default: break
}
}
Expand Down Expand Up @@ -91,7 +91,7 @@ public struct HTTPCookieValue: ExpressibleByStringLiteral {
/// A cookie which can only be sent in requests originating from the same origin as the target domain.
///
/// This restriction mitigates attacks such as cross-site request forgery (XSRF).
public var sameSite: HTTPSameSitePolicy?
public var sameSite: HTTPSameSitePolicy

// MARK: Init

Expand All @@ -107,7 +107,7 @@ public struct HTTPCookieValue: ExpressibleByStringLiteral {
/// - path: The path at which the cookie is active. Defaults to `"/"`.
/// - isSecure: Limits the cookie to secure connections. Defaults to `false`.
/// - isHTTPOnly: Does not expose the cookie over non-HTTP channels. Defaults to `false`.
/// - sameSite: See `HTTPSameSitePolicy`. Defaults to `nil`.
/// - sameSite: See `HTTPSameSitePolicy`. Defaults to `lax`.
public init(
string: String,
expires: Date? = nil,
Expand All @@ -118,14 +118,16 @@ public struct HTTPCookieValue: ExpressibleByStringLiteral {
isHTTPOnly: Bool = false,
sameSite: HTTPSameSitePolicy? = nil
) {
let sameSitePolicy = sameSite ?? .lax

self.string = string
self.expires = expires
self.maxAge = maxAge
self.domain = domain
self.path = path
self.isSecure = isSecure
self.isSecure = isSecure || sameSitePolicy == .none //samesite none requires secure attribute to be set
self.isHTTPOnly = isHTTPOnly
self.sameSite = sameSite
self.sameSite = sameSitePolicy
}

/// See `ExpressibleByStringLiteral`.
Expand Down Expand Up @@ -163,14 +165,14 @@ public struct HTTPCookieValue: ExpressibleByStringLiteral {
serialized += "; HttpOnly"
}

if let sameSite = self.sameSite {
serialized += "; SameSite"
switch sameSite {
case .lax:
serialized += "=Lax"
case .strict:
serialized += "=Strict"
}
serialized += "; SameSite"
switch sameSite {
case .lax:
serialized += "=Lax"
case .strict:
serialized += "=Strict"
case .none:
serialized += "=None"
}

return serialized
Expand All @@ -184,4 +186,6 @@ public enum HTTPSameSitePolicy: String {
case strict = "Strict"
/// Relaxed mode.
case lax = "Lax"
//The browser will send cookies with both cross-site requests and same-site requests.
case none = "None"
}
27 changes: 26 additions & 1 deletion Tests/HTTPTests/HTTPTests.swift
Expand Up @@ -2,6 +2,7 @@ import HTTP
import XCTest

class HTTPTests: XCTestCase {

func testCookieParse() throws {
/// from https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies
guard let (name, value) = HTTPCookieValue.parse("id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly") else {
Expand All @@ -12,14 +13,37 @@ class HTTPTests: XCTestCase {
XCTAssertEqual(value.expires, Date(rfc1123: "Wed, 21 Oct 2015 07:28:00 GMT"))
XCTAssertEqual(value.isSecure, true)
XCTAssertEqual(value.isHTTPOnly, true)
XCTAssertEqual(value.sameSite, .lax)

guard let cookie: (name: String, value: HTTPCookieValue) = HTTPCookieValue.parse("vapor=; Secure; HttpOnly") else {
guard let cookie: (name: String, value: HTTPCookieValue) = HTTPCookieValue.parse("vapor=; Secure; HttpOnly; SameSite=None") else {
throw HTTPError(identifier: "cookie", reason: "Could not parse test cookie")
}
XCTAssertEqual(cookie.name, "vapor")
XCTAssertEqual(cookie.value.string, "")
XCTAssertEqual(cookie.value.isSecure, true)
XCTAssertEqual(cookie.value.isHTTPOnly, true)
XCTAssertEqual(cookie.value.sameSite, .none)
}

func testCookieSameSiteAttribtue() throws {
let match = "id=cookie_data; Domain=example.com; Path=/; Secure; HttpOnly; SameSite=None"

let value = HTTPCookieValue(string: "cookie_data", domain: "example.com", path: "/", isHTTPOnly: true, sameSite: .some(.none))
let serialized = value.serialize(name: "id")

guard serialized == match else {
throw HTTPError(identifier: "cookie", reason: "Could not serialize test cookie")
}

guard let cookie: (name: String, value: HTTPCookieValue) = HTTPCookieValue.parse(match) else {
throw HTTPError(identifier: "cookie", reason: "Could not parse test cookie")
}

XCTAssertEqual(cookie.name, "id")
XCTAssertEqual(cookie.value.string, "cookie_data")
XCTAssertEqual(cookie.value.isSecure, true)
XCTAssertEqual(cookie.value.isHTTPOnly, true)
XCTAssertEqual(cookie.value.sameSite, .none)
}

func testCookieIsSerializedCorrectly() throws {
Expand Down Expand Up @@ -121,6 +145,7 @@ class HTTPTests: XCTestCase {

static let allTests = [
("testCookieParse", testCookieParse),
("testCookieSameSiteAttribtue", testCookieSameSiteAttribtue),
("testAcceptHeader", testAcceptHeader),
("testRemotePeer", testRemotePeer),
("testCookieIsSerializedCorrectly", testCookieIsSerializedCorrectly),
Expand Down

0 comments on commit 0464b71

Please sign in to comment.