diff --git a/.github/workflows/ai-policy.yml b/.github/workflows/ai-policy.yml index 4b4653ee67acd..ad8ed233f29d8 100644 --- a/.github/workflows/ai-policy.yml +++ b/.github/workflows/ai-policy.yml @@ -147,7 +147,8 @@ jobs: run: | gh api "repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/labels" \ --method POST \ - -f "labels[]=AI assisted" + -f "labels[]=AI assisted" \ + 2>/dev/null || true echo "Added 'AI assisted' label to PR #${{ github.event.pull_request.number }}" - name: Fail on coding-agent Signed-off-by 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..f204841688366 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()) @@ -274,24 +278,24 @@ 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 = [ '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()) @@ -328,24 +336,24 @@ 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 = [ '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', ],