· 5 years ago · May 07, 2020, 08:34 AM
1console.log("\n");
2 console.log(chalk.blue("--- < Runtime Application Self-Protection > ---"));
3 console.log("\n");
4 console.log("Calling RASP...");
5
6 datenow = new Date().toLocaleDateString();
7 splitted = datenow.split('-');
8 month = splitted[1];
9 day = splitted[2];
10 if(month < 10){
11 month = "0" + month;
12 }
13 if(day < 10){
14 day = "0" + day;
15 }
16 ymd = splitted[0] + "-" + month + "-" + day;
17
18
19 //UPLOAD THE APP
20 const uploadApp = async function(appPlatform, filePath) {
21
22 let questions = [{
23 type: 'input',
24 name: 'apikey',
25 message: 'Paste here the configuration api key from OneSpan Customer Portal: '
26 }];
27
28 answers = await inquirer.prompt(questions);
29 apikey = answers.apikey;
30
31 if (appPlatform == 'android'){
32 var form = new formData();
33 const stream = fs.createReadStream(filePath+"/"+"app-abi"+abi+"_"+brand+"-"+environment+"-"+debug+".apk");
34 form.append('api_key', apikey);
35 form.append('file', stream);
36 var spinner = ora("Starting android shielding process").start();
37 try {
38 await fetch('https://cp.onespan.com/public_api/v1/rasp/bind_package', { method: 'POST', body: form })
39 .then(function(res) {
40 return res.json();
41 }).then(function(json) {
42 bindingId = json.binding_id;
43 return bindingId;
44 });
45 }
46 catch (err) {
47 spinner.fail();
48 throw err;
49 }
50 spinner.succeed();
51
52 } else {
53 var form = new formData();
54 const stream = fs.createReadStream(filePath+"/"+abi+"-"+brand+"-"+environment+"-"+debug+".ipa");
55 form.append('api_key', apikey);
56 form.append('file', stream);
57 var spinner = ora("Starting ios shielding process").start();
58 try {
59 await fetch('https://cp.onespan.com/public_api/v1/rasp/bind_package', { method: 'POST', body: form })
60 .then(function(res) {
61 return res.json();
62 }).then(function(json) {
63 bindingId = json.binding_id;
64 return bindingId;
65 });
66 }
67 catch (err) {
68 spinner.fail();
69 throw err;
70 }
71 spinner.succeed();
72 }
73 }
74
75
76 // OPERAZIONI PRELIMINARI IOS
77 if (appPlatform == "ios"){
78
79 var spinner = ora("Preparing the .ipa for upload").start();
80 let appName = abi+"-"+brand+"-"+environment+"-"+debug;
81 let xarcPath = "/Users/rollod/Documents/links-build/buildsb/"+ymd+"/"+appName;
82
83 //creo cartella con nome app e sposto xcarchive nella cartella dell'app
84 spawn.sync("mkdir "+xarcPath+" && cd /Users/rollod/Documents/links-build/buildsb/"+ymd+" && mv "+appName+".xcarchive "+xarcPath , {
85 stdio: "inherit",
86 shell: true
87 });
88
89 //duplico xcarchive e rinomino in _secure
90 spawn.sync("cd "+xarcPath+" && cp -R "+appName+".xcarchive "+appName+"_secure.xcarchive" , {
91 stdio: "inherit",
92 shell: true
93 });
94
95 //sposto .app da xcarchive in cartella Payload
96 spawn.sync("cd "+xarcPath+" && mkdir Payload && cp -r "+appName+".xcarchive/Products/Applications/mang-app-"+abi+"-"+brand+".app "+xarcPath+"/Payload", {
97 stdio: "inherit",
98 shell: true
99 });
100
101 let appZip = new admZip();
102 appZip.addLocalFolder(xarcPath+"/Payload");
103 appZip.writeZip(xarcPath+"/"+appName+".ipa");
104 spinner.succeed();
105
106 var spinner = ora("Creating the xcent file\n").start();
107 try{
108 spawn.sync("cd "+xarcPath+"/Payload && codesign -d --entitlements :- mang-app-"+abi+"-"+brand+".app >> "+xarcPath+"/"+appName+".xcent", {
109 stdio: "inherit",
110 shell: true
111 });
112 } catch (error) {
113 throw error;
114 }
115 spinner.succeed();
116
117 await uploadApp('ios', xarcPath);
118
119 } else {
120 let apkPath = '../../'+androiddir+'/app/build/outputs/apk/abi'+abi+'_'+brand+environment.charAt(0).toUpperCase()+environment.slice(1)+"/"+debug;
121 await uploadApp('android', apkPath);
122 }
123
124
125 delay = function delay(ms) {
126 return new Promise(resolve => setTimeout(resolve, ms));
127 }
128
129
130 extract = async function(appPlatform, ymd) {
131 if (appPlatform == 'android') {
132 let zipFile = '../builds/'+ymd+'/android/rasp/mang-app-'+abi+'-'+brand+'-'+environment+'-'+debug+'.zip';
133 let targetFolder = '../builds/'+ymd+'/android/rasp/mang-app-'+abi+'-'+brand+'-'+environment+'-'+debug;
134 const resolve = require('path').resolve;
135
136 var resolveTargetFolder = resolve(targetFolder);
137 var spinner = ora("Unzipping the archive").start();
138 try {
139 await extractZip(zipFile , { dir: resolveTargetFolder });
140 } catch (err) {
141 throw err;
142 }
143 spinner.succeed();
144 appSign(appPlatform, abi, brand, ymd, environment, debug);
145 } else {
146
147 let zipFile = '../buildsb/'+ymd+'/'+abi+"-"+brand+"-"+environment+"-"+debug+"/"+"mang-app-"+abi+'-'+brand+'-'+environment+"-"+debug+'_rasped.zip';
148 let targetFolder = '../buildsb/'+ymd+'/'+abi+"-"+brand+"-"+environment+"-"+debug+"/"+"mang-app-"+abi+'-'+brand+'-'+environment+"-"+debug+"_rasped";
149 const resolve = require('path').resolve;
150
151 var resolveTargetFolder = resolve(targetFolder);
152 var spinner = ora("Unzipping the archive").start();
153 try {
154 await extractZip(zipFile , { dir: resolveTargetFolder });
155 } catch (err) {
156 throw err;
157 }
158 spinner.succeed();
159 appSign(appPlatform, abi, brand, ymd, environment, debug);
160 }
161 }
162
163
164 //DOWNLOAD THE SHIELDED APP
165 download = async function(appPlatform, bindingId, apikey, ymd, abi, brand, environment, debug) {
166
167 if (appPlatform == 'android') {
168
169 fs.mkdirSync('../builds/'+ymd+'/android/rasp', { recursive: true }, (err) => {
170 if (err) throw err;
171 });
172
173 let zipPath = '../builds/'+ymd+'/android/rasp/mang-app-'+abi+'-'+brand+'-'+environment+'-'+debug+'.zip';
174 let zipSave = fs.createWriteStream(zipPath);
175 let boundPackage = 'https://cp.onespan.com/public_api/v1/rasp/bound_package/'+bindingId;
176 var spinner = ora("Downloading the shielded binary").start();
177
178 try{
179 await axios
180 .get(
181 boundPackage,
182 { headers: {'X-API-KEY': apikey }, responseType: 'stream' }) //per scaricare un file zip il responseType deve essere 'stream'
183 .then(response => {
184 return new Promise((resolve, reject) => {
185 response.data.pipe(zipSave);
186 let error = null;
187 zipSave.on('error', err => {
188 error = err;
189 zipSave.close();
190 reject(err);
191 });
192 zipSave.on('close', () => {
193 if (!error) {
194 resolve(true);
195 }
196 });
197 });
198 });
199
200 } catch(err){
201 throw err;
202 }
203 spinner.succeed();
204 await extract(appPlatform, ymd);
205
206 } else {
207
208 let zipPath = '../buildsb/'+ymd+'/'+abi+"-"+brand+"-"+environment+"-"+debug+"/"+"mang-app-"+abi+'-'+brand+'-'+environment+"-"+debug+'_rasped.zip';
209 let zipSave = fs.createWriteStream(zipPath);
210 let boundPackage = 'https://cp.onespan.com/public_api/v1/rasp/bound_package/'+bindingId;
211 var spinner = ora("Downloading the shielded binary").start();
212
213 try{
214 await axios
215 .get(
216 boundPackage,
217 { headers: {'X-API-KEY': apikey }, responseType: 'stream' }) //per scaricare un file zip il responseType deve essere 'stream'
218 .then(response => {
219 return new Promise((resolve, reject) => {
220 response.data.pipe(zipSave);
221 let error = null;
222 zipSave.on('error', err => {
223 error = err;
224 zipSave.close();
225 reject(err);
226 });
227 zipSave.on('close', () => {
228 if (!error) {
229 resolve(true);
230 }
231 });
232 });
233 });
234
235 } catch(err){
236 throw err;
237 }
238 spinner.succeed();
239 await extract(appPlatform, ymd);
240 }
241 }
242
243
244 const recursiveCheck = async function(bindingId) {
245 let res = await fetch('https://cp.onespan.com/public_api/v1/rasp/status', { method: 'GET', headers: {'X-API-KEY': apikey}});
246 let json = await res.json();
247 let statusResult = json.bindings[bindingId].status;
248 if (statusResult !== 'succeeded'){
249 try{
250 await delay(30000);
251 await recursiveCheck(bindingId);
252 } catch(err) {
253 throw err;
254 }
255 }
256 }
257
258 const appSign = async function (appPlatform, abi, brand, ymd, environment, debug) {
259 if (appPlatform == 'android') {
260
261 let apkPath = '../builds/'+ymd+'/android/rasp/mang-app-'+abi+'-'+brand+'-'+environment+'-'+debug+'/wrapped-app-abi'+abi+'_'+brand+'-'+environment+'-'+debug+'.apk';
262
263 let ex = spawn.sync("jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore "+parametri.signParams[abi+brand].KEYSTOREFILEPATH+" -storepass "+parametri.signParams[abi+brand].KEYSTOREPASSWORD+" -keypass "+parametri.signParams[abi+brand].PRIVATEKEYPASSWORD+" "+apkPath+" "+parametri.signParams[abi+brand].PRIVATEKEYNAME, {
264 stdio: "inherit",
265 shell: true
266 });
267
268 if (ex.status != 0){
269 throw "Errore nell'esecuzione di jarsigner";
270 };
271
272 let ez = spawn.sync("cd "+androidsdkdir+" && zipalign -v 4 "+"../builds/"+ymd+'/android/rasp/mang-app-'+abi+'-'+brand+'-'+environment+'-'+debug+"_signed.apk"+" "+"../builds/"+ymd+'/android/rasp/mang-app-'+abi+'-'+brand+'-'+environment+'-'+debug+"_ok.apk", {
273 stdio: "inherit",
274 shell: true
275 });
276
277 if (ez.status != 0){
278 throw "Errore nell'esecuzione di zipalign";
279 };
280
281 let ew = spawn.sync("cp -p ../../"+androiddir+'/app/build/outputs/mapping/abi'+abi+'_'+brand+environment.charAt(0).toUpperCase()+environment.slice(1)+"/"+debug+"/mapping.txt ../builds/"+ymd+"android/rasp/mang-app-"+abi+"-"+brand+"-"+environment+"-"+debug+"/mapping" , {
282 stdio: "inherit",
283 shell: true
284 });
285
286 if (ex.status !=0){
287 throw "Impossibile copiare mapping.txt all'interno della cartella rasp";
288 }
289
290
291 } else {
292
293 fs.renameSync("../buildsb/"+ymd+'/'+abi+"-"+brand+"-"+environment+"-"+debug+"/"+"mang-app-"+abi+'-'+brand+'-'+environment+"-"+debug+"_rasped/wrapped-"+abi+"-"+brand+"-"+environment+"-"+debug+".ipa" , "../buildsb/"+ymd+'/'+abi+"-"+brand+"-"+environment+"-"+debug+"/"+"mang-app-"+abi+'-'+brand+'-'+environment+"-"+debug+"_rasped/wrapped-"+abi+"-"+brand+"-"+environment+"-"+debug+".zip")
294 let zipFile = "../buildsb/"+ymd+'/'+abi+"-"+brand+"-"+environment+"-"+debug+"/"+"mang-app-"+abi+'-'+brand+'-'+environment+"-"+debug+"_rasped/wrapped-"+abi+"-"+brand+"-"+environment+"-"+debug+".zip";
295 let targetFolder = "../buildsb/"+ymd+'/'+abi+"-"+brand+"-"+environment+"-"+debug+"/"+"mang-app-"+abi+'-'+brand+'-'+environment+"-"+debug+"_rasped/wrapped-"+abi+"-"+brand+"-"+environment+"-"+debug;
296 const resolve = require('path').resolve;
297
298 var resolveTargetFolder = resolve(targetFolder);
299 var spinner = ora("Extracting the ipa").start();
300 try {
301 await extractZip(zipFile , { dir: resolveTargetFolder });
302 } catch (err) {
303 throw err;
304 }
305 spinner.succeed();
306
307 //codesign 1 e 2
308
309 var questions = [{
310 type: 'input',
311 name: 'frameworkName',
312 message: "Write here the framework name: ",
313 }]
314
315 answers = await inquirer.prompt(questions);
316 let frameworkName = answers.frameworkName;
317
318 let cs = spawn.sync("codesign --verbose --force --sign '"+process.env["TEAMID_"+abi+brand]+"' "+targetFolder+"/mang-app-"+abi+"-"+brand+".app/Frameworks/"+frameworkName , {
319 stdio: "inherit",
320 shell: true
321 });
322
323 if (cs.status != 0){
324 throw "Errore nella firma del framework";
325 }
326
327 let cz = spawn.sync("codesign --verbose --force --sign '"+process.env["TEAMID_"+abi+brand]+"' –entitlements /Users/rollod/Documents/links-build/buildsb/"+ymd+"/"+abi+"-"+brand+"-"+environment+"-"+debug+"/"+abi+"-"+brand+"-"+environment+"-"+debug+".xcent /Users/rollod/Documents/links-build/buildsb/2020-05-07/saxo03075-0-dev-release/mang-app-saxo03075-0-dev-release_rasped//Users/rollod/Documents/links-build/buildsb/2020-05-07/saxo03075-0-dev-release/mang-app-saxo03075-0-dev-release_rasped/wrapped-saxo03075-0-dev-release/mang-app-"+abi+"-"+brand+".app" , {
328 stdio: "inherit",
329 shell: true
330 });
331
332 if (cz.status != 0) {
333 throw "Errore nella firma del .app";
334 }
335
336 //copiare .app firmato in xcarchive iniziale
337
338 spawn.sync("cp /Users/rollod/Documents/links-build/buildsb/2020-05-07/saxo03075-0-dev-release/mang-app-saxo03075-0-dev-release_rasped/wrapped-saxo03075-0-dev-release/mang-app-"+abi+"-"+brand+".app /Users/rollod/Documents/links-build/buildsb/2020-05-07/saxo03075-0-dev-release/"+abi+"-"+brand+"-"+environment+"-"+debug+"_secure.xcarchive/Products/Applications" , {
339 stdio: "inherit",
340 shell: true
341 });
342
343 }
344 }
345
346
347 //RETURN STATUS OF THE UPLOADED APP
348 var spinner = ora("Obtaining the shielding status").start();
349 await recursiveCheck(bindingId);
350 spinner.succeed();
351 await download(appPlatform, bindingId, apikey, ymd, abi, brand, environment, debug);