fix(integrity): clear stale per app results and use the local cache tier - #41735
fix(integrity): clear stale per app results and use the local cache tier#41735DeepDiver1975 wants to merge 1 commit into
Conversation
d04f81d to
4cbb897
Compare
878634c to
e37ca83
Compare
4cbb897 to
fc71e85
Compare
e37ca83 to
b0c16fc
Compare
Code reviewVerified against the PR head ( Verification performed
The core bug and the one-line fix are real. 1.
|
storeResults() writes one cache entry per checked scope in addition to the entry holding the combined results, but cleanResults() only removed the latter. The per app entries had no TTL either, so a verdict about an app was cached forever and kept being served through getVerifiedAppsFromCache() even after the app had been repaired or replaced. cleanResults() now clears the whole prefix - ICache::clear() is prefix scoped in every backend - and the entries expire. The results describe the files on disk of the host that produced them, so they also move to the host local cache tier. getResults() already falls back to appconfig, so a check run through occ stays visible to the web requests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Thomas Müller <1005065+DeepDiver1975@users.noreply.github.com>
b0c16fc to
9f6a49e
Compare
Description
Fixes a real invalidation bug in the code integrity checker and moves its cache to the local tier. The tier move alone would be hard to justify, so it is paired with the bug that shares its root cause.
The bug:
storeResults()writes one cache entry per checked scope —core, plus one per app — with no TTL (Checker.php:403), whilecleanResults()removed onlyCACHE_KEY(:411-413). The per-app entries were therefore never invalidated, cluster-wide, forever. A verdict about an app was cached indefinitely and kept being served after the app was repaired or replaced. Those stale verdicts are read viagetVerifiedAppsFromCache()intolib/private/legacy/app.php:808,settings/Controller/CheckSetupController.php:229-306andlib/private/TemplateLayout.php:69.The fix is one line:
cleanResults()now calls$this->cache->clear()instead ofremove(self::CACHE_KEY).ICache::clear($prefix = '')is@since 6.0.0and prefix-scoped in every backend (Redis SCANsprefix*, APCu usesAPCUIterator, ArrayCache truncates), so it drops the per-app keys andCACHE_KEYwithout touching other namespaces. This fixes local and distributed deployments.Belt-and-braces: a
CACHE_TTL(24 h) on the per-scope writes, so a node that never runscleanResults()itself still notices a repaired installation.The tier move: these results describe the files on disk of one host and are of no use to another — a second node consuming them is wrong even when nothing is malicious.
__constructnow usesLocalCacheFactory, keeping the existing null-factory branch.Low-risk because
getResults()already falls back to appconfig (:378-385), soocc integrity:check-corefollowed by a web request keeps working regardless of tier.Flagged but deliberately NOT fixed here
storeResults():403writes the whole$resultArrayunder the per-scope key, sogetVerifiedAppsFromCache()returns a JSON string while the uncachedverifyAppSignature()returns an array.app.php:808'sempty($result)then yields a differentlevel/removabledepending on cache warmth. That is an observable-semantics change andCheckerTest.php:628-650currently pins the string behaviour, so it belongs in its own PR.Related Issue
Motivation and Context
Per-app integrity verdicts were cached forever with no way to invalidate them, and they were shared between nodes that they do not describe.
How Has This Been Tested?
memcache.local => APCu+memcache.distributed => Redis.testRescanningDropsThePerAppResults()— seedsSomeApp,SomeOtherAppandCACHE_KEYinto anArrayCache, runsrunInstanceVerification(), asserts the per-app keys are gone andCACHE_KEYis'[]'. This is the test that justifies the PR.testUsesTheLocalCacheTier().tests/lib/IntegrityCheck/CheckerTest.phpbuilds a factory at six sites; all now useFixedCacheFactory, including the two that wrapcreateMock(Redis::class)and assert cache semantics (testVerifyCachedAppSignatureCheck,testAppNotCachedSignatureCheck) — so they keep asserting exactly what they asserted before. The prefix constraint that->with('oc.integritycheck.checker')used to pin is now covered bygetRequestedPrefixes(). 193 tests, 417 assertions, all green.runInstanceVerification(), confirmed both cleared,redis KEYS *integritycheck* == 0, TTL present at 86400 s.tests/lib— 7149 tests, 40221 assertions, no new failures.make test-php-styleand phan clean.Types of changes
Checklist:
🤖 Generated with Claude Code