crossmate

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

commit ebe8ff8782c9989a8bd3c37456c03fe37ed700de
parent 8d6844130f52fa859b24500dc84ccc7c581fa45a
Author: Michael Camilleri <[email protected]>
Date:   Thu,  2 Jul 2026 08:22:50 +0900

Stop leaking information about external providers

This commit removes release diagnostics that could expose a user's
session and puzzle contents relating to an external provider. The
fetcher still sends the stored cookie to the daily endpoint and
preserves the existing status handling, but it no longer prints the
request, partial cookie value, response bodies or converted XD source.

The open and resume paths for recent-change borders also stop emitting
the extra diagnostic summary that fetched the full peer-change ledger.
They now perform only the bounded recent-change read needed to update
the UI.

Co-Authored-By: Codex GPT 5.5 <[email protected]>

Diffstat:
MCrossmate/CrossmateApp.swift | 8--------
MCrossmate/Services/NYTPuzzleFetcher.swift | 89+------------------------------------------------------------------------------
2 files changed, 1 insertion(+), 96 deletions(-)

diff --git a/Crossmate/CrossmateApp.swift b/Crossmate/CrossmateApp.swift @@ -804,10 +804,6 @@ private struct PuzzleDisplayView: View { // whole board (the leave/background path below stamps it). guard let since = services.gameViewedStore.lastViewed(forGame: gameID) else { return [:] } - services.syncMonitor.note( - "recent changes[\(gameID.uuidString.prefix(8))] open diag: " - + store.recentChangesDiagnosticSummary(forGame: gameID, since: since) - ) return store.recentlyChangedCells(forGame: gameID, since: since) }, markPuzzleViewed: { stampPuzzleViewed() } @@ -1049,10 +1045,6 @@ private struct PuzzleDisplayView: View { guard let session, session.mutator.isShared, let since = services.gameViewedStore.lastViewed(forGame: gameID) else { return } - services.syncMonitor.note( - "recent changes[\(gameID.uuidString.prefix(8))] recapture diag: " - + store.recentChangesDiagnosticSummary(forGame: gameID, since: since) - ) session.recentChanges = store.recentlyChangedCells(forGame: gameID, since: since) } diff --git a/Crossmate/Services/NYTPuzzleFetcher.swift b/Crossmate/Services/NYTPuzzleFetcher.swift @@ -12,75 +12,6 @@ actor NYTPuzzleFetcher { self.cookieProvider = cookieProvider } - /// Fetches the puzzle list for a given date to get the puzzle ID, - /// then fetches the game data. For debugging, logs both responses. - func fetchPuzzleList(for date: Date) async throws -> String { - let cookie = try currentCookie() - - let formatter = DateFormatter() - formatter.dateFormat = "yyyy-MM-dd" - formatter.timeZone = TimeZone(identifier: "America/New_York") - let dateString = formatter.string(from: date) - - // Step 1: Get puzzle ID from list endpoint - let listURL = URL(string: "https://www.nytimes.com/svc/crosswords/v3/36569100/puzzles.json?publish_type=daily&date_start=\(dateString)&date_end=\(dateString)&limit=1")! - - var listRequest = URLRequest(url: listURL) - listRequest.setValue("NYT-S=\(cookie)", forHTTPHeaderField: "Cookie") - - let (listData, listResponse) = try await URLSession.shared.data(for: listRequest) - - guard let listHTTP = listResponse as? HTTPURLResponse else { - throw NYTFetchError.invalidResponse - } - - print("=== NYT LIST RESPONSE ===") - print("Status: \(listHTTP.statusCode)") - if let body = String(data: listData, encoding: .utf8) { - print("Body: \(body.prefix(2000))") - } - print("=== END LIST RESPONSE ===") - - guard listHTTP.statusCode == 200 else { - throw NYTFetchError.httpError(statusCode: listHTTP.statusCode) - } - - // Extract puzzle ID from the list - guard let listJSON = try? JSONSerialization.jsonObject(with: listData) as? [String: Any], - let results = listJSON["results"] as? [[String: Any]], - let first = results.first, - let puzzleID = first["puzzle_id"] as? Int else { - throw NYTFetchError.invalidResponse - } - - print("=== PUZZLE ID: \(puzzleID) ===") - - // Step 2: Fetch game data using the puzzle ID - let gameURL = URL(string: "https://www.nytimes.com/svc/crosswords/v6/game/\(puzzleID).json")! - - var gameRequest = URLRequest(url: gameURL) - gameRequest.setValue("NYT-S=\(cookie)", forHTTPHeaderField: "Cookie") - - let (gameData, gameResponse) = try await URLSession.shared.data(for: gameRequest) - - guard let gameHTTP = gameResponse as? HTTPURLResponse else { - throw NYTFetchError.invalidResponse - } - - print("=== NYT GAME RESPONSE ===") - print("Status: \(gameHTTP.statusCode)") - if let body = String(data: gameData, encoding: .utf8) { - print("Body: \(body.prefix(3000))") - } - print("=== END GAME RESPONSE ===") - - guard gameHTTP.statusCode == 200 else { - throw NYTFetchError.httpError(statusCode: gameHTTP.statusCode) - } - - return String(data: gameData, encoding: .utf8) ?? "" - } - func fetchPuzzle(for date: Date) async throws -> String { let cookie = try currentCookie() @@ -94,24 +25,12 @@ actor NYTPuzzleFetcher { var request = URLRequest(url: url) request.setValue("NYT-S=\(cookie)", forHTTPHeaderField: "Cookie") - print("=== NYT FETCH REQUEST ===") - print("URL: \(url)") - print("Cookie header: NYT-S=\(cookie.prefix(40))...") - print("=== END FETCH REQUEST ===") - let (data, response) = try await URLSession.shared.data(for: request) guard let httpResponse = response as? HTTPURLResponse else { throw NYTFetchError.invalidResponse } - print("=== NYT FETCH RESPONSE ===") - print("Status: \(httpResponse.statusCode)") - if httpResponse.statusCode != 200, let body = String(data: data, encoding: .utf8) { - print("Body: \(body.prefix(1000))") - } - print("=== END FETCH RESPONSE ===") - switch httpResponse.statusCode { case 200: break @@ -124,13 +43,7 @@ actor NYTPuzzleFetcher { } // Convert NYT JSON to .xd format - let xdSource = try NYTToXDConverter.convert(jsonData: data) - - print("=== CONVERTED XD ===") - print(xdSource) - print("=== END XD ===") - - return xdSource + return try NYTToXDConverter.convert(jsonData: data) } private func currentCookie() throws -> String {