22
33import assert = require( "assert" ) ;
44import childProcess = require( "child_process" ) ;
5- import crypto = require( "crypto" ) ;
65import fs = require( "fs" ) ;
76import mkdirp = require( "mkdirp" ) ;
87import os = require( "os" ) ;
98import path = require( "path" ) ;
109import slash = require( "slash" ) ;
10+ import { promisify } from "util" ;
1111
1212import { Platform , PluginTestingFramework , ProjectManager , setupTestRunScenario , setupUpdateScenario , ServerUtil , TestBuilder , TestConfig , TestUtil } from "code-push-plugin-testing-framework" ;
1313
1414import Q = require( "q" ) ;
1515
1616import del = require( "del" ) ;
1717
18+ import { codeSigningPublicKey , signAndRecordUpdateArchive , setupTamperedSignatureUpdateScenario } from "./codesign" ;
19+
1820function ensureAndroidCleartextTraffic ( androidManifestPath : string ) : void {
1921 const androidManifestContents = fs . readFileSync ( androidManifestPath , "utf8" ) ;
2022
@@ -37,6 +39,10 @@ function ensureAndroidCleartextTraffic(androidManifestPath: string): void {
3739 }
3840}
3941
42+ async function setPlistStringValue ( plistPath : string , key : string , value : string ) : Promise < void > {
43+ await promisify ( childProcess . execFile ) ( "plutil" , [ "-replace" , key , "-string" , value , plistPath ] ) ;
44+ }
45+
4046/**
4147 * Returns a " --platform <ios|android>" flag for `expo prebuild` when exactly one platform is
4248 * under test in this mocha run, so prebuild only regenerates that platform's native project
@@ -69,47 +75,6 @@ function installExpoBundleTooling(projectPath: string): Q.Promise<void> {
6975 ) . then ( ( ) => { return null ; } ) ;
7076}
7177
72- const CODEPUSH_METADATA_FILE_NAME = ".codepushrelease" ;
73-
74- function isHashIgnored ( relativePath : string ) : boolean {
75- return relativePath . startsWith ( "__MACOSX/" )
76- || relativePath === ".DS_Store"
77- || relativePath . endsWith ( "/.DS_Store" )
78- || relativePath === CODEPUSH_METADATA_FILE_NAME
79- || relativePath . endsWith ( `/${ CODEPUSH_METADATA_FILE_NAME } ` ) ;
80- }
81-
82- /**
83- * Computes the same content hash that the native SDKs compute over an installed update folder, so the mock server
84- * can hand back a package_hash that will actually match what the client expects.
85- */
86- function computeUpdateContentsHash ( folderPath : string ) : string {
87- const manifest : string [ ] = [ ] ;
88-
89- const walk = ( currentPath : string , relativePrefix : string ) => {
90- for ( const entryName of fs . readdirSync ( currentPath ) ) {
91- const entryPath = path . join ( currentPath , entryName ) ;
92- const relativePath = relativePrefix ? `${ relativePrefix } /${ entryName } ` : entryName ;
93-
94- if ( isHashIgnored ( relativePath ) ) {
95- continue ;
96- }
97-
98- if ( fs . statSync ( entryPath ) . isDirectory ( ) ) {
99- walk ( entryPath , relativePath ) ;
100- } else {
101- const fileHash = crypto . createHash ( "sha256" ) . update ( fs . readFileSync ( entryPath ) ) . digest ( "hex" ) ;
102- manifest . push ( `${ relativePath } :${ fileHash } ` ) ;
103- }
104- }
105- } ;
106-
107- walk ( folderPath , "" ) ;
108- manifest . sort ( ) ;
109-
110- return crypto . createHash ( "sha256" ) . update ( JSON . stringify ( manifest ) ) . digest ( "hex" ) ;
111- }
112-
11378//////////////////////////////////////////////////////////////////////////////////////////
11479// Create the platforms to run the tests on.
11580
@@ -209,6 +174,7 @@ class RNAndroid extends Platform.Android implements RNPlatform {
209174 const string = path . join ( innerprojectDirectory , "android" , "app" , "src" , "main" , "res" , "values" , "strings.xml" ) ;
210175 TestUtil . replaceString ( string , TestUtil . SERVER_URL_PLACEHOLDER , this . getServerUrl ( ) ) ;
211176 TestUtil . replaceString ( string , TestUtil . ANDROID_KEY_PLACEHOLDER , this . getDefaultDeploymentKey ( ) ) ;
177+ TestUtil . replaceString ( string , "</resources>" , `<string moduleConfig="true" name="CodePushPublicKey">${ codeSigningPublicKey } </string>\n</resources>` ) ;
212178 TestUtil . replaceString ( AndroidManifest , "\\${usesCleartextTraffic}" , "true" ) ;
213179
214180
@@ -280,14 +246,10 @@ class RNIOS extends Platform.IOS implements RNPlatform {
280246 // Install the Podfile
281247 return TestUtil . copyFile ( path . join ( TestConfig . templatePath , "ios" , "Podfile" ) , podfilePath , true )
282248 . then ( ( ) => TestUtil . getProcessOutput ( `pod install` , { cwd : iOSProject , noLogStdOut : true } ) )
283- // Put the IOS deployment key in the Info.plist
284- . then ( TestUtil . replaceString . bind ( undefined , infoPlistPath ,
285- "</dict>\n</plist>" ,
286- "<key>CodePushDeploymentKey</key>\n\t<string>" + this . getDefaultDeploymentKey ( ) + "</string>\n\t<key>CodePushServerURL</key>\n\t<string>" + this . getServerUrl ( ) + "</string>\n\t</dict>\n</plist>" ) )
287- // Set the app version to 1.0.0 instead of 1.0 in the Info.plist
288- . then ( TestUtil . replaceString . bind ( undefined , infoPlistPath , "1.0" , "1.0.0" ) )
289- // Remove dependence of CFBundleShortVersionString from project.pbxproj
290- . then ( TestUtil . replaceString . bind ( undefined , infoPlistPath , "\\$\\(MARKETING_VERSION\\)" , "1.0.0" ) )
249+ . then ( ( ) => setPlistStringValue ( infoPlistPath , "CFBundleShortVersionString" , "1.0.0" ) )
250+ . then ( ( ) => setPlistStringValue ( infoPlistPath , "CodePushDeploymentKey" , this . getDefaultDeploymentKey ( ) ) )
251+ . then ( ( ) => setPlistStringValue ( infoPlistPath , "CodePushServerURL" , this . getServerUrl ( ) ) )
252+ . then ( ( ) => setPlistStringValue ( infoPlistPath , "CodePushPublicKey" , codeSigningPublicKey ) )
291253 // Fix the linker flag list in project.pbxproj (pod install adds an extra comma)
292254 . then ( TestUtil . replaceString . bind ( undefined , path . join ( iOSProject , TestConfig . TestAppName + ".xcodeproj" , "project.pbxproj" ) ,
293255 "\"[$][(]inherited[)]\",\\s*[)];" , "\"$(inherited)\"\n\t\t\t\t);" ) )
@@ -539,28 +501,19 @@ class RNProjectManager extends ProjectManager {
539501 . then ( TestUtil . getProcessOutput . bind ( undefined , "npx expo prebuild --platform " + targetPlatform . getName ( ) , { cwd : path . join ( projectDirectory , TestConfig . TestAppName ) , noLogStdOut : true } ) )
540502 . then ( TestUtil . getProcessOutput . bind ( undefined , "npx react-native bundle --entry-file index.js --platform " + targetPlatform . getName ( ) + " --bundle-output " + bundlePath + " --assets-dest " + bundleFolder + " --dev false" ,
541503 { cwd : path . join ( projectDirectory , TestConfig . TestAppName ) , noLogStdOut : true } ) )
504+ . then ( ( ) => signAndRecordUpdateArchive ( bundleFolder , isDiff ) )
542505 . then < string > ( TestUtil . archiveFolder . bind ( undefined , bundleFolder , "" , path . join ( projectDirectory , TestConfig . TestAppName , "update.zip" ) , isDiff ) )
543- . then < string > ( this . updateMockPackageHash . bind ( this , bundleFolder , isDiff ) )
544506 . then ( ( result ) => { console . log ( `[TIMING] createUpdateArchive(${ projectDirectory } , ${ targetPlatform . getName ( ) } ) took ${ Date . now ( ) - t0 } ms` ) ; return result ; } ) ;
545507 } else {
546508 return deferred . promise
547509 . then ( TestUtil . getProcessOutput . bind ( undefined , "npx react-native bundle --entry-file index.js --platform " + targetPlatform . getName ( ) + " --bundle-output " + bundlePath + " --assets-dest " + bundleFolder + " --dev false" ,
548510 { cwd : path . join ( projectDirectory , TestConfig . TestAppName ) , noLogStdOut : true } ) )
511+ . then ( ( ) => signAndRecordUpdateArchive ( bundleFolder , isDiff ) )
549512 . then < string > ( TestUtil . archiveFolder . bind ( undefined , bundleFolder , "" , path . join ( projectDirectory , TestConfig . TestAppName , "update.zip" ) , isDiff ) )
550- . then < string > ( this . updateMockPackageHash . bind ( this , bundleFolder , isDiff ) )
551513 . then ( ( result ) => { console . log ( `[TIMING] createUpdateArchive(${ projectDirectory } , ${ targetPlatform . getName ( ) } ) took ${ Date . now ( ) - t0 } ms` ) ; return result ; } ) ;
552514 }
553515 }
554516
555- // Records the real hash of bundleFolder of an archive, so the mock server can hand back a
556- // package_hash that matches what the client's verifyFolderHash integrity check will compute.
557- private updateMockPackageHash ( bundleFolder : string , isDiff : boolean , archivePath : string ) : string {
558- // TODO(RA-4875): Diff updates clear it instead, since they are poorly implemented in the entire test harness.
559- // It's going to be a bigger refactor, so for now we just clear the known package hash to avoid using a stale value in diff tests.
560- ServerUtil . setKnownPackageHash ( isDiff ? undefined : computeUpdateContentsHash ( bundleFolder ) ) ;
561- return archivePath ;
562- }
563-
564517 /** JSON file containing the platforms the plugin is currently installed for.
565518 * Keys must match targetPlatform.getName()!
566519 *
@@ -1050,6 +1003,27 @@ PluginTestingFramework.initializeTests(new RNProjectManager(), supportedTargetPl
10501003 } ) ;
10511004 } , ScenarioInstall ) ;
10521005
1006+ TestBuilder . describe ( "#localPackage.install.codeSigning" ,
1007+ ( ) => {
1008+ TestBuilder . it ( "localPackage.install.codeSigning.tamperedSignature" , false ,
1009+ async ( done : Mocha . Done ) => {
1010+ try {
1011+ ServerUtil . updateResponse = { update_info : ServerUtil . createUpdateResponse ( false , targetPlatform ) } ;
1012+
1013+ /* create a normal update, then tamper with its signature after it's been signed */
1014+ const updatePath = await setupTamperedSignatureUpdateScenario ( projectManager , targetPlatform , UpdateNotifyApplicationReady , "Tampered Update" ) ;
1015+ ServerUtil . updatePackagePath = updatePath ;
1016+ projectManager . runApplication ( TestConfig . testRunDirectory , targetPlatform ) ;
1017+ await ServerUtil . expectTestMessages ( [
1018+ ServerUtil . TestMessage . CHECK_UPDATE_AVAILABLE ,
1019+ ServerUtil . TestMessage . DOWNLOAD_ERROR ] ) ;
1020+ done ( ) ;
1021+ } catch ( e ) {
1022+ done ( e ) ;
1023+ }
1024+ } ) ;
1025+ } , ScenarioInstall ) ;
1026+
10531027 TestBuilder . describe ( "#localPackage.install.revert" ,
10541028 ( ) => {
10551029 TestBuilder . it ( "localPackage.install.revert.dorevert" , false ,
0 commit comments