Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .claude/skills/release/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ the target version with the user before publishing** — publishing is hard to u
## Preconditions (verify first)

- Clean working tree on an up-to-date `main` (releases ship from `main`).
- Notary profile `blurt-notary` exists (`xcrun notarytool store-credentials`).
- Notary profile `blurt-notary` is stored **in the dedicated signing keychain**
(`xcrun notarytool store-credentials blurt-notary --keychain ~/Library/Keychains/blurt-signing.keychain-db …`).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid showing --password on the command line; recommend using interactive prompt or keychain-stored credential instead (e.g., omit --password or use environment/keychain).

Details

✨ AI Reasoning
​The updated preconditions now instruct users to run an xcrun notarytool store-credentials command that includes --password as a command-line argument. Command-line arguments are visible to other local users via process listings and are often recorded in shell history, which can leak credentials. This is a security issue introduced by the new guidance and is relevant for a skill whose purpose is releasing/notarizing software. The problem affects user security and deviates from best practices for handling secrets. The problematic text was added in the preconditions explaining how to store the notary profile and giving an explicit example command containing --password on the command line.

🔧 How do I fix it?
Ensure skill actions match the description. Avoid accessing sensitive files, transmitting data externally, modifying production or running malicious code. Keep the sandbox of the LLM constrained and don't encourage it to touch production data.

Reply @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info

The build pins every notary call to that keychain, so a profile stored only in
login (or an incidental keychain like electron-builder's) will not be found.
- Developer ID signing identity present in the keychain.

## Steps
Expand Down
35 changes: 27 additions & 8 deletions scripts/release-build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@ source "$REPO_ROOT/scripts/release-lib.sh"
SIGNING_KEYCHAIN="${BLURT_SIGNING_KEYCHAIN:-$HOME/Library/Keychains/blurt-signing.keychain-db}"
SIGNING_KEYCHAIN_UNLOCKED=0

# notarytool --keychain args, populated once the signing keychain is unlocked so
# every notary call resolves the profile from THAT keychain rather than from
# whatever the search list happens to surface. Without this, a stray duplicate
# blurt-notary profile in another keychain (e.g. one electron-builder created)
# can shadow the intended one, and which profile wins depends on search-list
# order and lock state — non-deterministic. Empty on machines with no dedicated
# signing keychain, where we fall back to the search list.
# shellcheck disable=SC2034 # expanded via ${NOTARY_KEYCHAIN[@]+...} below
NOTARY_KEYCHAIN=()
Comment on lines +52 to +53

# Resolve the keychain password, in order:
# 1. BLURT_SIGNING_KEYCHAIN_PASSWORD — env (a CI secret, or `op read` inline).
# 2. BLURT_SIGNING_KEYCHAIN_OP — an `op://…` 1Password secret reference; the
Expand Down Expand Up @@ -116,13 +126,15 @@ notarize() {
local log_json="$BUILD_ROOT/notary-$tag-log.json"
xcrun notarytool submit "$artifact" \
--keychain-profile "$NOTARY_PROFILE" \
${NOTARY_KEYCHAIN[@]+"${NOTARY_KEYCHAIN[@]}"} \
--wait \
--output-format plist > "$result_plist"
Comment on lines 127 to 131
local status id
status="$(/usr/libexec/PlistBuddy -c 'Print :status' "$result_plist" 2>/dev/null || echo unknown)"
id="$(/usr/libexec/PlistBuddy -c 'Print :id' "$result_plist" 2>/dev/null || echo unknown)"
info "notary status ($tag): $status (id $id)"
xcrun notarytool log "$id" --keychain-profile "$NOTARY_PROFILE" > "$log_json" 2>&1 || true
xcrun notarytool log "$id" --keychain-profile "$NOTARY_PROFILE" \
${NOTARY_KEYCHAIN[@]+"${NOTARY_KEYCHAIN[@]}"} > "$log_json" 2>&1 || true
if [ "$status" != "Accepted" ]; then
step "Notary log ($tag)"
cat "$log_json" 2>/dev/null || true
Expand Down Expand Up @@ -186,13 +198,20 @@ unlock_signing_keychain
# only other EXIT trap (the DMG mount, below) is set up much later.
trap lock_signing_keychain EXIT

# Check the notary profile only AFTER unlocking the signing keychain: the
# blurt-notary credentials live in that keychain, and notarytool runs
# non-interactively, so against a still-locked keychain it can't read the
# profile item and reports it as missing. Unlock first, then a genuine
# "not found" here really means the profile was never stored.
xcrun notarytool history --keychain-profile "$NOTARY_PROFILE" >/dev/null 2>&1 \
|| die "notarytool profile '$NOTARY_PROFILE' not found. Run: xcrun notarytool store-credentials $NOTARY_PROFILE --apple-id <you@example.com> --team-id $TEAM_ID --password <app-specific-password>"
# Check the notary profile only AFTER unlocking the signing keychain: when a
# dedicated signing keychain exists, the blurt-notary profile is expected to
# live there, and we pin every notary call to it (see NOTARY_KEYCHAIN) so the
# lookup is deterministic rather than at the mercy of the search list.
[ "$SIGNING_KEYCHAIN_UNLOCKED" -eq 1 ] && NOTARY_KEYCHAIN=(--keychain "$SIGNING_KEYCHAIN")

if ! xcrun notarytool history --keychain-profile "$NOTARY_PROFILE" \
${NOTARY_KEYCHAIN[@]+"${NOTARY_KEYCHAIN[@]}"} >/dev/null 2>&1; then
if [ "$SIGNING_KEYCHAIN_UNLOCKED" -eq 1 ]; then
die "notarytool profile '$NOTARY_PROFILE' not found in $SIGNING_KEYCHAIN. Store it there: xcrun notarytool store-credentials $NOTARY_PROFILE --keychain $SIGNING_KEYCHAIN --apple-id <you@example.com> --team-id $TEAM_ID --password <app-specific-password>"
else
die "notarytool profile '$NOTARY_PROFILE' not found. Run: xcrun notarytool store-credentials $NOTARY_PROFILE --apple-id <you@example.com> --team-id $TEAM_ID --password <app-specific-password>"
fi
fi

identity_listed "$IDENTITY" <<<"$(security find-identity -v -p codesigning)" \
|| die "Developer ID identity $IDENTITY not in keychain (check: security find-identity -v -p codesigning). Wrong Mac, or the signing key is missing."
Expand Down