Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
### Fixed
- Keychain: limit repeated cache ACL validation and memory growth while preserving recovery after temporary failures or external repairs (#3300, #3301). Thanks @IgorKhramtsov!
- Grok: restore 0% usage for a validated active billing period with an omitted usage scalar, preserving unknown usage for incomplete responses (#3261, #3325). Thanks @sf-jin-ku and @olddonkey!
- Grok: keep malformed billing responses from turning unknown usage into 0%, while safely ignoring unknown byte fields (#3357).
- Ollama: restore usage bars for monthly included credits and show matching history tabs while preserving legacy quota parsing and saved history (#3346). Thanks @haixing23!
- Menu bar: keep status components and website links scoped to their provider when switching cached tabs, preventing Claude status from appearing under Grok or Codex (#3320). Thanks @gianpaj!
- Usage & Spend: keep stalled or failed Codex catch-up paused until explicit Refresh, preventing background synchronization from restarting CPU-heavy scans (partial fix for #3316). Thanks @heyajulia!
Expand Down
18 changes: 16 additions & 2 deletions Sources/CodexBarCore/Providers/Grok/GrokWebBillingFetcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ public enum GrokWebBillingFetcher {

while index < bytes.count {
let fieldStart = index
guard let key = Self.readVarint(bytes, index: &index), key != 0 else {
guard let key = Self.readVarint(bytes, index: &index), key >> 3 > 0, key >> 3 <= 536_870_911 else {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid rejecting opaque bytes as invalid nested tags

When a valid response contains an unknown length-delimited string or bytes field, the unconditional recursion at lines 501–507 applies this new field-number validation to its opaque contents. For example, an unknown bytes field containing 0x01 followed by eight zero bytes is valid protobuf wire data, but this guard interprets that payload as nested field zero, marks the entire scan incomplete, and prevents an otherwise validated active period from supplying its implicit 0% usage. Restrict completeness validation to paths known to contain submessages, or avoid propagating nested malformedness from opaque unknown fields.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c983271. The scanner now recurses only into the message paths declared by the official billing schema. Unknown length-delimited payloads are skipped entirely after checking their outer length; their bytes contribute neither completeness failures nor invented percentages/reset dates.

The six root/config opaque-payload regressions fail with 14 assertions before the repair and pass afterward. Tests also verify that malformed messages still block implicit zero at all 16 known message paths, and historical nested timestamps remain readable. The broader Grok/architecture run passes 191 tests; full-suite and exact-head CI verification are still running before merge.

scan.isComplete = false
index = fieldStart + 1
continue
Expand Down Expand Up @@ -498,7 +498,7 @@ public enum GrokWebBillingFetcher {
}
let start = index
let end = index + Int(length)
if depth < 4 {
if depth < 4, Self.isKnownBillingMessage(path: fieldPath) {
let nested = Self.scanProtobuf(
Data(bytes[start..<end]),
depth: depth + 1,
Expand Down Expand Up @@ -534,12 +534,26 @@ public enum GrokWebBillingFetcher {
return (scan, nextOrder)
}

private static func isKnownBillingMessage(path: [UInt64]) -> Bool {
// The billing descriptor declares these messages; other length-delimited fields may be opaque bytes.
switch path {
case [1],
[1, 2], [1, 3], [1, 4], [1, 5], [1, 6], [1, 7], [1, 8], [1, 12],
[1, 6, 1], [1, 6, 2], [1, 6, 3], [1, 8, 2], [1, 8, 3],
[1, 6, 3, 2], [1, 6, 3, 3]:
true
default:
false
}
}

private static func readVarint(_ bytes: [UInt8], index: inout Int) -> UInt64? {
var value: UInt64 = 0
var shift: UInt64 = 0
while index < bytes.count, shift < 64 {
let byte = bytes[index]
index += 1
if shift == 63, byte > 1 { return nil }
value |= UInt64(byte & 0x7F) << shift
if byte & 0x80 == 0 {
return value
Expand Down
87 changes: 82 additions & 5 deletions Tests/CodexBarTests/GrokZeroUsageTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,61 @@ struct GrokZeroUsageTests {
#expect(try await Self.resolve(parsed).snapshot.usedPercent == 0)
}

@Test(arguments: Self.malformedSuffixes)
func `malformed frames cannot turn unknown usage into zero`(suffix: Data) async throws {
let parsed = try GrokWebBillingFetcher.parseGRPCWebResponse(Self.payload(suffix: suffix), now: Self.now)

#expect(!parsed.usedPercentIsImplicitZero)
#expect(try await Self.resolve(parsed).snapshot.usedPercent == nil)
}

@Test(arguments: [
Data([0x00]), // Invalid field key.
Data([0x0D, 0x00]), // Truncated percentage.
Data([0x72, 0x04, 0x08]), // Truncated nested message.
Data([0x70, 0x80]), // Truncated varint.
Self.fixed64Field(tag: [0xF9, 0xFF, 0xFF, 0xFF, 0x0F]), // Highest valid field number.
Data([0x70]) + Self.varint(.max), // A valid UInt64.max scalar.
])
func `malformed frames cannot turn unknown usage into zero`(suffix: Data) async throws {
func `valid unknown fields preserve implicit zero`(suffix: Data) async throws {
let parsed = try GrokWebBillingFetcher.parseGRPCWebResponse(Self.payload(suffix: suffix), now: Self.now)

#expect(parsed.usedPercentIsImplicitZero)
#expect(try await Self.resolve(parsed).snapshot.usedPercent == 0)
}

@Test(arguments: [false, true], Self.opaquePayloads)
func `unknown byte fields cannot invalidate or invent billing values`(
atRoot: Bool, bytes: Data) async throws
{
let opaqueField = Self.message(path: [14], contents: bytes)
let payload = atRoot ? Self.payload() + opaqueField : Self.payload(suffix: opaqueField)
let parsed = try GrokWebBillingFetcher.parseGRPCWebResponse(payload, now: Self.now)

#expect(parsed.usedPercent == 0)
#expect(parsed.usedPercentIsImplicitZero)
#expect(!parsed.usedPercentIsWirePublished)
#expect(parsed.resetsAt == Date(timeIntervalSince1970: 1_789_000_000))
#expect(try await Self.resolve(parsed).snapshot.usedPercent == 0)
}

@Test(arguments: Self.billingMessagePaths)
func `malformed known messages still prevent implicit zero`(path: [UInt64]) async throws {
let malformedMessage = Self.message(path: path, contents: Self.fixed64Field(tag: [0x01]))
let parsed = try GrokWebBillingFetcher.parseGRPCWebResponse(
Self.payload() + malformedMessage, now: Self.now)

#expect(!parsed.usedPercentIsImplicitZero)
#expect(try await Self.resolve(parsed).snapshot.usedPercent == nil)
}

@Test
func `historical period timestamps remain readable without becoming current usage`() throws {
let timestamp = Data([0x08]) + Self.varint(1_789_000_000)
let payload = Self.message(path: [1, 6, 3, 3], contents: timestamp)
let parsed = try GrokWebBillingFetcher.parseGRPCWebResponse(payload, now: Self.now)

#expect(parsed.resetsAt == Date(timeIntervalSince1970: 1_789_000_000))
#expect(!parsed.usedPercentIsImplicitZero)
#expect(!parsed.usedPercentIsWirePublished)
}

@Test(arguments: [0, 3])
func `unknown period types cannot supply implicit zero`(periodType: UInt8) throws {
#expect(throws: GrokWebBillingError.self) {
Expand All @@ -81,6 +123,41 @@ struct GrokZeroUsageTests {
}
}

private static let malformedSuffixes: [Data] = [
Data([0x00]), // Invalid field key.
Self.fixed64Field(tag: [0x01]), // Invalid field zero with a complete fixed64 value.
Data([0x02, 0x00]), // Invalid field zero with an empty length-delimited value.
Self.fixed64Field(tag: [0x81, 0x80, 0x80, 0x80, 0x10]), // Field number exceeds 29 bits.
// Overflowing varint must not truncate to a valid fixed64 tag.
Self.fixed64Field(tag: [0x89, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x02]),
Data([0x0D, 0x00]), // Truncated percentage.
Data([0x72, 0x04, 0x08]), // Truncated unknown length-delimited field.
Data([0x70, 0x80]), // Truncated varint.
]

private static let opaquePayloads: [Data] = [
Self.fixed64Field(tag: [0x01]), // Valid opaque bytes, not a valid protobuf message.
Data([0x0D, 0x00, 0x00, 0x14, 0x42]), // Looks like a published 37% usage field.
Data([0x08]) + Self.varint(1_788_500_000), // Looks like an earlier future reset.
]

private static let billingMessagePaths: [[UInt64]] = [
[1],
[1, 2], [1, 3], [1, 4], [1, 5], [1, 6], [1, 7], [1, 8], [1, 12],
[1, 6, 1], [1, 6, 2], [1, 6, 3], [1, 8, 2], [1, 8, 3],
[1, 6, 3, 2], [1, 6, 3, 3],
]

private static func message(path: [UInt64], contents: Data) -> Data {
path.reversed().reduce(contents) { payload, field in
Self.varint((field << 3) | 2) + Self.varint(UInt64(payload.count)) + payload
}
}

private static func fixed64Field(tag: [UInt8]) -> Data {
Data(tag + [UInt8](repeating: 0, count: 8))
}

private static let now = Date(timeIntervalSince1970: 1_788_000_000)
private static let proxyReset = Date(timeIntervalSince1970: 1_900_000_000)
private static let credentials = GrokCredentials(
Expand Down
4 changes: 4 additions & 0 deletions docs/grok.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ The grok.com billing gRPC-web endpoint remains a best-effort fallback.
from wire-published percentages; a bare inferred zero, historical-only period,
or malformed response cannot replace unknown proxy usage. Proxy reset and plan
metadata remain authoritative when the zero is adopted.
Invalid protobuf field numbers and overflowing varints prevent that response
from qualifying as complete. Only schema-declared messages are recursively
decoded; valid unknown length-delimited fields are skipped as opaque data,
so their contents cannot invalidate the response or invent usage/reset values.
Grok's public web client also reads its omitted proto3 scalar as zero, and its
[billing descriptor](https://cdn.grok.com/_next/static/chunks/32g78bk5hhe1q.js)
declares `credit_usage_percent` as an implicit-presence float (checked
Expand Down
Loading