swift-security-testing
| Data Category | Storage | Reason | |---------------|---------|--------| | API tokens, OAuth tokens | **Keychain** | Encrypted at rest; protected by device passcode / Secure Enclave | | Passwords, private keys | **Keychain** | Never stored in plaintext | | User preferences
$ npx -y skills add notque/vexjoy-agent --agent claude-codeHow it fires
How this agent gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
Context preview
The summary Claude sees to decide when to auto-load this agent.
| Data Category | Storage | Reason | |---------------|---------|--------| | API tokens, OAuth tokens | **Keychain** | Encrypted at rest; protected by device passcode / Secure Enclave | | Passwords, private keys | **Keychain** | Never stored in plaintext | | User preferences
Agent definition
swift-security-testing.mdSwift Security & Testing
Security
Keychain vs. UserDefaults
| Data Category | Storage | Reason | |---------------|---------|--------| | API tokens, OAuth tokens | **Keychain** | Encrypted at rest; protected by device passcode / Secure Enclave | | Passwords, private keys | **Keychain** | Never stored in plaintext | | User preferences (theme, language) | UserDefaults | Non-sensitive; loss is acceptable | | Feature flags | UserDefaults | Non-sensitive | | JWT refresh tokens | **Keychain** | Credential — same as token | | Device-specific identifiers | UserDefaults or Keychain depending on sensitivity | Evaluate case by case |
**Detection trigger**: Any `UserDefaults` call with a key string containing `token`, `password`, `key`, `secret`, `credential`, or `auth` is a security violation requiring Keychain migration.
// Wrong
UserDefaults.standard.set(apiToken, forKey: "auth_token")
// Correct — Keychain wrapper
struct KeychainStore {
static func save(token: String, service: String, account: String) throws {
let data = Data(token.utf8)
let query: [CFString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: service,
kSecAttrAccount: account,
kSecValueData: data,
kSecAttrAccessible: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
SecItemDelete(query as CFDictionary) // Remove existing item
let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecSuccess else {
throw KeychainError.saveFailed(status)
}
}
}App Transport Security (ATS)
- ATS is enabled by default — keep it enabled
- `NSAllowsArbitraryLoads: true` in Info.plist requires documented justification (e.g., streaming media exemption per Apple documentation)
- Use `NSExceptionDomains` for specific domains that require exceptions; keep ATS bypasses scoped to individual domains
- All production endpoints must use HTTPS with valid certificates
Certificate Pinning
For endpoints handling financial, healthcare, or authentication data, implement certificate or public key pinning via `URLSessionDelegate`.
final class PinningDelegate: NSObject, URLSessionDelegate, @unchecked Sendable {
private let pinnedHashes: Set<String>
init(pinnedHashes: Set<String>) {
self.pinnedHashes = pinnedHashes
}
func urlSession(
_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
) {
guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust,
let serverTrust = challenge.protectionSpace.serverTrust,
let certificate = SecTrustGetCertificateAtIndex(serverTrust, 0) else {
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
let serverCertData = SecCertificateCopyData(certificate) as Data
let hash = serverCertData.sha256HexString // implement SHA-256 helper
if pinnedHashes.contains(hash) {
completionHandler(.useCredential, URLCredential(trust: serverTrust))
} else {
completionHandler(.cancelAuthenticationChallenge, nil)
}
}
}Secret Management
| Source | Rule | |--------|------| | API keys in source files | **Hard boundary** — decompilation extracts them trivially | | API keys in Info.plist | **Hard boundary** — same decompilation risk | | Build-time secrets | Use `.xcconfig` files excluded from version control; read via `Bundle.main.infoDictionary` | | CI/CD secrets | Environment variables injected at build time; keep out of version control | | Runtime secrets | Fetched from server after authentication; stored in Keychain |
Input Validation
Validate all data from external sources before use:
// URL from deep link or pasteboard — never force-unwrap
guard let url = URL(string: rawString), url.scheme == "https" else {
logger.warning("Rejected invalid URL: \(rawString, privacy: .private)")
return
}
// API response data — always decode into typed models, never assume structure
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let response = try decoder.decode(APIResponse.self, from: data)---
Testing
Swift Testing over XCTest for New Code
Use `import Testing` for all new test files. Migrate XCTest suites to Swift Testing only when explicitly requested.
| Feature | Swift Testing | XCTest | |---------|--------------|--------| | Test declaration | `@Test func name()` | `func testName()` | | Assertion | `#expect(condition)` | `XCTAssertTrue(condition)` | | Parameterized tests | `@Test(arguments: [...])` | Manual loop or subclassing | | Expected failure | `@Test(.disabled("reason"))` | `XCTSkip` | | Test tags | `@Test(.tags(.performance))` | None built-in |
import Testing
@testable import MyApp
@Suite("UserRepository")
struct UserRepositoryTests {
let sut: UserRepository
let mockClient: MockHTTPClient
init() {
mockClient = MockHTTPClient()
sut = UserRepository(client: mockClient)
}
@Test("fetch returns decoded user on success")
func fetchSuccess() async throws {
mockClient.stubbedData = try JSONEncoder().encode(User.fixture)
let user = try await sut.fetchUser(id: User.fixture.id)
#expect(user.id == User.fixture.id)
#expect(user.displayName == User.fixture.displayName)
}
@Test("fetch throws on network failure", arguments: [
URLError(.notConnectedToInternet),
URLError(.timedOut)
])
func fetchNetworkFailure(error: URLError) async {
mockClient.errorToThrow = error
await #expect(throws: FetchError.self) {
try await sut.fetchUser(id: UUID())
}
}
}Fresh-Instance Isolation
- Instantiate the sys
Read more
Swift Security & Testing
Security
Keychain vs. UserDefaults
| Data Category | Storage | Reason | |---------------|---------|--------| | API tokens, OAuth tokens | **Keychain** | Encrypted at rest; protected by device passcode / Secure Enclave | | Passwords, private keys | **Keychain** | Never stored in plaintext | | User preferences (theme, language) | UserDefaults | Non-sensitive; loss is acceptable | | Feature flags | UserDefaults | Non-sensitive | | JWT refresh tokens | **Keychain** | Credential — same as token | | Device-specific identifiers | UserDefaults or Keychain depending on sensitivity | Evaluate case by case |
**Detection trigger**: Any `UserDefaults` call with a key string containing `token`, `password`, `key`, `secret`, `credential`, or `auth` is a security violation requiring Keychain migration.
// Wrong
UserDefaults.standard.set(apiToken, forKey: "auth_token")
// Correct — Keychain wrapper
struct KeychainStore {
static func save(token: String, service: String, account: String) throws {
let data = Data(token.utf8)
let query: [CFString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: service,
kSecAttrAccount: account,
kSecValueData: data,
kSecAttrAccessible: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
SecItemDelete(query as CFDictionary) // Remove existing item
let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecSuccess else {
throw KeychainError.saveFailed(status)
}
}
}App Transport Security (ATS)
- ATS is enabled by default — keep it enabled
- `NSAllowsArbitraryLoads: true` in Info.plist requires documented justification (e.g., streaming media exemption per Apple documentation)
- Use `NSExceptionDomains` for specific domains that require exceptions; keep ATS bypasses scoped to individual domains
- All production endpoints must use HTTPS with valid certificates
Certificate Pinning
For endpoints handling financial, healthcare, or authentication data, implement certificate or public key pinning via `URLSessionDelegate`.
final class PinningDelegate: NSObject, URLSessionDelegate, @unchecked Sendable {
private let pinnedHashes: Set<String>
init(pinnedHashes: Set<String>) {
self.pinnedHashes = pinnedHashes
}
func urlSession(
_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
) {
guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust,
let serverTrust = challenge.protectionSpace.serverTrust,
let certificate = SecTrustGetCertificateAtIndex(serverTrust, 0) else {
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
let serverCertData = SecCertificateCopyData(certificate) as Data
let hash = serverCertData.sha256HexString // implement SHA-256 helper
if pinnedHashes.contains(hash) {
completionHandler(.useCredential, URLCredential(trust: serverTrust))
} else {
completionHandler(.cancelAuthenticationChallenge, nil)
}
}
}Secret Management
| Source | Rule | |--------|------| | API keys in source files | **Hard boundary** — decompilation extracts them trivially | | API keys in Info.plist | **Hard boundary** — same decompilation risk | | Build-time secrets | Use `.xcconfig` files excluded from version control; read via `Bundle.main.infoDictionary` | | CI/CD secrets | Environment variables injected at build time; keep out of version control | | Runtime secrets | Fetched from server after authentication; stored in Keychain |
Input Validation
Validate all data from external sources before use:
// URL from deep link or pasteboard — never force-unwrap
guard let url = URL(string: rawString), url.scheme == "https" else {
logger.warning("Rejected invalid URL: \(rawString, privacy: .private)")
return
}
// API response data — always decode into typed models, never assume structure
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let response = try decoder.decode(APIResponse.self, from: data)---
Testing
Swift Testing over XCTest for New Code
Use `import Testing` for all new test files. Migrate XCTest suites to Swift Testing only when explicitly requested.
| Feature | Swift Testing | XCTest | |---------|--------------|--------| | Test declaration | `@Test func name()` | `func testName()` | | Assertion | `#expect(condition)` | `XCTAssertTrue(condition)` | | Parameterized tests | `@Test(arguments: [...])` | Manual loop or subclassing | | Expected failure | `@Test(.disabled("reason"))` | `XCTSkip` | | Test tags | `@Test(.tags(.performance))` | None built-in |
import Testing
@testable import MyApp
@Suite("UserRepository")
struct UserRepositoryTests {
let sut: UserRepository
let mockClient: MockHTTPClient
init() {
mockClient = MockHTTPClient()
sut = UserRepository(client: mockClient)
}
@Test("fetch returns decoded user on success")
func fetchSuccess() async throws {
mockClient.stubbedData = try JSONEncoder().encode(User.fixture)
let user = try await sut.fetchUser(id: User.fixture.id)
#expect(user.id == User.fixture.id)
#expect(user.displayName == User.fixture.displayName)
}
@Test("fetch throws on network failure", arguments: [
URLError(.notConnectedToInternet),
URLError(.timedOut)
])
func fetchNetworkFailure(error: URLError) async {
mockClient.errorToThrow = error
await #expect(throws: FetchError.self) {
try await sut.fetchUser(id: UUID())
}
}
}Fresh-Instance Isolation
- Instantiate the sys
Essays and writing behind this toolkit live at vexjoy.com. AI agents skip steps. "Looks correct" replaces running tests. "Trivial change" replaces verification.
Repo: notque/vexjoy-agent
Other agents on vexjoy-agent.
- ansible-automation-engineer
Ansible automation: playbooks, roles, collections, Molecule testing, Vault security.
Open agent - modules
**Scope**: Module selection patterns, builtin vs command/shell decisions, collection modules, and version-specific module changes **Version range**: ansible-core 2.14+ / Ansible Collections (community.general 7.0+) **Generated**: 2026-04-04 — verify against current Ansible
Open agent - testing
**Scope**: Molecule test scenarios, ansible-lint rules, idempotency validation, and check-mode patterns **Version range**: Molecule 6.0+ / ansible-lint 6.0+ / ansible-core 2.14+ **Generated**: 2026-04-04 — verify against current Molecule and ansible-lint documentation
Open agent - base-instructions
Universal operational rules injected by /do at agent dispatch. Domain-specific rules live in each agent's .md file.
Open agent - communication-patterns
**Scope**: Failure modes in agent output style — over-reporting, self-congratulation, verbose narration, and hedging. Covers what to detect and how to fix each. **Version range**: all versions **Generated**: 2026-05-11
Open agent - combat-effects-upgrade
Zero-dependency combat visual upgrades: CSS particle replacement, Framer Motion combat juice, CSS 3D card transforms.
Open agent

