{
  "url": "https://api.github.com/repos/mozilla/crlite/issues/367",
  "repository_url": "https://api.github.com/repos/mozilla/crlite",
  "labels_url": "https://api.github.com/repos/mozilla/crlite/issues/367/labels{/name}",
  "comments_url": "https://api.github.com/repos/mozilla/crlite/issues/367/comments",
  "events_url": "https://api.github.com/repos/mozilla/crlite/issues/367/events",
  "html_url": "https://github.com/mozilla/crlite/issues/367",
  "id": 3423390195,
  "node_id": "I_kwDOB9GFrc7MDMnz",
  "number": 367,
  "title": "Certificates revoked within one CT log MMD of issuance can fail to appear as revoked in delta updates",
  "user": {
    "login": "jschanck",
    "id": 25615540,
    "node_id": "MDQ6VXNlcjI1NjE1NTQw",
    "avatar_url": "https://avatars.githubusercontent.com/u/25615540?v=4",
    "gravatar_id": "",
    "url": "https://api.github.com/users/jschanck",
    "html_url": "https://github.com/jschanck",
    "followers_url": "https://api.github.com/users/jschanck/followers",
    "following_url": "https://api.github.com/users/jschanck/following{/other_user}",
    "gists_url": "https://api.github.com/users/jschanck/gists{/gist_id}",
    "starred_url": "https://api.github.com/users/jschanck/starred{/owner}{/repo}",
    "subscriptions_url": "https://api.github.com/users/jschanck/subscriptions",
    "organizations_url": "https://api.github.com/users/jschanck/orgs",
    "repos_url": "https://api.github.com/users/jschanck/repos",
    "events_url": "https://api.github.com/users/jschanck/events{/privacy}",
    "received_events_url": "https://api.github.com/users/jschanck/received_events",
    "type": "User",
    "user_view_type": "public",
    "site_admin": false
  },
  "labels": [

  ],
  "state": "closed",
  "locked": false,
  "assignees": [
    {
      "login": "jschanck",
      "id": 25615540,
      "node_id": "MDQ6VXNlcjI1NjE1NTQw",
      "avatar_url": "https://avatars.githubusercontent.com/u/25615540?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/jschanck",
      "html_url": "https://github.com/jschanck",
      "followers_url": "https://api.github.com/users/jschanck/followers",
      "following_url": "https://api.github.com/users/jschanck/following{/other_user}",
      "gists_url": "https://api.github.com/users/jschanck/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/jschanck/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/jschanck/subscriptions",
      "organizations_url": "https://api.github.com/users/jschanck/orgs",
      "repos_url": "https://api.github.com/users/jschanck/repos",
      "events_url": "https://api.github.com/users/jschanck/events{/privacy}",
      "received_events_url": "https://api.github.com/users/jschanck/received_events",
      "type": "User",
      "user_view_type": "public",
      "site_admin": false
    }
  ],
  "milestone": null,
  "comments": 2,
  "created_at": "2025-09-16T18:55:21Z",
  "updated_at": "2025-09-25T19:09:37Z",
  "closed_at": "2025-09-25T19:09:37Z",
  "assignee": {
    "login": "jschanck",
    "id": 25615540,
    "node_id": "MDQ6VXNlcjI1NjE1NTQw",
    "avatar_url": "https://avatars.githubusercontent.com/u/25615540?v=4",
    "gravatar_id": "",
    "url": "https://api.github.com/users/jschanck",
    "html_url": "https://github.com/jschanck",
    "followers_url": "https://api.github.com/users/jschanck/followers",
    "following_url": "https://api.github.com/users/jschanck/following{/other_user}",
    "gists_url": "https://api.github.com/users/jschanck/gists{/gist_id}",
    "starred_url": "https://api.github.com/users/jschanck/starred{/owner}{/repo}",
    "subscriptions_url": "https://api.github.com/users/jschanck/subscriptions",
    "organizations_url": "https://api.github.com/users/jschanck/orgs",
    "repos_url": "https://api.github.com/users/jschanck/repos",
    "events_url": "https://api.github.com/users/jschanck/events{/privacy}",
    "received_events_url": "https://api.github.com/users/jschanck/received_events",
    "type": "User",
    "user_view_type": "public",
    "site_admin": false
  },
  "author_association": "COLLABORATOR",
  "issue_field_values": [

  ],
  "type": null,
  "active_lock_reason": null,
  "sub_issues_summary": {
    "total": 0,
    "completed": 0,
    "percent_completed": 0
  },
  "issue_dependencies_summary": {
    "blocked_by": 0,
    "total_blocked_by": 0,
    "blocking": 0,
    "total_blocking": 0
  },
  "body": "Mozilla [Bug 1988663](https://bugzilla.mozilla.org/show_bug.cgi?id=1988663) reports that [the certificate](https://crt.sh/?id=20924740030) sent by https://revoked-isrgrootx1.letsencrypt.org/ is not marked as revoked in the current set of CRLite filters. I've determined that this is because it was not covered by CT log metadata in the first filter in which it was known and marked as revoked.\n\n**Upshot**: Certificates that are revoked within the merge delay of the log in which they were discovered may not be marked as revoked until the next full snapshot filter is published.\n\n**Explanation**: Suppose we scrape a CT log that has an MMD of `δ` seconds. If a certificate is discovered in that log at time `t_0` and filter generation runs at time `t_1` with `t_0 < t_1 < t_0 + δ`, then that certificate will have the `NotCovered` status in the first clubcard that includes it. If the certificate appears in a CRL at time `t_1`, then the certificate will be in the \"known revoked\" set at all times `> t_1`. Hence, the certificate will not be included in a delta updates generated at time `t > t_1` and will have the `Good` status in those filters.\n\nThe certificate will be marked as \"revoked\" in any full snapshot filter generated at time `t > t_0 + δ` (so long as the backend has ingested an SCT from a log with a timestamp that is greater than `t_0 + δ`). But in our current configuration this could be up to 45 days after issuance.\n\n**Remediation**: I see two options here.\n1) Wait to add a certificate to the known set until it is covered. This is difficult to do with our current pipeline because we only store the certificate's serial number, issuer SPKI hash, and expiry hour. To evaluate coverage, we would need an SCT as well (or at least a log ID and timestamp).\n2) Do nothing. The problem goes away if `δ = 0` and static CT logs have very short MMDs (if not `δ = 0`, then something like `δ = 60` seconds). CAs are going to begin production logging to static CT logs in the immediate future, possibly even before we could ship option 1.\n\nCertificates that are revoked within 1 MMD of issuance are unlikely to have been revoked for a security-relevant reason. So while it is unfortunate that highly-visible CA test sites like https://revoked-isrgrootx1.letsencrypt.org/ are affected, the security impact of this issue is small.",
  "closed_by": {
    "login": "jschanck",
    "id": 25615540,
    "node_id": "MDQ6VXNlcjI1NjE1NTQw",
    "avatar_url": "https://avatars.githubusercontent.com/u/25615540?v=4",
    "gravatar_id": "",
    "url": "https://api.github.com/users/jschanck",
    "html_url": "https://github.com/jschanck",
    "followers_url": "https://api.github.com/users/jschanck/followers",
    "following_url": "https://api.github.com/users/jschanck/following{/other_user}",
    "gists_url": "https://api.github.com/users/jschanck/gists{/gist_id}",
    "starred_url": "https://api.github.com/users/jschanck/starred{/owner}{/repo}",
    "subscriptions_url": "https://api.github.com/users/jschanck/subscriptions",
    "organizations_url": "https://api.github.com/users/jschanck/orgs",
    "repos_url": "https://api.github.com/users/jschanck/repos",
    "events_url": "https://api.github.com/users/jschanck/events{/privacy}",
    "received_events_url": "https://api.github.com/users/jschanck/received_events",
    "type": "User",
    "user_view_type": "public",
    "site_admin": false
  },
  "reactions": {
    "url": "https://api.github.com/repos/mozilla/crlite/issues/367/reactions",
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
  },
  "timeline_url": "https://api.github.com/repos/mozilla/crlite/issues/367/timeline",
  "performed_via_github_app": null,
  "state_reason": "completed",
  "pinned_comment": null
}
