Skip to content

Commit cf0eb29

Browse files
authored
[Collections] Tighten import conditions (#3296)
Some `Security` framework APIs are available on macOS only
1 parent 5faf3b1 commit cf0eb29

19 files changed

+233
-156
lines changed

Sources/PackageCollections/API.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,4 +182,6 @@ public enum PackageCollectionError: Equatable, Error {
182182
case invalidSignature
183183

184184
case missingSignature
185+
186+
case unsupportedPlatform
185187
}

Sources/PackageCollections/PackageCollections.swift

Lines changed: 68 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ import TSCBasic
1414

1515
// TODO: is there a better name? this conflicts with the module name which is okay in this case but not ideal in Swift
1616
public struct PackageCollections: PackageCollectionsProtocol {
17+
// Check JSONPackageCollectionProvider.isSignatureCheckSupported before updating or removing this
18+
#if os(macOS) || os(Linux) || os(Windows)
19+
static let isSupportedPlatform = true
20+
#else
21+
static let isSupportedPlatform = false
22+
#endif
23+
1724
private let configuration: Configuration
1825
private let diagnosticsEngine: DiagnosticsEngine?
1926
private let storageContainer: (storage: Storage, owned: Bool)
@@ -64,6 +71,10 @@ public struct PackageCollections: PackageCollectionsProtocol {
6471

6572
public func listCollections(identifiers: Set<PackageCollectionsModel.CollectionIdentifier>? = nil,
6673
callback: @escaping (Result<[PackageCollectionsModel.Collection], Error>) -> Void) {
74+
guard Self.isSupportedPlatform else {
75+
return callback(.failure(PackageCollectionError.unsupportedPlatform))
76+
}
77+
6778
self.storage.sources.list { result in
6879
switch result {
6980
case .failure(let error):
@@ -115,6 +126,10 @@ public struct PackageCollections: PackageCollectionsProtocol {
115126
}
116127

117128
public func refreshCollections(callback: @escaping (Result<[PackageCollectionsModel.CollectionSource], Error>) -> Void) {
129+
guard Self.isSupportedPlatform else {
130+
return callback(.failure(PackageCollectionError.unsupportedPlatform))
131+
}
132+
118133
self.storage.sources.list { result in
119134
switch result {
120135
case .failure(let error):
@@ -137,17 +152,23 @@ public struct PackageCollections: PackageCollectionsProtocol {
137152
}
138153
}
139154

140-
public func refreshCollection(
141-
_ source: PackageCollectionsModel.CollectionSource,
142-
callback: @escaping (Result<PackageCollectionsModel.Collection, Error>) -> Void
143-
) {
155+
public func refreshCollection(_ source: PackageCollectionsModel.CollectionSource,
156+
callback: @escaping (Result<PackageCollectionsModel.Collection, Error>) -> Void) {
157+
guard Self.isSupportedPlatform else {
158+
return callback(.failure(PackageCollectionError.unsupportedPlatform))
159+
}
160+
144161
self.refreshCollectionFromSource(source: source, trustConfirmationProvider: nil, callback: callback)
145162
}
146163

147164
public func addCollection(_ source: PackageCollectionsModel.CollectionSource,
148165
order: Int? = nil,
149166
trustConfirmationProvider: ((PackageCollectionsModel.Collection, @escaping (Bool) -> Void) -> Void)? = nil,
150167
callback: @escaping (Result<PackageCollectionsModel.Collection, Error>) -> Void) {
168+
guard Self.isSupportedPlatform else {
169+
return callback(.failure(PackageCollectionError.unsupportedPlatform))
170+
}
171+
151172
if let errors = source.validate()?.errors() {
152173
return callback(.failure(MultipleErrors(errors)))
153174
}
@@ -182,6 +203,10 @@ public struct PackageCollections: PackageCollectionsProtocol {
182203

183204
public func removeCollection(_ source: PackageCollectionsModel.CollectionSource,
184205
callback: @escaping (Result<Void, Error>) -> Void) {
206+
guard Self.isSupportedPlatform else {
207+
return callback(.failure(PackageCollectionError.unsupportedPlatform))
208+
}
209+
185210
self.storage.sources.remove(source: source) { result in
186211
switch result {
187212
case .failure(let error):
@@ -195,11 +220,19 @@ public struct PackageCollections: PackageCollectionsProtocol {
195220
public func moveCollection(_ source: PackageCollectionsModel.CollectionSource,
196221
to order: Int,
197222
callback: @escaping (Result<Void, Error>) -> Void) {
223+
guard Self.isSupportedPlatform else {
224+
return callback(.failure(PackageCollectionError.unsupportedPlatform))
225+
}
226+
198227
self.storage.sources.move(source: source, to: order, callback: callback)
199228
}
200229

201230
public func updateCollection(_ source: PackageCollectionsModel.CollectionSource,
202231
callback: @escaping (Result<PackageCollectionsModel.Collection, Error>) -> Void) {
232+
guard Self.isSupportedPlatform else {
233+
return callback(.failure(PackageCollectionError.unsupportedPlatform))
234+
}
235+
203236
self.storage.sources.update(source: source) { result in
204237
switch result {
205238
case .failure(let error):
@@ -215,6 +248,10 @@ public struct PackageCollections: PackageCollectionsProtocol {
215248
// If not found locally (storage), the collection will be fetched from the source.
216249
public func getCollection(_ source: PackageCollectionsModel.CollectionSource,
217250
callback: @escaping (Result<PackageCollectionsModel.Collection, Error>) -> Void) {
251+
guard Self.isSupportedPlatform else {
252+
return callback(.failure(PackageCollectionError.unsupportedPlatform))
253+
}
254+
218255
if let errors = source.validate()?.errors() {
219256
return callback(.failure(MultipleErrors(errors)))
220257
}
@@ -234,11 +271,13 @@ public struct PackageCollections: PackageCollectionsProtocol {
234271

235272
// MARK: - Packages
236273

237-
public func findPackages(
238-
_ query: String,
239-
collections: Set<PackageCollectionsModel.CollectionIdentifier>? = nil,
240-
callback: @escaping (Result<PackageCollectionsModel.PackageSearchResult, Error>) -> Void
241-
) {
274+
public func findPackages(_ query: String,
275+
collections: Set<PackageCollectionsModel.CollectionIdentifier>? = nil,
276+
callback: @escaping (Result<PackageCollectionsModel.PackageSearchResult, Error>) -> Void) {
277+
guard Self.isSupportedPlatform else {
278+
return callback(.failure(PackageCollectionError.unsupportedPlatform))
279+
}
280+
242281
self.storage.sources.list { result in
243282
switch result {
244283
case .failure(let error):
@@ -257,6 +296,10 @@ public struct PackageCollections: PackageCollectionsProtocol {
257296

258297
public func getPackageMetadata(_ reference: PackageReference,
259298
callback: @escaping (Result<PackageCollectionsModel.PackageMetadata, Error>) -> Void) {
299+
guard Self.isSupportedPlatform else {
300+
return callback(.failure(PackageCollectionError.unsupportedPlatform))
301+
}
302+
260303
// first find in storage
261304
self.findPackage(identifier: reference.identity) { result in
262305
switch result {
@@ -288,10 +331,12 @@ public struct PackageCollections: PackageCollectionsProtocol {
288331

289332
// MARK: - Targets
290333

291-
public func listTargets(
292-
collections: Set<PackageCollectionsModel.CollectionIdentifier>? = nil,
293-
callback: @escaping (Result<PackageCollectionsModel.TargetListResult, Error>) -> Void
294-
) {
334+
public func listTargets(collections: Set<PackageCollectionsModel.CollectionIdentifier>? = nil,
335+
callback: @escaping (Result<PackageCollectionsModel.TargetListResult, Error>) -> Void) {
336+
guard Self.isSupportedPlatform else {
337+
return callback(.failure(PackageCollectionError.unsupportedPlatform))
338+
}
339+
295340
self.listCollections(identifiers: collections) { result in
296341
switch result {
297342
case .failure(let error):
@@ -303,12 +348,14 @@ public struct PackageCollections: PackageCollectionsProtocol {
303348
}
304349
}
305350

306-
public func findTargets(
307-
_ query: String,
308-
searchType: PackageCollectionsModel.TargetSearchType? = nil,
309-
collections: Set<PackageCollectionsModel.CollectionIdentifier>? = nil,
310-
callback: @escaping (Result<PackageCollectionsModel.TargetSearchResult, Error>) -> Void
311-
) {
351+
public func findTargets(_ query: String,
352+
searchType: PackageCollectionsModel.TargetSearchType? = nil,
353+
collections: Set<PackageCollectionsModel.CollectionIdentifier>? = nil,
354+
callback: @escaping (Result<PackageCollectionsModel.TargetSearchResult, Error>) -> Void) {
355+
guard Self.isSupportedPlatform else {
356+
return callback(.failure(PackageCollectionError.unsupportedPlatform))
357+
}
358+
312359
let searchType = searchType ?? .exactMatch
313360

314361
self.storage.sources.list { result in
@@ -393,10 +440,8 @@ public struct PackageCollections: PackageCollectionsProtocol {
393440
}
394441
}
395442

396-
func findPackage(
397-
identifier: PackageIdentity,
398-
callback: @escaping (Result<PackageCollectionsModel.PackageSearchResult.Item, Error>) -> Void
399-
) {
443+
func findPackage(identifier: PackageIdentity,
444+
callback: @escaping (Result<PackageCollectionsModel.PackageSearchResult.Item, Error>) -> Void) {
400445
self.storage.sources.list { result in
401446
switch result {
402447
case .failure(let error):

Sources/PackageCollections/Providers/JSONPackageCollectionProvider.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ struct JSONPackageCollectionProvider: PackageCollectionProvider {
2828
// FIXME: remove
2929
static let enableSignatureCheck = ProcessInfo.processInfo.environment["ENABLE_COLLECTION_SIGNATURE_CHECK"] != nil
3030

31+
// TODO: This can be removed when the `Security` framework APIs that the `PackageCollectionsSigning`
32+
// module depends on are available on all Apple platforms.
33+
#if os(macOS) || os(Linux) || os(Windows)
34+
static let isSignatureCheckSupported = true
35+
#else
36+
static let isSignatureCheckSupported = false
37+
#endif
38+
3139
private let configuration: Configuration
3240
private let diagnosticsEngine: DiagnosticsEngine
3341
private let httpClient: HTTPClient
@@ -136,6 +144,10 @@ struct JSONPackageCollectionProvider: PackageCollectionProvider {
136144
// Don't validate signature; set isVerified=false
137145
callback(self.makeCollection(from: signedCollection.collection, source: source, signature: Model.SignatureData(from: signedCollection.signature, isVerified: false)))
138146
} else {
147+
if !Self.isSignatureCheckSupported {
148+
fatalError("Unsupported platform")
149+
}
150+
139151
// Check the signature
140152
self.signatureValidator.validate(signedCollection: signedCollection, certPolicyKey: certPolicyKey) { result in
141153
switch result {

Sources/PackageCollectionsSigning/Certificate/Certificate.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,19 @@
1010

1111
import struct Foundation.Data
1212

13-
#if canImport(Security)
13+
#if os(macOS)
1414
import Security
1515
#endif
1616

17-
#if canImport(Security)
17+
#if os(macOS)
1818
typealias Certificate = CoreCertificate
1919
#else
2020
typealias Certificate = BoringSSLCertificate
2121
#endif
2222

2323
// MARK: - Certificate implementation using the Security framework
2424

25-
#if canImport(Security)
25+
#if os(macOS)
2626
struct CoreCertificate {
2727
let underlying: SecCertificate
2828

Sources/PackageCollectionsSigning/Certificate/CertificatePolicy.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import struct Foundation.URL
1616

1717
import TSCBasic
1818

19-
#if canImport(Security)
19+
#if os(macOS)
2020
import Security
2121
#endif
2222

@@ -53,7 +53,7 @@ extension CertificatePolicy {
5353
return wrappedCallback(.failure(CertificatePolicyError.emptyCertChain))
5454
}
5555

56-
#if canImport(Security)
56+
#if os(macOS)
5757
let policy = SecPolicyCreateBasicX509()
5858
let revocationPolicy = SecPolicyCreateRevocation(kSecRevocationOCSPMethod)
5959

@@ -99,7 +99,7 @@ extension CertificatePolicy {
9999

100100
extension CertificatePolicy {
101101
func hasExtension(oid: String, in certificate: Certificate) throws -> Bool {
102-
#if canImport(Security)
102+
#if os(macOS)
103103
guard let dict = SecCertificateCopyValues(certificate.underlying, [oid as CFString] as CFArray, nil) as? [CFString: Any] else {
104104
throw CertificatePolicyError.extensionFailure
105105
}
@@ -110,7 +110,7 @@ extension CertificatePolicy {
110110
}
111111

112112
func hasExtendedKeyUsage(_ usage: CertificateExtendedKeyUsage, in certificate: Certificate) throws -> Bool {
113-
#if canImport(Security)
113+
#if os(macOS)
114114
guard let dict = SecCertificateCopyValues(certificate.underlying, [kSecOIDExtendedKeyUsage] as CFArray, nil) as? [CFString: Any] else {
115115
throw CertificatePolicyError.extensionFailure
116116
}
@@ -127,7 +127,7 @@ extension CertificatePolicy {
127127
/// Checks that the certificate supports OCSP. This **must** be done before calling `verify` to ensure
128128
/// the necessary properties are in place to trigger revocation check.
129129
func supportsOCSP(certificate: Certificate) throws -> Bool {
130-
#if canImport(Security)
130+
#if os(macOS)
131131
// Check that certificate has "Certificate Authority Information Access" extension and includes OCSP as access method.
132132
// The actual revocation check will be done by the Security framework in `verify`.
133133
guard let dict = SecCertificateCopyValues(certificate.underlying, [kSecOIDAuthorityInfoAccess] as CFArray, nil) as? [CFString: Any] else { // ignore error
@@ -147,7 +147,7 @@ extension CertificatePolicy {
147147
enum CertificateExtendedKeyUsage {
148148
case codeSigning
149149

150-
#if canImport(Security)
150+
#if os(macOS)
151151
var data: Data {
152152
switch self {
153153
case .codeSigning:

Sources/PackageCollectionsSigning/Key/Key+EC.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ struct ECPrivateKey: PrivateKey {
2121
init<Data>(pem data: Data) throws where Data: DataProtocol {
2222
let pem = String(decoding: data, as: UTF8.self)
2323
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
24-
if #available(macOS 11, *) {
24+
if #available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) {
2525
self.underlying = try CryptoECPrivateKey(pemRepresentation: pem)
2626
} else {
2727
let pemDocument = try ASN1.PEMDocument(pemString: pem)
@@ -45,7 +45,7 @@ struct ECPublicKey: PublicKey {
4545
init<Data>(pem data: Data) throws where Data: DataProtocol {
4646
let pem = String(decoding: data, as: UTF8.self)
4747
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
48-
if #available(macOS 11, *) {
48+
if #available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) {
4949
self.underlying = try CryptoECPublicKey(pemRepresentation: pem)
5050
} else {
5151
let pemDocument = try ASN1.PEMDocument(pemString: pem)

Sources/PackageCollectionsSigning/Key/Key+RSA.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@
1010

1111
import Foundation
1212

13-
#if canImport(Security)
13+
#if os(macOS)
1414
import Security
1515
#endif
1616

17-
#if canImport(Security)
17+
#if os(macOS)
1818
typealias RSAPublicKey = CoreRSAPublicKey
1919
typealias RSAPrivateKey = CoreRSAPrivateKey
2020
#else
@@ -24,7 +24,7 @@ typealias RSAPrivateKey = BoringSSLRSAPrivateKey
2424

2525
// MARK: - RSA key implementations using the Security framework
2626

27-
#if canImport(Security)
27+
#if os(macOS)
2828
struct CoreRSAPrivateKey: PrivateKey {
2929
let underlying: SecKey
3030

Sources/PackageCollectionsSigning/Signing/Signing+RSAKey.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@
1010

1111
import struct Foundation.Data
1212

13-
#if canImport(Security)
13+
#if os(macOS)
1414
import Security
1515
#endif
1616

1717
// MARK: - MessageSigner and MessageValidator conformance using the Security framework
1818

19-
#if canImport(Security)
19+
#if os(macOS)
2020
extension CoreRSAPrivateKey {
2121
func sign(message: Data) throws -> Data {
2222
var error: Unmanaged<CFError>?

0 commit comments

Comments
 (0)