| // Copyright 2019 The Flutter Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| import 'package:codesign/verify.dart'; | |
| import 'package:file/file.dart'; | |
| import 'package:file/memory.dart'; | |
| import 'package:logging/logging.dart'; | |
| import 'package:test/test.dart'; | |
| import 'src/fake_process_manager.dart'; | |
| void main() { | |
| const binaryPath = '/path/to/binary'; | |
| late FakeProcessManager processManager; | |
| late FileSystem fs; | |
| late Logger logger; | |
| late VerificationService service; | |
| late List<LogRecord> logs; | |
| setUp(() { | |
| fs = MemoryFileSystem.test(); | |
| fs.file(binaryPath).createSync(recursive: true); | |
| processManager = FakeProcessManager.empty(); | |
| logs = <LogRecord>[]; | |
| logger = Logger.detached('test'); | |
| logger.onRecord.listen((LogRecord record) => logs.add(record)); | |
| service = VerificationService( | |
| binaryPath: binaryPath, | |
| fs: fs, | |
| logger: logger, | |
| pm: processManager, | |
| ); | |
| }); | |
| test('parses codesign output and can present', () async { | |
| processManager.addCommands(const <FakeCommand>[ | |
| FakeCommand( | |
| command: <String>['codesign', '--display', '-vv', binaryPath], | |
| stderr: | |
| ''' | |
| Executable=$binaryPath | |
| Identifier=mybinary | |
| Format=Mach-O thin (x86_64) | |
| CodeDirectory v=20500 size=38000 flags=0x10000(runtime) hashes=1177+7 location=embedded | |
| Signature size=8979 | |
| Authority=Developer ID Application: Dev Shop ABC (ABCC0VV123) | |
| Authority=Developer ID Certification Authority | |
| Authority=Apple Root CA | |
| Timestamp=Jan 9, 2023 at 9:39:07 AM | |
| Info.plist=not bound | |
| TeamIdentifier=ABCC0VV123 | |
| Runtime Version=13.1.0 | |
| Sealed Resources=none | |
| Internal requirements count=1 size=164 | |
| ''', | |
| ), | |
| FakeCommand( | |
| command: <String>[ | |
| 'codesign', | |
| '--verify', | |
| '-v', | |
| '-R=notarized', | |
| '--check-notarization', | |
| binaryPath, | |
| ], | |
| stderr: | |
| ''' | |
| $binaryPath: valid on disk | |
| $binaryPath: satisfies its Designated Requirement | |
| $binaryPath: explicit requirement satisfied | |
| ''', | |
| ), | |
| ]); | |
| final result = await service.run(); | |
| expect(processManager, hasNoRemainingExpectations); | |
| expect(result, VerificationResult.codesignedAndNotarized); | |
| expect(service.present(), ''' | |
| Authority: Developer ID Application: Dev Shop ABC (ABCC0VV123) | |
| Time stamp: Jan 9, 2023 at 9:39:07 AM | |
| Format: Mach-O thin (x86_64) | |
| Signature size: 8979 | |
| Notarization: true | |
| '''); | |
| }); | |
| test('detects codesigned but not notarized binary', () async { | |
| processManager.addCommands(const <FakeCommand>[ | |
| FakeCommand( | |
| command: <String>['codesign', '--display', '-vv', binaryPath], | |
| stderr: | |
| ''' | |
| Executable=$binaryPath | |
| Identifier=mybinary | |
| Format=Mach-O thin (x86_64) | |
| CodeDirectory v=20500 size=38000 flags=0x10000(runtime) hashes=1177+7 location=embedded | |
| Signature size=8979 | |
| Authority=Developer ID Application: Dev Shop ABC (ABCC0VV123) | |
| Authority=Developer ID Certification Authority | |
| Authority=Apple Root CA | |
| Timestamp=Jan 9, 2023 at 9:39:07 AM | |
| Info.plist=not bound | |
| TeamIdentifier=ABCC0VV123 | |
| Runtime Version=13.1.0 | |
| Sealed Resources=none | |
| Internal requirements count=1 size=164 | |
| ''', | |
| ), | |
| FakeCommand( | |
| command: <String>[ | |
| 'codesign', | |
| '--verify', | |
| '-v', | |
| '-R=notarized', | |
| '--check-notarization', | |
| binaryPath, | |
| ], | |
| stderr: | |
| ''' | |
| $binaryPath: valid on disk | |
| $binaryPath: satisfies its Designated Requirement | |
| test-requirement: code failed to satisfy specified code requirement(s) | |
| ''', | |
| exitCode: 3, | |
| ), | |
| ]); | |
| final result = await service.run(); | |
| expect(processManager, hasNoRemainingExpectations); | |
| expect(result, VerificationResult.codesignedOnly); | |
| expect(service.present(), ''' | |
| Authority: Developer ID Application: Dev Shop ABC (ABCC0VV123) | |
| Time stamp: Jan 9, 2023 at 9:39:07 AM | |
| Format: Mach-O thin (x86_64) | |
| Signature size: 8979 | |
| Notarization: false | |
| '''); | |
| }); | |
| test('detects unsigned binary', () async { | |
| processManager.addCommands(const <FakeCommand>[ | |
| FakeCommand( | |
| command: <String>['codesign', '--display', '-vv', binaryPath], | |
| stderr: '$binaryPath: code object is not signed at all', | |
| exitCode: 1, | |
| ), | |
| ]); | |
| final result = await service.run(); | |
| expect(processManager, hasNoRemainingExpectations); | |
| expect(result, VerificationResult.unsigned); | |
| expect( | |
| logs.first, | |
| isA<LogRecord>().having( | |
| (LogRecord record) => record.message, | |
| 'message', | |
| contains('File $binaryPath is not codesigned.'), | |
| ), | |
| ); | |
| }); | |
| } |