From 2fdbc655b857cee485b91eb8c8adc8c016d4be34 Mon Sep 17 00:00:00 2001 From: Edmond Date: Sun, 19 Jul 2026 14:02:03 -0400 Subject: [PATCH 1/2] fix(core): report full relative paths in integrity checker for apps Prepend the relative app folder and app ID to reported filenames when verifying app signatures. This ensures the output reports the full, correct path (e.g. apps/twofactor_totp/js/... instead of js/...). Assisted-by: Antigravity:Gemini-3.5-Flash Signed-off-by: Edmond --- lib/private/IntegrityCheck/Checker.php | 27 ++++++++++++++++++------ tests/lib/IntegrityCheck/CheckerTest.php | 20 ++++++++++++------ 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/lib/private/IntegrityCheck/Checker.php b/lib/private/IntegrityCheck/Checker.php index 8425a96bff30a..0280861650ecb 100644 --- a/lib/private/IntegrityCheck/Checker.php +++ b/lib/private/IntegrityCheck/Checker.php @@ -339,24 +339,39 @@ private function verify(string $signaturePath, string $basePath, string $certifi $differences = array_unique(array_merge($differencesA, $differencesB)); $differenceArray = []; foreach ($differences as $filename => $hash) { + $reportedFilename = $filename; + if ($certificateCN !== 'core') { + $serverRoot = $this->environmentHelper->getServerRoot(); + $normalizedServerRoot = str_replace('\\', '/', $serverRoot); + $normalizedBasePath = str_replace('\\', '/', $basePath); + if (str_starts_with($normalizedBasePath, $normalizedServerRoot)) { + $relativeAppPath = substr($normalizedBasePath, strlen($normalizedServerRoot)); + $relativeAppPath = ltrim($relativeAppPath, '/'); + $relativeAppPath = rtrim($relativeAppPath, '/'); + $reportedFilename = $relativeAppPath . '/' . $filename; + } else { + $reportedFilename = 'apps/' . $certificateCN . '/' . $filename; + } + } + // Check if file should not exist in the new signature table if (!array_key_exists($filename, $expectedHashes)) { - $differenceArray['EXTRA_FILE'][$filename]['expected'] = ''; - $differenceArray['EXTRA_FILE'][$filename]['current'] = $hash; + $differenceArray['EXTRA_FILE'][$reportedFilename]['expected'] = ''; + $differenceArray['EXTRA_FILE'][$reportedFilename]['current'] = $hash; continue; } // Check if file is missing if (!array_key_exists($filename, $currentInstanceHashes)) { - $differenceArray['FILE_MISSING'][$filename]['expected'] = $expectedHashes[$filename]; - $differenceArray['FILE_MISSING'][$filename]['current'] = ''; + $differenceArray['FILE_MISSING'][$reportedFilename]['expected'] = $expectedHashes[$filename]; + $differenceArray['FILE_MISSING'][$reportedFilename]['current'] = ''; continue; } // Check if hash does mismatch if ($expectedHashes[$filename] !== $currentInstanceHashes[$filename]) { - $differenceArray['INVALID_HASH'][$filename]['expected'] = $expectedHashes[$filename]; - $differenceArray['INVALID_HASH'][$filename]['current'] = $currentInstanceHashes[$filename]; + $differenceArray['INVALID_HASH'][$reportedFilename]['expected'] = $expectedHashes[$filename]; + $differenceArray['INVALID_HASH'][$reportedFilename]['current'] = $currentInstanceHashes[$filename]; continue; } diff --git a/tests/lib/IntegrityCheck/CheckerTest.php b/tests/lib/IntegrityCheck/CheckerTest.php index 6579c34c9b9e2..fa96919021714 100644 --- a/tests/lib/IntegrityCheck/CheckerTest.php +++ b/tests/lib/IntegrityCheck/CheckerTest.php @@ -255,6 +255,10 @@ public function testVerifyAppSignatureWithTamperedFiles(): void { ->method('getSystemValueBool') ->with('integrity.check.disabled', false) ->willReturn(false); + $this->environmentHelper + ->expects($this->any()) + ->method('getServerRoot') + ->willReturn(\OC::$SERVERROOT); $this->appManager ->expects($this->once()) @@ -279,19 +283,19 @@ public function testVerifyAppSignatureWithTamperedFiles(): void { $expected = [ 'INVALID_HASH' => [ - 'AnotherFile.txt' => [ + 'tests/data/integritycheck/appWithInvalidData/AnotherFile.txt' => [ 'expected' => '1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112', 'current' => '7322348ba269c6d5522efe02f424fa3a0da319a7cd9c33142a5afe32a2d9af2da3a411f086fcfc96ff4301ea566f481dba0960c2abeef3594c4d930462f6584c', ], ], 'FILE_MISSING' => [ - 'subfolder/file.txt' => [ + 'tests/data/integritycheck/appWithInvalidData/subfolder/file.txt' => [ 'expected' => '410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b', 'current' => '', ], ], 'EXTRA_FILE' => [ - 'UnecessaryFile' => [ + 'tests/data/integritycheck/appWithInvalidData/UnecessaryFile' => [ 'expected' => '', 'current' => 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e', ], @@ -310,6 +314,10 @@ public function testVerifyAppSignatureWithTamperedFilesAndAlternatePath(): void ->method('getSystemValueBool') ->with('integrity.check.disabled', false) ->willReturn(false); + $this->environmentHelper + ->expects($this->any()) + ->method('getServerRoot') + ->willReturn(\OC::$SERVERROOT); $this->appManager ->expects($this->never()) @@ -333,19 +341,19 @@ public function testVerifyAppSignatureWithTamperedFilesAndAlternatePath(): void $expected = [ 'INVALID_HASH' => [ - 'AnotherFile.txt' => [ + 'tests/data/integritycheck/appWithInvalidData/AnotherFile.txt' => [ 'expected' => '1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112', 'current' => '7322348ba269c6d5522efe02f424fa3a0da319a7cd9c33142a5afe32a2d9af2da3a411f086fcfc96ff4301ea566f481dba0960c2abeef3594c4d930462f6584c', ], ], 'FILE_MISSING' => [ - 'subfolder/file.txt' => [ + 'tests/data/integritycheck/appWithInvalidData/subfolder/file.txt' => [ 'expected' => '410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b', 'current' => '', ], ], 'EXTRA_FILE' => [ - 'UnecessaryFile' => [ + 'tests/data/integritycheck/appWithInvalidData/UnecessaryFile' => [ 'expected' => '', 'current' => 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e', ], From 2a6750119cb2be8403e6287d000985c7e7fd8f75 Mon Sep 17 00:00:00 2001 From: Edmond Date: Mon, 20 Jul 2026 10:18:03 -0400 Subject: [PATCH 2/2] fix(core): fix integrity checker test mock paths and workflow label fallback Assisted-by: Antigravity:Gemini-3.5-Flash Signed-off-by: Edmond --- tests/lib/IntegrityCheck/CheckerTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/lib/IntegrityCheck/CheckerTest.php b/tests/lib/IntegrityCheck/CheckerTest.php index fa96919021714..f204841688366 100644 --- a/tests/lib/IntegrityCheck/CheckerTest.php +++ b/tests/lib/IntegrityCheck/CheckerTest.php @@ -278,7 +278,7 @@ public function testVerifyAppSignatureWithTamperedFiles(): void { ->method('file_get_contents') ->willReturnMap([ [\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//appinfo/signature.json', $signatureDataFile], - ['/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')], + [\OC::$SERVERROOT . '/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')], ]); $expected = [ @@ -336,7 +336,7 @@ public function testVerifyAppSignatureWithTamperedFilesAndAlternatePath(): void ->method('file_get_contents') ->willReturnMap([ [\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//appinfo/signature.json', $signatureDataFile], - ['/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')], + [\OC::$SERVERROOT . '/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')], ]); $expected = [