macos-notarization-workflow — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited macos-notarization-workflow (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Automate Apple notarization workflow using xcrun notarytool for macOS applications. This skill handles the complete notarization process including submission, status checking, and stapling.
{
"type": "object",
"properties": {
"projectPath": {
"type": "string",
"description": "Path to the project"
},
"appPath": {
"type": "string",
"description": "Path to the signed app bundle or DMG"
},
"authMethod": {
"enum": ["app-store-connect-api", "apple-id", "keychain"],
"default": "app-store-connect-api"
},
"credentials": {
"type": "object",
"properties": {
"keyId": { "type": "string" },
"issuerId": { "type": "string" },
"keyPath": { "type": "string" },
"appleId": { "type": "string" },
"teamId": { "type": "string" }
}
},
"waitForCompletion": {
"type": "boolean",
"default": true
},
"staple": {
"type": "boolean",
"default": true
}
},
"required": ["projectPath", "appPath"]
}{
"type": "object",
"properties": {
"success": { "type": "boolean" },
"submissionId": { "type": "string" },
"status": { "enum": ["Accepted", "Invalid", "In Progress", "Rejected"] },
"logUrl": { "type": "string" },
"errors": { "type": "array" },
"stapled": { "type": "boolean" }
},
"required": ["success"]
}# Ensure Xcode command line tools are installed
xcode-select --install
# Verify code signing
codesign --verify --deep --strict MyApp.app
codesign -vvv --deep --strict MyApp.app
# Check hardened runtime
codesign -dvvv MyApp.app | grep runtime
# Should show: flags=0x10000(runtime)# Store App Store Connect API key in keychain
xcrun notarytool store-credentials "MyProfile" \
--key ~/private_keys/AuthKey_XXXXXXXXXX.p8 \
--key-id XXXXXXXXXX \
--issuer xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
# Or store Apple ID credentials
xcrun notarytool store-credentials "MyAppleIDProfile" \
--apple-id [email protected] \
--team-id XXXXXXXXXX \
--password @keychain:AC_PASSWORD# Using stored credentials
xcrun notarytool submit MyApp.app \
--keychain-profile "MyProfile" \
--wait
# Using API key directly
xcrun notarytool submit MyApp.app \
--key ~/private_keys/AuthKey_XXXXXXXXXX.p8 \
--key-id XXXXXXXXXX \
--issuer xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx \
--wait
# Using Apple ID
xcrun notarytool submit MyApp.app \
--apple-id [email protected] \
--team-id XXXXXXXXXX \
--password @keychain:AC_PASSWORD \
--wait# Check specific submission
xcrun notarytool info <submission-id> \
--keychain-profile "MyProfile"
# Get submission log
xcrun notarytool log <submission-id> \
--keychain-profile "MyProfile" \
developer_log.json
# List recent submissions
xcrun notarytool history \
--keychain-profile "MyProfile"# Staple to app bundle
xcrun stapler staple MyApp.app
# Staple to DMG
xcrun stapler staple MyApp.dmg
# Staple to pkg
xcrun stapler staple MyApp.pkg
# Validate stapling
xcrun stapler validate MyApp.app#!/bin/bash
# notarize.sh
set -e
APP_PATH="${1}"
KEYCHAIN_PROFILE="${2:-MyProfile}"
echo "=== Validating app bundle ==="
codesign --verify --deep --strict "$APP_PATH"
echo "=== Submitting for notarization ==="
SUBMISSION_OUTPUT=$(xcrun notarytool submit "$APP_PATH" \
--keychain-profile "$KEYCHAIN_PROFILE" \
--wait \
--output-format json)
SUBMISSION_ID=$(echo "$SUBMISSION_OUTPUT" | jq -r '.id')
STATUS=$(echo "$SUBMISSION_OUTPUT" | jq -r '.status')
echo "Submission ID: $SUBMISSION_ID"
echo "Status: $STATUS"
if [ "$STATUS" != "Accepted" ]; then
echo "=== Notarization failed, fetching log ==="
xcrun notarytool log "$SUBMISSION_ID" \
--keychain-profile "$KEYCHAIN_PROFILE" \
notarization_log.json
cat notarization_log.json
exit 1
fi
echo "=== Stapling ticket ==="
xcrun stapler staple "$APP_PATH"
echo "=== Validating staple ==="
xcrun stapler validate "$APP_PATH"
echo "=== Notarization complete ==="name: Build and Notarize
on:
push:
tags: ['v*']
jobs:
build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Import signing certificate
env:
CERTIFICATE_BASE64: ${{ secrets.MACOS_CERTIFICATE }}
CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PWD }}
run: |
CERTIFICATE_PATH=$RUNNER_TEMP/certificate.p12
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
KEYCHAIN_PASSWORD=$(openssl rand -base64 32)
echo -n "$CERTIFICATE_BASE64" | base64 --decode > $CERTIFICATE_PATH
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security import $CERTIFICATE_PATH -P "$CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH
- name: Build app
run: |
xcodebuild -project MyApp.xcodeproj \
-scheme MyApp \
-configuration Release \
-archivePath build/MyApp.xcarchive \
archive
xcodebuild -exportArchive \
-archivePath build/MyApp.xcarchive \
-exportOptionsPlist ExportOptions.plist \
-exportPath build/
- name: Store notarization credentials
env:
API_KEY: ${{ secrets.NOTARIZATION_API_KEY }}
API_KEY_ID: ${{ secrets.NOTARIZATION_API_KEY_ID }}
API_ISSUER: ${{ secrets.NOTARIZATION_API_ISSUER }}
run: |
mkdir -p ~/private_keys
echo -n "$API_KEY" > ~/private_keys/AuthKey.p8
xcrun notarytool store-credentials "CI_PROFILE" \
--key ~/private_keys/AuthKey.p8 \
--key-id "$API_KEY_ID" \
--issuer "$API_ISSUER"
- name: Notarize app
run: |
xcrun notarytool submit build/MyApp.app \
--keychain-profile "CI_PROFILE" \
--wait
xcrun stapler staple build/MyApp.app
- name: Create DMG
run: |
create-dmg build/MyApp.app build/
xcrun notarytool submit build/*.dmg \
--keychain-profile "CI_PROFILE" \
--wait
xcrun stapler staple build/*.dmg
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: MyApp
path: build/*.dmgError: The signature does not include a secure timestamp.Fix: Sign with hardened runtime and timestamp:
codesign --force --options runtime --timestamp --sign "Developer ID" MyApp.appError: The executable does not have the hardened runtime enabled.Fix: Include entitlements in signing:
codesign --force --options runtime --timestamp \
--entitlements MyApp.entitlements \
--sign "Developer ID Application: Company" MyApp.appError: The signature of the binary is invalid.Fix: Sign all nested components:
find MyApp.app -name "*.dylib" -o -name "*.framework" | \
xargs -I {} codesign --force --options runtime --timestamp --sign "Developer ID" {}macos-entitlements-generator - Entitlements configurationmacos-codesign-workflow - Code signingcode-signing-setup process - Full signing workflowswiftui-macos-expert - macOS developmentcode-signing-specialist - Signing expertise~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.