Skip to content

Commit e281a8c

Browse files
committed
Implement enable/disable-get-task-allow for swift build (#8378)
Rewrite BuildCommandTests.getTaskAllowEntitlement to check for entitlements using codesign. Add debugging entitlement during build process using swift build. Fix wording in warning related to get-task-allow command line options.
1 parent b60528e commit e281a8c

File tree

3 files changed

+145
-89
lines changed

3 files changed

+145
-89
lines changed

Sources/CoreCommands/SwiftCommandState.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -900,7 +900,7 @@ public final class SwiftCommandState {
900900
}
901901

902902
static let entitlementsMacOSWarning = """
903-
`--disable-get-task-allow-entitlement` and `--disable-get-task-allow-entitlement` only have an effect \
903+
`--enable-get-task-allow-entitlement` and `--disable-get-task-allow-entitlement` only have an effect \
904904
when building on macOS.
905905
"""
906906

Sources/SwiftBuildSupport/SwiftBuildSystem.swift

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -119,17 +119,35 @@ func withSession(
119119
}
120120

121121
package final class SwiftBuildSystemPlanningOperationDelegate: SWBPlanningOperationDelegate, SWBIndexingDelegate, Sendable {
122-
package init() {}
123-
122+
private let shouldEnableDebuggingEntitlement: Bool
123+
124+
package init(shouldEnableDebuggingEntitlement: Bool) {
125+
self.shouldEnableDebuggingEntitlement = shouldEnableDebuggingEntitlement
126+
}
127+
124128
public func provisioningTaskInputs(
125129
targetGUID: String,
126130
provisioningSourceData: SWBProvisioningTaskInputsSourceData
127131
) async -> SWBProvisioningTaskInputs {
128132
let identity = provisioningSourceData.signingCertificateIdentifier
129-
if identity == "-" {
130-
let signedEntitlements = provisioningSourceData.entitlementsDestination == "Signature"
131-
? provisioningSourceData.productTypeEntitlements.merging(
132-
["application-identifier": .plString(provisioningSourceData.bundleIdentifier)],
133+
134+
if identity == "-" || identity.isEmpty {
135+
let getTaskAllowEntitlementKey: String
136+
let applicationIdentifierEntitlementKey: String
137+
138+
if provisioningSourceData.sdkRoot.contains("macos") || provisioningSourceData.sdkRoot
139+
.contains("simulator")
140+
{
141+
getTaskAllowEntitlementKey = "com.apple.security.get-task-allow"
142+
applicationIdentifierEntitlementKey = "com.apple.application-identifier"
143+
} else {
144+
getTaskAllowEntitlementKey = "get-task-allow"
145+
applicationIdentifierEntitlementKey = "application-identifier"
146+
}
147+
148+
let signedEntitlements = provisioningSourceData
149+
.entitlementsDestination == "Signature" ? provisioningSourceData.productTypeEntitlements.merging(
150+
[applicationIdentifierEntitlementKey: .plString(provisioningSourceData.bundleIdentifier)],
133151
uniquingKeysWith: { _, new in new }
134152
).merging(provisioningSourceData.projectEntitlements ?? [:], uniquingKeysWith: { _, new in new })
135153
: [:]
@@ -141,6 +159,12 @@ package final class SwiftBuildSystemPlanningOperationDelegate: SWBPlanningOperat
141159
).merging(provisioningSourceData.projectEntitlements ?? [:], uniquingKeysWith: { _, new in new })
142160
: [:]
143161

162+
var additionalEntitlements: [String: SWBPropertyListItem] = [:]
163+
164+
if shouldEnableDebuggingEntitlement {
165+
additionalEntitlements[getTaskAllowEntitlementKey] = .plBool(true)
166+
}
167+
144168
return SWBProvisioningTaskInputs(
145169
identityHash: "-",
146170
identityName: "-",
@@ -149,7 +173,7 @@ package final class SwiftBuildSystemPlanningOperationDelegate: SWBPlanningOperat
149173
profilePath: nil,
150174
designatedRequirements: nil,
151175
signedEntitlements: signedEntitlements.merging(
152-
provisioningSourceData.sdkRoot.contains("simulator") ? ["get-task-allow": .plBool(true)] : [:],
176+
additionalEntitlements,
153177
uniquingKeysWith: { _, new in new }
154178
),
155179
simulatedEntitlements: simulatedEntitlements,
@@ -160,8 +184,6 @@ package final class SwiftBuildSystemPlanningOperationDelegate: SWBPlanningOperat
160184
errors: [],
161185
warnings: []
162186
)
163-
} else if identity.isEmpty {
164-
return SWBProvisioningTaskInputs()
165187
} else {
166188
return SWBProvisioningTaskInputs(
167189
identityHash: "-",
@@ -599,7 +621,9 @@ public final class SwiftBuildSystem: SPMBuildCore.BuildSystem {
599621

600622
let operation = try await session.createBuildOperation(
601623
request: request,
602-
delegate: SwiftBuildSystemPlanningOperationDelegate(),
624+
delegate: SwiftBuildSystemPlanningOperationDelegate(shouldEnableDebuggingEntitlement: self.buildParameters
625+
.debuggingParameters.shouldEnableDebuggingEntitlement
626+
),
603627
retainBuildDescription: true
604628
)
605629

@@ -984,7 +1008,9 @@ public final class SwiftBuildSystem: SPMBuildCore.BuildSystem {
9841008
private static func constructDebuggingSettingsOverrides(from parameters: BuildParameters.Debugging) -> [String: String] {
9851009
var settings: [String: String] = [:]
9861010
// TODO: debugInfoFormat: https://github.com/swiftlang/swift-build/issues/560
987-
// TODO: shouldEnableDebuggingEntitlement: Enable/Disable get-task-allow
1011+
if parameters.shouldEnableDebuggingEntitlement {
1012+
settings["ENTITLEMENTS_DONT_REMOVE_GET_TASK_ALLOW"] = "YES"
1013+
}
9881014
// TODO: omitFramePointer: https://github.com/swiftlang/swift-build/issues/561
9891015
return settings
9901016
}

Tests/CommandsTests/BuildCommandTests.swift

Lines changed: 107 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,21 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
import Foundation
13+
import _InternalTestSupport
1414
import Basics
1515
@testable import Commands
1616
@testable import CoreCommands
17+
import Foundation
1718
import PackageGraph
1819
import PackageLoading
1920
import PackageModel
2021
import enum PackageModel.BuildConfiguration
2122
import SPMBuildCore
22-
import _InternalTestSupport
23+
import enum SWBUtil.PropertyList
24+
import enum SWBUtil.PropertyListItem
25+
import Testing
2326
import TSCTestSupport
2427
import Workspace
25-
import Testing
2628

2729
struct BuildResult {
2830
let binPath: AbsolutePath
@@ -1286,7 +1288,6 @@ struct BuildCommandTestCases {
12861288
}
12871289

12881290
@Test(
1289-
.SWBINTTODO("Test failed because swiftbuild doesn't output precis codesign commands. Once swift run works with swiftbuild the test can be investigated."),
12901291
.tags(
12911292
.Feature.CommandLineArguments.DisableGetTaskAllowEntitlement,
12921293
.Feature.CommandLineArguments.EnableGetTaskAllowEntitlement,
@@ -1295,98 +1296,127 @@ struct BuildCommandTestCases {
12951296
.tags(
12961297
.Feature.CommandLineArguments.BuildSystem,
12971298
),
1298-
arguments: SupportedBuildSystemOnPlatform,
1299+
arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms),
12991300
)
13001301
func getTaskAllowEntitlement(
1301-
buildSystem: BuildSystemProvider.Kind,
1302+
data: BuildData,
13021303
) async throws {
1303-
try await withKnownIssue(isIntermittent: (ProcessInfo.hostOperatingSystem == .linux)) {
1304-
try await fixture(name: "ValidLayouts/SingleModule/ExecutableNew") { fixturePath in
1305-
#if os(macOS)
1306-
// try await building with default parameters. This should succeed. We build verbosely so we get full command
1307-
// lines.
1308-
var buildResult = try await build(["-v"], packagePath: fixturePath, configuration: .debug, buildSystem: buildSystem,)
1309-
1310-
// TODO verification of the ad-hoc code signing can be done by `swift run` of the executable in these cases once swiftbuild build system is working with that
1311-
#expect(buildResult.stdout.contains("codesign --force --sign - --entitlements"))
1312-
1313-
buildResult = try await build(["-v"], packagePath: fixturePath, configuration:.debug, buildSystem: buildSystem,)
1314-
1315-
#expect(buildResult.stdout.contains("codesign --force --sign - --entitlements"))
1316-
1317-
// Build with different combinations of the entitlement flag and debug/release build configurations.
1318-
1319-
buildResult = try await build(
1320-
["--enable-get-task-allow-entitlement", "-v"],
1321-
packagePath: fixturePath,
1322-
configuration: .release,
1323-
buildSystem: buildSystem,
1324-
)
1304+
let buildSystem = data.buildSystem
1305+
let buildConfiguration = data.config
1306+
try await fixture(name: "ValidLayouts/SingleModule/ExecutableNew") { fixturePath in
1307+
#if os(macOS)
1308+
func codesignDisplay(execPath: AbsolutePath) async throws
1309+
-> (AsyncProcessResult.ExitStatus, PropertyListItem?)
1310+
{
1311+
let args = ["codesign", "-d", "--entitlements", "-", "--xml", execPath.pathString]
1312+
let result = try await AsyncProcess.popen(arguments: args)
1313+
let entitlements: PropertyListItem? = if case .success(let output) = result.output,
1314+
!output.isEmpty
1315+
{
1316+
try PropertyList.fromBytes(output)
1317+
} else {
1318+
nil
1319+
}
13251320

1326-
#expect(buildResult.stdout.contains("codesign --force --sign - --entitlements"))
1321+
return (result.exitStatus, entitlements)
1322+
}
13271323

1328-
buildResult = try await build(
1329-
["--enable-get-task-allow-entitlement", "-v"],
1330-
packagePath: fixturePath,
1331-
configuration: .debug,
1332-
buildSystem: buildSystem,
1333-
)
1324+
func verify(entitlements: PropertyListItem?, getTaskAllowRequired: Bool) {
1325+
guard let entitlements, case .plDict(let dict) = entitlements else {
1326+
if getTaskAllowRequired {
1327+
Issue.record("Missing expected entitlements")
1328+
}
1329+
return
1330+
}
13341331

1335-
#expect(buildResult.stdout.contains("codesign --force --sign - --entitlements"))
1332+
if getTaskAllowRequired {
1333+
#expect(dict["com.apple.security.get-task-allow"] == .plBool(true))
1334+
}
1335+
}
13361336

1337-
buildResult = try await build(
1338-
["--disable-get-task-allow-entitlement", "-v"],
1339-
packagePath: fixturePath,
1340-
configuration: .debug,
1341-
buildSystem: buildSystem,
1342-
)
1337+
let execName = "ExecutableNew"
13431338

1344-
#expect(!buildResult.stdout.contains("codesign --force --sign - --entitlements"))
1339+
var buildResult = try await build(
1340+
["-v"],
1341+
packagePath: fixturePath,
1342+
configuration: buildConfiguration,
1343+
cleanAfterward: false,
1344+
buildSystem: buildSystem
1345+
)
1346+
var (
1347+
exitStatus,
1348+
entitlements
1349+
) = try await codesignDisplay(execPath: buildResult.binPath.appending(execName))
13451350

1346-
buildResult = try await build(
1347-
["--disable-get-task-allow-entitlement", "-v"],
1348-
packagePath: fixturePath,
1349-
configuration: .release,
1350-
buildSystem: buildSystem,
1351-
)
1351+
// codesign performs basic verification in display mode, which is enough to confirm ad-hoc signature
1352+
// if verification fails (eg. no signature) termination code will be 1
1353+
// though on Apple Silicon binary will always be signed because linker signs it by default
1354+
#expect(exitStatus == .terminated(code: 0))
1355+
verify(entitlements: entitlements, getTaskAllowRequired: buildConfiguration == .debug)
13521356

1353-
#expect(!buildResult.stdout.contains("codesign --force --sign - --entitlements"))
1354-
#else
1355-
var buildResult = try await build(["-v"], packagePath: fixturePath, configuration: .debug, buildSystem: buildSystem,)
1357+
try await executeSwiftPackage(fixturePath, extraArgs: ["clean"], buildSystem: buildSystem)
13561358

1357-
#expect(!buildResult.stdout.contains("codesign --force --sign - --entitlements"))
1359+
buildResult = try await build(
1360+
["--enable-get-task-allow-entitlement"],
1361+
packagePath: fixturePath,
1362+
configuration: buildConfiguration,
1363+
cleanAfterward: false,
1364+
buildSystem: buildSystem
1365+
)
1366+
(
1367+
exitStatus,
1368+
entitlements
1369+
) = try await codesignDisplay(execPath: buildResult.binPath.appending(execName))
13581370

1359-
buildResult = try await build(["-v"], packagePath: fixturePath, configuration: .release,buildSystem: buildSystem,)
1371+
#expect(exitStatus == .terminated(code: 0))
1372+
verify(entitlements: entitlements, getTaskAllowRequired: true)
13601373

1361-
#expect(!buildResult.stdout.contains("codesign --force --sign - --entitlements"))
1374+
try await executeSwiftPackage(fixturePath, extraArgs: ["clean"], buildSystem: buildSystem)
13621375

1363-
buildResult = try await build(
1364-
["--disable-get-task-allow-entitlement", "-v"],
1365-
packagePath: fixturePath,
1366-
configuration: .release,
1367-
buildSystem: buildSystem,
1368-
)
1376+
buildResult = try await build(
1377+
["--disable-get-task-allow-entitlement"],
1378+
packagePath: fixturePath,
1379+
configuration: buildConfiguration,
1380+
cleanAfterward: false,
1381+
buildSystem: buildSystem
1382+
)
1383+
(
1384+
exitStatus,
1385+
entitlements
1386+
) = try await codesignDisplay(execPath: buildResult.binPath.appending(execName))
1387+
1388+
#expect(exitStatus == .terminated(code: 0))
1389+
verify(entitlements: entitlements, getTaskAllowRequired: false)
1390+
#else
1391+
var buildResult = try await build(
1392+
["-v"],
1393+
packagePath: fixturePath,
1394+
configuration: buildConfiguration,
1395+
buildSystem: buildSystem
1396+
)
13691397

1370-
#expect(!buildResult.stdout.contains("codesign --force --sign - --entitlements"))
1371-
#expect(buildResult.stderr.contains(SwiftCommandState.entitlementsMacOSWarning))
1398+
#expect(!buildResult.stdout.contains("codesign --force --sign - --entitlements"))
13721399

1373-
buildResult = try await build(
1374-
["--enable-get-task-allow-entitlement", "-v"],
1375-
packagePath: fixturePath,
1376-
configuration: .release,
1377-
buildSystem: buildSystem,
1378-
)
1400+
buildResult = try await build(
1401+
["--disable-get-task-allow-entitlement", "-v"],
1402+
packagePath: fixturePath,
1403+
configuration: buildConfiguration,
1404+
buildSystem: buildSystem,
1405+
)
13791406

1380-
#expect(!buildResult.stdout.contains("codesign --force --sign - --entitlements"))
1381-
#expect(buildResult.stderr.contains(SwiftCommandState.entitlementsMacOSWarning))
1382-
#endif
1407+
#expect(!buildResult.stdout.contains("codesign --force --sign - --entitlements"))
1408+
#expect(buildResult.stderr.contains(SwiftCommandState.entitlementsMacOSWarning))
13831409

1384-
buildResult = try await build(["-v"], packagePath: fixturePath, configuration: .release, buildSystem: buildSystem)
1410+
buildResult = try await build(
1411+
["--enable-get-task-allow-entitlement", "-v"],
1412+
packagePath: fixturePath,
1413+
configuration: buildConfiguration,
1414+
buildSystem: buildSystem,
1415+
)
13851416

1386-
#expect(!buildResult.stdout.contains("codesign --force --sign - --entitlements"))
1387-
}
1388-
} when: {
1389-
[.swiftbuild, .xcode].contains(buildSystem) && ProcessInfo.hostOperatingSystem != .linux
1417+
#expect(!buildResult.stdout.contains("codesign --force --sign - --entitlements"))
1418+
#expect(buildResult.stderr.contains(SwiftCommandState.entitlementsMacOSWarning))
1419+
#endif
13901420
}
13911421
}
13921422

0 commit comments

Comments
 (0)