import { readFile } from "node:fs/promises"; import path from "node:path"; import { repositoryRoot } from "./repository-files.mjs"; const manifestPath = path.join(repositoryRoot, "app", "src", "main", "AndroidManifest.xml"); const backupRulesPath = path.join( repositoryRoot, "app", "src", "main", "res", "xml", "backup_rules.xml", ); const extractionRulesPath = path.join( repositoryRoot, "app", "src", "main", "res", "xml", "data_extraction_rules.xml", ); const [manifest, backupRules, extractionRules] = await Promise.all([ readFile(manifestPath, "utf8"), readFile(backupRulesPath, "utf8"), readFile(extractionRulesPath, "utf8"), ]); const failures = []; if (!manifest.includes('android:allowBackup="false"')) { failures.push("Android backup must remain disabled"); } if (!manifest.includes('android:usesCleartextTraffic="false"')) { failures.push("cleartext traffic must remain disabled"); } if (/android\.permission\.INTERNET/u.test(manifest)) { failures.push("the offline application must not request INTERNET permission"); } for (const domain of ["root", "file", "database", "sharedpref", "external"]) { const exclusion = ``; if (!backupRules.includes(exclusion)) { failures.push(`legacy backup rules do not exclude ${domain}`); } const extractionOccurrences = extractionRules.split(exclusion).length - 1; if (extractionOccurrences !== 2) { failures.push( `data extraction rules must exclude ${domain} from cloud backup and device transfer`, ); } } if (failures.length > 0) { console.error("Local-data boundary verification failed:"); for (const failure of failures) console.error(`- ${failure}`); process.exitCode = 1; } else { console.log("Local-data boundary verification passed."); }