about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2024-12-14 19:46:50 +0100
committerbptato <nincsnevem662@gmail.com>2024-12-14 19:46:50 +0100
commit16002b04f3923b3bc4001b38590385410a3e044c (patch)
tree741f5685857b8105f1e505ea8320ca3db9ebaa49 /src
parentf531f6d9132ce26b8c9ade6c4d53cf153229560a (diff)
downloadchawan-16002b04f3923b3bc4001b38590385410a3e044c.tar.gz
cookie: allow first-party cookies for subdomains with cookie sharing
Cookie jar separation is already enough to mitigate tracking issues
in this case. (Also, the fact that third-party-cookie controlled this
made things even more confusing.)

Also, add the previously missing host-only flag.
Diffstat (limited to 'src')
-rw-r--r--src/types/cookie.nim88
1 files changed, 50 insertions, 38 deletions
diff --git a/src/types/cookie.nim b/src/types/cookie.nim
index 5706ff97..486c6789 100644
--- a/src/types/cookie.nim
+++ b/src/types/cookie.nim
@@ -3,7 +3,6 @@ import std/strutils
 import std/times
 
 import monoucha/jsregex
-import server/urlfilter
 import types/opt
 import types/url
 import utils/twtstr
@@ -16,12 +15,13 @@ type
     expires: int64 # unix time
     secure: bool
     httponly: bool
-    samesite: bool
     domain: string
+    hostOnly: bool
     path: string
 
   CookieJar* = ref object
-    filter*: URLFilter
+    domain: string
+    allowHosts: seq[Regex]
     cookies*: seq[Cookie]
 
 proc parseCookieDate(val: string): Option[DateTime] =
@@ -112,10 +112,12 @@ proc parseCookieDate(val: string): Option[DateTime] =
   return some(dt)
 
 # For debugging
-proc `$`*(cookiejar: CookieJar): string =
-  result &= $cookiejar.filter
-  result &= "\n"
-  for cookie in cookiejar.cookies:
+proc `$`*(cookieJar: CookieJar): string =
+  result &= $cookieJar.domain
+  result &= ":\n"
+  for re in cookieJar.allowHosts:
+    result &= "third-party " & $re & '\n'
+  for cookie in cookieJar.cookies:
     result &= "Cookie "
     result &= $cookie[]
     result &= "\n"
@@ -152,12 +154,14 @@ func cookiePathMatches(cookiePath, requestPath: string): bool =
 #         domain string is a %x2E (".") character. (???)
 #      *  The string is a host name (i.e., not an IP address).)
 func cookieDomainMatches(cookieDomain: string; url: URL): bool =
+  if cookieDomain.len == 0:
+    return false
   let host = url.host
   if host == cookieDomain:
     return true
   if url.isIP():
     return false
-  let cookieDomain = if cookieDomain.len > 0 and cookieDomain[0] == '.':
+  let cookieDomain = if cookieDomain[0] == '.':
     cookieDomain.substr(1)
   else:
     cookieDomain
@@ -176,34 +180,47 @@ proc add*(cookieJar: CookieJar; cookie: Cookie) =
     cookieJar.cookies.del(i)
   cookieJar.cookies.add(cookie)
 
+proc match(cookieJar: CookieJar; url: URL): bool =
+  if cookieJar.domain.cookieDomainMatches(url):
+    return true
+  if cookieJar.allowHosts.len > 0:
+    let host = url.host
+    for re in cookieJar.allowHosts:
+      if re.match(host):
+        return true
+  return false
+
 # https://www.rfc-editor.org/rfc/rfc6265#section-5.4
-proc serialize*(cookiejar: CookieJar; url: URL): string =
-  if not cookiejar.filter.match(url):
-    return "" # fail
-  let t = getTime().utc().toTime().toUnix()
+proc serialize*(cookieJar: CookieJar; url: URL): string =
+  if not cookieJar.match(url):
+    return ""
+  var res = ""
+  let t = getTime().toUnix()
   #TODO sort
-  for i in countdown(cookiejar.cookies.high, 0):
-    let cookie = cookiejar.cookies[i]
+  for i in countdown(cookieJar.cookies.high, 0):
+    let cookie = cookieJar.cookies[i]
     if cookie.expires != -1 and cookie.expires <= t:
-      cookiejar.cookies.delete(i)
+      cookieJar.cookies.delete(i)
       continue
     if cookie.secure and url.scheme != "https":
       continue
     if not cookiePathMatches(cookie.path, url.pathname):
       continue
-    if not cookieDomainMatches(cookie.domain, url):
+    if cookie.hostOnly and cookie.domain != url.host:
+      continue
+    if not cookie.hostOnly and not cookieDomainMatches(cookie.domain, url):
       continue
-    if result != "":
-      result &= "; "
-    result &= cookie.name
-    result &= "="
-    result &= cookie.value
+    if res != "":
+      res &= "; "
+    res &= cookie.name
+    res &= "="
+    res &= cookie.value
+  return res
 
 proc newCookie*(str: string; url: URL): Opt[Cookie] =
-  let cookie = Cookie(expires: -1, created: getTime().utc().toTime().toUnix())
+  let cookie = Cookie(expires: -1, created: getTime().toUnix(), hostOnly: true)
   var first = true
-  var haspath = false
-  var hasdomain = false
+  var hasPath = false
   for part in str.split(';'):
     if first:
       cookie.name = part.until('=')
@@ -227,28 +244,23 @@ proc newCookie*(str: string; url: URL): Opt[Cookie] =
         cookie.expires = cookie.created + x.get
     of "secure": cookie.secure = true
     of "httponly": cookie.httponly = true
-    of "samesite": cookie.samesite = true
     of "path":
       if val != "" and val[0] == '/':
-        haspath = true
+        hasPath = true
         cookie.path = val
     of "domain":
-      if cookieDomainMatches(val, url):
-        cookie.domain = val
-        hasdomain = true
-      else:
+      if not cookieDomainMatches(val, url):
         return err()
-  if not hasdomain:
+      cookie.domain = val
+      cookie.hostOnly = true
+  if cookie.hostOnly:
     cookie.domain = url.host
-  if not haspath:
+  if not hasPath:
     cookie.path = defaultCookiePath(url)
   return ok(cookie)
 
-proc newCookieJar*(url: URL; allowhosts: seq[Regex]): CookieJar =
+proc newCookieJar*(url: URL; allowHosts: seq[Regex]): CookieJar =
   return CookieJar(
-    filter: newURLFilter(
-      scheme = some(url.scheme),
-      allowhost = some(url.host),
-      allowhosts = allowhosts
-    )
+    domain: url.host,
+    allowHosts: allowHosts
   )