From f910fd1996f3f490000d9e70bbfc081917a82dce Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Fri, 21 Aug 2026 16:53:30 -0700
Subject: [PATCH 1/6] Replace Quill CLI with focused Mach-O tool
---
Herebyfile.mjs | 22 +---
tools/cmd/machotool/main.go | 103 +++++++++++++++
tools/go.mod | 96 +-------------
tools/go.sum | 248 ------------------------------------
4 files changed, 107 insertions(+), 362 deletions(-)
create mode 100644 tools/cmd/machotool/main.go
diff --git a/Herebyfile.mjs b/Herebyfile.mjs
index 444aed6179fb5..30dfa528ea055 100644
--- a/Herebyfile.mjs
+++ b/Herebyfile.mjs
@@ -1523,24 +1523,6 @@ ${entries}
`;
}
-/**
- * @param {string} filePath
- */
-async function verifyTypeScriptMacEntitlements(filePath) {
- const { stdout } = await $pipe`go -C ./tools tool quill describe --quiet --output json ${filePath}`;
- const details = JSON.parse(stdout);
- const entitlements = details[0]?.superBlob?.entitlements?.entitlements;
- if (typeof entitlements !== "string") {
- throw new Error(`Signed file has no macOS entitlements: ${filePath}`);
- }
- for (const entitlement of typescriptMacEntitlements) {
- const escapedEntitlement = entitlement.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
- if (!new RegExp(`\\s*${escapedEntitlement}\\s*\\s*`).test(entitlements)) {
- throw new Error(`Signed file is missing macOS entitlement '${entitlement}': ${filePath}`);
- }
- }
-}
-
/**
* @typedef {"win32" | "linux" | "darwin" | "aix" | "android" | "freebsd" | "netbsd" | "openbsd" | "sunos"} OS
* @typedef {"x64" | "arm" | "arm64" | "ia32" | "ppc64" | "loong64" | "mips64el" | "riscv64" | "s390x"} Arch
@@ -2096,7 +2078,7 @@ async function runSignNativePreviewPackages() {
// along with a notarization step.
for (const p of filelistPaths) {
// ESRP preserves entitlements from an existing ad-hoc signature.
- await $pipe`go -C ./tools tool quill sign --quiet --ad-hoc --identity ${path.basename(p.path)} --entitlements ${typescriptMacEntitlementsPath} ${p.path}`;
+ await $pipe`go -C ./tools run ./cmd/machotool sign ${typescriptMacEntitlementsPath} ${p.path}`;
const unsignedZipPath = path.join(tmp, `${p.tmpName}.unsigned.zip`);
const signedZipPath = path.join(tmp, `${p.tmpName}.signed.zip`);
@@ -2157,7 +2139,7 @@ async function runSignNativePreviewPackages() {
for (const p of macZips) {
await fs.promises.chmod(p.path, 0o755);
- await verifyTypeScriptMacEntitlements(p.path);
+ await $pipe`go -C ./tools run ./cmd/machotool verify ${typescriptMacEntitlementsPath} ${p.path}`;
}
}
}
diff --git a/tools/cmd/machotool/main.go b/tools/cmd/machotool/main.go
new file mode 100644
index 0000000000000..d2a189d73a38b
--- /dev/null
+++ b/tools/cmd/machotool/main.go
@@ -0,0 +1,103 @@
+// Portions of this file are derived from github.com/anchore/quill v0.7.1.
+// The original code is licensed under the Apache License, Version 2.0.
+// This version is modified to support only the operations used by TypeScript.
+
+package main
+
+import (
+ "errors"
+ "fmt"
+ "os"
+ "path/filepath"
+
+ "github.com/anchore/quill/quill/macho"
+ "github.com/anchore/quill/quill/pki"
+ "github.com/anchore/quill/quill/sign"
+ blacktopmacho "github.com/blacktop/go-macho"
+)
+
+func main() {
+ if err := run(os.Args[1:]); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+}
+
+func run(args []string) error {
+ if len(args) != 3 {
+ return errors.New("usage: machotool ")
+ }
+
+ entitlements, err := os.ReadFile(args[1])
+ if err != nil {
+ return fmt.Errorf("read entitlements: %w", err)
+ }
+ switch args[0] {
+ case "sign":
+ return signBinary(args[2], string(entitlements))
+ case "verify":
+ return verifyBinary(args[2], string(entitlements))
+ default:
+ return fmt.Errorf("unknown command %q", args[0])
+ }
+}
+
+func verifyBinary(path, expectedEntitlements string) error {
+ file, err := blacktopmacho.Open(path)
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+
+ codeSignature := file.CodeSignature()
+ if codeSignature == nil || codeSignature.Entitlements == "" {
+ return fmt.Errorf("signed file has no macOS entitlements: %s", path)
+ }
+ if codeSignature.Entitlements != expectedEntitlements {
+ return fmt.Errorf("signed file has unexpected macOS entitlements: %s", path)
+ }
+ return nil
+}
+
+func signBinary(path, entitlements string) error {
+ file, err := macho.NewFile(path)
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+
+ if file.HasCodeSigningCmd() {
+ err = file.RemoveSigningContent()
+ if err != nil {
+ return fmt.Errorf("remove existing signature: %w", err)
+ }
+ }
+ signingMaterial := pki.SigningMaterial{}
+
+ err = file.AddEmptyCodeSigningCmd()
+ if err != nil {
+ return err
+ }
+ identity := filepath.Base(path)
+ superBlobSize, superBlob, err := sign.GenerateSigningSuperBlob(identity, file, signingMaterial, entitlements, 0)
+ if err != nil {
+ return fmt.Errorf("generate initial signature: %w", err)
+ }
+ err = sign.UpdateSuperBlobOffsetReferences(file, uint64(len(superBlob)))
+ if err != nil {
+ return fmt.Errorf("update signature offsets: %w", err)
+ }
+ _, superBlob, err = sign.GenerateSigningSuperBlob(identity, file, signingMaterial, entitlements, superBlobSize)
+ if err != nil {
+ return fmt.Errorf("generate final signature: %w", err)
+ }
+
+ command, _, err := file.CodeSigningCmd()
+ if err != nil {
+ return err
+ }
+ if err := file.Patch(superBlob, len(superBlob), uint64(command.DataOffset)); err != nil {
+ return fmt.Errorf("write signature: %w", err)
+ }
+ return nil
+}
diff --git a/tools/go.mod b/tools/go.mod
index 95eb3e0c70eea..59e6b959ec355 100644
--- a/tools/go.mod
+++ b/tools/go.mod
@@ -3,6 +3,8 @@ module github.com/microsoft/TypeScript/tools
go 1.26
require (
+ github.com/anchore/quill v0.7.1
+ github.com/blacktop/go-macho v1.1.263
github.com/golangci/plugin-module-register v0.1.2
golang.org/x/mod v0.37.0
golang.org/x/tools v0.47.0
@@ -10,110 +12,16 @@ require (
)
require (
- dario.cat/mergo v1.0.2 // indirect
- github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
- github.com/adrg/xdg v0.5.3 // indirect
- github.com/anchore/bubbly v0.0.0-20250717181826-8a411f9d8cbf // indirect
- github.com/anchore/clio v0.0.0-20251028140840-721eea5390a4 // indirect
- github.com/anchore/fangs v0.0.0-20251204220743-df7ac3652565 // indirect
- github.com/anchore/go-homedir v0.0.0-20250319154043-c29668562e4d // indirect
github.com/anchore/go-logger v0.0.0-20251106021608-a5b0513fa9a9 // indirect
github.com/anchore/go-macholibre v0.0.0-20250826193721-3cd206ca93aa // indirect
- github.com/anchore/quill v0.7.1 // indirect
- github.com/atotto/clipboard v0.1.4 // indirect
- github.com/aws/aws-sdk-go-v2 v1.41.3 // indirect
- github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.5 // indirect
- github.com/aws/aws-sdk-go-v2/config v1.32.10 // indirect
- github.com/aws/aws-sdk-go-v2/credentials v1.19.11 // indirect
- github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.19 // indirect
- github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.21.1 // indirect
- github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.19 // indirect
- github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.19 // indirect
- github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect
- github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.18 // indirect
- github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.6 // indirect
- github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.10 // indirect
- github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.19 // indirect
- github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.18 // indirect
- github.com/aws/aws-sdk-go-v2/service/s3 v1.96.2 // indirect
- github.com/aws/aws-sdk-go-v2/service/signin v1.0.7 // indirect
- github.com/aws/aws-sdk-go-v2/service/sso v1.30.12 // indirect
- github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.16 // indirect
- github.com/aws/aws-sdk-go-v2/service/sts v1.41.8 // indirect
- github.com/aws/smithy-go v1.24.2 // indirect
- github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/blacktop/go-dwarf v1.0.14 // indirect
- github.com/blacktop/go-macho v1.1.263 // indirect
- github.com/charmbracelet/bubbles v0.21.0 // indirect
- github.com/charmbracelet/bubbletea v1.3.10 // indirect
- github.com/charmbracelet/colorprofile v0.4.1 // indirect
- github.com/charmbracelet/harmonica v0.2.0 // indirect
- github.com/charmbracelet/lipgloss v1.1.0 // indirect
- github.com/charmbracelet/x/ansi v0.11.3 // indirect
- github.com/charmbracelet/x/cellbuf v0.0.14 // indirect
- github.com/charmbracelet/x/term v0.2.2 // indirect
- github.com/clipperhouse/displaywidth v0.6.2 // indirect
- github.com/clipperhouse/stringish v0.1.1 // indirect
- github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
- github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
- github.com/erikgeiser/promptkit v0.9.0 // indirect
- github.com/felixge/fgprof v0.9.5 // indirect
- github.com/fsnotify/fsnotify v1.9.0 // indirect
- github.com/gabriel-vasile/mimetype v1.4.13 // indirect
github.com/github/smimesign v0.2.0 // indirect
- github.com/go-openapi/errors v0.22.5 // indirect
- github.com/go-openapi/strfmt v0.25.0 // indirect
github.com/go-restruct/restruct v1.2.0-alpha // indirect
- github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
- github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
github.com/google/go-cmp v0.7.0 // indirect
- github.com/google/pprof v0.0.0-20251213031049-b05bdaca462f // indirect
github.com/google/uuid v1.6.0 // indirect
- github.com/gookit/color v1.6.0 // indirect
- github.com/hashicorp/errwrap v1.1.0 // indirect
- github.com/hashicorp/go-multierror v1.1.1 // indirect
- github.com/iancoleman/strcase v0.3.0 // indirect
- github.com/inconshreveable/mousetrap v1.1.0 // indirect
- github.com/jedib0t/go-pretty v4.3.0+incompatible // indirect
- github.com/klauspost/compress v1.18.4 // indirect
- github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
- github.com/mattn/go-colorable v0.1.14 // indirect
- github.com/mattn/go-isatty v0.0.20 // indirect
- github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 // indirect
- github.com/mattn/go-runewidth v0.0.19 // indirect
- github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
- github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
- github.com/muesli/cancelreader v0.2.2 // indirect
- github.com/muesli/reflow v0.3.0 // indirect
- github.com/muesli/termenv v0.16.0 // indirect
- github.com/oklog/ulid v1.3.1 // indirect
- github.com/pborman/indent v1.2.1 // indirect
- github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pkg/errors v0.9.1 // indirect
- github.com/pkg/profile v1.7.0 // indirect
- github.com/rivo/uniseg v0.4.7 // indirect
- github.com/sagikazarmark/locafero v0.12.0 // indirect
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e // indirect
- github.com/sirupsen/logrus v1.9.3 // indirect
- github.com/spf13/afero v1.15.0 // indirect
- github.com/spf13/cast v1.10.0 // indirect
- github.com/spf13/cobra v1.10.2 // indirect
- github.com/spf13/pflag v1.0.10 // indirect
- github.com/spf13/viper v1.21.0 // indirect
- github.com/subosito/gotenv v1.6.0 // indirect
- github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651 // indirect
- github.com/wagoodman/go-progress v0.0.0-20230925121702-07e42b3cdba0 // indirect
- github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
- go.mongodb.org/mongo-driver v1.17.6 // indirect
- go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.53.0 // indirect
- golang.org/x/net v0.56.0 // indirect
golang.org/x/sync v0.21.0 // indirect
- golang.org/x/sys v0.46.0 // indirect
- golang.org/x/term v0.44.0 // indirect
- golang.org/x/text v0.38.0 // indirect
- gopkg.in/yaml.v3 v3.0.1 // indirect
software.sslmate.com/src/go-pkcs12 v0.7.0 // indirect
)
-
-tool github.com/anchore/quill/cmd/quill
diff --git a/tools/go.sum b/tools/go.sum
index 4e02665f65614..370dd96892f4a 100644
--- a/tools/go.sum
+++ b/tools/go.sum
@@ -1,324 +1,76 @@
-dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
-dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
-github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78=
-github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ=
-github.com/anchore/bubbly v0.0.0-20250717181826-8a411f9d8cbf h1:UY7SQkfVVaeGUpPZrJxqmTc8M0ZSWc5ChiKF6I6aL3I=
-github.com/anchore/bubbly v0.0.0-20250717181826-8a411f9d8cbf/go.mod h1:w8Br1ZKk1Nk82YRSh10pcD7LO7avPyFmNnaY1TRPgs0=
-github.com/anchore/clio v0.0.0-20251028140840-721eea5390a4 h1:ou5Co2rLHT5mqVdVmVE7QukGe2cCFTryAaaVzEEDnb0=
-github.com/anchore/clio v0.0.0-20251028140840-721eea5390a4/go.mod h1:Qps+CdrdcMx1k7GhKaJ1ZkKYZvRaIfbNmW6B8/5ONPs=
-github.com/anchore/fangs v0.0.0-20251204220743-df7ac3652565 h1:w7AFkXXSlBSgY/+NbuQIg5qp8hBq2VTliVL1y9p3QzU=
-github.com/anchore/fangs v0.0.0-20251204220743-df7ac3652565/go.mod h1:yYsgDLrzipKlyDAzrjtd35lMcXyZ60b8Ko8vTaMwqoU=
-github.com/anchore/go-homedir v0.0.0-20250319154043-c29668562e4d h1:gT69osH9AsdpOfqxbRwtxcNnSZ1zg4aKy2BevO3ZBdc=
-github.com/anchore/go-homedir v0.0.0-20250319154043-c29668562e4d/go.mod h1:PhSnuFYknwPZkOWKB1jXBNToChBA+l0FjwOxtViIc50=
github.com/anchore/go-logger v0.0.0-20251106021608-a5b0513fa9a9 h1:k+D5tVys8MC+CxfzXIOGm3bZ0FEkmn8udlKACPm4PPo=
github.com/anchore/go-logger v0.0.0-20251106021608-a5b0513fa9a9/go.mod h1:oFuE8YuTCM+spgMXhePGzk3asS94yO9biUfDzVTFqNw=
github.com/anchore/go-macholibre v0.0.0-20250826193721-3cd206ca93aa h1:KPEP8f3enFJeus3Wo51I+riVuCvlf4OEYl2B4IfycbQ=
github.com/anchore/go-macholibre v0.0.0-20250826193721-3cd206ca93aa/go.mod h1:7YJA6tAfRm4SzIF93b32pR4xnbf8g2nJIeQnp+2vzzI=
github.com/anchore/quill v0.7.1 h1:/K2Yvcjs3ipmP+hjxXmy+3IL/nZ0Tnj8S+OLZvtXRYI=
github.com/anchore/quill v0.7.1/go.mod h1:1ExdNP//bdjTRROE/MJg8MxLoDzIJH5HvsbMW4chpBo=
-github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
-github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
-github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
-github.com/aws/aws-sdk-go-v2 v1.41.3 h1:4kQ/fa22KjDt13QCy1+bYADvdgcxpfH18f0zP542kZA=
-github.com/aws/aws-sdk-go-v2 v1.41.3/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=
-github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.5 h1:zWFmPmgw4sveAYi1mRqG+E/g0461cJ5M4bJ8/nc6d3Q=
-github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.5/go.mod h1:nVUlMLVV8ycXSb7mSkcNu9e3v/1TJq2RTlrPwhYWr5c=
-github.com/aws/aws-sdk-go-v2/config v1.32.10 h1:9DMthfO6XWZYLfzZglAgW5Fyou2nRI5CuV44sTedKBI=
-github.com/aws/aws-sdk-go-v2/config v1.32.10/go.mod h1:2rUIOnA2JaiqYmSKYmRJlcMWy6qTj1vuRFscppSBMcw=
-github.com/aws/aws-sdk-go-v2/credentials v1.19.11 h1:NdV8cwCcAXrCWyxArt58BrvZJ9pZ9Fhf9w6Uh5W3Uyc=
-github.com/aws/aws-sdk-go-v2/credentials v1.19.11/go.mod h1:30yY2zqkMPdrvxBqzI9xQCM+WrlrZKSOpSJEsylVU+8=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.19 h1:INUvJxmhdEbVulJYHI061k4TVuS3jzzthNvjqvVvTKM=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.19/go.mod h1:FpZN2QISLdEBWkayloda+sZjVJL+e9Gl0k1SyTgcswU=
-github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.21.1 h1:1hWFp+52Vq8Fevy/KUhbW/1MEApMz7uitCF/PQXRJpk=
-github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.21.1/go.mod h1:sIec8j802/rCkCKgZV678HFR0s7lhQUYXT77tIvlaa4=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.19 h1:/sECfyq2JTifMI2JPyZ4bdRN77zJmr6SrS1eL3augIA=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.19/go.mod h1:dMf8A5oAqr9/oxOfLkC/c2LU/uMcALP0Rgn2BD5LWn0=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.19 h1:AWeJMk33GTBf6J20XJe6qZoRSJo0WfUhsMdUKhoODXE=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.19/go.mod h1:+GWrYoaAsV7/4pNHpwh1kiNLXkKaSoppxQq9lbH8Ejw=
-github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk=
-github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc=
-github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.18 h1:eZioDaZGJ0tMM4gzmkNIO2aAoQd+je7Ug7TkvAzlmkU=
-github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.18/go.mod h1:CCXwUKAJdoWr6/NcxZ+zsiPr6oH/Q5aTooRGYieAyj4=
-github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.6 h1:XAq62tBTJP/85lFD5oqOOe7YYgWxY9LvWq8plyDvDVg=
-github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.6/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI=
-github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.10 h1:fJvQ5mIBVfKtiyx0AHY6HeWcRX5LGANLpq8SVR+Uazs=
-github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.10/go.mod h1:Kzm5e6OmNH8VMkgK9t+ry5jEih4Y8whqs+1hrkxim1I=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.19 h1:X1Tow7suZk9UCJHE1Iw9GMZJJl0dAnKXXP1NaSDHwmw=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.19/go.mod h1:/rARO8psX+4sfjUQXp5LLifjUt8DuATZ31WptNJTyQA=
-github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.18 h1:/A/xDuZAVD2BpsS2fftFRo/NoEKQJ8YTnJDEHBy2Gtg=
-github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.18/go.mod h1:hWe9b4f+djUQGmyiGEeOnZv69dtMSgpDRIvNMvuvzvY=
-github.com/aws/aws-sdk-go-v2/service/s3 v1.96.2 h1:M1A9AjcFwlxTLuf0Faj88L8Iqw0n/AJHjpZTQzMMsSc=
-github.com/aws/aws-sdk-go-v2/service/s3 v1.96.2/go.mod h1:KsdTV6Q9WKUZm2mNJnUFmIoXfZux91M3sr/a4REX8e0=
-github.com/aws/aws-sdk-go-v2/service/signin v1.0.7 h1:Y2cAXlClHsXkkOvWZFXATr34b0hxxloeQu/pAZz2row=
-github.com/aws/aws-sdk-go-v2/service/signin v1.0.7/go.mod h1:idzZ7gmDeqeNrSPkdbtMp9qWMgcBwykA7P7Rzh5DXVU=
-github.com/aws/aws-sdk-go-v2/service/sso v1.30.12 h1:iSsvB9EtQ09YrsmIc44Heqlx5ByGErqhPK1ZQLppias=
-github.com/aws/aws-sdk-go-v2/service/sso v1.30.12/go.mod h1:fEWYKTRGoZNl8tZ77i61/ccwOMJdGxwOhWCkp6TXAr0=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.16 h1:EnUdUqRP1CNzt2DkV67tJx6XDN4xlfBFm+bzeNOQVb0=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.16/go.mod h1:Jic/xv0Rq/pFNCh3WwpH4BEqdbSAl+IyHro8LbibHD8=
-github.com/aws/aws-sdk-go-v2/service/sts v1.41.8 h1:XQTQTF75vnug2TXS8m7CVJfC2nniYPZnO1D4Np761Oo=
-github.com/aws/aws-sdk-go-v2/service/sts v1.41.8/go.mod h1:Xgx+PR1NUOjNmQY+tRMnouRp83JRM8pRMw/vCaVhPkI=
-github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng=
-github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
-github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
-github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/blacktop/go-dwarf v1.0.14 h1:OjmzfSgg/qAKckn2tWFebcgKgJ7HOqCj7bS+CiE1lrY=
github.com/blacktop/go-dwarf v1.0.14/go.mod h1:4W2FKgSFYcZLDwnR7k+apv5i3nrau4NGl9N6VQ9DSTo=
github.com/blacktop/go-macho v1.1.263 h1:wzcLdip1PD2UwOZ5liKijF4YlYbFFF7XdqjLp3VQ1d0=
github.com/blacktop/go-macho v1.1.263/go.mod h1:Hc5E2Lvt/U1VT+jOxr1O5l/LNFJeMYK4eAmDfazTiGc=
github.com/certifi/gocertifi v0.0.0-20180118203423-deb3ae2ef261/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4=
-github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
-github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg=
-github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
-github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
-github.com/charmbracelet/colorprofile v0.4.1 h1:a1lO03qTrSIRaK8c3JRxJDZOvhvIeSco3ej+ngLk1kk=
-github.com/charmbracelet/colorprofile v0.4.1/go.mod h1:U1d9Dljmdf9DLegaJ0nGZNJvoXAhayhmidOdcBwAvKk=
-github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ=
-github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
-github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
-github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
-github.com/charmbracelet/x/ansi v0.11.3 h1:6DcVaqWI82BBVM/atTyq6yBoRLZFBsnoDoX9GCu2YOI=
-github.com/charmbracelet/x/ansi v0.11.3/go.mod h1:yI7Zslym9tCJcedxz5+WBq+eUGMJT0bM06Fqy1/Y4dI=
-github.com/charmbracelet/x/cellbuf v0.0.14 h1:iUEMryGyFTelKW3THW4+FfPgi4fkmKnnaLOXuc+/Kj4=
-github.com/charmbracelet/x/cellbuf v0.0.14/go.mod h1:P447lJl49ywBbil/KjCk2HexGh4tEY9LH0/1QrZZ9rA=
-github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
-github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
-github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
-github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs=
-github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
-github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
-github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
-github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
-github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
-github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
-github.com/clipperhouse/displaywidth v0.6.2 h1:ZDpTkFfpHOKte4RG5O/BOyf3ysnvFswpyYrV7z2uAKo=
-github.com/clipperhouse/displaywidth v0.6.2/go.mod h1:R+kHuzaYWFkTm7xoMmK1lFydbci4X2CicfbGstSGg0o=
-github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
-github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
-github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
-github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
-github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
-github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
-github.com/erikgeiser/promptkit v0.9.0 h1:3qL1mS/ntCrXdb8sTP/ka82CJ9kEQaGuYXNrYJkWYBc=
-github.com/erikgeiser/promptkit v0.9.0/go.mod h1:pU9dtogSe3Jlc2AY77EP7R4WFP/vgD4v+iImC83KsCo=
github.com/fatih/set v0.2.1 h1:nn2CaJyknWE/6txyUDGwysr3G5QC6xWB/PtVjPBbeaA=
github.com/fatih/set v0.2.1/go.mod h1:+RKtMCH+favT2+3YecHGxcc0b4KyVWA1QWWJUs4E0CI=
-github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
-github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY=
-github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM=
-github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
-github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
-github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
-github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
-github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
-github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/github/smimesign v0.2.0 h1:Hho4YcX5N1I9XNqhq0fNx0Sts8MhLonHd+HRXVGNjvk=
github.com/github/smimesign v0.2.0/go.mod h1:iZiiwNT4HbtGRVqCQu7uJPEZCuEE5sfSSttcnePkDl4=
-github.com/gkampitakis/ciinfo v0.3.0 h1:gWZlOC2+RYYttL0hBqcoQhM7h1qNkVqvRCV1fOvpAv8=
-github.com/gkampitakis/ciinfo v0.3.0/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
-github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
-github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk=
-github.com/gkampitakis/go-snaps v0.5.7 h1:uVGjHR4t4pPHU944udMx7VKHpwepZXmvDMF+yDmI0rg=
-github.com/gkampitakis/go-snaps v0.5.7/go.mod h1:ZABkO14uCuVxBHAXAfKG+bqNz+aa1bGPAg8jkI0Nk8Y=
-github.com/go-openapi/errors v0.22.5 h1:Yfv4O/PRYpNF3BNmVkEizcHb3uLVVsrDt3LNdgAKRY4=
-github.com/go-openapi/errors v0.22.5/go.mod h1:z9S8ASTUqx7+CP1Q8dD8ewGH/1JWFFLX/2PmAYNQLgk=
-github.com/go-openapi/strfmt v0.25.0 h1:7R0RX7mbKLa9EYCTHRcCuIPcaqlyQiWNPTXwClK0saQ=
-github.com/go-openapi/strfmt v0.25.0/go.mod h1:nNXct7OzbwrMY9+5tLX4I21pzcmE6ccMGXl3jFdPfn8=
-github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls=
-github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
github.com/go-restruct/restruct v1.2.0-alpha h1:2Lp474S/9660+SJjpVxoKuWX09JsXHSrdV7Nv3/gkvc=
github.com/go-restruct/restruct v1.2.0-alpha/go.mod h1:KqrpKpn4M8OLznErihXTGLlsXFGeLxHUrLRRI/1YjGk=
-github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
-github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
-github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
-github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
-github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
-github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
-github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golangci/plugin-module-register v0.1.2 h1:e5WM6PO6NIAEcij3B053CohVp3HIYbzSuP53UAYgOpg=
github.com/golangci/plugin-module-register v0.1.2/go.mod h1:1+QGTsKBvAIvPvoY/os+G5eoqxWn70HYDm2uvUyGuVw=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
-github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
-github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
-github.com/google/pprof v0.0.0-20251213031049-b05bdaca462f h1:HU1RgM6NALf/KW9HEY6zry3ADbDKcmpQ+hJedoNGQYQ=
-github.com/google/pprof v0.0.0-20251213031049-b05bdaca462f/go.mod h1:67FPmZWbr+KDT/VlpWtw6sO9XSjpJmLuHpoLmWiTGgY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/gookit/assert v0.1.1 h1:lh3GcawXe/p+cU7ESTZ5Ui3Sm/x8JWpIis4/1aF0mY0=
-github.com/gookit/assert v0.1.1/go.mod h1:jS5bmIVQZTIwk42uXl4lyj4iaaxx32tqH16CFj0VX2E=
-github.com/gookit/color v1.2.5/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg=
-github.com/gookit/color v1.6.0 h1:JjJXBTk1ETNyqyilJhkTXJYYigHG24TM9Xa2M1xAhRA=
-github.com/gookit/color v1.6.0/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs=
-github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
-github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
-github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
-github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
-github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
-github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
-github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
-github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
-github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
-github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
-github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
-github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
-github.com/jedib0t/go-pretty v4.3.0+incompatible h1:CGs8AVhEKg/n9YbUenWmNStRW2PHJzaeDodcfvRAbIo=
-github.com/jedib0t/go-pretty v4.3.0+incompatible/go.mod h1:XemHduiw8R651AF9Pt4FwCTKeG3oo7hrHJAoznj9nag=
-github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
-github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=
-github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
-github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
-github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
-github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
-github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
-github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
-github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
-github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
-github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo=
-github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 h1:P8UmIzZMYDR+NGImiFvErt6VWfIRPuGM+vyjiEdkmIw=
-github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
-github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
-github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
-github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
-github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
-github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
-github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
-github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
-github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
-github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
-github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
-github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
-github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
-github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
-github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
-github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
github.com/pborman/getopt v0.0.0-20180811024354-2b5b3bfb099b/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
-github.com/pborman/indent v1.2.1 h1:lFiviAbISHv3Rf0jcuh489bi06hj98JsVMtIDZQb9yM=
-github.com/pborman/indent v1.2.1/go.mod h1:FitS+t35kIYtB5xWTZAPhnmrxcciEEOdbyrrpz5K6Vw=
-github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
-github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
-github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
-github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
-github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
-github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
-github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=
-github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI=
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e h1:7q6NSFZDeGfvvtIRwBrU/aegEYJYmvev0cHAwo17zZQ=
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e/go.mod h1:DkpGd78rljTxKAnTDPFqXSGxvETQnJyuSOQwsHycqfs=
-github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
-github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
-github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
-github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
-github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
-github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
-github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
-github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
-github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
-github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
-github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
-github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
-github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
-github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
-github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
-github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
-github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
-github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
-github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
-github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
-github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
-github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
-github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
-github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651 h1:jIVmlAFIqV3d+DOxazTR9v+zgj8+VYuQBzPgBZvWBHA=
-github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651/go.mod h1:b26F2tHLqaoRQf8DywqzVaV1MQ9yvjb0OMcNl7Nxu20=
-github.com/wagoodman/go-progress v0.0.0-20230925121702-07e42b3cdba0 h1:0KGbf+0SMg+UFy4e1A/CPVvXn21f1qtWdeJwxZFoQG8=
-github.com/wagoodman/go-progress v0.0.0-20230925121702-07e42b3cdba0/go.mod h1:jLXFoL31zFaHKAAyZUh+sxiTDFe1L1ZHrcK2T1itVKA=
-github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
-github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
-go.mongodb.org/mongo-driver v1.17.6 h1:87JUG1wZfWsr6rIz3ZmpH90rL5tea7O3IHuSwHUpsss=
-go.mongodb.org/mongo-driver v1.17.6/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
-go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
-go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
-golang.org/x/exp v0.0.0-20251209150349-8475f28825e9 h1:MDfG8Cvcqlt9XXrmEiD4epKn7VJHZO84hejP9Jmp0MM=
-golang.org/x/exp v0.0.0-20251209150349-8475f28825e9/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=
golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
-golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
-golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q=
golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
-gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
From eb21093c0307a9656dbfafda35107a6e07a0590f Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Fri, 21 Aug 2026 17:41:49 -0700
Subject: [PATCH 2/6] Compare Mach-O entitlements semantically
---
tools/cmd/machotool/main.go | 82 +++++++++++++++++++++++++++++++-
tools/cmd/machotool/main_test.go | 49 +++++++++++++++++++
2 files changed, 129 insertions(+), 2 deletions(-)
create mode 100644 tools/cmd/machotool/main_test.go
diff --git a/tools/cmd/machotool/main.go b/tools/cmd/machotool/main.go
index d2a189d73a38b..ce7d7f33a4c6f 100644
--- a/tools/cmd/machotool/main.go
+++ b/tools/cmd/machotool/main.go
@@ -5,10 +5,13 @@
package main
import (
+ "encoding/xml"
"errors"
"fmt"
+ "io"
"os"
"path/filepath"
+ "strings"
"github.com/anchore/quill/quill/macho"
"github.com/anchore/quill/quill/pki"
@@ -53,12 +56,87 @@ func verifyBinary(path, expectedEntitlements string) error {
if codeSignature == nil || codeSignature.Entitlements == "" {
return fmt.Errorf("signed file has no macOS entitlements: %s", path)
}
- if codeSignature.Entitlements != expectedEntitlements {
- return fmt.Errorf("signed file has unexpected macOS entitlements: %s", path)
+ if err := verifyEntitlements(codeSignature.Entitlements, expectedEntitlements); err != nil {
+ return fmt.Errorf("verify entitlements in %s: %w", path, err)
}
return nil
}
+func verifyEntitlements(actual, expected string) error {
+ expectedValues, err := parseBooleanEntitlements(expected)
+ if err != nil {
+ return fmt.Errorf("parse expected entitlements: %w", err)
+ }
+ if len(expectedValues) == 0 {
+ return errors.New("expected plist contains no boolean entitlements")
+ }
+
+ actualValues, err := parseBooleanEntitlements(actual)
+ if err != nil {
+ return fmt.Errorf("parse signed entitlements: %w", err)
+ }
+ for key, expectedValue := range expectedValues {
+ if actualValue, ok := actualValues[key]; !ok || actualValue != expectedValue {
+ return fmt.Errorf("entitlement %q does not have expected value %t", key, expectedValue)
+ }
+ }
+ return nil
+}
+
+func parseBooleanEntitlements(contents string) (map[string]bool, error) {
+ decoder := xml.NewDecoder(strings.NewReader(contents))
+ values := map[string]bool{}
+ dictDepth := 0
+ foundDict := false
+ pendingKey := ""
+
+ for {
+ token, err := decoder.Token()
+ if errors.Is(err, io.EOF) {
+ break
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ switch token := token.(type) {
+ case xml.StartElement:
+ switch token.Name.Local {
+ case "dict":
+ if dictDepth == 0 {
+ foundDict = true
+ } else if dictDepth == 1 {
+ pendingKey = ""
+ }
+ dictDepth++
+ case "key":
+ if dictDepth == 1 {
+ if err := decoder.DecodeElement(&pendingKey, &token); err != nil {
+ return nil, err
+ }
+ }
+ case "true", "false":
+ if dictDepth == 1 && pendingKey != "" {
+ values[pendingKey] = token.Name.Local == "true"
+ pendingKey = ""
+ }
+ default:
+ if dictDepth == 1 {
+ pendingKey = ""
+ }
+ }
+ case xml.EndElement:
+ if token.Name.Local == "dict" {
+ dictDepth--
+ }
+ }
+ }
+ if !foundDict {
+ return nil, errors.New("plist contains no dictionary")
+ }
+ return values, nil
+}
+
func signBinary(path, entitlements string) error {
file, err := macho.NewFile(path)
if err != nil {
diff --git a/tools/cmd/machotool/main_test.go b/tools/cmd/machotool/main_test.go
new file mode 100644
index 0000000000000..c36bb9c90c34c
--- /dev/null
+++ b/tools/cmd/machotool/main_test.go
@@ -0,0 +1,49 @@
+package main
+
+import "testing"
+
+func TestVerifyEntitlements(t *testing.T) {
+ t.Parallel()
+
+ expected := `
+ first
+ second
+ `
+
+ for _, test := range []struct {
+ name string
+ actual string
+ wantErr bool
+ }{
+ {
+ name: "equivalent formatting and order",
+ actual: `
+
+ second
+
+ extra
+ first
+
+
+ `,
+ },
+ {
+ name: "missing",
+ actual: `first`,
+ wantErr: true,
+ },
+ {
+ name: "wrong value",
+ actual: `firstsecond`,
+ wantErr: true,
+ },
+ } {
+ t.Run(test.name, func(t *testing.T) {
+ t.Parallel()
+ err := verifyEntitlements(test.actual, expected)
+ if (err != nil) != test.wantErr {
+ t.Fatalf("verifyEntitlements() error = %v, wantErr %v", err, test.wantErr)
+ }
+ })
+ }
+}
From 09a19b79b8d5e7aac42547c0f26a0bcd61d47aaa Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Fri, 21 Aug 2026 17:43:36 -0700
Subject: [PATCH 3/6] Simplify Mach-O entitlement verification
---
Herebyfile.mjs | 2 +-
tools/cmd/machotool/main.go | 103 +++++++------------------------
tools/cmd/machotool/main_test.go | 13 ++--
3 files changed, 27 insertions(+), 91 deletions(-)
diff --git a/Herebyfile.mjs b/Herebyfile.mjs
index 30dfa528ea055..013dd88fa9e6d 100644
--- a/Herebyfile.mjs
+++ b/Herebyfile.mjs
@@ -2139,7 +2139,7 @@ async function runSignNativePreviewPackages() {
for (const p of macZips) {
await fs.promises.chmod(p.path, 0o755);
- await $pipe`go -C ./tools run ./cmd/machotool verify ${typescriptMacEntitlementsPath} ${p.path}`;
+ await $pipe`go -C ./tools run ./cmd/machotool verify ${p.path} ${typescriptMacEntitlements}`;
}
}
}
diff --git a/tools/cmd/machotool/main.go b/tools/cmd/machotool/main.go
index ce7d7f33a4c6f..99e56ff97b5ce 100644
--- a/tools/cmd/machotool/main.go
+++ b/tools/cmd/machotool/main.go
@@ -5,13 +5,11 @@
package main
import (
- "encoding/xml"
"errors"
"fmt"
- "io"
"os"
"path/filepath"
- "strings"
+ "regexp"
"github.com/anchore/quill/quill/macho"
"github.com/anchore/quill/quill/pki"
@@ -27,25 +25,31 @@ func main() {
}
func run(args []string) error {
- if len(args) != 3 {
- return errors.New("usage: machotool ")
+ if len(args) == 0 {
+ return errors.New("usage: machotool sign | machotool verify ...")
}
- entitlements, err := os.ReadFile(args[1])
- if err != nil {
- return fmt.Errorf("read entitlements: %w", err)
- }
switch args[0] {
case "sign":
+ if len(args) != 3 {
+ return errors.New("usage: machotool sign ")
+ }
+ entitlements, err := os.ReadFile(args[1])
+ if err != nil {
+ return fmt.Errorf("read entitlements: %w", err)
+ }
return signBinary(args[2], string(entitlements))
case "verify":
- return verifyBinary(args[2], string(entitlements))
+ if len(args) < 3 {
+ return errors.New("usage: machotool verify ...")
+ }
+ return verifyBinary(args[1], args[2:])
default:
return fmt.Errorf("unknown command %q", args[0])
}
}
-func verifyBinary(path, expectedEntitlements string) error {
+func verifyBinary(path string, requiredEntitlements []string) error {
file, err := blacktopmacho.Open(path)
if err != nil {
return err
@@ -56,87 +60,22 @@ func verifyBinary(path, expectedEntitlements string) error {
if codeSignature == nil || codeSignature.Entitlements == "" {
return fmt.Errorf("signed file has no macOS entitlements: %s", path)
}
- if err := verifyEntitlements(codeSignature.Entitlements, expectedEntitlements); err != nil {
+ if err := verifyEntitlements(codeSignature.Entitlements, requiredEntitlements); err != nil {
return fmt.Errorf("verify entitlements in %s: %w", path, err)
}
return nil
}
-func verifyEntitlements(actual, expected string) error {
- expectedValues, err := parseBooleanEntitlements(expected)
- if err != nil {
- return fmt.Errorf("parse expected entitlements: %w", err)
- }
- if len(expectedValues) == 0 {
- return errors.New("expected plist contains no boolean entitlements")
- }
-
- actualValues, err := parseBooleanEntitlements(actual)
- if err != nil {
- return fmt.Errorf("parse signed entitlements: %w", err)
- }
- for key, expectedValue := range expectedValues {
- if actualValue, ok := actualValues[key]; !ok || actualValue != expectedValue {
- return fmt.Errorf("entitlement %q does not have expected value %t", key, expectedValue)
+func verifyEntitlements(actual string, required []string) error {
+ for _, entitlement := range required {
+ pattern := `\s*` + regexp.QuoteMeta(entitlement) + `\s*\s*(|\s*)`
+ if !regexp.MustCompile(pattern).MatchString(actual) {
+ return fmt.Errorf("required entitlement %q is not enabled", entitlement)
}
}
return nil
}
-func parseBooleanEntitlements(contents string) (map[string]bool, error) {
- decoder := xml.NewDecoder(strings.NewReader(contents))
- values := map[string]bool{}
- dictDepth := 0
- foundDict := false
- pendingKey := ""
-
- for {
- token, err := decoder.Token()
- if errors.Is(err, io.EOF) {
- break
- }
- if err != nil {
- return nil, err
- }
-
- switch token := token.(type) {
- case xml.StartElement:
- switch token.Name.Local {
- case "dict":
- if dictDepth == 0 {
- foundDict = true
- } else if dictDepth == 1 {
- pendingKey = ""
- }
- dictDepth++
- case "key":
- if dictDepth == 1 {
- if err := decoder.DecodeElement(&pendingKey, &token); err != nil {
- return nil, err
- }
- }
- case "true", "false":
- if dictDepth == 1 && pendingKey != "" {
- values[pendingKey] = token.Name.Local == "true"
- pendingKey = ""
- }
- default:
- if dictDepth == 1 {
- pendingKey = ""
- }
- }
- case xml.EndElement:
- if token.Name.Local == "dict" {
- dictDepth--
- }
- }
- }
- if !foundDict {
- return nil, errors.New("plist contains no dictionary")
- }
- return values, nil
-}
-
func signBinary(path, entitlements string) error {
file, err := macho.NewFile(path)
if err != nil {
diff --git a/tools/cmd/machotool/main_test.go b/tools/cmd/machotool/main_test.go
index c36bb9c90c34c..567f2bf88c810 100644
--- a/tools/cmd/machotool/main_test.go
+++ b/tools/cmd/machotool/main_test.go
@@ -5,10 +5,7 @@ import "testing"
func TestVerifyEntitlements(t *testing.T) {
t.Parallel()
- expected := `
- first
- second
- `
+ required := []string{"first", "second"}
for _, test := range []struct {
name string
@@ -20,7 +17,7 @@ func TestVerifyEntitlements(t *testing.T) {
actual: `
second
-
+
extra
first
@@ -33,14 +30,14 @@ func TestVerifyEntitlements(t *testing.T) {
wantErr: true,
},
{
- name: "wrong value",
- actual: `firstsecond`,
+ name: "disabled",
+ actual: `firstsecond`,
wantErr: true,
},
} {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
- err := verifyEntitlements(test.actual, expected)
+ err := verifyEntitlements(test.actual, required)
if (err != nil) != test.wantErr {
t.Fatalf("verifyEntitlements() error = %v, wantErr %v", err, test.wantErr)
}
From d6274d701533203012ebb5272bf7e986d841eb7d Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Fri, 21 Aug 2026 19:47:12 -0700
Subject: [PATCH 4/6] Parse Mach-O entitlements as XML
---
Herebyfile.mjs | 2 +-
tools/cmd/machotool/main.go | 84 ++++++++++++++++++++++++--------
tools/cmd/machotool/main_test.go | 19 +++++++-
3 files changed, 81 insertions(+), 24 deletions(-)
diff --git a/Herebyfile.mjs b/Herebyfile.mjs
index 013dd88fa9e6d..30dfa528ea055 100644
--- a/Herebyfile.mjs
+++ b/Herebyfile.mjs
@@ -2139,7 +2139,7 @@ async function runSignNativePreviewPackages() {
for (const p of macZips) {
await fs.promises.chmod(p.path, 0o755);
- await $pipe`go -C ./tools run ./cmd/machotool verify ${p.path} ${typescriptMacEntitlements}`;
+ await $pipe`go -C ./tools run ./cmd/machotool verify ${typescriptMacEntitlementsPath} ${p.path}`;
}
}
}
diff --git a/tools/cmd/machotool/main.go b/tools/cmd/machotool/main.go
index 99e56ff97b5ce..b8e7073569e77 100644
--- a/tools/cmd/machotool/main.go
+++ b/tools/cmd/machotool/main.go
@@ -5,11 +5,12 @@
package main
import (
+ "encoding/xml"
"errors"
"fmt"
"os"
"path/filepath"
- "regexp"
+ "strings"
"github.com/anchore/quill/quill/macho"
"github.com/anchore/quill/quill/pki"
@@ -25,31 +26,25 @@ func main() {
}
func run(args []string) error {
- if len(args) == 0 {
- return errors.New("usage: machotool sign | machotool verify ...")
+ if len(args) != 3 {
+ return errors.New("usage: machotool ")
}
+ entitlements, err := os.ReadFile(args[1])
+ if err != nil {
+ return fmt.Errorf("read entitlements: %w", err)
+ }
switch args[0] {
case "sign":
- if len(args) != 3 {
- return errors.New("usage: machotool sign ")
- }
- entitlements, err := os.ReadFile(args[1])
- if err != nil {
- return fmt.Errorf("read entitlements: %w", err)
- }
return signBinary(args[2], string(entitlements))
case "verify":
- if len(args) < 3 {
- return errors.New("usage: machotool verify ...")
- }
- return verifyBinary(args[1], args[2:])
+ return verifyBinary(args[2], string(entitlements))
default:
return fmt.Errorf("unknown command %q", args[0])
}
}
-func verifyBinary(path string, requiredEntitlements []string) error {
+func verifyBinary(path, expectedEntitlements string) error {
file, err := blacktopmacho.Open(path)
if err != nil {
return err
@@ -60,17 +55,64 @@ func verifyBinary(path string, requiredEntitlements []string) error {
if codeSignature == nil || codeSignature.Entitlements == "" {
return fmt.Errorf("signed file has no macOS entitlements: %s", path)
}
- if err := verifyEntitlements(codeSignature.Entitlements, requiredEntitlements); err != nil {
+ if err := verifyEntitlements(codeSignature.Entitlements, expectedEntitlements); err != nil {
return fmt.Errorf("verify entitlements in %s: %w", path, err)
}
return nil
}
-func verifyEntitlements(actual string, required []string) error {
- for _, entitlement := range required {
- pattern := `\s*` + regexp.QuoteMeta(entitlement) + `\s*\s*(|\s*)`
- if !regexp.MustCompile(pattern).MatchString(actual) {
- return fmt.Errorf("required entitlement %q is not enabled", entitlement)
+type plistElement struct {
+ XMLName xml.Name `xml:""`
+ Value string `xml:",chardata"`
+}
+
+type entitlementsPlist struct {
+ XMLName xml.Name `xml:"plist"`
+ Dict struct {
+ Elements []plistElement `xml:",any"`
+ } `xml:"dict"`
+}
+
+func parseBooleanEntitlements(contents string) (map[string]bool, error) {
+ var plist entitlementsPlist
+ if err := xml.Unmarshal([]byte(contents), &plist); err != nil {
+ return nil, err
+ }
+
+ entitlements := map[string]bool{}
+ key := ""
+ for _, element := range plist.Dict.Elements {
+ switch element.XMLName.Local {
+ case "key":
+ key = strings.TrimSpace(element.Value)
+ case "true", "false":
+ if key != "" {
+ entitlements[key] = element.XMLName.Local == "true"
+ }
+ key = ""
+ default:
+ key = ""
+ }
+ }
+ return entitlements, nil
+}
+
+func verifyEntitlements(actual, expected string) error {
+ expectedValues, err := parseBooleanEntitlements(expected)
+ if err != nil {
+ return fmt.Errorf("parse expected entitlements: %w", err)
+ }
+ if len(expectedValues) == 0 {
+ return errors.New("expected plist contains no boolean entitlements")
+ }
+
+ actualValues, err := parseBooleanEntitlements(actual)
+ if err != nil {
+ return fmt.Errorf("parse signed entitlements: %w", err)
+ }
+ for key, expectedValue := range expectedValues {
+ if actualValue, ok := actualValues[key]; !ok || actualValue != expectedValue {
+ return fmt.Errorf("entitlement %q does not have expected value %t", key, expectedValue)
}
}
return nil
diff --git a/tools/cmd/machotool/main_test.go b/tools/cmd/machotool/main_test.go
index 567f2bf88c810..8409825d9df56 100644
--- a/tools/cmd/machotool/main_test.go
+++ b/tools/cmd/machotool/main_test.go
@@ -5,7 +5,12 @@ import "testing"
func TestVerifyEntitlements(t *testing.T) {
t.Parallel()
- required := []string{"first", "second"}
+ expected := `
+
+
+ first
+ second
+ `
for _, test := range []struct {
name string
@@ -37,10 +42,20 @@ func TestVerifyEntitlements(t *testing.T) {
} {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
- err := verifyEntitlements(test.actual, required)
+ err := verifyEntitlements(test.actual, expected)
if (err != nil) != test.wantErr {
t.Fatalf("verifyEntitlements() error = %v, wantErr %v", err, test.wantErr)
}
})
}
}
+
+func TestVerifyEntitlementsRejectsNestedValue(t *testing.T) {
+ t.Parallel()
+
+ expected := `first`
+ actual := `otherfirst`
+ if err := verifyEntitlements(actual, expected); err == nil {
+ t.Fatal("verifyEntitlements() succeeded for a nested entitlement")
+ }
+}
From 3473bdc07530864575f3c36f204508aff7ff3108 Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Fri, 21 Aug 2026 21:38:32 -0700
Subject: [PATCH 5/6] Keep macOS entitlements in Mach-O tool
---
Herebyfile.mjs | 24 ++----------------------
tools/cmd/machotool/main.go | 25 ++++++++++++++++---------
tools/cmd/machotool/main_test.go | 17 +++++------------
3 files changed, 23 insertions(+), 43 deletions(-)
diff --git a/Herebyfile.mjs b/Herebyfile.mjs
index 30dfa528ea055..7aeb135283de5 100644
--- a/Herebyfile.mjs
+++ b/Herebyfile.mjs
@@ -1506,23 +1506,6 @@ const mainNativePreviewPackage = {
npmTarball: path.join(builtNpm, publishAsTypescript ? "typescript.tgz" : "native-preview.tgz"),
};
-const typescriptMacEntitlements = [
- "com.apple.security.cs.allow-dyld-environment-variables",
- "com.apple.security.cs.disable-library-validation",
-];
-
-function createTypeScriptMacEntitlementsPlist() {
- const entries = typescriptMacEntitlements.map(entitlement => ` ${entitlement}\n `).join("\n");
- return `
-
-
-
-${entries}
-
-
-`;
-}
-
/**
* @typedef {"win32" | "linux" | "darwin" | "aix" | "android" | "freebsd" | "netbsd" | "openbsd" | "sunos"} OS
* @typedef {"x64" | "arm" | "arm64" | "ia32" | "ppc64" | "loong64" | "mips64el" | "riscv64" | "s390x"} Arch
@@ -2044,9 +2027,6 @@ async function runSignNativePreviewPackages() {
}
const tmp = await getSignTempDir();
- const typescriptMacEntitlementsPath = path.join(tmp, "typescript-macos-entitlements.plist");
- await fs.promises.writeFile(typescriptMacEntitlementsPath, createTypeScriptMacEntitlementsPlist());
-
/** @type {DDSignFileList} */
const filelist = {
SignFileRecordList: [],
@@ -2078,7 +2058,7 @@ async function runSignNativePreviewPackages() {
// along with a notarization step.
for (const p of filelistPaths) {
// ESRP preserves entitlements from an existing ad-hoc signature.
- await $pipe`go -C ./tools run ./cmd/machotool sign ${typescriptMacEntitlementsPath} ${p.path}`;
+ await $pipe`go -C ./tools run ./cmd/machotool sign ${p.path}`;
const unsignedZipPath = path.join(tmp, `${p.tmpName}.unsigned.zip`);
const signedZipPath = path.join(tmp, `${p.tmpName}.signed.zip`);
@@ -2139,7 +2119,7 @@ async function runSignNativePreviewPackages() {
for (const p of macZips) {
await fs.promises.chmod(p.path, 0o755);
- await $pipe`go -C ./tools run ./cmd/machotool verify ${typescriptMacEntitlementsPath} ${p.path}`;
+ await $pipe`go -C ./tools run ./cmd/machotool verify ${p.path}`;
}
}
}
diff --git a/tools/cmd/machotool/main.go b/tools/cmd/machotool/main.go
index b8e7073569e77..b25ece27837f8 100644
--- a/tools/cmd/machotool/main.go
+++ b/tools/cmd/machotool/main.go
@@ -18,6 +18,18 @@ import (
blacktopmacho "github.com/blacktop/go-macho"
)
+const typescriptMacEntitlements = `
+
+
+
+ com.apple.security.cs.allow-dyld-environment-variables
+
+ com.apple.security.cs.disable-library-validation
+
+
+
+`
+
func main() {
if err := run(os.Args[1:]); err != nil {
fmt.Fprintln(os.Stderr, err)
@@ -26,19 +38,14 @@ func main() {
}
func run(args []string) error {
- if len(args) != 3 {
- return errors.New("usage: machotool ")
- }
-
- entitlements, err := os.ReadFile(args[1])
- if err != nil {
- return fmt.Errorf("read entitlements: %w", err)
+ if len(args) != 2 {
+ return errors.New("usage: machotool ")
}
switch args[0] {
case "sign":
- return signBinary(args[2], string(entitlements))
+ return signBinary(args[1], typescriptMacEntitlements)
case "verify":
- return verifyBinary(args[2], string(entitlements))
+ return verifyBinary(args[1], typescriptMacEntitlements)
default:
return fmt.Errorf("unknown command %q", args[0])
}
diff --git a/tools/cmd/machotool/main_test.go b/tools/cmd/machotool/main_test.go
index 8409825d9df56..510c58b082418 100644
--- a/tools/cmd/machotool/main_test.go
+++ b/tools/cmd/machotool/main_test.go
@@ -5,13 +5,6 @@ import "testing"
func TestVerifyEntitlements(t *testing.T) {
t.Parallel()
- expected := `
-
-
- first
- second
- `
-
for _, test := range []struct {
name string
actual string
@@ -21,28 +14,28 @@ func TestVerifyEntitlements(t *testing.T) {
name: "equivalent formatting and order",
actual: `
- second
+ com.apple.security.cs.disable-library-validation
extra
- first
+ com.apple.security.cs.allow-dyld-environment-variables
`,
},
{
name: "missing",
- actual: `first`,
+ actual: `com.apple.security.cs.allow-dyld-environment-variables`,
wantErr: true,
},
{
name: "disabled",
- actual: `firstsecond`,
+ actual: `com.apple.security.cs.allow-dyld-environment-variablescom.apple.security.cs.disable-library-validation`,
wantErr: true,
},
} {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
- err := verifyEntitlements(test.actual, expected)
+ err := verifyEntitlements(test.actual, typescriptMacEntitlements)
if (err != nil) != test.wantErr {
t.Fatalf("verifyEntitlements() error = %v, wantErr %v", err, test.wantErr)
}
From 9a01f7153558170e7e0c9c9d6ad7681a6e7640e7 Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Fri, 21 Aug 2026 22:22:03 -0700
Subject: [PATCH 6/6] Revert "Keep macOS entitlements in Mach-O tool"
This reverts commit 3473bdc07530864575f3c36f204508aff7ff3108.
---
Herebyfile.mjs | 24 ++++++++++++++++++++++--
tools/cmd/machotool/main.go | 25 +++++++++----------------
tools/cmd/machotool/main_test.go | 17 ++++++++++++-----
3 files changed, 43 insertions(+), 23 deletions(-)
diff --git a/Herebyfile.mjs b/Herebyfile.mjs
index 7aeb135283de5..30dfa528ea055 100644
--- a/Herebyfile.mjs
+++ b/Herebyfile.mjs
@@ -1506,6 +1506,23 @@ const mainNativePreviewPackage = {
npmTarball: path.join(builtNpm, publishAsTypescript ? "typescript.tgz" : "native-preview.tgz"),
};
+const typescriptMacEntitlements = [
+ "com.apple.security.cs.allow-dyld-environment-variables",
+ "com.apple.security.cs.disable-library-validation",
+];
+
+function createTypeScriptMacEntitlementsPlist() {
+ const entries = typescriptMacEntitlements.map(entitlement => ` ${entitlement}\n `).join("\n");
+ return `
+
+
+
+${entries}
+
+
+`;
+}
+
/**
* @typedef {"win32" | "linux" | "darwin" | "aix" | "android" | "freebsd" | "netbsd" | "openbsd" | "sunos"} OS
* @typedef {"x64" | "arm" | "arm64" | "ia32" | "ppc64" | "loong64" | "mips64el" | "riscv64" | "s390x"} Arch
@@ -2027,6 +2044,9 @@ async function runSignNativePreviewPackages() {
}
const tmp = await getSignTempDir();
+ const typescriptMacEntitlementsPath = path.join(tmp, "typescript-macos-entitlements.plist");
+ await fs.promises.writeFile(typescriptMacEntitlementsPath, createTypeScriptMacEntitlementsPlist());
+
/** @type {DDSignFileList} */
const filelist = {
SignFileRecordList: [],
@@ -2058,7 +2078,7 @@ async function runSignNativePreviewPackages() {
// along with a notarization step.
for (const p of filelistPaths) {
// ESRP preserves entitlements from an existing ad-hoc signature.
- await $pipe`go -C ./tools run ./cmd/machotool sign ${p.path}`;
+ await $pipe`go -C ./tools run ./cmd/machotool sign ${typescriptMacEntitlementsPath} ${p.path}`;
const unsignedZipPath = path.join(tmp, `${p.tmpName}.unsigned.zip`);
const signedZipPath = path.join(tmp, `${p.tmpName}.signed.zip`);
@@ -2119,7 +2139,7 @@ async function runSignNativePreviewPackages() {
for (const p of macZips) {
await fs.promises.chmod(p.path, 0o755);
- await $pipe`go -C ./tools run ./cmd/machotool verify ${p.path}`;
+ await $pipe`go -C ./tools run ./cmd/machotool verify ${typescriptMacEntitlementsPath} ${p.path}`;
}
}
}
diff --git a/tools/cmd/machotool/main.go b/tools/cmd/machotool/main.go
index b25ece27837f8..b8e7073569e77 100644
--- a/tools/cmd/machotool/main.go
+++ b/tools/cmd/machotool/main.go
@@ -18,18 +18,6 @@ import (
blacktopmacho "github.com/blacktop/go-macho"
)
-const typescriptMacEntitlements = `
-
-
-
- com.apple.security.cs.allow-dyld-environment-variables
-
- com.apple.security.cs.disable-library-validation
-
-
-
-`
-
func main() {
if err := run(os.Args[1:]); err != nil {
fmt.Fprintln(os.Stderr, err)
@@ -38,14 +26,19 @@ func main() {
}
func run(args []string) error {
- if len(args) != 2 {
- return errors.New("usage: machotool ")
+ if len(args) != 3 {
+ return errors.New("usage: machotool ")
+ }
+
+ entitlements, err := os.ReadFile(args[1])
+ if err != nil {
+ return fmt.Errorf("read entitlements: %w", err)
}
switch args[0] {
case "sign":
- return signBinary(args[1], typescriptMacEntitlements)
+ return signBinary(args[2], string(entitlements))
case "verify":
- return verifyBinary(args[1], typescriptMacEntitlements)
+ return verifyBinary(args[2], string(entitlements))
default:
return fmt.Errorf("unknown command %q", args[0])
}
diff --git a/tools/cmd/machotool/main_test.go b/tools/cmd/machotool/main_test.go
index 510c58b082418..8409825d9df56 100644
--- a/tools/cmd/machotool/main_test.go
+++ b/tools/cmd/machotool/main_test.go
@@ -5,6 +5,13 @@ import "testing"
func TestVerifyEntitlements(t *testing.T) {
t.Parallel()
+ expected := `
+
+
+ first
+ second
+ `
+
for _, test := range []struct {
name string
actual string
@@ -14,28 +21,28 @@ func TestVerifyEntitlements(t *testing.T) {
name: "equivalent formatting and order",
actual: `
- com.apple.security.cs.disable-library-validation
+ second
extra
- com.apple.security.cs.allow-dyld-environment-variables
+ first
`,
},
{
name: "missing",
- actual: `com.apple.security.cs.allow-dyld-environment-variables`,
+ actual: `first`,
wantErr: true,
},
{
name: "disabled",
- actual: `com.apple.security.cs.allow-dyld-environment-variablescom.apple.security.cs.disable-library-validation`,
+ actual: `firstsecond`,
wantErr: true,
},
} {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
- err := verifyEntitlements(test.actual, typescriptMacEntitlements)
+ err := verifyEntitlements(test.actual, expected)
if (err != nil) != test.wantErr {
t.Fatalf("verifyEntitlements() error = %v, wantErr %v", err, test.wantErr)
}