UNPKG

@mhealth/cordova-plugin-antitampering

Version:

Verify the integrity of cordova static assets - Android / iOS

160 lines (144 loc) 6.31 kB
#!/usr/bin/env node var crypto = require('crypto'); var helpers = require('./helpers'); module.exports = function (context) { var path = require('path'); var fs = require('fs'); var cordovaUtil = context.requireCordovaModule('cordova-lib/src/cordova/util'); var platforms = context.requireCordovaModule('cordova-lib/src/platforms/platforms'); var projectRoot = cordovaUtil.isCordova(); var excludeExts = getExtensionsPreference(); process.stdout.write('[ANTI-TAMPERING] Saving a hash for each platforms asset \n'); function getExtensionsPreference () { var extensionsPref = helpers.getPluginPreference(context, 'EXCLUDE_ASSETS_EXTENSIONS'); if (typeof extensionsPref !== 'string' || !extensionsPref.trim().length) { if (helpers.isVerbose(context)) { process.stdout.write('No extensions to exclude provided \n'); } return false; } var extensions = []; extensionsPref.split(/\s+|,+/).forEach(function (ext) { if (ext !== '') { extensions.push(ext); } }); if (helpers.isVerbose(context)) { process.stdout.write('Excluding following extensions: ' + extensions.join(',') + ' \n'); } if (!extensions.length) { return null; } return new RegExp('.*\.(' + extensions.join('|') + ')$'); } function isDirectoryToIgnore(dir) { var directoryPref = helpers.getPluginPreference(context, 'EXCLUDE_DIRECTORIES'); if (typeof directoryPref !== 'string' || !directoryPref.trim().length) { if (helpers.isVerbose(context)) { process.stdout.write('No directories to exclude provided \n'); } return false; } var isExcludeDir = false; directoryPref.split(/\s+|,+/).forEach(function (excludeDir) { if(excludeDir !== '') { if(dir.includes(excludeDir)) { isExcludeDir = true; if (helpers.isVerbose(context)) { process.stdout.write('Excluding following directory: ' + dir + ' \n'); } } } }); return isExcludeDir; } function getPlatformAssets (dir) { var assetsList = []; var list = fs.readdirSync(dir); list.map(function (file) { var filePath = path.join(dir, file); if (!isDirectoryToIgnore(filePath)) { if (fs.statSync(filePath).isDirectory()) { var subDirList = getPlatformAssets(filePath, excludeExts); assetsList = assetsList.concat(subDirList); } if (fs.statSync(filePath).isFile() && (!excludeExts || !excludeExts.test(file))) { assetsList.push(filePath); } } }); return assetsList; } helpers.getPlatformsList(context).forEach(function (platform) { var platformPath = path.join(projectRoot, 'platforms', platform); var platformApi = platforms.getPlatformApi(platform, platformPath); var platformInfo = platformApi.getPlatformInfo(); var platformWww = platformInfo.locations.www; var source = helpers.getPluginSource(context, platform); var content = source.content; var hashes = getPlatformAssets(platformWww).map(function (file) { var fileName = file.replace(/\\/g, '/'); fileName = fileName.replace(platformWww.replace(/\\/g, '/') + '/', ''); var hash; var hashHex; hash = crypto.createHash('sha256'); try { hash.update(fs.readFileSync(file), 'utf8'); } catch (e) { helpers.exit('Unable to read file at path ' + file, e); } hashHex = hash.digest('hex'); if (helpers.isVerbose(context)) { process.stdout.write('Hash: ' + hashHex + ' < ' + fileName + '\n'); } return { file: Buffer.from(fileName).toString('base64'), hash: hashHex }; }); if (platform === 'android') { content = content.replace(/\s*put\("[^"]+",\s"[^"]{64}"\);/g, '') .replace(/assetsHashes\s*=.+\s*new.*(\(\d+\)[^\w]*)\);/, function (match, group) { return match.replace(group, '()\n' + tab()); }) .replace(/assetsHashes\s*=.+\s*new.*(\(.*\))/, function (match, group) { var replace = match.replace(group, '(' + (hashes.length || '') + ')'); if (hashes.length) { replace += ' {{\n' + tab(); hashes.forEach(function (h) { replace += tab(2) + 'put("' + h.file + '", "' + h.hash + '");\n' + tab(); }); replace += tab() + '}}'; } return replace; }); try { fs.writeFileSync(source.path, content, 'utf-8'); } catch (e) { helpers.exit('Unable to write java class source at path ' + source.path, e); } } if (platform === 'ios') { content = content.replace(/assetsHashes = (@{([^}]*)});/, function (a, b) { var list = '@{\n' + tab(); hashes.forEach(function (h) { list += tab() + '@"' + h.file + '": @"' + h.hash + '",\n' + tab(); }); list += '}'; return a.replace(b, list); }); try { fs.writeFileSync(source.path, content, 'utf-8'); } catch (e) { helpers.exit('Unable to write obj-c source at path ' + source.path, e); } } }); function tab (size) { var str = ''; for (var i = 0; i < (size || 1); i++) { str += ' '; } return str; } };