)]}'
{
  "commit": "e33749537254d42895ef084e482faaba230fddc2",
  "tree": "b5bd6a6e0323a70bd7cf12dec5f195a163d875c8",
  "parents": [
    "abdf212e92115bda18485e606a79c073e4d462de"
  ],
  "author": {
    "name": "Anthony Hurtado",
    "email": "amhurtado@pm.me",
    "time": "Fri Jun 26 11:56:25 2026 -0700"
  },
  "committer": {
    "name": "Copybara-Service",
    "email": "copybara-worker@google.com",
    "time": "Fri Jun 26 11:58:19 2026 -0700"
  },
  "message": "util: validate Duration/Timestamp in TimeUtil::FromString against spec limits (#27443)\n\n## Summary\n\n- `TimeUtil::FromString` for both `Duration` and `Timestamp` accepts malformed inputs that produce protos failing `IsDurationValid`/`IsTimestampValid`.\n- The companion JSON parser (`JsonStringToMessage`) correctly rejects all the same inputs, creating a **cross-API divergence**.\n- **8 sub-bug classes** identified (4 Duration, 4 Timestamp):\n\n| Class | Type | Trigger | Effect |\n|-------|------|---------|--------|\n| A | Duration | Leading whitespace + negative (`\" -3.54s\"`) | Signs mismatch: seconds\u003c0, nanos\u003e0 |\n| B | Duration | Double minus (`\"--3.54s\"`) | Signs mismatch: seconds\u003e0, nanos\u003c0 |\n| C | Duration | \u003e9 fractional digits (`\"0.1234567890s\"`) | nanos exceeds 999999999 |\n| D | Duration | seconds \u003e 315576000000 (`\"315576000001s\"`) | Out-of-range seconds |\n| E | Timestamp | Year 0000 (`\"0000-12-31T23:59:59Z\"`) | seconds \u003c kTimestampMinSeconds |\n| F | Timestamp | Year 10000 (`\"10000-01-01T00:00:00Z\"`) | seconds \u003e kTimestampMaxSeconds |\n| G | Timestamp | 5-digit year (`\"99999-01-01T00:00:00Z\"`) | Wildly past spec max |\n| H | Timestamp | Negative year (`\"-0001-01-01T00:00:00Z\"`) | Not in RFC3339 spec |\n\n**Fix**: Add `IsDurationValid()` check after parsing in the Duration overload, and `IsTimestampValid()` check in the Timestamp overload. Return false and clear output on failure. Both validation functions already exist in `TimeUtil` and are used by `ABSL_DCHECK` elsewhere.\n\n**Impact**: Systems using Duration/Timestamp for security-relevant time bounds (gRPC deadlines, Envoy timeouts, retry intervals, IAM token TTLs). Class A is most dangerous: `seconds * 1e9 + nanos` gives `-2.46e9` instead of intended `-3.54e9` — a 31% magnitude shift. 17 fuzzer crash inputs collected.\n\nFound by AFL++ property-check fuzzing (`pb_json_wkt_fuzzer` mode 1).\n\n## Test plan\n\n- [ ] Verify `TimeUtil::FromString(\" -3.54s\", \u0026d)` returns false (class A)\n- [ ] Verify `TimeUtil::FromString(\"--3.54s\", \u0026d)` returns false (class B)\n- [ ] Verify `TimeUtil::FromString(\"0.1234567890s\", \u0026d)` returns false (class C)\n- [ ] Verify `TimeUtil::FromString(\"315576000001s\", \u0026d)` returns false (class D)\n- [ ] Verify `TimeUtil::FromString(\"0000-12-31T23:59:59Z\", \u0026t)` returns false (class E)\n- [ ] Verify `TimeUtil::FromString(\"10000-01-01T00:00:00Z\", \u0026t)` returns false (class F)\n- [ ] Verify valid inputs still parse successfully\n- [ ] Run existing TimeUtil tests to confirm no regressions\n\nCloses #27443\n\nCOPYBARA_INTEGRATE_REVIEW\u003dhttps://github.com/protocolbuffers/protobuf/pull/27443 from jortles:fix/timeutil-fromstring-validation b57f4459f9672337fbd1b6ee77c299c4aebaa33f\nPiperOrigin-RevId: 938703001\n",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "8cbfe00f55e793c73df5dc36015afc4782a148c6",
      "old_mode": 33188,
      "old_path": "src/google/protobuf/util/time_util.cc",
      "new_id": "d61554582993cb6e8ec24b745ae4c0c6cafa9f95",
      "new_mode": 33188,
      "new_path": "src/google/protobuf/util/time_util.cc"
    },
    {
      "type": "modify",
      "old_id": "7a9c961287a26ddf580c09e16441ae018259f4f6",
      "old_mode": 33188,
      "old_path": "src/google/protobuf/util/time_util_test.cc",
      "new_id": "0b3528d3ce8c5c100fdb8b3b507d884f010fa9a0",
      "new_mode": 33188,
      "new_path": "src/google/protobuf/util/time_util_test.cc"
    }
  ]
}
