Skip to content

Commit c080982

Browse files
committed
Reject unsafe Muse reset timestamps
1 parent 0b131e7 commit c080982

3 files changed

Lines changed: 24 additions & 1 deletion

File tree

Sources/CodexBarCore/Providers/Muse/MuseUsageFetcher.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ public enum MuseUsageFetcher {
211211
}
212212

213213
private static func date(fromUnixSeconds value: Double?) -> Date? {
214-
guard let value, value > 0, value.isFinite else { return nil }
214+
guard let value, value > 0, value <= Date.distantFuture.timeIntervalSince1970 else { return nil }
215215
return Date(timeIntervalSince1970: value)
216216
}
217217

Tests/CodexBarTests/MuseUsageFetcherTests.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,27 @@ struct MuseUsageFetcherTests {
181181
}
182182
}
183183

184+
@Test(arguments: ["window", "weekly"])
185+
func `oversized reset timestamps preserve usage without unsafe countdowns`(_ windowName: String) throws {
186+
var payload = try #require(JSONSerialization.jsonObject(with: Self.liveMintData) as? [String: Any])
187+
var windows = try #require(payload["subs_usage"] as? [String: Any])
188+
var window = try #require(windows[windowName] as? [String: Any])
189+
window["resets_at"] = 1e30
190+
windows[windowName] = window
191+
payload["subs_usage"] = windows
192+
let data = try JSONSerialization.data(withJSONObject: payload)
193+
let now = Date(timeIntervalSince1970: 1_788_580_000)
194+
let usage = try MuseUsageFetcher._parseMintResponseForTesting(data, now: now).toUsageSnapshot()
195+
let affected = try #require(windowName == "window" ? usage.primary : usage.secondary)
196+
let unaffected = try #require(windowName == "window" ? usage.secondary : usage.primary)
197+
198+
#expect(affected.usedPercent == 96)
199+
try #require(affected.resetsAt == nil)
200+
#expect(UsageFormatter.resetLine(for: affected, style: .countdown, now: now) == nil)
201+
#expect(unaffected.resetsAt != nil)
202+
#expect(UsageFormatter.resetLine(for: unaffected, style: .countdown, now: now) != nil)
203+
}
204+
184205
@Test
185206
func `descriptor reports oauth login without api key support`() throws {
186207
let descriptor = ProviderDescriptorRegistry.descriptor(for: .muse)

docs/muse.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ Credential precedence: when `providers.meta.access_token` is present inline in t
2727
- 5-hour window percent, duration, and `resets_at`.
2828
- Weekly window percent and `resets_at`.
2929

30+
Reset timestamps outside the supported date range are omitted without discarding the window's usage percentage.
31+
3032
Pay-as-you-go accounts without `is_subs_active` are reported as having no subscription rather than a fake 0% bar. Accounts that still need a payment method are reported as billing-incomplete.
3133

3234
## Privacy

0 commit comments

Comments
 (0)