#include "nocal/sync/microsoft_graph.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { using namespace std::chrono; using nocal::storage::CacheSnapshot; using nocal::storage::CachedCalendar; using nocal::storage::CachedEvent; using nocal::storage::PullPage; using nocal::storage::SyncCache; using nocal::storage::SyncCheckpoint; using nocal::sync::HttpHeader; using nocal::sync::HttpMethod; using nocal::sync::HttpRequest; using nocal::sync::HttpResponse; using nocal::sync::HttpTransport; using nocal::sync::MicrosoftGraphCredentials; using nocal::sync::MicrosoftGraphSync; using nocal::sync::MicrosoftGraphWindow; constexpr std::string_view graph_prefix = "https://graph.microsoft.com/"; constexpr std::string_view primary_remote_id = "remote-primary-calendar"; constexpr std::string_view primary_local_prefix = "graph-calendar-"; constexpr std::string_view event_local_prefix = "graph-event-"; void require(bool condition, std::string_view message) { if (!condition) { throw std::runtime_error(std::string{message}); } } template std::string require_throws_redacted(Function&& function, std::string_view message, std::span sensitive = {}) { try { function(); } catch (const std::exception& error) { const std::string description = error.what(); for (const auto value : sensitive) { require(value.empty() || description.find(value) == std::string::npos, std::string{message}.append(": sensitive data leaked in exception")); } return description; } throw std::runtime_error(std::string{message}.append(": no exception")); } class TempDirectory { public: TempDirectory() { std::string pattern = (std::filesystem::temp_directory_path() / "nocal-graph-security.XXXXXX").string(); std::vector writable(pattern.begin(), pattern.end()); writable.push_back('\0'); const char* created = ::mkdtemp(writable.data()); if (created == nullptr) { throw std::runtime_error("mkdtemp failed"); } path_ = created; } TempDirectory(const TempDirectory&) = delete; TempDirectory& operator=(const TempDirectory&) = delete; ~TempDirectory() { std::error_code ignored; std::filesystem::remove_all(path_, ignored); } [[nodiscard]] const std::filesystem::path& path() const noexcept { return path_; } private: std::filesystem::path path_; }; struct ScriptStep { HttpResponse response; std::optional exception; }; class ScriptedTransport final : public HttpTransport { public: HttpResponse send(const HttpRequest& request) override { requests.push_back(request); if (position >= steps.size()) { throw std::runtime_error("unscripted Graph request"); } ScriptStep& step = steps[position++]; if (step.exception) { throw std::runtime_error(*step.exception); } return step.response; } void respond(std::string body, int status = 200) { steps.push_back({HttpResponse{status, {}, std::move(body)}, std::nullopt}); } void fail(std::string description) { steps.push_back({HttpResponse{}, std::move(description)}); } std::vector steps; std::vector requests; std::size_t position{0}; }; struct Rig { explicit Rig(const std::filesystem::path& root) : cache(root / ("graph-" + std::to_string(sequence++) + ".db")), graph(transport, cache) {} static inline int sequence = 0; ScriptedTransport transport; SyncCache cache; MicrosoftGraphSync graph; MicrosoftGraphCredentials credentials{"local-account", "SENSITIVE_ACCESS_TOKEN"}; }; [[nodiscard]] std::string profile_body( std::string_view remote_subject = "remote-user", std::string_view display = "Alice") { return nlohmann::json{{"id", remote_subject}, {"displayName", display}, {"userPrincipalName", "alice@example.test"}} .dump(); } [[nodiscard]] nlohmann::json calendar_json(std::string_view id, std::string_view name, bool primary, bool can_edit = true) { return {{"id", id}, {"name", name}, {"color", "auto"}, {"hexColor", "#123456"}, {"canEdit", can_edit}, {"isDefaultCalendar", primary}}; } [[nodiscard]] std::string calendar_page(const nlohmann::json& calendars, std::optional next = std::nullopt) { nlohmann::json page{{"value", calendars}}; if (next) { page["@odata.nextLink"] = *next; } return page.dump(); } [[nodiscard]] nlohmann::json live_event(std::string_view id = "remote-event-1", std::string_view start = "2026-07-18T09:00:00.1234567", std::string_view end = "2026-07-18T10:00:00.1234567", std::string_view zone = "UTC") { return {{"id", id}, {"iCalUId", "uid-1"}, {"subject", "Planning"}, {"@odata.etag", "etag-1"}, {"changeKey", "change-1"}, {"isAllDay", false}, {"start", {{"dateTime", start}, {"timeZone", zone}}}, {"end", {{"dateTime", end}, {"timeZone", zone}}}, {"location", {{"displayName", "Room 1"}}}, {"bodyPreview", "Agenda"}}; } [[nodiscard]] std::string delta_page(const nlohmann::json& events, std::optional next, std::optional delta) { nlohmann::json page{{"value", events}}; if (next) { page["@odata.nextLink"] = *next; } if (delta) { page["@odata.deltaLink"] = *delta; } return page.dump(); } [[nodiscard]] MicrosoftGraphWindow july_window() { const auto start = sys_days{year{2026} / July / 18}; return {duration_cast(start.time_since_epoch()).count(), duration_cast((start + days{1}).time_since_epoch()).count()}; } [[nodiscard]] const HttpHeader* header(const HttpRequest& request, std::string_view name) { const auto found = std::ranges::find_if(request.headers, [&](const HttpHeader& value) { if (value.name.size() != name.size()) { return false; } for (std::size_t index = 0; index < name.size(); ++index) { const auto lower = [](unsigned char character) { return character >= 'A' && character <= 'Z' ? static_cast(character + ('a' - 'A')) : character; }; if (lower(static_cast(value.name[index])) != lower(static_cast(name[index]))) { return false; } } return true; }); return found == request.headers.end() ? nullptr : &*found; } void require_safe_request(const HttpRequest& request, std::string_view token) { require(request.method == HttpMethod::get, "Graph read adapter sent a non-GET request"); require(request.body.empty(), "Graph GET request unexpectedly contains a body"); const HttpHeader* authorization = header(request, "Authorization"); require(authorization != nullptr && authorization->value == "Bearer " + std::string{token}, "Graph request is missing the exact bearer credential"); require(request.url.starts_with(graph_prefix), "Graph request left the trusted origin"); require(request.url.find("/beta/") == std::string::npos, "Graph adapter called the beta API"); } void seed_account(Rig& rig) { rig.transport.respond(profile_body()); static_cast(rig.graph.refresh_account(rig.credentials)); } void seed_calendars(Rig& rig, bool include_other = true) { seed_account(rig); nlohmann::json values = nlohmann::json::array( {calendar_json(primary_remote_id, "Primary", true)}); if (include_other) { values.push_back(calendar_json("remote-other-calendar", "Other", false, false)); } rig.transport.respond(calendar_page(values)); rig.graph.refresh_calendars(rig.credentials); } [[nodiscard]] std::string sha256_hex(std::string_view value) { std::array digest{}; unsigned int size = 0; require(EVP_Digest(value.data(), value.size(), digest.data(), &size, EVP_sha256(), nullptr) == 1 && size == digest.size(), "could not hash Graph local identifier fixture"); constexpr char hexadecimal[] = "0123456789abcdef"; std::string result; result.reserve(digest.size() * 2); for (const unsigned char byte : digest) { result.push_back(hexadecimal[byte >> 4U]); result.push_back(hexadecimal[byte & 0x0fU]); } return result; } [[nodiscard]] std::string local_calendar_id( std::string_view account, std::string_view remote) { std::string input{"calendar", 8}; input.push_back('\0'); input += account; input.push_back('\0'); input += remote; return std::string{primary_local_prefix} + sha256_hex(input); } [[nodiscard]] std::string local_event_id( std::string_view calendar_id, std::string_view remote) { std::string input{"event", 5}; input.push_back('\0'); input += calendar_id; input.push_back('\0'); input += remote; return std::string{event_local_prefix} + sha256_hex(input); } void test_credentials_and_request_security(const std::filesystem::path& root) { const std::vector invalid{ {"", "token"}, {"bad\naccount", "token"}, {"SENSITIVE_ACCOUNT_ID_123", ""}, {"SENSITIVE_ACCOUNT_ID_123", " "}, {"SENSITIVE_ACCOUNT_ID_123", "token\r\nInjected: yes"}}; for (const auto& credentials : invalid) { Rig rig{root}; const CacheSnapshot before = rig.cache.snapshot(); const std::array sensitive{ credentials.account_id, credentials.access_token}; require_throws_redacted( [&] { static_cast(rig.graph.refresh_account(credentials)); }, "invalid Graph credentials were accepted", sensitive); require(rig.transport.requests.empty(), "invalid credentials reached the transport"); require(rig.cache.snapshot() == before, "invalid credentials changed the cache"); } Rig rig{root}; rig.transport.respond(profile_body()); const auto account = rig.graph.refresh_account(rig.credentials); require(account.id == rig.credentials.account_id && account.provider == "microsoft-graph" && account.remote_subject == "remote-user" && account.display_name == "Alice", "valid profile did not map to the frozen account contract"); require(rig.transport.requests.size() == 1, "profile refresh sent the wrong request count"); require_safe_request(rig.transport.requests[0], rig.credentials.access_token); rig.transport.respond(profile_body("remote-user", "")); const auto fallback = rig.graph.refresh_account(rig.credentials); require(fallback.display_name == "alice@example.test", "empty displayName did not fall back to userPrincipalName"); } void test_account_failures_are_atomic_and_redacted(const std::filesystem::path& root) { Rig rig{root}; rig.transport.respond(profile_body()); static_cast(rig.graph.refresh_account(rig.credentials)); const CacheSnapshot baseline = rig.cache.snapshot(); const std::string response_secret = "SENSITIVE_PROFILE_BODY"; rig.transport.respond(response_secret, 401); const std::array response_sensitive{ rig.credentials.access_token, response_secret}; require_throws_redacted([&] { static_cast(rig.graph.refresh_account(rig.credentials)); }, "non-2xx profile response was accepted", response_sensitive); require(rig.cache.snapshot() == baseline, "non-2xx profile response mutated account cache"); rig.transport.fail("transport detail " + rig.credentials.access_token); const std::array token_sensitive{rig.credentials.access_token}; require_throws_redacted([&] { static_cast(rig.graph.refresh_account(rig.credentials)); }, "profile transport failure did not propagate safely", token_sensitive); require(rig.cache.snapshot() == baseline, "profile transport failure mutated account cache"); const std::vector malformed{"{", "[]", R"({"id":"x"})", R"({"id":"first","id":"second","displayName":"name"})", std::string(8U * 1024U * 1024U + 1U, 'x')}; for (const auto& body : malformed) { rig.transport.respond(body); const std::array sensitive{body, rig.credentials.access_token}; require_throws_redacted( [&] { static_cast(rig.graph.refresh_account(rig.credentials)); }, "malformed profile body was accepted", sensitive); require(rig.cache.snapshot() == baseline, "malformed profile body mutated account cache"); } } void test_calendar_listing_atomicity_and_duplicates(const std::filesystem::path& root) { Rig rig{root}; seed_calendars(rig); const CacheSnapshot baseline = rig.cache.snapshot(); const std::string next = "https://graph.microsoft.com/v1.0/me/calendars?$skiptoken=partial-cursor"; rig.transport.respond(calendar_page( nlohmann::json::array({calendar_json(primary_remote_id, "Changed", true)}), next)); rig.transport.fail("later calendar page failed SENSITIVE_ACCESS_TOKEN"); const std::array sensitive{ rig.credentials.access_token, next, primary_remote_id}; require_throws_redacted([&] { rig.graph.refresh_calendars(rig.credentials); }, "partial calendar listing was accepted", sensitive); require(rig.cache.snapshot() == baseline, "partial calendar listing changed or deactivated cached calendars"); const std::vector invalid_pages{ calendar_page(nlohmann::json::array( {calendar_json("duplicate", "One", true), calendar_json("duplicate", "Two", false)})), calendar_page(nlohmann::json::array( {calendar_json("primary-a", "One", true), calendar_json("primary-b", "Two", true)})), R"({"value":{},"@odata.nextLink":null})", R"({"value":[],"value":[]})", "{", std::string(8U * 1024U * 1024U + 1U, 'x')}; for (const auto& body : invalid_pages) { rig.transport.respond(body); const std::array page_sensitive{body, rig.credentials.access_token}; require_throws_redacted([&] { rig.graph.refresh_calendars(rig.credentials); }, "invalid calendar listing was accepted", page_sensitive); require(rig.cache.snapshot() == baseline, "invalid calendar listing mutated cache"); } } void test_continuation_url_rejection(const std::filesystem::path& root) { const std::vector unsafe{ "http://graph.microsoft.com/v1.0/next", "https://evil.example.test/v1.0/next", "https://graph.microsoft.com.evil.test/v1.0/next", "https://user@graph.microsoft.com/v1.0/next", "https://graph.microsoft.com:443/v1.0/next", "https://graph.microsoft.com/v1.0/next#fragment", "https://graph.microsoft.com/v1.0/next\nInjected: yes", std::string{"https://graph.microsoft.com/"} + std::string(16U * 1024U, 'x')}; for (const auto& url : unsafe) { Rig rig{root}; seed_calendars(rig); const std::size_t requests_before = rig.transport.requests.size(); const CacheSnapshot baseline = rig.cache.snapshot(); rig.transport.respond(calendar_page(nlohmann::json::array(), url)); const std::array sensitive{ url, rig.credentials.access_token, primary_remote_id}; require_throws_redacted([&] { rig.graph.refresh_calendars(rig.credentials); }, "unsafe Graph continuation was accepted", sensitive); require(rig.transport.requests.size() == requests_before + 1, "unsafe continuation reached a credentialed request"); require(rig.cache.snapshot() == baseline, "unsafe continuation mutated cache"); } Rig rig{root}; seed_calendars(rig); const std::string repeated = "https://graph.microsoft.com/v1.0/me/calendars?$skiptoken=repeated"; const std::size_t before = rig.transport.requests.size(); rig.transport.respond(calendar_page(nlohmann::json::array(), repeated)); rig.transport.respond(calendar_page(nlohmann::json::array(), repeated)); const std::array sensitive{repeated, rig.credentials.access_token}; require_throws_redacted([&] { rig.graph.refresh_calendars(rig.credentials); }, "repeated Graph continuation was accepted", sensitive); require(rig.transport.requests.size() == before + 2, "repeated continuation triggered another credentialed request"); } void test_window_and_primary_preconditions(const std::filesystem::path& root) { const std::vector invalid{{0, 0}, {1, 0}, {std::numeric_limits::min(), 0}, {0, std::numeric_limits::max()}}; for (const auto window : invalid) { Rig rig{root}; seed_calendars(rig); const std::size_t before = rig.transport.requests.size(); require_throws_redacted( [&] { rig.graph.sync_primary_calendar_window(rig.credentials, window); }, "invalid Graph window was accepted"); require(rig.transport.requests.size() == before, "invalid Graph window reached the transport"); } Rig no_primary{root}; seed_account(no_primary); no_primary.transport.respond(calendar_page( nlohmann::json::array({calendar_json("not-primary", "Other", false)}))); no_primary.graph.refresh_calendars(no_primary.credentials); const std::size_t before = no_primary.transport.requests.size(); require_throws_redacted( [&] { no_primary.graph.sync_primary_calendar_window( no_primary.credentials, july_window()); }, "sync without a primary calendar was accepted"); require(no_primary.transport.requests.size() == before, "missing-primary sync reached the transport"); } void test_delta_request_and_strict_page_shape(const std::filesystem::path& root) { Rig rig{root}; seed_calendars(rig); const CacheSnapshot baseline = rig.cache.snapshot(); const std::vector invalid{ R"({"value":[]})", R"({"value":[],"@odata.nextLink":"https://graph.microsoft.com/v1.0/next","@odata.deltaLink":"https://graph.microsoft.com/v1.0/delta"})", R"({"value":{},"@odata.deltaLink":"https://graph.microsoft.com/v1.0/delta"})", R"({"value":[],"value":[],"@odata.deltaLink":"https://graph.microsoft.com/v1.0/delta"})", "{", std::string(8U * 1024U * 1024U + 1U, 'x')}; for (const auto& body : invalid) { rig.transport.respond(body); const std::array sensitive{body, rig.credentials.access_token}; require_throws_redacted( [&] { rig.graph.sync_primary_calendar_window(rig.credentials, july_window()); }, "invalid delta page was accepted", sensitive); require(rig.cache.snapshot() == baseline, "invalid delta page advanced cursor or changed cache"); } const std::string delta = "https://graph.microsoft.com/v1.0/me/calendarView/delta?$deltatoken=done"; rig.transport.respond(delta_page(nlohmann::json::array(), std::nullopt, delta)); const std::size_t request_index = rig.transport.requests.size(); rig.graph.sync_primary_calendar_window(rig.credentials, july_window()); const HttpRequest& request = rig.transport.requests[request_index]; require_safe_request(request, rig.credentials.access_token); require(request.url.find("/v1.0/me/calendarView/delta?") != std::string::npos, "initial sync did not use stable primary calendarView/delta"); require(request.url.find("startDateTime=2026-07-18T00%3A00%3A00.000000Z") != std::string::npos && request.url.find("endDateTime=2026-07-19T00%3A00%3A00.000000Z") != std::string::npos, "initial delta window was not canonically encoded"); require(request.url.find("/beta/") == std::string::npos && request.url.find("/calendars/") == std::string::npos, "adapter used beta or undocumented per-calendar delta"); const HttpHeader* prefer = header(request, "Prefer"); require(prefer != nullptr && prefer->value.find("UTC") != std::string::npos, "delta request is missing its UTC Prefer header"); } void test_delta_continuation_security(const std::filesystem::path& root) { { Rig rig{root}; seed_calendars(rig); const CacheSnapshot baseline = rig.cache.snapshot(); const std::string unsafe = "https://evil.example.test/v1.0/delta?SENSITIVE_CURSOR"; const std::string body = delta_page( nlohmann::json::array({live_event("SENSITIVE_REMOTE_ID")}), unsafe, std::nullopt); const std::size_t before = rig.transport.requests.size(); rig.transport.respond(body); const std::array sensitive{ unsafe, body, "SENSITIVE_REMOTE_ID", rig.credentials.access_token}; require_throws_redacted( [&] { rig.graph.sync_primary_calendar_window(rig.credentials, july_window()); }, "unsafe delta continuation was accepted", sensitive); require(rig.transport.requests.size() == before + 1, "unsafe delta continuation reached a credentialed request"); require(rig.cache.snapshot() == baseline, "page with unsafe delta continuation was committed"); } { Rig rig{root}; seed_calendars(rig); const std::string repeated = "https://graph.microsoft.com/v1.0/me/calendarView/delta?$skiptoken=repeated"; const std::size_t before = rig.transport.requests.size(); rig.transport.respond(delta_page(nlohmann::json::array(), repeated, std::nullopt)); rig.transport.respond(delta_page(nlohmann::json::array(), repeated, std::nullopt)); const std::array sensitive{repeated, rig.credentials.access_token}; require_throws_redacted( [&] { rig.graph.sync_primary_calendar_window(rig.credentials, july_window()); }, "repeated delta continuation was accepted", sensitive); require(rig.transport.requests.size() == before + 2, "repeated delta continuation triggered a third credentialed request"); } } void test_authoritative_timestamp_offsets(const std::filesystem::path& root) { Rig rig{root}; seed_calendars(rig); const auto event = live_event("offset-event", "2026-07-18T09:00:00.1234567Z", "2026-07-18T11:00:00.1234567+01:00", "Unknown/But-Ignored"); rig.transport.respond(delta_page(nlohmann::json::array({event}), std::nullopt, "https://graph.microsoft.com/v1.0/me/calendarView/delta?$deltatoken=offset")); rig.graph.sync_primary_calendar_window(rig.credentials, july_window()); const CacheSnapshot snapshot = rig.cache.snapshot(); require(snapshot.instances.size() == 1, "valid explicit-offset event was not cached"); const auto day = sys_days{year{2026} / July / 18}; const auto expected_start = duration_cast((day + 9h).time_since_epoch()).count() + 123'456; const auto expected_end = duration_cast((day + 10h).time_since_epoch()).count() + 123'456; require(snapshot.instances[0].start_epoch_microseconds == expected_start && snapshot.instances[0].end_epoch_microseconds == expected_end, "explicit offset authority or seventh-digit truncation is incorrect"); } void test_timestamp_identity_and_interval_rejection(const std::filesystem::path& root) { const std::vector invalid_events{ live_event("unknown-zone", "2026-07-18T09:00:00", "2026-07-18T10:00:00", "Europe/London"), live_event("invalid-date", "2026-02-30T09:00:00", "2026-02-30T10:00:00"), live_event("leap-second", "2026-07-18T09:00:60Z", "2026-07-18T10:00:00Z", "Ignored"), live_event("bad-offset", "2026-07-18T09:00:00+25:00", "2026-07-18T10:00:00+25:00", "Ignored"), live_event("backwards", "2026-07-18T10:00:00", "2026-07-18T09:00:00"), }; for (const auto& event : invalid_events) { Rig rig{root}; seed_calendars(rig); const CacheSnapshot baseline = rig.cache.snapshot(); const std::string delta = "https://graph.microsoft.com/v1.0/me/calendarView/delta?$deltatoken=invalid"; const std::string body = delta_page(nlohmann::json::array({event}), std::nullopt, delta); rig.transport.respond(body); const std::array sensitive{ body, event.at("id").get_ref(), rig.credentials.access_token}; require_throws_redacted( [&] { rig.graph.sync_primary_calendar_window(rig.credentials, july_window()); }, "invalid Graph event timestamp was accepted", sensitive); require(rig.cache.snapshot() == baseline, "invalid Graph event advanced cursor or changed cache"); } Rig duplicate{root}; seed_calendars(duplicate); const CacheSnapshot baseline = duplicate.cache.snapshot(); duplicate.transport.respond(delta_page( nlohmann::json::array({live_event("duplicate"), live_event("duplicate")}), std::nullopt, "https://graph.microsoft.com/v1.0/me/calendarView/delta?$deltatoken=duplicate")); require_throws_redacted( [&] { duplicate.graph.sync_primary_calendar_window( duplicate.credentials, july_window()); }, "duplicate Graph event identity was accepted"); require(duplicate.cache.snapshot() == baseline, "duplicate Graph event advanced cursor or changed cache"); } void test_resume_after_later_failure(const std::filesystem::path& root) { Rig rig{root}; seed_calendars(rig); const std::string next = "https://graph.microsoft.com/v1.0/me/calendarView/delta?$skiptoken=page-2"; rig.transport.respond( delta_page(nlohmann::json::array({live_event()}), next, std::nullopt)); rig.transport.fail("later transport failed " + next + " remote-event-1 SENSITIVE_ACCESS_TOKEN"); const std::size_t start_request = rig.transport.requests.size(); const std::array sensitive{ next, "remote-event-1", rig.credentials.access_token}; require_throws_redacted( [&] { rig.graph.sync_primary_calendar_window(rig.credentials, july_window()); }, "later delta transport failure was accepted", sensitive); const CacheSnapshot committed = rig.cache.snapshot(); require(committed.events.size() == 1 && committed.instances.size() == 1 && committed.checkpoints.size() == 1 && committed.checkpoints[0].cursor == next && !committed.checkpoints[0].complete, "validated first delta page was not committed atomically before later failure"); require(rig.transport.requests.size() == start_request + 2, "paged delta failure sent an unexpected request count"); const std::string final = "https://graph.microsoft.com/v1.0/me/calendarView/delta?$deltatoken=complete"; rig.transport.respond(delta_page(nlohmann::json::array(), std::nullopt, final)); const std::size_t resume_index = rig.transport.requests.size(); rig.graph.sync_primary_calendar_window(rig.credentials, july_window()); require(rig.transport.requests[resume_index].url == next, "later sync did not resume from the last committed opaque cursor"); require(rig.cache.snapshot().checkpoints[0].cursor == final && rig.cache.snapshot().checkpoints[0].complete, "resumed delta did not commit its final cursor"); } void test_tombstone_preserves_payload(const std::filesystem::path& root) { Rig rig{root}; seed_calendars(rig); const std::string first_delta = "https://graph.microsoft.com/v1.0/me/calendarView/delta?$deltatoken=first"; rig.transport.respond( delta_page(nlohmann::json::array({live_event()}), std::nullopt, first_delta)); rig.graph.sync_primary_calendar_window(rig.credentials, july_window()); const std::string raw = rig.cache.snapshot().events[0].raw_payload; const std::string second_delta = "https://graph.microsoft.com/v1.0/me/calendarView/delta?$deltatoken=second"; const nlohmann::json removed = {{"id", "remote-event-1"}, {"@removed", {{"reason", "deleted"}}}}; rig.transport.respond( delta_page(nlohmann::json::array({removed}), std::nullopt, second_delta)); rig.graph.sync_primary_calendar_window(rig.credentials, july_window()); const CacheSnapshot snapshot = rig.cache.snapshot(); require(snapshot.events.size() == 1 && snapshot.events[0].deleted && snapshot.events[0].raw_payload == raw && snapshot.instances.empty(), "Graph tombstone did not retain raw payload and remove instances"); } void test_cache_failure_does_not_advance_cursor(const std::filesystem::path& root) { Rig rig{root}; seed_calendars(rig, false); const std::string calendar_id = local_calendar_id(rig.credentials.account_id, primary_remote_id); const std::string poisoned_id = local_event_id(calendar_id, "target-remote-event"); const std::string prior = "https://graph.microsoft.com/v1.0/me/calendarView/delta?$skiptoken=prior"; CachedEvent poisoned{poisoned_id, calendar_id, "different-remote-id", "uid", "", "", R"({"poisoned":true})", false}; rig.cache.apply_pull_page(PullPage{{poisoned}, {}, SyncCheckpoint{calendar_id, "2026-07-18T00:00:00.000000Z", "2026-07-19T00:00:00.000000Z", prior, false}}); const CacheSnapshot baseline = rig.cache.snapshot(); const std::string next = "https://graph.microsoft.com/v1.0/me/calendarView/delta?$deltatoken=must-not-commit"; const std::string body = delta_page( nlohmann::json::array({live_event("target-remote-event")}), std::nullopt, next); rig.transport.respond(body); const std::array sensitive{ prior, next, "target-remote-event", rig.credentials.access_token}; require_throws_redacted( [&] { rig.graph.sync_primary_calendar_window(rig.credentials, july_window()); }, "Graph cache identity failure was accepted", sensitive); require(rig.cache.snapshot() == baseline, "cache failure advanced Graph cursor or partially replaced event state"); } } // namespace int main() { try { TempDirectory temporary; test_credentials_and_request_security(temporary.path()); test_account_failures_are_atomic_and_redacted(temporary.path()); test_calendar_listing_atomicity_and_duplicates(temporary.path()); test_continuation_url_rejection(temporary.path()); test_window_and_primary_preconditions(temporary.path()); test_delta_request_and_strict_page_shape(temporary.path()); test_delta_continuation_security(temporary.path()); test_authoritative_timestamp_offsets(temporary.path()); test_timestamp_identity_and_interval_rejection(temporary.path()); test_resume_after_later_failure(temporary.path()); test_tombstone_preserves_payload(temporary.path()); test_cache_failure_does_not_advance_cursor(temporary.path()); } catch (const std::exception& error) { std::cerr << "Microsoft Graph security test failure: " << error.what() << '\n'; return 1; } std::cout << "Microsoft Graph security tests passed\n"; return 0; }