mirror of
https://github.com/bulwarkmail/native.git
synced 2026-11-05 00:07:36 +00:00
fix: answer ios export compliance per testflight build so uploads with pgp and s/mime pass validation #58
This commit is contained in:
+7
-6
@@ -57,12 +57,13 @@ module.exports = {
|
||||
supportsTablet: true,
|
||||
bundleIdentifier: 'org.bulwarkmail.mobile',
|
||||
buildNumber: IOS_BUILD_NUMBER,
|
||||
config: {
|
||||
// PGP and S/MIME (#58) add end-to-end encryption beyond HTTPS/TLS,
|
||||
// so the app declares non-exempt encryption; App Store Connect asks
|
||||
// for the export compliance documentation on upload.
|
||||
usesNonExemptEncryption: true,
|
||||
},
|
||||
// No usesNonExemptEncryption here. PGP and S/MIME (#58) are non-exempt
|
||||
// encryption, but only standard algorithms from open-source libraries
|
||||
// and the app is not on the French store, so App Store Connect keeps no
|
||||
// declaration and has no ITSEncryptionExportComplianceCode to give. An
|
||||
// Info.plist saying YES without that code fails upload validation
|
||||
// (90592), so the key stays out and scripts/testflight-public.mjs
|
||||
// answers the question for each build through the API.
|
||||
},
|
||||
android: {
|
||||
adaptiveIcon: {
|
||||
|
||||
@@ -99,7 +99,7 @@ if (!group) {
|
||||
// ── Wait for the build ─────────────────────────────────────────
|
||||
const query =
|
||||
`/v1/builds?filter[app]=${app.id}&filter[version]=${encodeURIComponent(buildNumber)}` +
|
||||
`&filter[preReleaseVersion.version]=${encodeURIComponent(version)}&fields[builds]=version,processingState`;
|
||||
`&filter[preReleaseVersion.version]=${encodeURIComponent(version)}&fields[builds]=version,processingState,usesNonExemptEncryption`;
|
||||
const started = Date.now();
|
||||
let build;
|
||||
for (;;) {
|
||||
@@ -121,6 +121,18 @@ for (;;) {
|
||||
}
|
||||
console.log(`Build ${version} (${buildNumber}) is processed.`);
|
||||
|
||||
// ── Export compliance ──────────────────────────────────────────
|
||||
// The app uses non-exempt encryption (PGP and S/MIME, #58) with standard
|
||||
// algorithms only and is not on the French store: App Store Connect needs no
|
||||
// documentation, so Info.plist has no key and the answer is given here.
|
||||
if (build.attributes.usesNonExemptEncryption == null) {
|
||||
const answer = await api('PATCH', `/v1/builds/${build.id}`, {
|
||||
data: { type: 'builds', id: build.id, attributes: { usesNonExemptEncryption: true } },
|
||||
});
|
||||
if (!answer.ok) fail('Answering export compliance', answer);
|
||||
console.log('Export compliance answered: standard encryption, no documentation needed.');
|
||||
}
|
||||
|
||||
// ── Add it to the group ────────────────────────────────────────
|
||||
const add = await api('POST', `/v1/betaGroups/${group.id}/relationships/builds`, {
|
||||
data: [{ type: 'builds', id: build.id }],
|
||||
@@ -150,7 +162,7 @@ const detail = await api('GET', `/v1/builds/${build.id}/buildBetaDetail`);
|
||||
if (!detail.ok) fail('Reading the build state', detail);
|
||||
const external = detail.json.data.attributes.externalBuildState;
|
||||
if (external === 'MISSING_EXPORT_COMPLIANCE') {
|
||||
console.error('The build has no export compliance answer; set usesNonExemptEncryption in app.config.js.');
|
||||
console.error('The build still has no export compliance answer; answer it in App Store Connect > TestFlight.');
|
||||
process.exit(1);
|
||||
}
|
||||
if (external === 'READY_FOR_BETA_SUBMISSION') {
|
||||
|
||||
@@ -2,13 +2,22 @@ import { describe, expect, it } from 'vitest';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
// With PGP and S/MIME in the app (#58) the encryption is no longer only
|
||||
// HTTPS/TLS, so the App Store export compliance answer must say so.
|
||||
// PGP and S/MIME (#58) are non-exempt encryption with standard algorithms, and
|
||||
// the app is not on the French store, so App Store Connect has no declaration
|
||||
// and no compliance code. Info.plist must not claim YES (upload validation
|
||||
// rejects it without the code, 90592) nor NO (that would be untrue); the
|
||||
// TestFlight script answers per build instead.
|
||||
|
||||
const root = path.resolve(__dirname, '../../..');
|
||||
|
||||
describe('export compliance', () => {
|
||||
it('declares non-exempt encryption for iOS', () => {
|
||||
const src = fs.readFileSync(path.resolve(__dirname, '../../../app.config.js'), 'utf8');
|
||||
expect(src).toMatch(/usesNonExemptEncryption: true/);
|
||||
expect(src).not.toMatch(/usesNonExemptEncryption: false/);
|
||||
it('leaves the encryption key out of the iOS config', () => {
|
||||
const src = fs.readFileSync(path.join(root, 'app.config.js'), 'utf8');
|
||||
expect(src).not.toMatch(/usesNonExemptEncryption\s*:/);
|
||||
});
|
||||
|
||||
it('answers non-exempt encryption for each TestFlight build', () => {
|
||||
const src = fs.readFileSync(path.join(root, 'scripts/testflight-public.mjs'), 'utf8');
|
||||
expect(src).toMatch(/attributes: \{ usesNonExemptEncryption: true \}/);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user