Skip to content

Commit 37a3723

Browse files
committed
fix(release): allow embedded signing profiles
1 parent f451f9c commit 37a3723

2 files changed

Lines changed: 36 additions & 6 deletions

File tree

scripts/__tests__/verify-release-privacy.test.ts

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ import { join, resolve } from 'node:path';
55
import { afterEach, describe, expect, it } from 'vitest';
66

77
const tempRoots: string[] = [];
8+
const scannerPath = resolve(process.cwd(), 'scripts/verify-release-privacy.mjs');
9+
10+
function runScanner(artifact: string) {
11+
return spawnSync(process.execPath, [scannerPath, artifact], { encoding: 'utf8' });
12+
}
813

914
afterEach(() => {
1015
for (const root of tempRoots.splice(0)) rmSync(root, { force: true, recursive: true });
@@ -23,13 +28,29 @@ describe('release privacy verification', () => {
2328
writeFileSync(join(externalDirectory, 'private.txt'), '/Users/private-owner/secret');
2429
symlinkSync(externalDirectory, join(artifact, 'Applications'));
2530

26-
const result = spawnSync(
27-
process.execPath,
28-
[resolve(process.cwd(), 'scripts/verify-release-privacy.mjs'), artifact],
29-
{ encoding: 'utf8' },
30-
);
31+
const result = runScanner(artifact);
3132

3233
expect(result.status, result.stderr).toBe(0);
3334
expect(result.stdout).toContain('Release privacy check passed (1 files, 1 symlinks)');
3435
});
36+
37+
it('allows only the embedded provisioning profiles required by signed apps', () => {
38+
const tempRoot = mkdtempSync(join(tmpdir(), 'voyager-release-privacy-'));
39+
tempRoots.push(tempRoot);
40+
41+
const contents = join(tempRoot, 'Voyager.app', 'Contents');
42+
const extensionContents = join(contents, 'PlugIns', 'Voyager Extension.appex', 'Contents');
43+
mkdirSync(contents, { recursive: true });
44+
mkdirSync(extensionContents, { recursive: true });
45+
writeFileSync(join(contents, 'embedded.provisionprofile'), 'signed profile content');
46+
writeFileSync(join(extensionContents, 'embedded.provisionprofile'), 'signed profile content');
47+
48+
const expectedProfileResult = runScanner(tempRoot);
49+
expect(expectedProfileResult.status, expectedProfileResult.stderr).toBe(0);
50+
51+
writeFileSync(join(contents, 'exported.provisionprofile'), 'unexpected profile content');
52+
const unexpectedProfileResult = runScanner(tempRoot);
53+
expect(unexpectedProfileResult.status).toBe(1);
54+
expect(unexpectedProfileResult.stderr).toContain('forbidden release filename');
55+
});
3556
});

scripts/verify-release-privacy.mjs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ const forbiddenPatterns = [
3939
const files = [];
4040
const symlinks = [];
4141

42+
function isExpectedEmbeddedProfile(path) {
43+
return /\.app\/Contents\/(?:PlugIns\/[^/]+\.appex\/Contents\/)?embedded\.provisionprofile$/.test(
44+
path,
45+
);
46+
}
47+
4248
async function collect(path) {
4349
const metadata = await lstat(path);
4450
if (metadata.isSymbolicLink()) {
@@ -70,7 +76,10 @@ function scanContent(path, content) {
7076
}
7177

7278
for (const file of files) {
73-
if (forbiddenNames.some((pattern) => pattern.test(basename(file)))) {
79+
if (
80+
forbiddenNames.some((pattern) => pattern.test(basename(file))) &&
81+
!isExpectedEmbeddedProfile(file)
82+
) {
7483
failures.push(`${file}: forbidden release filename`);
7584
continue;
7685
}

0 commit comments

Comments
 (0)