| // Copyright 2014 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 'dart:io'; |
| import 'package:path/path.dart' as path; |
| |
| String get repoRoot => path.normalize(path.join(path.dirname(Platform.script.toFilePath()), '..', '..')); |
| String get cacheDirectory => path.normalize(path.join(repoRoot, 'bin', 'cache')); |
| |
| /// Check mime-type of file at [filePath] to determine if it is binary |
| bool isBinary(String filePath) { |
| final ProcessResult result = Process.runSync( |
| 'file', |
| <String>[ |
| '--mime-type', |
| '-b', // is binary |
| filePath, |
| ], |
| ); |
| return (result.stdout as String).contains('application/x-mach-binary'); |
| } |
| |
| /// Find every binary file in the given [rootDirectory] |
| List<String> findBinaryPaths([String rootDirectory]) { |
| rootDirectory ??= cacheDirectory; |
| final ProcessResult result = Process.runSync( |
| 'find', |
| <String>[ |
| rootDirectory, |
| '-type', |
| 'f', |
| '-perm', |
| '+111', // is executable |
| ], |
| ); |
| final List<String> allFiles = (result.stdout as String).split('\n').where((String s) => s.isNotEmpty).toList(); |
| return allFiles.where(isBinary).toList(); |
| } |
| |
| /// Given the path to a stamp file, read the contents. |
| /// |
| /// Will throw if the file doesn't exist. |
| String readStamp(String filePath) { |
| final File file = File(filePath); |
| if (!file.existsSync()) { |
| throw 'Error! Stamp file $filePath does not exist!'; |
| } |
| return file.readAsStringSync().trim(); |
| } |
| |
| /// Return whether or not the flutter cache is up to date. |
| bool checkCacheIsCurrent() { |
| try { |
| final String dartSdkStamp = readStamp(path.join(cacheDirectory, 'engine-dart-sdk.stamp')); |
| final String engineVersion = readStamp(path.join(repoRoot, 'bin', 'internal', 'engine.version')); |
| return dartSdkStamp == engineVersion; |
| } catch (e) { |
| print(e); |
| return false; |
| } |
| } |
| |
| List<String> get binariesWithEntitlements => List<String>.unmodifiable(<String>[ |
| 'ideviceinfo', |
| 'idevicename', |
| 'idevicescreenshot', |
| 'idevicesyslog', |
| 'libimobiledevice.6.dylib', |
| 'libplist.3.dylib', |
| 'iproxy', |
| 'libusbmuxd.4.dylib', |
| 'libssl.1.0.0.dylib', |
| 'libcrypto.1.0.0.dylib', |
| 'libzip.5.0.dylib', |
| 'libzip.5.dylib', |
| 'gen_snapshot', |
| 'dart', |
| 'flutter_tester', |
| 'gen_snapshot_arm64', |
| 'gen_snapshot_armv7', |
| ]); |
| |
| List<String> get expectedEntitlements => List<String>.unmodifiable(<String>[ |
| 'com.apple.security.cs.allow-jit', |
| 'com.apple.security.cs.allow-unsigned-executable-memory', |
| 'com.apple.security.cs.allow-dyld-environment-variables', |
| 'com.apple.security.network.client', |
| 'com.apple.security.network.server', |
| 'com.apple.security.cs.disable-library-validation', |
| ]); |
| |
| |
| /// Check if the binary has the expected entitlements. |
| bool hasExpectedEntitlements(String binaryPath) { |
| try { |
| final ProcessResult entitlementResult = Process.runSync( |
| 'codesign', |
| <String>[ |
| '--display', |
| '--entitlements', |
| ':-', |
| binaryPath, |
| ], |
| ); |
| |
| if (entitlementResult.exitCode != 0) { |
| print('The `codesign --entitlements` command failed with exit code ${entitlementResult.exitCode}:\n' |
| '${entitlementResult.stderr}\n'); |
| return false; |
| } |
| |
| bool passes = true; |
| final String output = entitlementResult.stdout as String; |
| for (final String entitlement in expectedEntitlements) { |
| final bool entitlementExpected = binariesWithEntitlements.contains(path.basename(binaryPath)); |
| if (output.contains(entitlement) != entitlementExpected) { |
| print('File "$binaryPath" ${entitlementExpected ? 'does not have expected' : 'has unexpected'} entitlement $entitlement.'); |
| passes = false; |
| } |
| } |
| return passes; |
| } catch (e) { |
| print(e); |
| return false; |
| } |
| } |
| |
| void main() { |
| if (!Platform.isMacOS) { |
| print('Error! Expected operating system "macos", actual operating system ' |
| 'is: "${Platform.operatingSystem}"'); |
| exit(1); |
| } |
| |
| if (!checkCacheIsCurrent()) { |
| print( |
| 'Warning! Your cache is either not present or not matching your flutter\n' |
| 'version. Run a `flutter` command to update your cache, and re-try this\n' |
| 'test.'); |
| exit(1); |
| } |
| |
| final List<String> unsignedBinaries = <String>[]; |
| final List<String> wrongEntitlementBinaries = <String>[]; |
| for (final String binaryPath in findBinaryPaths(cacheDirectory)) { |
| print('Verifying the code signature of $binaryPath'); |
| final ProcessResult codeSignResult = Process.runSync( |
| 'codesign', |
| <String>[ |
| '-vvv', |
| binaryPath, |
| ], |
| ); |
| if (codeSignResult.exitCode != 0) { |
| unsignedBinaries.add(binaryPath); |
| print('File "$binaryPath" does not appear to be codesigned.\n' |
| 'The `codesign` command failed with exit code ${codeSignResult.exitCode}:\n' |
| '${codeSignResult.stderr}\n'); |
| continue; |
| } else { |
| print('Verifying entitlements of $binaryPath'); |
| if (!hasExpectedEntitlements(binaryPath)) { |
| wrongEntitlementBinaries.add(binaryPath); |
| } |
| } |
| } |
| |
| if (unsignedBinaries.isNotEmpty) { |
| print('Found ${unsignedBinaries.length} unsigned binaries:'); |
| unsignedBinaries.forEach(print); |
| } |
| |
| if (wrongEntitlementBinaries.isNotEmpty) { |
| print('Found ${wrongEntitlementBinaries.length} binaries with unexpected entitlements:'); |
| wrongEntitlementBinaries.forEach(print); |
| } |
| |
| if (unsignedBinaries.isNotEmpty) { |
| // TODO(jmagman): Also exit if `wrongEntitlementBinaries.isNotEmpty` after https://github.com/flutter/flutter/issues/46704 is done. |
| exit(1); |
| } |
| |
| print('Verified that binaries are codesigned and have expected entitlements.'); |
| } |