agentsclimarketplace

Swift security

Skill Mattakushi432/Claude-Code-Skills-Custom-DevTools-Pack/plugins/devtools-pack/skills/swift-security

When to activate: Keychain, certificate pinning, encryption, biometrics, secure storage, App Transport Security in SwiftFrom its SKILL.md

Install
npx -y skills add Mattakushi432/Claude-Code-Skills-Custom-DevTools-Pack --skill swift-security

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

5.7 KB, ~1.2k tokens by cl100k_base, as published. Nobody here has run it

Swift Security Patterns

Keychain Storage

Never store secrets in UserDefaults. Use the Keychain.

import Security

struct KeychainManager {
    static func save(_ value: String, forKey key: String, service: String) throws {
        let data = Data(value.utf8)
        let query: [String: Any] = [
            kSecClass as String:       kSecClassGenericPassword,
            kSecAttrService as String: service,
            kSecAttrAccount as String: key,
            kSecValueData as String:   data,
            // Store in Secure Enclave-backed class when available
            kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
        ]
        SecItemDelete(query as CFDictionary)  // delete existing before adding
        let status = SecItemAdd(query as CFDictionary, nil)
        guard status == errSecSuccess else { throw KeychainError.saveFailed(status) }
    }

    static func load(forKey key: String, service: String) throws -> String {
        let query: [String: Any] = [
            kSecClass as String:            kSecClassGenericPassword,
            kSecAttrService as String:      service,
            kSecAttrAccount as String:      key,
            kSecReturnData as String:       true,
            kSecMatchLimit as String:       kSecMatchLimitOne,
        ]
        var result: AnyObject?
        let status = SecItemCopyMatching(query as CFDictionary, &result)
        guard status == errSecSuccess, let data = result as? Data,
              let string = String(data: data, encoding: .utf8) else {
            throw KeychainError.loadFailed(status)
        }
        return string
    }
}

enum KeychainError: Error {
    case saveFailed(OSStatus)
    case loadFailed(OSStatus)
}

Biometric Authentication

import LocalAuthentication

func authenticateWithBiometrics() async throws -> Bool {
    let context = LAContext()
    var error: NSError?
    guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
        throw error ?? LAError(.biometryNotAvailable)
    }

    return try await context.evaluatePolicy(
        .deviceOwnerAuthenticationWithBiometrics,
        localizedReason: "Confirm your identity to access sensitive data"
    )
}

Certificate Pinning

final class PinnedURLSessionDelegate: NSObject, URLSessionDelegate {
    private let pinnedPublicKeyHashes: Set<String>

    init(pinnedHashes: Set<String>) {
        self.pinnedPublicKeyHashes = pinnedHashes
    }

    func urlSession(
        _ session: URLSession,
        didReceive challenge: URLAuthenticationChallenge,
        completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
    ) {
        guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust,
              let serverTrust = challenge.protectionSpace.serverTrust else {
            completionHandler(.cancelAuthenticationChallenge, nil)
            return
        }

        // Evaluate the server certificate chain
        var secResult = SecTrustResultType.invalid
        SecTrustGetTrustResult(serverTrust, &secResult)

        if let publicKey = SecTrustCopyKey(serverTrust),
           let keyData = SecKeyCopyExternalRepresentation(publicKey, nil) as Data? {
            let hash = SHA256.hash(data: keyData).compactMap { String(format: "%02x", $0) }.joined()
            if pinnedPublicKeyHashes.contains(hash) {
                completionHandler(.useCredential, URLCredential(trust: serverTrust))
                return
            }
        }
        completionHandler(.cancelAuthenticationChallenge, nil)
    }
}

AES-GCM Encryption

import CryptoKit

struct Encryptor {
    static func encrypt(_ data: Data, key: SymmetricKey) throws -> Data {
        let sealed = try AES.GCM.seal(data, using: key)
        return sealed.combined!
    }

    static func decrypt(_ encryptedData: Data, key: SymmetricKey) throws -> Data {
        let box = try AES.GCM.SealedBox(combined: encryptedData)
        return try AES.GCM.open(box, using: key)
    }

    static func generateKey() -> SymmetricKey {
        SymmetricKey(size: .bits256)
    }
}

App Transport Security

<!-- Info.plist — do NOT disable ATS globally -->
<!-- If a third-party domain requires HTTP, be specific -->
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSExceptionDomains</key>
    <dict>
        <key>legacy-api.example.com</key>
        <dict>
            <key>NSExceptionAllowsInsecureHTTPLoads</key>
            <true/>
            <key>NSIncludesSubdomains</key>
            <false/>
        </dict>
    </dict>
</dict>

Preventing Screen Capture of Sensitive Fields

// SwiftUI — redact content from screenshots and screen recording
TextField("Card Number", text: $cardNumber)
    .privacySensitive()

// UIKit
let field = UITextField()
field.isSecureTextEntry = true  // also hides keyboard in screenshots

Common Anti-Patterns

  • UserDefaults for tokens — always use Keychain
  • Hardcoded API keys in source — load from config or secure backend
  • Disabled ATS globally — Apple may reject; use targeted exceptions
  • MD5/SHA1 for hashing — use SHA-256 or CryptoKit primitives
  • No certificate pinning for sensitive APIs — at minimum pin the leaf certificate
  • Logging sensitive data — never log tokens, passwords, or PII; redact in crash reporters

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 326,851. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.