Back to skill

Security audit

TestFlight

Security checks for vulnerabilities and agentic risk

Overview

The skill is purpose-aligned for TestFlight automation, but its CI guidance handles Apple signing credentials in weak temporary files and an empty-password keychain without cleanup.

Review and harden the CI snippets before use: use isolated ephemeral runners, generate a non-empty temporary keychain password, restrict file permissions, delete temporary credential files and keychains in an always-run cleanup step, and use the least-privileged App Store Connect role that supports your release process.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
ci-cd.md:52
Finding

CI Workflow Materializes Sensitive Signing Credentials and Uses an Empty Keychain Password

Content
View full analysis

Vulnerability Details

File Location: ci-cd.md, lines 52–71
Vulnerability Type: Plaintext sensitive files and weak temporary keychain protection
Risk Level: Medium

yaml
    - name: Install certificates
      env:
        P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
        P12_BASE64: ${{ secrets.P12_BASE64 }}
        PROVISION_BASE64: ${{ secrets.PROVISION_BASE64 }}
      run: |
        # Create keychain
        security create-keychain -p "" build.keychain
        security default-keychain -s build.keychain
        security unlock-keychain -p "" build.keychain
        
        # Import certificate
        echo "$P12_BASE64" | base64 -d > cert.p12
        security import cert.p12 -k build.keychain -P "$P12_PASSWORD" -T /usr/bin/codesign
        security set-key-partition-list -S apple-tool:,apple: -s -k "" build.keychain
        
        # Install provisioning profile
        mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
        echo "$PROVISION_BASE64" | base64 -d > ~/Library/MobileDevice/Provisioning\ Profiles/profile.mobileprovision

    - name: Build and upload
      env:
        APPSTORE_API_KEY: ${{ secrets.APPSTORE_API_KEY }}
      run: |
        echo "$APPSTORE_API_KEY" > api_key.json
        fastlane beta

Technical Analysis

The workflow creates a build keychain with an empty password, makes it the default keychain, and leaves it unlocked. It also decodes the PKCS#12 signing archive into cert.p12 and writes the App Store Connect private key configuration into api_key.json. No restrictive umask or explicit file permissions are applied, and no cleanup step removes the files or keychain after deployment.

CI secret storage protects values before they enter a job, but it does not protect plaintext files after they are created on the runner. Any untrusted build script, compromised Fastlane component, malicious dependency, or subsequent process running ...[truncated 2041 chars]

Remediation
View remediation

Remediation Suggestions

  1. Generate a strong random password for the temporary keychain rather than using an empty password:

    bash
    KEYCHAIN_PASSWORD="$(openssl rand -base64 32)"
    security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
    security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
    security set-key-partition-list -S apple-tool:,apple: -s \
      -k "$KEYCHAIN_PASSWORD" build.keychain
    
  2. Set restrictive permissions before creating temporary credential files:

    bash
    umask 077
    printf '%s' "$P12_BASE64" | base64 -d > cert.p12
    printf '%s' "$APPSTORE_API_KEY" > api_key.json
    chmod 600 cert.p12 api_key.json
    
  3. Add an always-run cleanup step using if: ${{ always() }} to delete temporary files and the keychain even if the build fails:

    yaml
    - name: Remove signing credentials
      if: ${{ always() }}
      run: |
        rm -f cert.p12 api_key.json
        rm -f ~/Library/MobileDevice/Provisioning\ Profiles/profile.mobileprovision
        security delete-keychain build.keychain || true
    
  4. Keep credential files present only for the command that requires them. Prefer Fastlane mechanisms that construct API-key objects from environment-backed CI secrets rather than retaining a repository-relative JSON file.

  5. Use the least-privileged App Store Connect role that supports the required deployment operation, restrict secret access to protected tags and environments, and require deployment approvals where appropriate.

  6. Do not execute pull-request code, untrusted build scripts, or third-party plugins in the credential-bearing upload job. Separate compilation and deployment into isolated jobs, passing only verified build artifacts into the deployment stage.

  7. Pin CI actions and Ruby/Fastlane dependencies to reviewed versions or immutable revisions to reduce the likelihood that compromised dependencies can access deployment credentials.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (8)

Credential Access

High
Category
Privilege Escalation
Confidence
95% confidence
Finding

The guide creates a keychain with an empty password and makes it the default keychain for the job. Although this is a common automation pattern, an unprotected keychain weakens local protection for imported signing credentials if the runner is compromised or reused.

Content

Scanner excerpt · ci-cd.md (reported line 54)May include surrounding context.

md
P12_BASE64: ${{ secrets.P12_BASE64 }}
          PROVISION_BASE64: ${{ secrets.PROVISION_BASE64 }}
        run: |
          # Create keychain
          security create-keychain -p "" build.keychain
          security default-keychain -s build.keychain
          security unlock-keychain -p "" build.keychain

Credential Access

High
Category
Privilege Escalation
Confidence
94% confidence
Finding

Setting the temporary build keychain as the default keychain can broaden exposure of imported credentials to subsequent commands and tools in the job. In a compromised pipeline or on a persistent runner, this increases the chance that signing material is accessed beyond its intended scope.

Content

Scanner excerpt · ci-cd.md (reported line 55)May include surrounding context.

md
PROVISION_BASE64: ${{ secrets.PROVISION_BASE64 }}
        run: |
          # Create keychain
          security create-keychain -p "" build.keychain
          security default-keychain -s build.keychain
          security unlock-keychain -p "" build.keychain

Credential Access

High
Category
Privilege Escalation
Confidence
95% confidence
Finding

Unlocking the keychain with an empty password leaves signing credentials readily accessible during the build process. If malicious code runs in the same CI context, it could potentially read or misuse the imported identities while the keychain is unlocked.

Content

Scanner excerpt · ci-cd.md (reported line 56)May include surrounding context.

md
run: |
          # Create keychain
          security create-keychain -p "" build.keychain
          security default-keychain -s build.keychain
          security unlock-keychain -p "" build.keychain
          
          # Import certificate

Credential Access

High
Category
Privilege Escalation
Confidence
90% confidence
Finding

The workflow decodes a base64-encoded certificate into a plaintext cert.p12 file on disk without cleanup guidance. Temporary credential files on disk can be recovered from persistent runners, debugging sessions, or leftover workspace artifacts.

Content

Scanner excerpt · ci-cd.md (reported line 57)May include surrounding context.

md
# Create keychain
          security create-keychain -p "" build.keychain
          security default-keychain -s build.keychain
          security unlock-keychain -p "" build.keychain
          
          # Import certificate
          echo "$P12_BASE64" | base64 -d > cert.p12

Credential Access

High
Category
Privilege Escalation
Confidence
88% confidence
Finding

Importing the certificate into the temporary keychain is necessary for signing, but in this workflow it occurs in a weakly protected keychain context and without lifecycle cleanup. That makes credential misuse more plausible on shared, self-hosted, or persistent runners.

Content

Scanner excerpt · ci-cd.md (reported line 61)May include surrounding context.

md
# Import certificate
          echo "$P12_BASE64" | base64 -d > cert.p12
          security import cert.p12 -k build.keychain -P "$P12_PASSWORD" -T /usr/bin/codesign
          security set-key-partition-list -S apple-tool:,apple: -s -k "" build.keychain
          
          # Install provisioning profile

Credential Access

High
Category
Privilege Escalation
Confidence
86% confidence
Finding

The partition list command grants tool access to the imported key material in the temporary keychain, which is expected for codesigning but increases access if the runner is not tightly controlled. Combined with the empty-password keychain, this meaningfully lowers resistance to credential misuse within the job environment.

Content

Scanner excerpt · ci-cd.md (reported line 62)May include surrounding context.

md
# Import certificate
          echo "$P12_BASE64" | base64 -d > cert.p12
          security import cert.p12 -k build.keychain -P "$P12_PASSWORD" -T /usr/bin/codesign
          security set-key-partition-list -S apple-tool:,apple: -s -k "" build.keychain
          
          # Install provisioning profile
          mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · ci-cd.md (reported line 191)May include surrounding context.

"Missing compliance information"

Add to Info.plist:

xml
<key>ITSAppUsesNonExemptEncryption</key>

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

The workflow writes sensitive material such as the App Store Connect API key and signing assets to files on the CI runner but does not instruct users to restrict permissions, securely delete them, or clean them up after use. On shared or persistent runners, these temporary files may be exposed to later jobs, debugging artifacts, or accidental logging, creating unnecessary secret leakage risk.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.