codex-copilot-dx 0.6.12 → 0.6.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -36
- package/package.json +1 -1
- package/src/doctor.mjs +2 -2
- package/src/pm-studio-asar.mjs +136 -22
- package/src/pm-studio-setup.mjs +103 -301
- package/src/pm-studio-status.mjs +15 -22
- package/src/pm-studio-provenance.mjs +0 -701
package/src/pm-studio-setup.mjs
CHANGED
|
@@ -17,10 +17,6 @@ import {
|
|
|
17
17
|
patchAsarBuffer,
|
|
18
18
|
sha256Hex,
|
|
19
19
|
} from "./pm-studio-asar.mjs";
|
|
20
|
-
import {
|
|
21
|
-
PM_STUDIO_OFFICIAL_REPOSITORY,
|
|
22
|
-
verifyOfficialPmStudioProvenance,
|
|
23
|
-
} from "./pm-studio-provenance.mjs";
|
|
24
20
|
import { withPmStudioSetupLock } from "./pm-studio-setup-lock.mjs";
|
|
25
21
|
|
|
26
22
|
export const DEFAULT_PM_STUDIO_APP_PATH = "/Applications/PM Studio.app";
|
|
@@ -275,9 +271,7 @@ function defaultInspectCodeSign({ appPath, processRunner }) {
|
|
|
275
271
|
: "";
|
|
276
272
|
return {
|
|
277
273
|
valid: !verify.error && verify.status === 0
|
|
278
|
-
&& !display.error && display.status === 0
|
|
279
|
-
&& !entitlementsResult.error && entitlementsResult.status === 0
|
|
280
|
-
&& hasEntitlements,
|
|
274
|
+
&& !display.error && display.status === 0,
|
|
281
275
|
verifyValid: !verify.error && verify.status === 0,
|
|
282
276
|
displayValid: !display.error && display.status === 0,
|
|
283
277
|
entitlementsState: hasEntitlements
|
|
@@ -322,32 +316,19 @@ function defaultInspectExecutableIntegrity({ appPath, infoPlistPath, recipe, pro
|
|
|
322
316
|
};
|
|
323
317
|
}
|
|
324
318
|
|
|
325
|
-
function
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
"PM Studio must contain exactly one supported macOS architecture");
|
|
319
|
+
function defaultSignApp({ appPath, source, processRunner }) {
|
|
320
|
+
const preserve = [];
|
|
321
|
+
if (source?.codeSign?.displayValid === true) {
|
|
322
|
+
if (source.codeSign.entitlementsState === "xml") preserve.push("entitlements");
|
|
323
|
+
if (source.codeSign.flags) preserve.push("flags");
|
|
324
|
+
if (source.codeSign.runtimeVersion) preserve.push("runtime");
|
|
332
325
|
}
|
|
333
|
-
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
async function defaultVerifyOfficialProvenance(options) {
|
|
337
|
-
return verifyOfficialPmStudioProvenance({
|
|
338
|
-
...options,
|
|
339
|
-
extractor: async ({ archivePath, destination }) => {
|
|
340
|
-
runChecked(options.processRunner, "/usr/bin/ditto", ["-x", "-k", archivePath, destination],
|
|
341
|
-
"Extracting official PM Studio release");
|
|
342
|
-
},
|
|
343
|
-
});
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
function defaultSignApp({ appPath, processRunner }) {
|
|
347
|
-
runChecked(processRunner, "/usr/bin/codesign", [
|
|
326
|
+
const args = [
|
|
348
327
|
"--force", "--deep", "--sign", "-", "--timestamp=none",
|
|
349
|
-
|
|
350
|
-
|
|
328
|
+
];
|
|
329
|
+
if (preserve.length > 0) args.push(`--preserve-metadata=${preserve.join(",")}`);
|
|
330
|
+
args.push(appPath);
|
|
331
|
+
runChecked(processRunner, "/usr/bin/codesign", args, "Ad-hoc codesigning PM Studio");
|
|
351
332
|
}
|
|
352
333
|
|
|
353
334
|
function defaultListBlockingProcesses({ processRunner }) {
|
|
@@ -440,8 +421,6 @@ export function createPmStudioSetupOperations(overrides = {}) {
|
|
|
440
421
|
inspectCodeSign: defaultInspectCodeSign,
|
|
441
422
|
inspectBundleContent: defaultInspectBundleContent,
|
|
442
423
|
inspectExecutableIntegrity: defaultInspectExecutableIntegrity,
|
|
443
|
-
readAppArchitecture: defaultReadAppArchitecture,
|
|
444
|
-
verifyOfficialProvenance: defaultVerifyOfficialProvenance,
|
|
445
424
|
withSetupLock: ({ appPath }, fn) => withPmStudioSetupLock({ appPath }, fn),
|
|
446
425
|
signApp: defaultSignApp,
|
|
447
426
|
listBlockingProcesses: defaultListBlockingProcesses,
|
|
@@ -483,17 +462,6 @@ function bundleContentMatches(actual, expected) {
|
|
|
483
462
|
&& actual?.xattrCount === expected?.xattrCount;
|
|
484
463
|
}
|
|
485
464
|
|
|
486
|
-
function sourceCodeSignatureMatches(actual, expected) {
|
|
487
|
-
return actual?.adHoc === false
|
|
488
|
-
&& actual?.displayValid === true
|
|
489
|
-
&& actual?.identifier === expected?.identifier
|
|
490
|
-
&& actual?.teamIdentifier === expected?.teamIdentifier
|
|
491
|
-
&& actual?.flags === expected?.flags
|
|
492
|
-
&& actual?.runtimeVersion === expected?.runtimeVersion
|
|
493
|
-
&& actual?.cdHashFull === expected?.cdHashFull
|
|
494
|
-
&& actual?.notarizationTicket === expected?.notarizationTicket;
|
|
495
|
-
}
|
|
496
|
-
|
|
497
465
|
export function inspectPmStudioApp({
|
|
498
466
|
appPath,
|
|
499
467
|
recipe = PM_STUDIO_2_9_7_RECIPE,
|
|
@@ -536,23 +504,14 @@ export function inspectPmStudioApp({
|
|
|
536
504
|
|
|
537
505
|
const exactSourceBundle = !recipe.sourceBundleContent
|
|
538
506
|
|| bundleContentMatches(bundleContent, recipe.sourceBundleContent);
|
|
539
|
-
const strictVendorSignature = codeSign?.valid === true
|
|
540
|
-
&& codeSign?.adHoc === false
|
|
541
|
-
&& codeSign?.identifier === recipe.bundleIdentifier
|
|
542
|
-
&& codeSign?.teamIdentifier === recipe.sourceTeamIdentifier;
|
|
543
|
-
const exactInvalidVendorSignature = recipe.sourceBundleContent
|
|
544
|
-
&& exactSourceBundle
|
|
545
|
-
&& codeSign?.valid === false
|
|
546
|
-
&& codeSign?.verifyValid === false
|
|
547
|
-
&& codeSign?.entitlementsState === "invalid"
|
|
548
|
-
&& sourceCodeSignatureMatches(codeSign, recipe.sourceCodeSignature);
|
|
549
|
-
const sourceSignatureAccepted = strictVendorSignature || exactInvalidVendorSignature;
|
|
550
507
|
const patchedSignatureAccepted = codeSign?.valid === true
|
|
551
508
|
&& codeSign?.adHoc === true
|
|
509
|
+
&& codeSign?.identifier === recipe.bundleIdentifier
|
|
552
510
|
&& (!recipe.patchedSigningMetadata
|
|
553
511
|
|| JSON.stringify(signingMetadata(codeSign)) === JSON.stringify(recipe.patchedSigningMetadata));
|
|
554
512
|
const predecessorSignatureAccepted = codeSign?.valid === true
|
|
555
513
|
&& codeSign?.adHoc === true
|
|
514
|
+
&& codeSign?.identifier === recipe.bundleIdentifier
|
|
556
515
|
&& Boolean(recipe.predecessor?.signingMetadata)
|
|
557
516
|
&& JSON.stringify(signingMetadata(codeSign)) === JSON.stringify(recipe.predecessor.signingMetadata);
|
|
558
517
|
const clean = issues.length === 0
|
|
@@ -560,8 +519,7 @@ export function inspectPmStudioApp({
|
|
|
560
519
|
&& integrityMatches(plistIntegrity, "SHA256", recipe.sourceHeaderSha256)
|
|
561
520
|
&& executableIntegrity.executableSha256 === recipe.sourceExecutableSha256
|
|
562
521
|
&& executableIntegrity.frameworkSha256 === recipe.sourceElectronFrameworkSha256
|
|
563
|
-
&& exactSourceBundle
|
|
564
|
-
&& sourceSignatureAccepted;
|
|
522
|
+
&& exactSourceBundle;
|
|
565
523
|
const patched = issues.length === 0
|
|
566
524
|
&& asar.state === "patched"
|
|
567
525
|
&& integrityMatches(plistIntegrity, "SHA256", recipe.patchedHeaderSha256)
|
|
@@ -593,15 +551,15 @@ export function inspectPmStudioApp({
|
|
|
593
551
|
if (asar.state === "clean" && recipe.sourceBundleContent && !exactSourceBundle) {
|
|
594
552
|
issues.push("bundle content does not match the exact clean recipe");
|
|
595
553
|
}
|
|
596
|
-
if (
|
|
554
|
+
if (asar.state !== "clean" && !codeSign?.valid) {
|
|
597
555
|
issues.push("codesign verification failed");
|
|
598
556
|
}
|
|
599
|
-
else if (asar.state === "clean" && codeSign.adHoc) issues.push("clean bundle has an unexpected ad-hoc signature");
|
|
600
557
|
else if (["patched", "predecessor", "legacy"].includes(asar.state) && !codeSign.adHoc) {
|
|
601
558
|
issues.push(`${asar.state} bundle is not ad-hoc signed`);
|
|
602
559
|
}
|
|
603
|
-
else if (
|
|
604
|
-
|
|
560
|
+
else if (["patched", "predecessor", "legacy"].includes(asar.state)
|
|
561
|
+
&& codeSign.identifier !== recipe.bundleIdentifier) {
|
|
562
|
+
issues.push(`${asar.state} bundle identifier does not match the recipe`);
|
|
605
563
|
}
|
|
606
564
|
if (asar.state === "clean"
|
|
607
565
|
&& executableIntegrity.executableSha256 !== recipe.sourceExecutableSha256) {
|
|
@@ -637,7 +595,7 @@ export function inspectPmStudioApp({
|
|
|
637
595
|
codeSign,
|
|
638
596
|
bundleContent,
|
|
639
597
|
sourceVerification: clean
|
|
640
|
-
?
|
|
598
|
+
? recipe.sourceBundleContent ? "local-content" : "exact-recipe"
|
|
641
599
|
: "",
|
|
642
600
|
paths,
|
|
643
601
|
issues,
|
|
@@ -680,13 +638,13 @@ function selectRecipe(metadata, recipes) {
|
|
|
680
638
|
&& recipe.bundleIdentifier === String(metadata.bundleIdentifier));
|
|
681
639
|
}
|
|
682
640
|
|
|
683
|
-
function compatibleStaticOptions(metadata) {
|
|
641
|
+
function compatibleStaticOptions(metadata, executable) {
|
|
684
642
|
return {
|
|
685
643
|
version: String(metadata.version),
|
|
686
644
|
build: String(metadata.build),
|
|
687
645
|
bundleIdentifier: String(metadata.bundleIdentifier),
|
|
688
|
-
sourceTeamIdentifier:
|
|
689
|
-
executable
|
|
646
|
+
sourceTeamIdentifier: "local-app",
|
|
647
|
+
executable,
|
|
690
648
|
electronFrameworkPath: PM_STUDIO_2_9_10_RECIPE.electronFrameworkPath,
|
|
691
649
|
embeddedAsarIntegrity: PM_STUDIO_2_9_10_RECIPE.embeddedAsarIntegrity,
|
|
692
650
|
asarPath: PM_STUDIO_2_9_10_RECIPE.asarPath,
|
|
@@ -710,23 +668,19 @@ function compatibleSourceBundleContent({ appPath, operations }) {
|
|
|
710
668
|
|
|
711
669
|
function compatibleSourceStructure({ appPath, metadata, operations }) {
|
|
712
670
|
const paths = appPaths(appPath, PM_STUDIO_2_9_10_RECIPE);
|
|
713
|
-
if (String(metadata.bundleIdentifier) !== PM_STUDIO_2_9_10_RECIPE.bundleIdentifier) {
|
|
714
|
-
throw setupError("PM_STUDIO_COMPATIBLE_SOURCE_UNTRUSTED",
|
|
715
|
-
"PM Studio compatible-version discovery found an unexpected bundle identifier");
|
|
716
|
-
}
|
|
717
671
|
const executableIntegrity = operations.inspectExecutableIntegrity({
|
|
718
672
|
...paths,
|
|
719
673
|
recipe: PM_STUDIO_2_9_10_RECIPE,
|
|
720
674
|
processRunner: operations.processRunner,
|
|
721
675
|
});
|
|
722
|
-
if (!executableIntegrity?.
|
|
676
|
+
if (!executableIntegrity?.executableName
|
|
723
677
|
|| !executableIntegrity?.embeddedIntegrity?.supported
|
|
724
678
|
|| executableIntegrity.embeddedIntegrity.state !== PM_STUDIO_2_9_10_RECIPE.embeddedAsarIntegrity) {
|
|
725
|
-
throw setupError("
|
|
679
|
+
throw setupError("PM_STUDIO_ASAR_INCOMPATIBLE",
|
|
726
680
|
"PM Studio compatible-version discovery found an unsupported executable or embedded ASAR integrity policy");
|
|
727
681
|
}
|
|
728
682
|
const compatibleRecipe = createCompatiblePmStudioRecipe(fs.readFileSync(paths.asarPath), {
|
|
729
|
-
...compatibleStaticOptions(metadata),
|
|
683
|
+
...compatibleStaticOptions(metadata, executableIntegrity.executableName),
|
|
730
684
|
sourceExecutableSha256: executableIntegrity.executableSha256,
|
|
731
685
|
sourceElectronFrameworkSha256: executableIntegrity.frameworkSha256,
|
|
732
686
|
});
|
|
@@ -736,7 +690,7 @@ function compatibleSourceStructure({ appPath, metadata, operations }) {
|
|
|
736
690
|
processRunner: operations.processRunner,
|
|
737
691
|
});
|
|
738
692
|
if (!integrityMatches(plistIntegrity, "SHA256", compatibleRecipe.sourceHeaderSha256)) {
|
|
739
|
-
throw setupError("
|
|
693
|
+
throw setupError("PM_STUDIO_ASAR_INCOMPATIBLE",
|
|
740
694
|
"PM Studio compatible-version discovery found mismatched ElectronAsarIntegrity metadata");
|
|
741
695
|
}
|
|
742
696
|
return { paths, executableIntegrity, compatibleRecipe };
|
|
@@ -749,31 +703,8 @@ function frozenBundleContent(value) {
|
|
|
749
703
|
});
|
|
750
704
|
}
|
|
751
705
|
|
|
752
|
-
function sourceCodeSignature(codeSign) {
|
|
753
|
-
return Object.freeze({
|
|
754
|
-
identifier: codeSign.identifier,
|
|
755
|
-
teamIdentifier: codeSign.teamIdentifier,
|
|
756
|
-
flags: codeSign.flags,
|
|
757
|
-
runtimeVersion: codeSign.runtimeVersion,
|
|
758
|
-
cdHashFull: codeSign.cdHashFull,
|
|
759
|
-
notarizationTicket: codeSign.notarizationTicket,
|
|
760
|
-
});
|
|
761
|
-
}
|
|
762
|
-
|
|
763
706
|
function createCompatibleSourceRecipe({ appPath, metadata, operations }) {
|
|
764
|
-
const {
|
|
765
|
-
const codeSign = operations.inspectCodeSign({
|
|
766
|
-
...paths,
|
|
767
|
-
recipe: PM_STUDIO_2_9_10_RECIPE,
|
|
768
|
-
processRunner: operations.processRunner,
|
|
769
|
-
});
|
|
770
|
-
if (codeSign?.valid !== true
|
|
771
|
-
|| codeSign?.adHoc !== false
|
|
772
|
-
|| codeSign?.identifier !== PM_STUDIO_2_9_10_RECIPE.bundleIdentifier
|
|
773
|
-
|| codeSign?.teamIdentifier !== PM_STUDIO_2_9_10_RECIPE.sourceTeamIdentifier) {
|
|
774
|
-
throw setupError("PM_STUDIO_COMPATIBLE_SOURCE_UNTRUSTED",
|
|
775
|
-
"PM Studio compatible-version discovery requires an intact vendor signature");
|
|
776
|
-
}
|
|
707
|
+
const { compatibleRecipe } = compatibleSourceStructure({ appPath, metadata, operations });
|
|
777
708
|
const sourceBundleContent = compatibleSourceBundleContent({ appPath, operations });
|
|
778
709
|
return Object.freeze({
|
|
779
710
|
...compatibleRecipe,
|
|
@@ -781,134 +712,11 @@ function createCompatibleSourceRecipe({ appPath, metadata, operations }) {
|
|
|
781
712
|
});
|
|
782
713
|
}
|
|
783
714
|
|
|
784
|
-
function exactInvalidVendorSignature(codeSign) {
|
|
785
|
-
return codeSign?.valid === false
|
|
786
|
-
&& codeSign?.verifyValid === false
|
|
787
|
-
&& codeSign?.displayValid === true
|
|
788
|
-
&& codeSign?.entitlementsState === "invalid"
|
|
789
|
-
&& codeSign?.adHoc === false
|
|
790
|
-
&& codeSign?.identifier === PM_STUDIO_2_9_10_RECIPE.bundleIdentifier
|
|
791
|
-
&& codeSign?.teamIdentifier === PM_STUDIO_2_9_10_RECIPE.sourceTeamIdentifier
|
|
792
|
-
&& typeof codeSign?.flags === "string" && codeSign.flags.length > 0
|
|
793
|
-
&& typeof codeSign?.runtimeVersion === "string" && codeSign.runtimeVersion.length > 0
|
|
794
|
-
&& /^[0-9a-f]{64}$/.test(codeSign?.cdHashFull || "")
|
|
795
|
-
&& codeSign?.notarizationTicket === "stapled";
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
function officialArtifact(provenance, metadata, architecture) {
|
|
799
|
-
if (provenance?.verified !== true
|
|
800
|
-
|| provenance.repository !== PM_STUDIO_OFFICIAL_REPOSITORY
|
|
801
|
-
|| provenance.version !== String(metadata.version)
|
|
802
|
-
|| provenance.tag !== `v${metadata.version}`
|
|
803
|
-
|| provenance.arch !== architecture
|
|
804
|
-
|| provenance.comparison?.matched !== true
|
|
805
|
-
|| typeof provenance.releaseUrl !== "string"
|
|
806
|
-
|| typeof provenance.asset?.name !== "string"
|
|
807
|
-
|| !Number.isSafeInteger(provenance.asset?.size)
|
|
808
|
-
|| provenance.asset.size <= 0
|
|
809
|
-
|| !/^[0-9a-f]{64}$/.test(provenance.asset?.sha256 || "")) {
|
|
810
|
-
throw setupError("PM_STUDIO_COMPATIBLE_SOURCE_UNTRUSTED",
|
|
811
|
-
"Official PM Studio release verification returned incomplete evidence");
|
|
812
|
-
}
|
|
813
|
-
return Object.freeze({
|
|
814
|
-
repository: provenance.repository,
|
|
815
|
-
tag: provenance.tag,
|
|
816
|
-
arch: provenance.arch,
|
|
817
|
-
releaseUrl: provenance.releaseUrl,
|
|
818
|
-
asset: Object.freeze({
|
|
819
|
-
name: provenance.asset.name,
|
|
820
|
-
size: provenance.asset.size,
|
|
821
|
-
sha256: provenance.asset.sha256,
|
|
822
|
-
}),
|
|
823
|
-
});
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
async function createOfficialCompatibleSourceRecipe({ appPath, metadata, operations }) {
|
|
827
|
-
const { paths, executableIntegrity, compatibleRecipe } = compatibleSourceStructure({
|
|
828
|
-
appPath, metadata, operations,
|
|
829
|
-
});
|
|
830
|
-
const codeSign = operations.inspectCodeSign({
|
|
831
|
-
...paths,
|
|
832
|
-
recipe: PM_STUDIO_2_9_10_RECIPE,
|
|
833
|
-
processRunner: operations.processRunner,
|
|
834
|
-
});
|
|
835
|
-
if (!exactInvalidVendorSignature(codeSign)) {
|
|
836
|
-
throw setupError("PM_STUDIO_COMPATIBLE_SOURCE_UNTRUSTED",
|
|
837
|
-
"PM Studio has neither an intact vendor signature nor the exact invalid-signature shape accepted for official artifact verification");
|
|
838
|
-
}
|
|
839
|
-
const architecture = operations.readAppArchitecture({
|
|
840
|
-
...paths,
|
|
841
|
-
executablePath: executableIntegrity.executablePath,
|
|
842
|
-
processRunner: operations.processRunner,
|
|
843
|
-
});
|
|
844
|
-
const sourceBundleContent = compatibleSourceBundleContent({ appPath, operations });
|
|
845
|
-
const provenance = await operations.verifyOfficialProvenance({
|
|
846
|
-
installedAppPath: appPath,
|
|
847
|
-
version: String(metadata.version),
|
|
848
|
-
arch: architecture,
|
|
849
|
-
processRunner: operations.processRunner,
|
|
850
|
-
});
|
|
851
|
-
return Object.freeze({
|
|
852
|
-
...compatibleRecipe,
|
|
853
|
-
sourceBundleContent: frozenBundleContent(sourceBundleContent),
|
|
854
|
-
sourceArtifact: officialArtifact(provenance, metadata, architecture),
|
|
855
|
-
sourceCodeSignature: sourceCodeSignature(codeSign),
|
|
856
|
-
});
|
|
857
|
-
}
|
|
858
|
-
|
|
859
715
|
function sameIgnoredXattrs(value) {
|
|
860
716
|
return JSON.stringify(value)
|
|
861
717
|
=== JSON.stringify(PM_STUDIO_2_9_10_RECIPE.sourceBundleContent.ignoredXattrs);
|
|
862
718
|
}
|
|
863
719
|
|
|
864
|
-
function storedOfficialArtifact(value, metadata) {
|
|
865
|
-
const version = String(metadata.version);
|
|
866
|
-
const arch = value?.arch;
|
|
867
|
-
const assetName = `PM-Studio-${version}-mac-${arch}.zip`;
|
|
868
|
-
if (!value || Array.isArray(value)
|
|
869
|
-
|| JSON.stringify(Object.keys(value).sort())
|
|
870
|
-
!== JSON.stringify(["arch", "asset", "releaseUrl", "repository", "tag"])
|
|
871
|
-
|| value.repository !== PM_STUDIO_OFFICIAL_REPOSITORY
|
|
872
|
-
|| value.tag !== `v${version}`
|
|
873
|
-
|| !["arm64", "x64"].includes(arch)
|
|
874
|
-
|| value.releaseUrl !== `https://github.com/${PM_STUDIO_OFFICIAL_REPOSITORY}/releases/tag/v${version}`
|
|
875
|
-
|| !value.asset || Array.isArray(value.asset)
|
|
876
|
-
|| JSON.stringify(Object.keys(value.asset).sort())
|
|
877
|
-
!== JSON.stringify(["name", "sha256", "size"])
|
|
878
|
-
|| value.asset.name !== assetName
|
|
879
|
-
|| !Number.isSafeInteger(value.asset.size)
|
|
880
|
-
|| value.asset.size <= 0
|
|
881
|
-
|| !/^[0-9a-f]{64}$/.test(value.asset.sha256 || "")) {
|
|
882
|
-
throw setupError("PM_STUDIO_BACKUP_INVALID",
|
|
883
|
-
"Compatible PM Studio backup has invalid official release evidence");
|
|
884
|
-
}
|
|
885
|
-
return Object.freeze({
|
|
886
|
-
...value,
|
|
887
|
-
asset: Object.freeze({ ...value.asset }),
|
|
888
|
-
});
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
function storedSourceCodeSignature(value) {
|
|
892
|
-
if (!value || Array.isArray(value)
|
|
893
|
-
|| JSON.stringify(Object.keys(value).sort()) !== JSON.stringify([
|
|
894
|
-
"cdHashFull",
|
|
895
|
-
"flags",
|
|
896
|
-
"identifier",
|
|
897
|
-
"notarizationTicket",
|
|
898
|
-
"runtimeVersion",
|
|
899
|
-
"teamIdentifier",
|
|
900
|
-
])
|
|
901
|
-
|| value.identifier !== PM_STUDIO_2_9_10_RECIPE.bundleIdentifier
|
|
902
|
-
|| value.teamIdentifier !== PM_STUDIO_2_9_10_RECIPE.sourceTeamIdentifier
|
|
903
|
-
|| typeof value.flags !== "string" || value.flags.length === 0
|
|
904
|
-
|| typeof value.runtimeVersion !== "string" || value.runtimeVersion.length === 0
|
|
905
|
-
|| !/^[0-9a-f]{64}$/.test(value.cdHashFull || "")
|
|
906
|
-
|| value.notarizationTicket !== "stapled") {
|
|
907
|
-
throw setupError("PM_STUDIO_BACKUP_INVALID",
|
|
908
|
-
"Compatible PM Studio backup has invalid source signing evidence");
|
|
909
|
-
}
|
|
910
|
-
return Object.freeze({ ...value });
|
|
911
|
-
}
|
|
912
720
|
|
|
913
721
|
function compatibleRecipeFromManifest({ backupDir, manifest, metadata, operations }) {
|
|
914
722
|
if (manifest?.schema_version !== 2
|
|
@@ -923,24 +731,20 @@ function compatibleRecipeFromManifest({ backupDir, manifest, metadata, operation
|
|
|
923
731
|
const backupAppPath = path.join(backupDir, "PM Studio.app");
|
|
924
732
|
assertInstalledAppPath(backupAppPath);
|
|
925
733
|
const sourceAsarPath = path.join(backupAppPath, PM_STUDIO_2_9_10_RECIPE.asarPath);
|
|
734
|
+
const backupPaths = appPaths(backupAppPath, PM_STUDIO_2_9_10_RECIPE);
|
|
735
|
+
const executableIntegrity = operations.inspectExecutableIntegrity({
|
|
736
|
+
...backupPaths,
|
|
737
|
+
recipe: PM_STUDIO_2_9_10_RECIPE,
|
|
738
|
+
processRunner: operations.processRunner,
|
|
739
|
+
});
|
|
926
740
|
const compatibleRecipe = createCompatiblePmStudioRecipe(fs.readFileSync(sourceAsarPath), {
|
|
927
|
-
...compatibleStaticOptions(metadata),
|
|
741
|
+
...compatibleStaticOptions(metadata, executableIntegrity.executableName),
|
|
928
742
|
sourceExecutableSha256: manifest?.source?.binaries?.main_executable_sha256,
|
|
929
743
|
sourceElectronFrameworkSha256: manifest?.source?.binaries?.electron_framework_sha256,
|
|
930
744
|
});
|
|
931
|
-
const hasOfficialArtifact = manifest?.source?.artifact !== undefined;
|
|
932
|
-
const hasSourceCodeSignature = manifest?.source?.code_signature !== undefined;
|
|
933
|
-
if (hasOfficialArtifact !== hasSourceCodeSignature) {
|
|
934
|
-
throw setupError("PM_STUDIO_BACKUP_INVALID",
|
|
935
|
-
"Compatible PM Studio backup has incomplete official source evidence");
|
|
936
|
-
}
|
|
937
745
|
const recipe = Object.freeze({
|
|
938
746
|
...compatibleRecipe,
|
|
939
747
|
sourceBundleContent: frozenBundleContent(manifest.source.bundle_content),
|
|
940
|
-
...(hasOfficialArtifact ? {
|
|
941
|
-
sourceArtifact: storedOfficialArtifact(manifest.source.artifact, metadata),
|
|
942
|
-
sourceCodeSignature: storedSourceCodeSignature(manifest.source.code_signature),
|
|
943
|
-
} : {}),
|
|
944
748
|
});
|
|
945
749
|
if (manifest.compatibility !== recipe.compatibility
|
|
946
750
|
|| manifest.recipe_id !== recipe.id
|
|
@@ -952,8 +756,7 @@ function compatibleRecipeFromManifest({ backupDir, manifest, metadata, operation
|
|
|
952
756
|
`Compatible PM Studio backup does not match its reconstructed recipe: ${backupDir}`);
|
|
953
757
|
}
|
|
954
758
|
const inspection = inspectPmStudioApp({ appPath: backupAppPath, recipe, operations });
|
|
955
|
-
|
|
956
|
-
if (inspection.state !== "clean" || inspection.sourceVerification !== expectedVerification) {
|
|
759
|
+
if (inspection.state !== "clean" || inspection.sourceVerification !== "local-content") {
|
|
957
760
|
throw setupError("PM_STUDIO_BACKUP_INVALID",
|
|
958
761
|
`Compatible PM Studio backup source verification failed: ${backupDir}`,
|
|
959
762
|
inspection.issues);
|
|
@@ -1036,7 +839,7 @@ function selectCompatibleRecipe({ appPath, backupRoot, metadata, operations }) {
|
|
|
1036
839
|
sourceError = error;
|
|
1037
840
|
}
|
|
1038
841
|
throw setupError("PM_STUDIO_UNSUPPORTED_VERSION",
|
|
1039
|
-
`PM Studio ${metadata.version} build ${metadata.build} is not structurally compatible with the safe patch; no files were changed`,
|
|
842
|
+
`PM Studio ${metadata.version} build ${metadata.build} is not structurally compatible with the safe patch; no files were changed${sourceError?.message ? `: ${sourceError.message}` : ""}`,
|
|
1040
843
|
sourceError?.message ? [sourceError.message] : undefined);
|
|
1041
844
|
}
|
|
1042
845
|
|
|
@@ -1047,16 +850,44 @@ export function resolvePmStudioCompatibleRecipe({
|
|
|
1047
850
|
metadata,
|
|
1048
851
|
recipes = PM_STUDIO_RECIPES,
|
|
1049
852
|
operations = createPmStudioSetupOperations(),
|
|
853
|
+
inspectApp = inspectPmStudioApp,
|
|
1050
854
|
} = {}) {
|
|
1051
855
|
if (!metadata || typeof metadata !== "object") {
|
|
1052
856
|
throw new TypeError("PM Studio bundle metadata is required");
|
|
1053
857
|
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
858
|
+
const staticRecipe = selectRecipe(metadata, recipes);
|
|
859
|
+
let staticInspection = null;
|
|
860
|
+
if (staticRecipe) {
|
|
861
|
+
try {
|
|
862
|
+
staticInspection = inspectApp({ appPath, recipe: staticRecipe, operations });
|
|
863
|
+
} catch {
|
|
864
|
+
// A metadata match is not an admission gate. Local structure remains authoritative.
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
try {
|
|
868
|
+
return selectCompatibleRecipe({
|
|
869
|
+
appPath,
|
|
870
|
+
backupRoot,
|
|
871
|
+
metadata,
|
|
872
|
+
operations,
|
|
873
|
+
});
|
|
874
|
+
} catch (error) {
|
|
875
|
+
if (staticInspection?.state === "drift" && error?.code === "PM_STUDIO_UNSUPPORTED_VERSION") {
|
|
876
|
+
throw setupError("PM_STUDIO_BUNDLE_DRIFT",
|
|
877
|
+
`PM Studio ${metadata.version} build ${metadata.build} differs from its known local structure; no files were changed`,
|
|
878
|
+
staticInspection.issues);
|
|
879
|
+
}
|
|
880
|
+
const staticSourceCanBeSigned = staticInspection?.state !== "clean"
|
|
881
|
+
|| !staticRecipe?.patchedSigningMetadata
|
|
882
|
+
|| (staticInspection?.codeSign?.displayValid === true
|
|
883
|
+
&& staticInspection.codeSign.entitlementsState === "xml");
|
|
884
|
+
if (error?.code === "PM_STUDIO_UNSUPPORTED_VERSION"
|
|
885
|
+
&& staticInspection?.state && staticInspection.state !== "drift"
|
|
886
|
+
&& staticSourceCanBeSigned) {
|
|
887
|
+
return staticRecipe;
|
|
888
|
+
}
|
|
889
|
+
throw error;
|
|
890
|
+
}
|
|
1060
891
|
}
|
|
1061
892
|
|
|
1062
893
|
function backupName(recipe) {
|
|
@@ -1100,8 +931,6 @@ function backupManifest(recipe, createdAt) {
|
|
|
1100
931
|
embedded_asar_integrity: recipe.embeddedAsarIntegrity,
|
|
1101
932
|
},
|
|
1102
933
|
...(recipe.sourceBundleContent ? { bundle_content: recipe.sourceBundleContent } : {}),
|
|
1103
|
-
...(recipe.sourceArtifact ? { artifact: recipe.sourceArtifact } : {}),
|
|
1104
|
-
...(recipe.sourceCodeSignature ? { code_signature: recipe.sourceCodeSignature } : {}),
|
|
1105
934
|
},
|
|
1106
935
|
patched: {
|
|
1107
936
|
asar_sha256: recipe.patchedAsarSha256,
|
|
@@ -1162,10 +991,6 @@ function validateBackup({ backupDir, recipe, operations }) {
|
|
|
1162
991
|
|| manifest?.source?.binaries?.embedded_asar_integrity !== recipe.embeddedAsarIntegrity
|
|
1163
992
|
|| (recipe.sourceBundleContent
|
|
1164
993
|
&& JSON.stringify(manifest?.source?.bundle_content) !== JSON.stringify(recipe.sourceBundleContent))
|
|
1165
|
-
|| (recipe.sourceArtifact
|
|
1166
|
-
&& JSON.stringify(manifest?.source?.artifact) !== JSON.stringify(recipe.sourceArtifact))
|
|
1167
|
-
|| (recipe.compatibility && recipe.sourceCodeSignature
|
|
1168
|
-
&& JSON.stringify(manifest?.source?.code_signature) !== JSON.stringify(recipe.sourceCodeSignature))
|
|
1169
994
|
|| (manifest?.predecessor_patched !== undefined
|
|
1170
995
|
&& !patchRecordMatches(manifest.predecessor_patched, recipe, "predecessor"))
|
|
1171
996
|
|| (!patchRecordForState(manifest, recipe, "patched")
|
|
@@ -1279,11 +1104,7 @@ export function assertPatchedBinaryRecord({
|
|
|
1279
1104
|
|| manifest?.source?.binaries?.electron_framework_sha256 !== recipe.sourceElectronFrameworkSha256
|
|
1280
1105
|
|| manifest?.source?.binaries?.embedded_asar_integrity !== recipe.embeddedAsarIntegrity
|
|
1281
1106
|
|| JSON.stringify(manifest?.source?.bundle_content) !== JSON.stringify(recipe.sourceBundleContent)
|
|
1282
|
-
))
|
|
1283
|
-
|| (recipe.sourceArtifact
|
|
1284
|
-
&& JSON.stringify(manifest?.source?.artifact) !== JSON.stringify(recipe.sourceArtifact))
|
|
1285
|
-
|| (recipe.compatibility && recipe.sourceCodeSignature
|
|
1286
|
-
&& JSON.stringify(manifest?.source?.code_signature) !== JSON.stringify(recipe.sourceCodeSignature)))) {
|
|
1107
|
+
)))) {
|
|
1287
1108
|
throw setupError("PM_STUDIO_PATCH_RECORD_INVALID", "PM Studio patched-binary record does not match the installed patch recipe");
|
|
1288
1109
|
}
|
|
1289
1110
|
const record = patchRecordForState(manifest, recipe, recordState);
|
|
@@ -1322,32 +1143,29 @@ function flagsWithoutAdHoc(value) {
|
|
|
1322
1143
|
}
|
|
1323
1144
|
|
|
1324
1145
|
function assertSigningMetadataPreserved(source, staged, recipe) {
|
|
1146
|
+
const after = signingMetadata(staged.codeSign);
|
|
1147
|
+
if (after.identifier !== recipe.bundleIdentifier) {
|
|
1148
|
+
throw setupError("PM_STUDIO_SIGNING_METADATA_CHANGED",
|
|
1149
|
+
"Ad-hoc signing did not use the PM Studio bundle identifier");
|
|
1150
|
+
}
|
|
1325
1151
|
if (recipe.patchedSigningMetadata) {
|
|
1326
|
-
if (JSON.stringify(
|
|
1327
|
-
!== JSON.stringify(recipe.patchedSigningMetadata)) {
|
|
1152
|
+
if (JSON.stringify(after) !== JSON.stringify(recipe.patchedSigningMetadata)) {
|
|
1328
1153
|
throw setupError("PM_STUDIO_SIGNING_METADATA_CHANGED",
|
|
1329
1154
|
"Ad-hoc signing did not produce the exact PM Studio signing metadata required by the recipe");
|
|
1330
1155
|
}
|
|
1331
1156
|
return;
|
|
1332
1157
|
}
|
|
1333
|
-
const
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
&&
|
|
1339
|
-
|
|
1340
|
-
&&
|
|
1341
|
-
|
|
1342
|
-
|| !/^[0-9a-f]{64}$/.test(after.entitlements_sha256)) {
|
|
1343
|
-
throw setupError("PM_STUDIO_SIGNING_METADATA_CHANGED",
|
|
1344
|
-
"Ad-hoc signing did not produce verifiable PM Studio entitlements from the official source bundle");
|
|
1345
|
-
}
|
|
1346
|
-
before.entitlements_sha256 = after.entitlements_sha256;
|
|
1347
|
-
}
|
|
1348
|
-
if (JSON.stringify(before) !== JSON.stringify(after)) {
|
|
1158
|
+
const sourceSignature = source.codeSign;
|
|
1159
|
+
if (sourceSignature?.displayValid !== true) return;
|
|
1160
|
+
const flagsChanged = sourceSignature.flags
|
|
1161
|
+
&& flagsWithoutAdHoc(sourceSignature.flags) !== flagsWithoutAdHoc(after.flags);
|
|
1162
|
+
const runtimeChanged = sourceSignature.runtimeVersion
|
|
1163
|
+
&& sourceSignature.runtimeVersion !== after.runtime_version;
|
|
1164
|
+
const entitlementsChanged = sourceSignature.entitlementsState === "xml"
|
|
1165
|
+
&& sourceSignature.entitlementsSha256 !== after.entitlements_sha256;
|
|
1166
|
+
if (flagsChanged || runtimeChanged || entitlementsChanged) {
|
|
1349
1167
|
throw setupError("PM_STUDIO_SIGNING_METADATA_CHANGED",
|
|
1350
|
-
"Ad-hoc signing did not preserve PM Studio
|
|
1168
|
+
"Ad-hoc signing did not preserve available PM Studio entitlements, flags, and runtime metadata");
|
|
1351
1169
|
}
|
|
1352
1170
|
}
|
|
1353
1171
|
|
|
@@ -1380,7 +1198,7 @@ function replaceVerifiedStage({ appPath, stagePath, operations }) {
|
|
|
1380
1198
|
}
|
|
1381
1199
|
|
|
1382
1200
|
function recoveryMessage(backup) {
|
|
1383
|
-
return `Verified backup: ${backup.backupAppPath}; manifest: ${backup.manifestPath}. Quit PM Studio, confirm the installed version/build still match, then restore only this version-matched backup or reinstall
|
|
1201
|
+
return `Verified backup: ${backup.backupAppPath}; manifest: ${backup.manifestPath}. Quit PM Studio, confirm the installed version/build still match, then restore only this version-matched backup or reinstall PM Studio.`;
|
|
1384
1202
|
}
|
|
1385
1203
|
|
|
1386
1204
|
export async function runPmStudioSetup({
|
|
@@ -1411,28 +1229,13 @@ export async function runPmStudioSetup({
|
|
|
1411
1229
|
processRunner: operations.processRunner,
|
|
1412
1230
|
};
|
|
1413
1231
|
const metadata = operations.readBundleMetadata(genericPaths);
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
recipes,
|
|
1422
|
-
operations,
|
|
1423
|
-
});
|
|
1424
|
-
} catch (error) {
|
|
1425
|
-
if (error?.code !== "PM_STUDIO_UNSUPPORTED_VERSION") throw error;
|
|
1426
|
-
emit(`[INFO] Verifying PM Studio ${metadata.version} build ${metadata.build} against its official GitHub release before patching.`);
|
|
1427
|
-
try {
|
|
1428
|
-
recipe = await createOfficialCompatibleSourceRecipe({ appPath, metadata, operations });
|
|
1429
|
-
} catch (sourceError) {
|
|
1430
|
-
throw setupError("PM_STUDIO_UNSUPPORTED_VERSION",
|
|
1431
|
-
`PM Studio ${metadata.version} build ${metadata.build} could not be safely verified as a structurally compatible official release; no files were changed`,
|
|
1432
|
-
sourceError?.message ? [sourceError.message] : undefined);
|
|
1433
|
-
}
|
|
1434
|
-
}
|
|
1435
|
-
}
|
|
1232
|
+
const recipe = resolvePmStudioCompatibleRecipe({
|
|
1233
|
+
appPath,
|
|
1234
|
+
backupRoot,
|
|
1235
|
+
metadata,
|
|
1236
|
+
recipes,
|
|
1237
|
+
operations,
|
|
1238
|
+
});
|
|
1436
1239
|
|
|
1437
1240
|
const source = inspectPmStudioApp({ appPath, recipe, operations });
|
|
1438
1241
|
const finalBackupDir = path.join(backupRoot, backupName(recipe));
|
|
@@ -1456,12 +1259,6 @@ export async function runPmStudioSetup({
|
|
|
1456
1259
|
`PM Studio ${recipe.version} build ${recipe.build} does not match the clean, current split-origin, predecessor, or legacy recipe; no files were changed`, source.issues);
|
|
1457
1260
|
}
|
|
1458
1261
|
|
|
1459
|
-
if (source.sourceVerification === "exact-bundle-content") {
|
|
1460
|
-
emit(recipe.sourceArtifact
|
|
1461
|
-
? `[WARN] macOS rejected the vendor signature; setup accepted PM Studio ${recipe.version} only after its files matched the SHA-256-verified official GitHub release.`
|
|
1462
|
-
: `[WARN] macOS rejected the vendor signature; setup accepted only the exact PM Studio ${recipe.version} official bundle content fingerprint.`);
|
|
1463
|
-
}
|
|
1464
|
-
|
|
1465
1262
|
const backupExists = fs.existsSync(finalBackupDir);
|
|
1466
1263
|
let verifiedBackup = null;
|
|
1467
1264
|
if (migratingExistingPatch) {
|
|
@@ -1482,7 +1279,7 @@ export async function runPmStudioSetup({
|
|
|
1482
1279
|
}
|
|
1483
1280
|
assertSpace({ appPath, backupRoot, backupExists, operations });
|
|
1484
1281
|
|
|
1485
|
-
emit("[WARN] PM Studio
|
|
1282
|
+
emit("[WARN] PM Studio will be ad-hoc signed after patching; an update or reinstall may overwrite this patch.");
|
|
1486
1283
|
const backup = verifiedBackup || ensureBackup({ appPath, backupRoot, recipe, operations });
|
|
1487
1284
|
const stagePath = path.join(path.dirname(appPath), `.${path.basename(appPath)}.ccdx-stage-${operations.uuid()}`);
|
|
1488
1285
|
let preserveStage = false;
|
|
@@ -1509,7 +1306,12 @@ export async function runPmStudioSetup({
|
|
|
1509
1306
|
hash: recipe.patchedHeaderSha256,
|
|
1510
1307
|
processRunner: operations.processRunner,
|
|
1511
1308
|
});
|
|
1512
|
-
operations.signApp({
|
|
1309
|
+
operations.signApp({
|
|
1310
|
+
appPath: stagePath,
|
|
1311
|
+
recipe,
|
|
1312
|
+
source: stagedSource,
|
|
1313
|
+
processRunner: operations.processRunner,
|
|
1314
|
+
});
|
|
1513
1315
|
|
|
1514
1316
|
const staged = inspectPmStudioApp({ appPath: stagePath, recipe, operations });
|
|
1515
1317
|
if (staged.state !== "patched") {
|