A month the ASB fetch found missing (Google had not published it yet) was cached as empty for three days. The November 2026 bulletin went live on 2026-11-02, after the sync of 2026-11-01 had asked, so non-Samsung Android devices would have got its CVEs on 2026-11-04 at the earliest, two nightly syncs late. The empty answer now expires after 12 hours: still long enough that the devices of one sync do not each re-fetch it, short enough that the next nightly sync reads the published page. Samsung devices were not affected: their months come from the SMR page, and ASB is read for a month only when SMR has no section for it. That is also why their findings show samsung-smr and never android-asb.
74 lines
2.6 KiB
Python
74 lines
2.6 KiB
Python
"""A month Google publishes after we first asked is read on the next nightly sync — run: python tests/test_asb_negative_cache.py
|
|
|
|
Field case 2026-11-03: the November ASB went live on 2026-11-02, but the
|
|
sync of 2026-11-01 had already found the page missing and cached that empty
|
|
answer for three days. Non-Samsung Android devices got the month's CVEs on
|
|
2026-11-04 at the earliest, two nightly syncs late. Samsung devices were not
|
|
hit (their November came from the SMR page, which the ASB cache never sees).
|
|
|
|
Pinned here: an empty month cached by the previous night is fetched again,
|
|
one cached minutes ago (same sync, many devices) is not.
|
|
"""
|
|
import json
|
|
import os
|
|
import sys
|
|
from datetime import datetime, timedelta
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
os.environ.setdefault("JWT_SECRET_KEY", "test-key-test-key-test-key-test-key")
|
|
|
|
import httpx # noqa: E402
|
|
from sqlalchemy import create_engine # noqa: E402
|
|
from sqlalchemy.orm import sessionmaker # noqa: E402
|
|
|
|
import app.models # noqa: F401,E402 (registers every mapper)
|
|
from app.models.base import Base # noqa: E402
|
|
from app.models.setting import Setting # noqa: E402
|
|
from app.services import android_cve_service as asb # noqa: E402
|
|
|
|
_PAGE = ('<h3>Framework</h3><table><tr><td>CVE-2026-58868</td>'
|
|
'<td>RCE</td><td>Critical</td></tr></table>')
|
|
|
|
|
|
def _db_with_empty_month(age: timedelta):
|
|
engine = create_engine("sqlite:///:memory:")
|
|
Base.metadata.create_all(engine)
|
|
db = sessionmaker(bind=engine)()
|
|
db.add(Setting(key=asb._CACHE_PREFIX + "2026-11-01", value=json.dumps(
|
|
{"ts": (datetime.now() - age).isoformat(), "pairs": []})))
|
|
db.commit()
|
|
return db
|
|
|
|
|
|
def demo():
|
|
calls = []
|
|
|
|
def handler(request):
|
|
calls.append(str(request.url))
|
|
return httpx.Response(200, text=_PAGE)
|
|
|
|
real_client = httpx.Client
|
|
httpx.Client = lambda **kw: real_client(transport=httpx.MockTransport(handler), **kw)
|
|
try:
|
|
# Empty answer from last night's sync: the page is live now.
|
|
db = _db_with_empty_month(timedelta(hours=23, minutes=30))
|
|
assert asb.fetch_asb_month(db, "2026-11-01") == [("CVE-2026-58868", "critical")]
|
|
assert len(calls) == 1, calls
|
|
|
|
# Empty answer from earlier in this sync: no second request.
|
|
calls.clear()
|
|
db = _db_with_empty_month(timedelta(minutes=5))
|
|
assert asb.fetch_asb_month(db, "2026-11-01") == []
|
|
assert calls == [], calls
|
|
finally:
|
|
httpx.Client = real_client
|
|
print("OK")
|
|
|
|
|
|
def test_asb_negative_cache():
|
|
demo()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
demo()
|