crossmate

A collaborative crossword app for iOS
Log | Files | Refs | LICENSE

commit 52847a9fa10d2a16f5c1bfcc8f60ce0d83738ac9
parent 616df25068762910884dc8bb94e67d6edc05d961
Author: Michael Camilleri <[email protected]>
Date:   Sat, 18 Jul 2026 08:47:30 +0900

Require HTTPS before forwarding the cookies

When determining the signed-in account email, NYTAuthService scrapes
gqlUrlClient out of the account-page HTML and posts the full cookie
header to whatever URL it finds, without checking its scheme. A
malformed or misconfigured value could therefore direct the session
cookie over cleartext HTTP, exposing it to any network observer.

This commit rejects a non-HTTPS gqlUrlClient during configuration
extraction, so the request is never built and the flow degrades to the
existing no-email path.

Co-Authored-By: Claude Fable 5 <[email protected]>

Diffstat:
MCrossmate/Services/NYTAuthService.swift | 1+
MTests/Unit/NYTAuthServiceTests.swift | 40++++++++++++++++++++++++++++++++++++++++
2 files changed, 41 insertions(+), 0 deletions(-)

diff --git a/Crossmate/Services/NYTAuthService.swift b/Crossmate/Services/NYTAuthService.swift @@ -275,6 +275,7 @@ final class NYTAuthService { ) -> AccountGraphQLConfiguration? { guard let urlString = firstJSONStringValue(named: "gqlUrlClient", in: html), let url = URL(string: urlString), + url.scheme?.lowercased() == "https", let appType = firstJSONStringValue(named: "nyt-app-type", in: html), let appVersion = firstJSONStringValue(named: "nyt-app-version", in: html), let token = firstJSONStringValue(named: "nyt-token", in: html) else { diff --git a/Tests/Unit/NYTAuthServiceTests.swift b/Tests/Unit/NYTAuthServiceTests.swift @@ -84,4 +84,44 @@ struct NYTAuthServiceTests { #expect(configuration?.headers["nyt-app-version"] == "0.0.5") #expect(configuration?.headers["nyt-token"] == "abc/123") } + + @Test("Rejects non-HTTPS GraphQL endpoint in account page HTML") + func rejectsNonHTTPSGraphQLEndpoint() throws { + let html = #""" + <script> + window.__preloadedData = { + "config": { + "gqlUrlClient": "http://samizdat-graphql.nytimes.com/graphql/v2", + "gqlRequestHeaders": { + "nyt-app-type": "project-vi", + "nyt-app-version": "0.0.5", + "nyt-token": "abc/123" + } + } + } + </script> + """# + + #expect(NYTAuthService.extractAccountGraphQLConfiguration(from: html) == nil) + } + + @Test("Rejects schemeless GraphQL endpoint in account page HTML") + func rejectsSchemelessGraphQLEndpoint() throws { + let html = #""" + <script> + window.__preloadedData = { + "config": { + "gqlUrlClient": "/graphql/v2", + "gqlRequestHeaders": { + "nyt-app-type": "project-vi", + "nyt-app-version": "0.0.5", + "nyt-token": "abc/123" + } + } + } + </script> + """# + + #expect(NYTAuthService.extractAccountGraphQLConfiguration(from: html) == nil) + } }